Create jlmkr shell aliases
This commit is contained in:
parent
f9730d3a32
commit
d7b30011b0
16
README.md
16
README.md
|
@ -10,7 +10,6 @@ Persistent Linux 'jails' on TrueNAS SCALE to install software (docker-compose, p
|
||||||
|
|
||||||
TrueNAS SCALE can create persistent Linux 'jails' with systemd-nspawn. This script helps with the following:
|
TrueNAS SCALE can create persistent Linux 'jails' with systemd-nspawn. This script helps with the following:
|
||||||
|
|
||||||
- Installing the systemd-container package (which includes systemd-nspawn)
|
|
||||||
- Setting up the jail so it won't be lost when you update SCALE
|
- Setting up the jail so it won't be lost when you update SCALE
|
||||||
- Choosing a distro (Debian 12 strongly recommended, but Ubuntu, Arch Linux or Rocky Linux seem good choices too)
|
- Choosing a distro (Debian 12 strongly recommended, but Ubuntu, Arch Linux or Rocky Linux seem good choices too)
|
||||||
- Optional: configuring the jail so you can run Docker inside it
|
- Optional: configuring the jail so you can run Docker inside it
|
||||||
|
@ -23,7 +22,7 @@ Despite what the word 'jail' implies, jailmaker's intended use case is to create
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Create a new dataset called `jailmaker` with the default settings (from TrueNAS web interface). Then login as the root user and download `jlmkr.py`.
|
[Installation steps with screenshots](https://www.truenas.com/docs/scale/scaletutorials/apps/sandboxes/) are provided on the TrueNAS website. Start by creating a new dataset called `jailmaker` with the default settings (from TrueNAS web interface). Then login as the root user and download `jlmkr.py`.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cd /mnt/mypool/jailmaker
|
cd /mnt/mypool/jailmaker
|
||||||
|
@ -32,9 +31,9 @@ chmod +x jlmkr.py
|
||||||
./jlmkr.py install
|
./jlmkr.py install
|
||||||
```
|
```
|
||||||
|
|
||||||
The `jlmkr.py` script (and the jails + config it creates) are now stored on the `jailmaker` dataset and will survive updates of TrueNAS SCALE. Additionally a symlink has been created so you can call `jlmkr` from anywhere.
|
The `jlmkr.py` script (and the jails + config it creates) are now stored on the `jailmaker` dataset and will survive updates of TrueNAS SCALE. Additionally a symlink has been created (if the boot pool is not readonly) so you can call `jlmkr` from anywhere.
|
||||||
|
|
||||||
After an update of TrueNAS SCALE the symlink will be lost and `systemd-nspawn` (the core package which makes `jailmaker` work) may be gone too. Not to worry, just run `./jlmkr.py install` again or use [the `./jlmkr.py startup` command](#startup-jails-on-boot).
|
After an update of TrueNAS SCALE the symlink will be lost (but the shell aliases will remain). To restore the symlink, just run `./jlmkr.py install` again or use [the `./jlmkr.py startup` command](#startup-jails-on-boot).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -51,15 +50,12 @@ After answering a few questions you should have your first jail up and running!
|
||||||
### Startup Jails on Boot
|
### Startup Jails on Boot
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# Best to call startup directly (not through the jlmkr symlink)
|
# Call startup using the absolute path to jlmkr.py
|
||||||
|
# The jlmkr shell alias doesn't work in Init/Shutdown Scripts
|
||||||
/mnt/mypool/jailmaker/jlmkr.py startup
|
/mnt/mypool/jailmaker/jlmkr.py startup
|
||||||
|
|
||||||
# Can be called from the symlink too...
|
|
||||||
# But this may not be available after a TrueNAS SCALE update
|
|
||||||
jlmkr startup
|
|
||||||
```
|
```
|
||||||
|
|
||||||
In order to start jails automatically after TrueNAS boots, run `/mnt/mypool/jailmaker/jlmkr.py startup` as Post Init Script with Type `Command` from the TrueNAS web interface. This will automatically fix the installation of `systemd-nspawn` and setup the `jlmkr` symlink, as well as start all the jails with `startup=1` in the config file. Running the `startup` command Post Init is recommended to keep `jailmaker` working after a TrueNAS SCALE update.
|
In order to start jails automatically after TrueNAS boots, run `/mnt/mypool/jailmaker/jlmkr.py startup` as Post Init Script with Type `Command` from the TrueNAS web interface. This creates the `jlmkr` symlink (if possible), as well as start all the jails with `startup=1` in the config file.
|
||||||
|
|
||||||
### Start Jail
|
### Start Jail
|
||||||
|
|
||||||
|
|
99
jlmkr.py
99
jlmkr.py
|
@ -4,6 +4,7 @@ import argparse
|
||||||
import configparser
|
import configparser
|
||||||
import contextlib
|
import contextlib
|
||||||
import ctypes
|
import ctypes
|
||||||
|
import errno
|
||||||
import glob
|
import glob
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
@ -1073,7 +1074,9 @@ def create_jail(jail_name, distro="debian", release="bookworm"):
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
config += f"\n\nsystemd_nspawn_user_args={systemd_nspawn_user_args_multiline}\n\n"
|
config += (
|
||||||
|
f"\n\nsystemd_nspawn_user_args={systemd_nspawn_user_args_multiline}\n\n"
|
||||||
|
)
|
||||||
|
|
||||||
config += cleandoc(
|
config += cleandoc(
|
||||||
"""
|
"""
|
||||||
|
@ -1314,6 +1317,58 @@ def list_jails():
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def replace_or_add_string(file_path, regex, replacement_string):
|
||||||
|
"""
|
||||||
|
Replace all occurrences of a regular expression in a file with a given string.
|
||||||
|
Add the string to the end of the file if regex doesn't match.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path (str): The path to the file.
|
||||||
|
regex (str): The regular expression to search for.
|
||||||
|
replacement_string (str): The string to replace the matches with.
|
||||||
|
"""
|
||||||
|
|
||||||
|
with open(file_path, "a+") as f:
|
||||||
|
f.seek(0)
|
||||||
|
|
||||||
|
updated = False
|
||||||
|
found = False
|
||||||
|
new_text = ""
|
||||||
|
replacement_line = f"{replacement_string}\n"
|
||||||
|
|
||||||
|
for line in f:
|
||||||
|
if not re.match(regex, line):
|
||||||
|
new_text += line
|
||||||
|
continue
|
||||||
|
|
||||||
|
found = True
|
||||||
|
new_text += replacement_line
|
||||||
|
|
||||||
|
if replacement_line != line:
|
||||||
|
updated = True
|
||||||
|
|
||||||
|
if not new_text.strip():
|
||||||
|
# In case of an empty file just write the replacement_string
|
||||||
|
new_text = replacement_line
|
||||||
|
updated = True
|
||||||
|
elif not found:
|
||||||
|
# Add a newline to the end of the file in case it's not there
|
||||||
|
if not new_text.endswith("\n"):
|
||||||
|
new_text += "\n"
|
||||||
|
# Then add our replacement_string to the end of the file
|
||||||
|
new_text += replacement_line
|
||||||
|
updated = True
|
||||||
|
|
||||||
|
# Only overwrite in case there are change to the file
|
||||||
|
if updated:
|
||||||
|
f.seek(0)
|
||||||
|
f.truncate()
|
||||||
|
f.write(new_text)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def install_jailmaker():
|
def install_jailmaker():
|
||||||
# Check if command exists in path
|
# Check if command exists in path
|
||||||
if shutil.which("systemd-nspawn"):
|
if shutil.which("systemd-nspawn"):
|
||||||
|
@ -1341,19 +1396,41 @@ def install_jailmaker():
|
||||||
for file, original_permission in original_permissions.items():
|
for file, original_permission in original_permissions.items():
|
||||||
stat_chmod(file, original_permission)
|
stat_chmod(file, original_permission)
|
||||||
|
|
||||||
target = f"/usr/local/sbin/{SYMLINK_NAME}"
|
symlink = f"/usr/local/sbin/{SYMLINK_NAME}"
|
||||||
|
|
||||||
# Check if command exists in path
|
if os.path.lexists(symlink) and not os.path.islink(symlink):
|
||||||
if shutil.which(SYMLINK_NAME):
|
|
||||||
print(f"The {SYMLINK_NAME} command is available.")
|
|
||||||
elif not os.path.lexists(target):
|
|
||||||
print(f"Creating symlink {target} to {SCRIPT_PATH}.")
|
|
||||||
os.symlink(SCRIPT_PATH, target)
|
|
||||||
else:
|
|
||||||
print(
|
print(
|
||||||
f"File {target} already exists... Maybe it's a broken symlink from a previous install attempt?"
|
f"Unable to create symlink at {symlink}. File already exists but is not a symlink."
|
||||||
)
|
)
|
||||||
print(f"Skipped creating new symlink {target} to {SCRIPT_PATH}.")
|
# Check if the symlink is already pointing to the desired destination
|
||||||
|
elif os.path.realpath(symlink) != SCRIPT_PATH:
|
||||||
|
try:
|
||||||
|
Path(symlink).unlink(missing_ok=True)
|
||||||
|
os.symlink(SCRIPT_PATH, symlink)
|
||||||
|
print(f"Created symlink {symlink} to {SCRIPT_PATH}.")
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.EROFS:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Cannot create symlink because {symlink} is on a readonly filesystem."
|
||||||
|
)
|
||||||
|
|
||||||
|
alias = f"alias jlmkr={shlex.quote(SCRIPT_PATH)} # managed by jailmaker"
|
||||||
|
alias_regex = re.compile(r"^\s*alias jlmkr=.*# managed by jailmaker\s*")
|
||||||
|
shell_env = os.getenv("SHELL")
|
||||||
|
|
||||||
|
for shell_type in ["bash", "zsh"]:
|
||||||
|
file = "/root/.bashrc" if shell_type == "bash" else "/root/.zshrc"
|
||||||
|
|
||||||
|
if replace_or_add_string(file, alias_regex, alias):
|
||||||
|
print(f"Created {shell_type} alias {SYMLINK_NAME}.")
|
||||||
|
if shell_env.endswith(shell_type):
|
||||||
|
print(
|
||||||
|
f"Please source {file} manually for the {SYMLINK_NAME} alias to become effective immediately."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(f"The {shell_type} alias {SYMLINK_NAME} is already present.")
|
||||||
|
|
||||||
print("Done installing jailmaker.")
|
print("Done installing jailmaker.")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue