diff --git a/library/2.1.9/__init__.py b/library/2.1.10/__init__.py similarity index 100% rename from library/2.1.9/__init__.py rename to library/2.1.10/__init__.py diff --git a/library/2.1.9/configs.py b/library/2.1.10/configs.py similarity index 100% rename from library/2.1.9/configs.py rename to library/2.1.10/configs.py diff --git a/library/2.1.9/container.py b/library/2.1.10/container.py similarity index 94% rename from library/2.1.9/container.py rename to library/2.1.10/container.py index d0abd6edc7..b727ef190b 100644 --- a/library/2.1.9/container.py +++ b/library/2.1.10/container.py @@ -8,6 +8,7 @@ from .configs import ContainerConfigs from .depends import Depends from .deploy import Deploy + from .device_cgroup_rules import DeviceCGroupRules from .devices import Devices from .dns import Dns from .environment import Environment @@ -19,10 +20,11 @@ from .ports import Ports from .restart import RestartPolicy from .validations import ( - valid_network_mode_or_raise, valid_cap_or_raise, - valid_pull_policy_or_raise, + valid_ipc_mode_or_raise, + valid_network_mode_or_raise, valid_port_bind_mode_or_raise, + valid_pull_policy_or_raise, ) from .storage import Storage from .sysctls import Sysctls @@ -30,6 +32,7 @@ from configs import ContainerConfigs from depends import Depends from deploy import Deploy + from device_cgroup_rules import DeviceCGroupRules from devices import Devices from dns import Dns from environment import Environment @@ -41,10 +44,11 @@ from ports import Ports from restart import RestartPolicy from validations import ( - valid_network_mode_or_raise, valid_cap_or_raise, - valid_pull_policy_or_raise, + valid_ipc_mode_or_raise, + valid_network_mode_or_raise, valid_port_bind_mode_or_raise, + valid_pull_policy_or_raise, ) from storage import Storage from sysctls import Sysctls @@ -75,6 +79,8 @@ def __init__(self, render_instance: "Render", name: str, image: str): self._grace_period: int | None = None self._shm_size: int | None = None self._storage: Storage = Storage(self._render_instance) + self._ipc_mode: str | None = None + self._device_cgroup_rules: DeviceCGroupRules = DeviceCGroupRules(self._render_instance) self.sysctls: Sysctls = Sysctls(self._render_instance, self) self.configs: ContainerConfigs = ContainerConfigs(self._render_instance, self._render_instance.configs) self.deploy: Deploy = Deploy(self._render_instance) @@ -182,6 +188,12 @@ def set_tty(self, enabled: bool = False): def set_stdin(self, enabled: bool = False): self._stdin_open = enabled + def set_ipc_mode(self, ipc_mode: str): + self._ipc_mode = valid_ipc_mode_or_raise(ipc_mode, self._render_instance.container_names()) + + def add_device_cgroup_rule(self, dev_grp_rule: str): + self._device_cgroup_rules.add_rule(dev_grp_rule) + def set_init(self, enabled: bool = False): self._init = enabled @@ -309,6 +321,12 @@ def render(self) -> dict[str, Any]: if self.configs.has_configs(): result["configs"] = self.configs.render() + if self._ipc_mode is not None: + result["ipc_mode"] = self._ipc_mode + + if self._device_cgroup_rules.has_rules(): + result["device_cgroup_rules"] = self._device_cgroup_rules.render() + if self._init is not None: result["init"] = self._init diff --git a/library/2.1.9/depends.py b/library/2.1.10/depends.py similarity index 100% rename from library/2.1.9/depends.py rename to library/2.1.10/depends.py diff --git a/library/2.1.9/deploy.py b/library/2.1.10/deploy.py similarity index 100% rename from library/2.1.9/deploy.py rename to library/2.1.10/deploy.py diff --git a/library/2.1.9/deps.py b/library/2.1.10/deps.py similarity index 100% rename from library/2.1.9/deps.py rename to library/2.1.10/deps.py diff --git a/library/2.1.9/deps_mariadb.py b/library/2.1.10/deps_mariadb.py similarity index 100% rename from library/2.1.9/deps_mariadb.py rename to library/2.1.10/deps_mariadb.py diff --git a/library/2.1.9/deps_perms.py b/library/2.1.10/deps_perms.py similarity index 100% rename from library/2.1.9/deps_perms.py rename to library/2.1.10/deps_perms.py diff --git a/library/2.1.9/deps_postgres.py b/library/2.1.10/deps_postgres.py similarity index 100% rename from library/2.1.9/deps_postgres.py rename to library/2.1.10/deps_postgres.py diff --git a/library/2.1.9/deps_redis.py b/library/2.1.10/deps_redis.py similarity index 100% rename from library/2.1.9/deps_redis.py rename to library/2.1.10/deps_redis.py diff --git a/library/2.1.9/device.py b/library/2.1.10/device.py similarity index 100% rename from library/2.1.9/device.py rename to library/2.1.10/device.py diff --git a/library/2.1.10/device_cgroup_rules.py b/library/2.1.10/device_cgroup_rules.py new file mode 100644 index 0000000000..dcccfee773 --- /dev/null +++ b/library/2.1.10/device_cgroup_rules.py @@ -0,0 +1,54 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from render import Render + +try: + from .error import RenderError + from .validations import valid_device_cgroup_rule_or_raise +except ImportError: + from error import RenderError + from validations import valid_device_cgroup_rule_or_raise + + +class DeviceCGroupRule: + def __init__(self, rule: str): + rule = valid_device_cgroup_rule_or_raise(rule) + parts = rule.split(" ") + major, minor = parts[1].split(":") + + self._type = parts[0] + self._major = major + self._minor = minor + self._permissions = parts[2] + + def get_key(self): + return f"{self._type}_{self._major}_{self._minor}" + + def render(self): + return f"{self._type} {self._major}:{self._minor} {self._permissions}" + + +class DeviceCGroupRules: + def __init__(self, render_instance: "Render"): + self._render_instance = render_instance + self._rules: set[DeviceCGroupRule] = set() + self._track_rule_combos: set[str] = set() + + def add_rule(self, rule: str): + dev_group_rule = DeviceCGroupRule(rule) + if dev_group_rule in self._rules: + raise RenderError(f"Device Group Rule [{rule}] already added") + + rule_key = dev_group_rule.get_key() + if rule_key in self._track_rule_combos: + raise RenderError(f"Device Group Rule [{rule}] has already been added for this device group") + + self._rules.add(dev_group_rule) + self._track_rule_combos.add(rule_key) + + def has_rules(self): + return len(self._rules) > 0 + + def render(self): + return sorted([rule.render() for rule in self._rules]) diff --git a/library/2.1.9/devices.py b/library/2.1.10/devices.py similarity index 100% rename from library/2.1.9/devices.py rename to library/2.1.10/devices.py diff --git a/library/2.1.9/dns.py b/library/2.1.10/dns.py similarity index 100% rename from library/2.1.9/dns.py rename to library/2.1.10/dns.py diff --git a/library/2.1.9/environment.py b/library/2.1.10/environment.py similarity index 100% rename from library/2.1.9/environment.py rename to library/2.1.10/environment.py diff --git a/library/2.1.9/error.py b/library/2.1.10/error.py similarity index 100% rename from library/2.1.9/error.py rename to library/2.1.10/error.py diff --git a/library/2.1.9/expose.py b/library/2.1.10/expose.py similarity index 100% rename from library/2.1.9/expose.py rename to library/2.1.10/expose.py diff --git a/library/2.1.9/formatter.py b/library/2.1.10/formatter.py similarity index 100% rename from library/2.1.9/formatter.py rename to library/2.1.10/formatter.py diff --git a/library/2.1.9/functions.py b/library/2.1.10/functions.py similarity index 100% rename from library/2.1.9/functions.py rename to library/2.1.10/functions.py diff --git a/library/2.1.9/healthcheck.py b/library/2.1.10/healthcheck.py similarity index 100% rename from library/2.1.9/healthcheck.py rename to library/2.1.10/healthcheck.py diff --git a/library/2.1.9/labels.py b/library/2.1.10/labels.py similarity index 100% rename from library/2.1.9/labels.py rename to library/2.1.10/labels.py diff --git a/library/2.1.9/notes.py b/library/2.1.10/notes.py similarity index 100% rename from library/2.1.9/notes.py rename to library/2.1.10/notes.py diff --git a/library/2.1.9/portal.py b/library/2.1.10/portal.py similarity index 100% rename from library/2.1.9/portal.py rename to library/2.1.10/portal.py diff --git a/library/2.1.9/portals.py b/library/2.1.10/portals.py similarity index 100% rename from library/2.1.9/portals.py rename to library/2.1.10/portals.py diff --git a/library/2.1.9/ports.py b/library/2.1.10/ports.py similarity index 100% rename from library/2.1.9/ports.py rename to library/2.1.10/ports.py diff --git a/library/2.1.9/render.py b/library/2.1.10/render.py similarity index 100% rename from library/2.1.9/render.py rename to library/2.1.10/render.py diff --git a/library/2.1.9/resources.py b/library/2.1.10/resources.py similarity index 100% rename from library/2.1.9/resources.py rename to library/2.1.10/resources.py diff --git a/library/2.1.9/restart.py b/library/2.1.10/restart.py similarity index 100% rename from library/2.1.9/restart.py rename to library/2.1.10/restart.py diff --git a/library/2.1.9/storage.py b/library/2.1.10/storage.py similarity index 100% rename from library/2.1.9/storage.py rename to library/2.1.10/storage.py diff --git a/library/2.1.9/sysctls.py b/library/2.1.10/sysctls.py similarity index 100% rename from library/2.1.9/sysctls.py rename to library/2.1.10/sysctls.py diff --git a/library/2.1.9/tests/__init__.py b/library/2.1.10/tests/__init__.py similarity index 100% rename from library/2.1.9/tests/__init__.py rename to library/2.1.10/tests/__init__.py diff --git a/library/2.1.9/tests/test_build_image.py b/library/2.1.10/tests/test_build_image.py similarity index 100% rename from library/2.1.9/tests/test_build_image.py rename to library/2.1.10/tests/test_build_image.py diff --git a/library/2.1.9/tests/test_configs.py b/library/2.1.10/tests/test_configs.py similarity index 100% rename from library/2.1.9/tests/test_configs.py rename to library/2.1.10/tests/test_configs.py diff --git a/library/2.1.9/tests/test_container.py b/library/2.1.10/tests/test_container.py similarity index 88% rename from library/2.1.9/tests/test_container.py rename to library/2.1.10/tests/test_container.py index bb3d98dffc..fdd3aa67d3 100644 --- a/library/2.1.9/tests/test_container.py +++ b/library/2.1.10/tests/test_container.py @@ -367,3 +367,48 @@ def test_add_ports_with_invalid_host_ips(mock_values): c1.healthcheck.disable() with pytest.raises(Exception): c1.add_port({"port_number": 8081, "container_port": 8080, "bind_mode": "published", "host_ips": "invalid"}) + + +def test_set_ipc_mode(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + c1.set_ipc_mode("host") + output = render.render() + assert output["services"]["test_container"]["ipc_mode"] == "host" + + +def test_set_ipc_empty_mode(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + c1.set_ipc_mode("") + output = render.render() + assert output["services"]["test_container"]["ipc_mode"] == "" + + +def test_set_ipc_mode_with_invalid_ipc_mode(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + with pytest.raises(Exception): + c1.set_ipc_mode("invalid") + + +def test_set_ipc_mode_with_container_ipc_mode(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + c2 = render.add_container("test_container2", "test_image") + c2.healthcheck.disable() + c1.set_ipc_mode("container:test_container2") + output = render.render() + assert output["services"]["test_container"]["ipc_mode"] == "container:test_container2" + + +def test_set_ipc_mode_with_container_ipc_mode_and_invalid_container(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + with pytest.raises(Exception): + c1.set_ipc_mode("container:invalid") diff --git a/library/2.1.9/tests/test_depends.py b/library/2.1.10/tests/test_depends.py similarity index 100% rename from library/2.1.9/tests/test_depends.py rename to library/2.1.10/tests/test_depends.py diff --git a/library/2.1.9/tests/test_deps.py b/library/2.1.10/tests/test_deps.py similarity index 100% rename from library/2.1.9/tests/test_deps.py rename to library/2.1.10/tests/test_deps.py diff --git a/library/2.1.9/tests/test_device.py b/library/2.1.10/tests/test_device.py similarity index 100% rename from library/2.1.9/tests/test_device.py rename to library/2.1.10/tests/test_device.py diff --git a/library/2.1.10/tests/test_device_cgroup_rules.py b/library/2.1.10/tests/test_device_cgroup_rules.py new file mode 100644 index 0000000000..581fe82017 --- /dev/null +++ b/library/2.1.10/tests/test_device_cgroup_rules.py @@ -0,0 +1,79 @@ +import pytest + + +from render import Render + + +@pytest.fixture +def mock_values(): + return { + "images": { + "test_image": { + "repository": "nginx", + "tag": "latest", + } + }, + } + + +def test_device_cgroup_rule(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + c1.add_device_cgroup_rule("c 13:* rwm") + c1.add_device_cgroup_rule("b 10:20 rwm") + output = render.render() + assert output["services"]["test_container"]["device_cgroup_rules"] == [ + "b 10:20 rwm", + "c 13:* rwm", + ] + + +def test_device_cgroup_rule_duplicate(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + c1.add_device_cgroup_rule("c 13:* rwm") + with pytest.raises(Exception): + c1.add_device_cgroup_rule("c 13:* rwm") + + +def test_device_cgroup_rule_duplicate_group(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + c1.add_device_cgroup_rule("c 13:* rwm") + with pytest.raises(Exception): + c1.add_device_cgroup_rule("c 13:* rm") + + +def test_device_cgroup_rule_invalid_device(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + with pytest.raises(Exception): + c1.add_device_cgroup_rule("d 10:20 rwm") + + +def test_device_cgroup_rule_invalid_perm(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + with pytest.raises(Exception): + c1.add_device_cgroup_rule("a 10:20 rwd") + + +def test_device_cgroup_rule_invalid_format(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + with pytest.raises(Exception): + c1.add_device_cgroup_rule("a 10 20 rwd") + + +def test_device_cgroup_rule_invalid_format_missing_major(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + with pytest.raises(Exception): + c1.add_device_cgroup_rule("a 10 rwd") diff --git a/library/2.1.9/tests/test_dns.py b/library/2.1.10/tests/test_dns.py similarity index 100% rename from library/2.1.9/tests/test_dns.py rename to library/2.1.10/tests/test_dns.py diff --git a/library/2.1.9/tests/test_environment.py b/library/2.1.10/tests/test_environment.py similarity index 100% rename from library/2.1.9/tests/test_environment.py rename to library/2.1.10/tests/test_environment.py diff --git a/library/2.1.9/tests/test_expose.py b/library/2.1.10/tests/test_expose.py similarity index 100% rename from library/2.1.9/tests/test_expose.py rename to library/2.1.10/tests/test_expose.py diff --git a/library/2.1.9/tests/test_formatter.py b/library/2.1.10/tests/test_formatter.py similarity index 100% rename from library/2.1.9/tests/test_formatter.py rename to library/2.1.10/tests/test_formatter.py diff --git a/library/2.1.9/tests/test_functions.py b/library/2.1.10/tests/test_functions.py similarity index 100% rename from library/2.1.9/tests/test_functions.py rename to library/2.1.10/tests/test_functions.py diff --git a/library/2.1.9/tests/test_healthcheck.py b/library/2.1.10/tests/test_healthcheck.py similarity index 100% rename from library/2.1.9/tests/test_healthcheck.py rename to library/2.1.10/tests/test_healthcheck.py diff --git a/library/2.1.9/tests/test_labels.py b/library/2.1.10/tests/test_labels.py similarity index 100% rename from library/2.1.9/tests/test_labels.py rename to library/2.1.10/tests/test_labels.py diff --git a/library/2.1.9/tests/test_notes.py b/library/2.1.10/tests/test_notes.py similarity index 100% rename from library/2.1.9/tests/test_notes.py rename to library/2.1.10/tests/test_notes.py diff --git a/library/2.1.9/tests/test_portal.py b/library/2.1.10/tests/test_portal.py similarity index 100% rename from library/2.1.9/tests/test_portal.py rename to library/2.1.10/tests/test_portal.py diff --git a/library/2.1.9/tests/test_ports.py b/library/2.1.10/tests/test_ports.py similarity index 100% rename from library/2.1.9/tests/test_ports.py rename to library/2.1.10/tests/test_ports.py diff --git a/library/2.1.9/tests/test_render.py b/library/2.1.10/tests/test_render.py similarity index 100% rename from library/2.1.9/tests/test_render.py rename to library/2.1.10/tests/test_render.py diff --git a/library/2.1.9/tests/test_resources.py b/library/2.1.10/tests/test_resources.py similarity index 100% rename from library/2.1.9/tests/test_resources.py rename to library/2.1.10/tests/test_resources.py diff --git a/library/2.1.9/tests/test_restart.py b/library/2.1.10/tests/test_restart.py similarity index 100% rename from library/2.1.9/tests/test_restart.py rename to library/2.1.10/tests/test_restart.py diff --git a/library/2.1.9/tests/test_sysctls.py b/library/2.1.10/tests/test_sysctls.py similarity index 100% rename from library/2.1.9/tests/test_sysctls.py rename to library/2.1.10/tests/test_sysctls.py diff --git a/library/2.1.9/tests/test_validations.py b/library/2.1.10/tests/test_validations.py similarity index 100% rename from library/2.1.9/tests/test_validations.py rename to library/2.1.10/tests/test_validations.py diff --git a/library/2.1.9/tests/test_volumes.py b/library/2.1.10/tests/test_volumes.py similarity index 100% rename from library/2.1.9/tests/test_volumes.py rename to library/2.1.10/tests/test_volumes.py diff --git a/library/2.1.9/validations.py b/library/2.1.10/validations.py similarity index 85% rename from library/2.1.9/validations.py rename to library/2.1.10/validations.py index 3b0722afbc..d4aa633006 100644 --- a/library/2.1.9/validations.py +++ b/library/2.1.10/validations.py @@ -38,6 +38,17 @@ def valid_pull_policy_or_raise(pull_policy: str): return pull_policy +def valid_ipc_mode_or_raise(ipc_mode: str, containers: list[str]): + valid_modes = ("", "host", "private", "shareable", "none") + if ipc_mode in valid_modes: + return ipc_mode + if ipc_mode.startswith("container:"): + if ipc_mode[10:] not in containers: + raise RenderError(f"IPC mode [{ipc_mode}] is not valid. Container [{ipc_mode[10:]}] does not exist") + return ipc_mode + raise RenderError(f"IPC mode [{ipc_mode}] is not valid. Valid options are: [{', '.join(valid_modes)}]") + + def valid_sysctl_or_raise(sysctl: str, host_network: bool): if not sysctl: raise RenderError("Sysctl cannot be empty") @@ -136,6 +147,33 @@ def valid_cgroup_perm_or_raise(cgroup_perm: str): return cgroup_perm +def valid_device_cgroup_rule_or_raise(dev_grp_rule: str): + parts = dev_grp_rule.split(" ") + if len(parts) != 3: + raise RenderError( + f"Device Group Rule [{dev_grp_rule}] is not valid. Expected format is [ : ]" + ) + + valid_types = ("a", "b", "c") + if parts[0] not in valid_types: + raise RenderError( + f"Device Group Rule [{dev_grp_rule}] is not valid. Expected type to be one of [{', '.join(valid_types)}]" + f" but got [{parts[0]}]" + ) + + major, minor = parts[1].split(":") + for part in (major, minor): + if part != "*" and not part.isdigit(): + raise RenderError( + f"Device Group Rule [{dev_grp_rule}] is not valid. Expected major and minor to be digits" + f" or [*] but got [{major}] and [{minor}]" + ) + + valid_cgroup_perm_or_raise(parts[2]) + + return dev_grp_rule + + def allowed_dns_opt_or_raise(dns_opt: str): disallowed_dns_opts = [] if dns_opt in disallowed_dns_opts: diff --git a/library/2.1.9/volume_mount.py b/library/2.1.10/volume_mount.py similarity index 100% rename from library/2.1.9/volume_mount.py rename to library/2.1.10/volume_mount.py diff --git a/library/2.1.9/volume_mount_types.py b/library/2.1.10/volume_mount_types.py similarity index 100% rename from library/2.1.9/volume_mount_types.py rename to library/2.1.10/volume_mount_types.py diff --git a/library/2.1.9/volume_sources.py b/library/2.1.10/volume_sources.py similarity index 100% rename from library/2.1.9/volume_sources.py rename to library/2.1.10/volume_sources.py diff --git a/library/2.1.9/volume_types.py b/library/2.1.10/volume_types.py similarity index 100% rename from library/2.1.9/volume_types.py rename to library/2.1.10/volume_types.py diff --git a/library/2.1.9/volumes.py b/library/2.1.10/volumes.py similarity index 100% rename from library/2.1.9/volumes.py rename to library/2.1.10/volumes.py diff --git a/library/hashes.yaml b/library/hashes.yaml index feef728ab5..4fc718955d 100644 --- a/library/hashes.yaml +++ b/library/hashes.yaml @@ -1,2 +1,2 @@ 0.0.1: f074617a82a86d2a6cc78a4c8a4296fc9d168e456f12713e50c696557b302133 -2.1.9: 6bd4d433db7dce2d4b8cc456a0f7874b45d52a6e2b145d5a1f48f327654a7125 +2.1.10: b830a401d55a4c0a45b1a9b12a5b489ca7c94a510fc213303596e591daee0d91