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:
|
||||
|
||||
- Installing the systemd-container package (which includes systemd-nspawn)
|
||||
- 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)
|
||||
- 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
|
||||
|
||||
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
|
||||
cd /mnt/mypool/jailmaker
|
||||
|
@ -32,9 +31,9 @@ chmod +x jlmkr.py
|
|||
./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
|
||||
|
||||
|
@ -51,15 +50,12 @@ After answering a few questions you should have your first jail up and running!
|
|||
### Startup Jails on Boot
|
||||
|
||||
```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
|
||||
|
||||
# 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
|
||||
|
||||
|
|
99
jlmkr.py
99
jlmkr.py
|
@ -4,6 +4,7 @@ import argparse
|
|||
import configparser
|
||||
import contextlib
|
||||
import ctypes
|
||||
import errno
|
||||
import glob
|
||||
import hashlib
|
||||
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(
|
||||
"""
|
||||
|
@ -1314,6 +1317,58 @@ def list_jails():
|
|||
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():
|
||||
# Check if command exists in path
|
||||
if shutil.which("systemd-nspawn"):
|
||||
|
@ -1341,19 +1396,41 @@ def install_jailmaker():
|
|||
for file, original_permission in original_permissions.items():
|
||||
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 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:
|
||||
if os.path.lexists(symlink) and not os.path.islink(symlink):
|
||||
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.")
|
||||
|
||||
|
|
Loading…
Reference in New Issue