Skip to content

Commit

Permalink
Implement prefix for ZFS dataset tree
Browse files Browse the repository at this point in the history
Instead of using pool/iocage, we can now use anything like
pool/some/prefix/iocage, which enables iocage use within jails. To do
so, use the -p / --prefix option with iocage activate. The prefix is
then stored as a ZFS property of the pool and used for all commands.
A fixture is also added to the tests to enable functional tests.
  • Loading branch information
Defenso-QTH committed Sep 23, 2024
1 parent 700f5d6 commit f454100
Show file tree
Hide file tree
Showing 21 changed files with 264 additions and 114 deletions.
6 changes: 5 additions & 1 deletion .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ iocage_tests_task:
- pkg
install_iocage_script:
- python3 setup.py install
test_script: pytest --zpool=pool tests/functional_tests --junit-xml=reports/pytest-report.xml -rA --image
matrix:
- name: No prefix
test_script: pytest -rA --zpool=pool tests/functional_tests --junit-xml=reports/pytest-report.xml --image
- name: Custom prefix
test_script: pytest -rA --zpool=pool --prefix=test-prefix tests/functional_tests --junit-xml=reports/pytest-report.xml --image
always:
pytest_results_artifacts:
path: reports/*.xml
Expand Down
14 changes: 12 additions & 2 deletions iocage_cli/activate.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,22 @@


@click.command(name="activate", help="Set a zpool active for iocage usage.")
@click.option(
"--prefix", "-p", default='',
help="Provide a prefix for dataset path."
)
@click.argument("zpool")
def cli(zpool):
def cli(zpool, prefix):
"""Calls ZFS set to change the property org.freebsd.ioc:active to yes."""
ioc.IOCage(activate=True).activate(zpool)
ioc.IOCage(activate=True).activate(zpool, prefix)

ioc_common.logit({
"level" : "INFO",
"message": f"ZFS pool '{zpool}' successfully activated."
})

if prefix:
ioc_common.logit({
"level" : "INFO",
"message": f"Dataset prefix '{prefix}' set."
})
6 changes: 3 additions & 3 deletions iocage_cli/migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ def cli(force, delete):
for uuid, path in jails.items():
pool = ioc_json.IOCJson().json_get_value("pool")
iocroot = ioc_json.IOCJson(pool).json_get_value("iocroot")
jail = f"{pool}/iocage/jails/{uuid}"
jail_old = f"{pool}/iocage/jails_old/{uuid}"
jail = f"{iocroot}/jails/{uuid}"
jail_old = f"{iocroot}/jails_old/{uuid}"
conf = ioc_json.IOCJson(path).json_get_value('all')

try:
Expand Down Expand Up @@ -164,7 +164,7 @@ def cli(force, delete):
try:
su.check_call([
"zfs", "destroy", "-r", "-f",
f"{pool}/iocage/jails_old"
f"{iocroot}/jails_old"
])
except su.CalledProcessError:
# We just want the top level dataset gone, no big deal.
Expand Down
12 changes: 8 additions & 4 deletions iocage_lib/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import threading

from iocage_lib.zfs import (
all_properties, dataset_exists, get_all_dependents, get_dependents_with_depth,
all_properties, dataset_exists, get_all_dependents, get_dependents_with_depth, IOCAGE_POOL_PROP,
IOCAGE_PREFIX_PROP,
)


Expand Down Expand Up @@ -43,10 +44,13 @@ def iocage_activated_pool_internal(self, lock=True):
all_properties([p for p in pools], types=['filesystem'])
)
for p in filter(
lambda p: self.dataset_data.get(p, {}).get('org.freebsd.ioc:active') == 'yes',
lambda p: self.dataset_data.get(p, {}).get(IOCAGE_POOL_PROP) == 'yes',
pools
):
self.ioc_pool = p
self.ioc_prefix = self.dataset_data.get(
self.ioc_pool, {}
).get(IOCAGE_PREFIX_PROP, '')
return self.ioc_pool
finally:
if lock:
Expand All @@ -58,7 +62,7 @@ def iocage_activated_dataset(self):
ioc_pool = self.iocage_activated_pool_internal(lock=False)
if ioc_pool:
dependents = self.dependents_internal(ioc_pool, 1, lock=False)
ioc_ds = os.path.join(ioc_pool, 'iocage')
ioc_ds = os.path.join(ioc_pool, self.ioc_prefix, 'iocage')
if not self.ioc_dataset and ioc_pool and ioc_ds in dependents:
self.ioc_dataset = ioc_ds
return self.ioc_dataset
Expand All @@ -70,7 +74,7 @@ def datasets(self):
if not self.dataset_data or set(self.dataset_data) == set(self.pool_data):
ds = ''
if ioc_pool:
ds = os.path.join(ioc_pool, 'iocage')
ds = os.path.join(ioc_pool, self.ioc_prefix, 'iocage')
self.dataset_data.update(all_properties(
[ds] if ds and dataset_exists(ds) else [], recursive=True, types=['filesystem']
))
Expand Down
15 changes: 13 additions & 2 deletions iocage_lib/ioc_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@

from iocage_lib.cache import cache
from iocage_lib.dataset import Dataset
from iocage_lib.zfs import ZFSException
from iocage_lib.pools import Pool
from iocage_lib.zfs import ZFSException, IOCAGE_PREFIX_PROP

DATASET_CREATION_LOCK = threading.Lock()

Expand All @@ -51,6 +52,7 @@ def __init__(
silent=silent,
checking_datasets=True
).json_get_value("pool")
self.zpool = Pool(self.pool)
self.callback = callback
self.silent = silent

Expand All @@ -59,7 +61,8 @@ def __init__(

self.pool_root_dataset = Dataset(self.pool, cache=reset_cache)
self.iocage_dataset = Dataset(
os.path.join(self.pool, 'iocage'), cache=reset_cache
os.path.join(self.zpool.name, self.zpool.prefix, 'iocage'),
cache=reset_cache
)

if migrate:
Expand Down Expand Up @@ -87,6 +90,14 @@ def __check_datasets__(self):
datasets = ("iocage", "iocage/download", "iocage/images",
"iocage/jails", "iocage/log", "iocage/releases",
"iocage/templates")
if self.zpool.prefix != '':
prefix_split = self.zpool.prefix.split('/')
datasets = *(
os.path.join(*prefix_split[:n+1])
for n in range(len(prefix_split))
), *(
os.path.join(self.zpool.prefix, ioc_ds) for ioc_ds in datasets
)

for dataset in datasets:
zfs_dataset_name = f"{self.pool}/{dataset}"
Expand Down
17 changes: 11 additions & 6 deletions iocage_lib/ioc_clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,18 @@
import iocage_lib.ioc_destroy
import iocage_lib.ioc_json
import shutil
import os

from iocage_lib.dataset import Dataset
from iocage_lib.pools import Pool


class IOCClean:
"""Cleans datasets and snapshots of a given type."""

def __init__(self, callback=None, silent=False):
self.pool = iocage_lib.ioc_json.IOCJson().json_get_value('pool')
self.zpool = Pool(self.pool)
self.iocroot = iocage_lib.ioc_json.IOCJson(self.pool).json_get_value(
'iocroot')

Expand All @@ -52,7 +55,7 @@ def clean_jails(self):
silent=self.silent)

iocage_lib.ioc_destroy.IOCDestroy().destroy_jail(
f'{self.pool}/iocage/jails',
f'{self.iocroot}/jails',
clean=True
)

Expand All @@ -66,7 +69,7 @@ def clean_releases(self):
silent=self.silent)

iocage_lib.ioc_destroy.IOCDestroy().destroy_jail(
f'{self.pool}/iocage/download',
f'{self.iocroot}/download',
clean=True
)

Expand All @@ -78,7 +81,7 @@ def clean_releases(self):
silent=self.silent)

iocage_lib.ioc_destroy.IOCDestroy().destroy_jail(
f'{self.pool}/iocage/releases',
f'{self.iocroot}/releases',
clean=True)

def clean_all(self):
Expand All @@ -96,7 +99,9 @@ def clean_all(self):
silent=self.silent)

iocage_lib.ioc_destroy.IOCDestroy().__destroy_parse_datasets__(
f'{self.pool}/{dataset}', clean=True)
os.path.join(self.zpool.name, self.zpool.prefix, dataset),
clean=True
)

def clean_templates(self):
"""Cleans all templates and their respective children."""
Expand All @@ -108,7 +113,7 @@ def clean_templates(self):
silent=self.silent)

iocage_lib.ioc_destroy.IOCDestroy().__destroy_parse_datasets__(
f"{self.pool}/iocage/templates",
f"{self.iocroot}/templates",
clean=True)

def clean_images(self):
Expand All @@ -120,7 +125,7 @@ def clean_images(self):
_callback=self.callback,
silent=self.silent)

Dataset(f'{self.pool}/iocage/images').destroy(True, True)
Dataset(f'{self.iocroot}/images').destroy(True, True)

def clean_debug(self):
"""Removes the debug directory"""
Expand Down
52 changes: 41 additions & 11 deletions iocage_lib/ioc_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import shutil

from iocage_lib.cache import cache
from iocage_lib.pools import Pool
from iocage_lib.dataset import Dataset


Expand All @@ -59,6 +60,7 @@ def __init__(self, release, props, num, pkglist=None, plugin=False,
self.pool = iocage_lib.ioc_json.IOCJson().json_get_value("pool")
self.iocroot = iocage_lib.ioc_json.IOCJson(self.pool).json_get_value(
"iocroot")
self.zpool = Pool(self.pool)
self.release = release
self.props = props
self.num = num
Expand Down Expand Up @@ -218,10 +220,16 @@ def _create_jail(self, jail_uuid, location):
clone_etc_hosts = \
f"{self.iocroot}/jails/{jail_uuid}/root/etc/hosts"

jail = f"{self.pool}/iocage/jails/{jail_uuid}/root"
jail = os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage',
'jails', jail_uuid, 'root'
)

if self.template:
source = f'{self.pool}/iocage/templates/{self.release}@{jail_uuid}'
source = os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage',
'templates', f'{self.release}@{jail_uuid}'
)
snap_cmd = ['zfs', 'snapshot', '-r', source]

if self.thickjail:
Expand Down Expand Up @@ -255,7 +263,10 @@ def _create_jail(self, jail_uuid, location):
# Thick jails won't have this
pass
elif self.clone:
source = f'{self.pool}/iocage/jails/{self.release}@{jail_uuid}'
source = os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage',
'jails', f'{self.release}@{jail_uuid}'
)
snap_cmd = ['zfs', 'snapshot', '-r', source]

if self.thickjail:
Expand Down Expand Up @@ -319,13 +330,16 @@ def _create_jail(self, jail_uuid, location):
config[k] = v
else:
if not self.empty:
dataset = f'{self.pool}/iocage/releases/{self.release}/' \
f'root@{jail_uuid}'
dataset = os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage',
'releases', self.release, f'root@{jail_uuid}'
)
try:
su.check_call(['zfs', 'snapshot', dataset], stderr=su.PIPE)
except su.CalledProcessError:
release = os.path.join(
self.pool, 'iocage/releases', self.release
self.zpool.name, self.zpool.prefix, 'iocage',
'releases', self.release
)
if not Dataset(release).exists:
raise RuntimeError(
Expand Down Expand Up @@ -366,7 +380,11 @@ def _create_jail(self, jail_uuid, location):
if jail_uuid == "default" or jail_uuid == "help":
iocage_lib.ioc_destroy.IOCDestroy(
).__destroy_parse_datasets__(
f"{self.pool}/iocage/jails/{jail_uuid}")
os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage',
'jails', jail_uuid
)
)
iocage_lib.ioc_common.logit({
"level": "EXCEPTION",
"message": f"You cannot name a jail {jail_uuid}, "
Expand All @@ -391,7 +409,10 @@ def _create_jail(self, jail_uuid, location):
iocjson.json_set_value("type=template")
iocjson.json_set_value("template=1")
Dataset(
os.path.join(self.pool, 'iocage', 'templates', jail_uuid)
os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage',
'templates', jail_uuid
)
).set_property('readonly', 'off')

# If you supply pkglist and templates without setting the
Expand Down Expand Up @@ -532,7 +553,10 @@ def _create_jail(self, jail_uuid, location):
# If jail is template, the dataset would be readonly at this point
if is_template:
Dataset(
os.path.join(self.pool, 'iocage/templates', jail_uuid)
os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage',
'templates', jail_uuid
)
).set_property('readonly', 'off')

if self.empty:
Expand Down Expand Up @@ -691,7 +715,10 @@ def _create_jail(self, jail_uuid, location):
if is_template:
# We have to set readonly back, since we're done with our tasks
Dataset(
os.path.join(self.pool, 'iocage/templates', jail_uuid)
os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage',
'templates', jail_uuid
)
).set_property('readonly', 'on')

return jail_uuid
Expand Down Expand Up @@ -1047,7 +1074,10 @@ def create_rc(self, location, host_hostname, basejail=0):
['umount', '-F', f'{location}/fstab', '-a']).communicate()

def create_thickjail(self, jail_uuid, source):
jail = f"{self.pool}/iocage/jails/{jail_uuid}"
jail = os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage',
'jails', jail_uuid
)

try:
su.Popen(['zfs', 'create', '-p', jail],
Expand Down
11 changes: 8 additions & 3 deletions iocage_lib/ioc_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import iocage_lib.ioc_list as ioc_list

from iocage_lib.dataset import Dataset
from iocage_lib.pools import PoolListableResource
from iocage_lib.pools import PoolListableResource, Pool


class IOCDebug(object):
Expand All @@ -55,6 +55,7 @@ class IOCDebug(object):

def __init__(self, path, silent=False, callback=None):
self.pool = ioc_json.IOCJson(' ').json_get_value('pool')
self.zpool = Pool(self.pool)
self.path = path
self.callback = callback
self.silent = silent
Expand All @@ -64,10 +65,14 @@ def run_debug(self):
self.run_host_debug()

jails = Dataset(
os.path.join(self.pool, 'iocage/jails')
os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage', 'jails'
)
).get_dependents()
templates = Dataset(
os.path.join(self.pool, 'iocage/templates')
os.path.join(
self.zpool.name, self.zpool.prefix, 'iocage', 'templates'
)
).get_dependents()

for jail in jails:
Expand Down
Loading

0 comments on commit f454100

Please sign in to comment.