From faf0b84a19db5b72d74849d88c9961018b44f8ca Mon Sep 17 00:00:00 2001 From: Stavros Kois <47820033+stavros-k@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:37:08 +0200 Subject: [PATCH] v2-lib: auto add labels from a top level structure (#746) * auto add labels from a top level structure * improve message --- library/2.0.3/tests/test_labels.py | 45 ---------- library/{2.0.3 => 2.0.4}/__init__.py | 0 library/{2.0.3 => 2.0.4}/configs.py | 0 library/{2.0.3 => 2.0.4}/container.py | 14 +++ library/{2.0.3 => 2.0.4}/depends.py | 0 library/{2.0.3 => 2.0.4}/deploy.py | 0 library/{2.0.3 => 2.0.4}/deps.py | 0 library/{2.0.3 => 2.0.4}/device.py | 0 library/{2.0.3 => 2.0.4}/devices.py | 0 library/{2.0.3 => 2.0.4}/dns.py | 0 library/{2.0.3 => 2.0.4}/environment.py | 0 library/{2.0.3 => 2.0.4}/error.py | 0 library/{2.0.3 => 2.0.4}/formatter.py | 0 library/{2.0.3 => 2.0.4}/functions.py | 0 library/{2.0.3 => 2.0.4}/healthcheck.py | 0 library/{2.0.3 => 2.0.4}/labels.py | 3 + library/{2.0.3 => 2.0.4}/notes.py | 0 library/{2.0.3 => 2.0.4}/portal.py | 0 library/{2.0.3 => 2.0.4}/portals.py | 0 library/{2.0.3 => 2.0.4}/ports.py | 0 library/{2.0.3 => 2.0.4}/render.py | 8 ++ library/{2.0.3 => 2.0.4}/resources.py | 0 library/{2.0.3 => 2.0.4}/restart.py | 0 library/{2.0.3 => 2.0.4}/storage.py | 0 library/{2.0.3 => 2.0.4}/tests/__init__.py | 0 .../tests/test_build_image.py | 0 .../{2.0.3 => 2.0.4}/tests/test_configs.py | 0 .../{2.0.3 => 2.0.4}/tests/test_container.py | 0 .../{2.0.3 => 2.0.4}/tests/test_depends.py | 0 library/{2.0.3 => 2.0.4}/tests/test_deps.py | 0 library/{2.0.3 => 2.0.4}/tests/test_device.py | 0 library/{2.0.3 => 2.0.4}/tests/test_dns.py | 0 .../tests/test_environment.py | 0 .../{2.0.3 => 2.0.4}/tests/test_functions.py | 0 .../tests/test_healthcheck.py | 0 library/2.0.4/tests/test_labels.py | 88 +++++++++++++++++++ library/{2.0.3 => 2.0.4}/tests/test_notes.py | 0 library/{2.0.3 => 2.0.4}/tests/test_portal.py | 0 library/{2.0.3 => 2.0.4}/tests/test_ports.py | 0 library/{2.0.3 => 2.0.4}/tests/test_render.py | 0 .../{2.0.3 => 2.0.4}/tests/test_resources.py | 0 .../{2.0.3 => 2.0.4}/tests/test_restart.py | 0 .../{2.0.3 => 2.0.4}/tests/test_volumes.py | 0 library/{2.0.3 => 2.0.4}/validations.py | 0 library/{2.0.3 => 2.0.4}/volume_mount.py | 0 .../{2.0.3 => 2.0.4}/volume_mount_types.py | 0 library/{2.0.3 => 2.0.4}/volume_sources.py | 0 library/{2.0.3 => 2.0.4}/volume_types.py | 0 library/{2.0.3 => 2.0.4}/volumes.py | 0 library/hashes.yaml | 2 +- 50 files changed, 114 insertions(+), 46 deletions(-) delete mode 100644 library/2.0.3/tests/test_labels.py rename library/{2.0.3 => 2.0.4}/__init__.py (100%) rename library/{2.0.3 => 2.0.4}/configs.py (100%) rename library/{2.0.3 => 2.0.4}/container.py (94%) rename library/{2.0.3 => 2.0.4}/depends.py (100%) rename library/{2.0.3 => 2.0.4}/deploy.py (100%) rename library/{2.0.3 => 2.0.4}/deps.py (100%) rename library/{2.0.3 => 2.0.4}/device.py (100%) rename library/{2.0.3 => 2.0.4}/devices.py (100%) rename library/{2.0.3 => 2.0.4}/dns.py (100%) rename library/{2.0.3 => 2.0.4}/environment.py (100%) rename library/{2.0.3 => 2.0.4}/error.py (100%) rename library/{2.0.3 => 2.0.4}/formatter.py (100%) rename library/{2.0.3 => 2.0.4}/functions.py (100%) rename library/{2.0.3 => 2.0.4}/healthcheck.py (100%) rename library/{2.0.3 => 2.0.4}/labels.py (92%) rename library/{2.0.3 => 2.0.4}/notes.py (100%) rename library/{2.0.3 => 2.0.4}/portal.py (100%) rename library/{2.0.3 => 2.0.4}/portals.py (100%) rename library/{2.0.3 => 2.0.4}/ports.py (100%) rename library/{2.0.3 => 2.0.4}/render.py (85%) rename library/{2.0.3 => 2.0.4}/resources.py (100%) rename library/{2.0.3 => 2.0.4}/restart.py (100%) rename library/{2.0.3 => 2.0.4}/storage.py (100%) rename library/{2.0.3 => 2.0.4}/tests/__init__.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_build_image.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_configs.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_container.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_depends.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_deps.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_device.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_dns.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_environment.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_functions.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_healthcheck.py (100%) create mode 100644 library/2.0.4/tests/test_labels.py rename library/{2.0.3 => 2.0.4}/tests/test_notes.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_portal.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_ports.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_render.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_resources.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_restart.py (100%) rename library/{2.0.3 => 2.0.4}/tests/test_volumes.py (100%) rename library/{2.0.3 => 2.0.4}/validations.py (100%) rename library/{2.0.3 => 2.0.4}/volume_mount.py (100%) rename library/{2.0.3 => 2.0.4}/volume_mount_types.py (100%) rename library/{2.0.3 => 2.0.4}/volume_sources.py (100%) rename library/{2.0.3 => 2.0.4}/volume_types.py (100%) rename library/{2.0.3 => 2.0.4}/volumes.py (100%) diff --git a/library/2.0.3/tests/test_labels.py b/library/2.0.3/tests/test_labels.py deleted file mode 100644 index 0ce94fde40..0000000000 --- a/library/2.0.3/tests/test_labels.py +++ /dev/null @@ -1,45 +0,0 @@ -import pytest - -from render import Render - - -@pytest.fixture -def mock_values(): - return { - "images": { - "test_image": { - "repository": "nginx", - "tag": "latest", - } - }, - } - - -def test_add_disallowed_label(mock_values): - render = Render(mock_values) - c1 = render.add_container("test_container", "test_image") - c1.healthcheck.disable() - with pytest.raises(Exception): - c1.labels.add_label("com.docker.compose.service", "test_service") - - -def test_add_duplicate_label(mock_values): - render = Render(mock_values) - c1 = render.add_container("test_container", "test_image") - c1.healthcheck.disable() - c1.labels.add_label("my.custom.label", "test_value") - with pytest.raises(Exception): - c1.labels.add_label("my.custom.label", "test_value1") - - -def test_add_label(mock_values): - render = Render(mock_values) - c1 = render.add_container("test_container", "test_image") - c1.healthcheck.disable() - c1.labels.add_label("my.custom.label1", "test_value1") - c1.labels.add_label("my.custom.label2", "test_value2") - output = render.render() - assert output["services"]["test_container"]["labels"] == { - "my.custom.label1": "test_value1", - "my.custom.label2": "test_value2", - } diff --git a/library/2.0.3/__init__.py b/library/2.0.4/__init__.py similarity index 100% rename from library/2.0.3/__init__.py rename to library/2.0.4/__init__.py diff --git a/library/2.0.3/configs.py b/library/2.0.4/configs.py similarity index 100% rename from library/2.0.3/configs.py rename to library/2.0.4/configs.py diff --git a/library/2.0.3/container.py b/library/2.0.4/container.py similarity index 94% rename from library/2.0.3/container.py rename to library/2.0.4/container.py index 5f2e275d4b..8089be4f75 100644 --- a/library/2.0.3/container.py +++ b/library/2.0.4/container.py @@ -70,11 +70,25 @@ def __init__(self, render_instance: "Render", name: str, image: str): self.ports: Ports = Ports(self._render_instance) self._auto_set_network_mode() + self._auto_add_labels() def _auto_set_network_mode(self): if self._render_instance.values.get("network", {}).get("host_network", False): self.set_network_mode("host") + def _auto_add_labels(self): + labels = self._render_instance.values.get("labels", []) + if not labels: + return + + for label in labels: + containers = label.get("containers", []) + if not containers: + raise RenderError(f'Label [{label.get("key", "")}] must have at least one container') + + if self._name in containers: + self.labels.add_label(label["key"], label["value"]) + def _resolve_image(self, image: str): images = self._render_instance.values["images"] if image not in images: diff --git a/library/2.0.3/depends.py b/library/2.0.4/depends.py similarity index 100% rename from library/2.0.3/depends.py rename to library/2.0.4/depends.py diff --git a/library/2.0.3/deploy.py b/library/2.0.4/deploy.py similarity index 100% rename from library/2.0.3/deploy.py rename to library/2.0.4/deploy.py diff --git a/library/2.0.3/deps.py b/library/2.0.4/deps.py similarity index 100% rename from library/2.0.3/deps.py rename to library/2.0.4/deps.py diff --git a/library/2.0.3/device.py b/library/2.0.4/device.py similarity index 100% rename from library/2.0.3/device.py rename to library/2.0.4/device.py diff --git a/library/2.0.3/devices.py b/library/2.0.4/devices.py similarity index 100% rename from library/2.0.3/devices.py rename to library/2.0.4/devices.py diff --git a/library/2.0.3/dns.py b/library/2.0.4/dns.py similarity index 100% rename from library/2.0.3/dns.py rename to library/2.0.4/dns.py diff --git a/library/2.0.3/environment.py b/library/2.0.4/environment.py similarity index 100% rename from library/2.0.3/environment.py rename to library/2.0.4/environment.py diff --git a/library/2.0.3/error.py b/library/2.0.4/error.py similarity index 100% rename from library/2.0.3/error.py rename to library/2.0.4/error.py diff --git a/library/2.0.3/formatter.py b/library/2.0.4/formatter.py similarity index 100% rename from library/2.0.3/formatter.py rename to library/2.0.4/formatter.py diff --git a/library/2.0.3/functions.py b/library/2.0.4/functions.py similarity index 100% rename from library/2.0.3/functions.py rename to library/2.0.4/functions.py diff --git a/library/2.0.3/healthcheck.py b/library/2.0.4/healthcheck.py similarity index 100% rename from library/2.0.3/healthcheck.py rename to library/2.0.4/healthcheck.py diff --git a/library/2.0.3/labels.py b/library/2.0.4/labels.py similarity index 92% rename from library/2.0.3/labels.py rename to library/2.0.4/labels.py index 438226650d..f1e667ba00 100644 --- a/library/2.0.3/labels.py +++ b/library/2.0.4/labels.py @@ -17,6 +17,9 @@ def __init__(self, render_instance: "Render"): self._labels: dict[str, str] = {} def add_label(self, key: str, value: str): + if not key: + raise RenderError("Labels must have a key") + if key.startswith("com.docker.compose"): raise RenderError(f"Label [{key}] cannot start with [com.docker.compose] as it is reserved") diff --git a/library/2.0.3/notes.py b/library/2.0.4/notes.py similarity index 100% rename from library/2.0.3/notes.py rename to library/2.0.4/notes.py diff --git a/library/2.0.3/portal.py b/library/2.0.4/portal.py similarity index 100% rename from library/2.0.3/portal.py rename to library/2.0.4/portal.py diff --git a/library/2.0.3/portals.py b/library/2.0.4/portals.py similarity index 100% rename from library/2.0.3/portals.py rename to library/2.0.4/portals.py diff --git a/library/2.0.3/ports.py b/library/2.0.4/ports.py similarity index 100% rename from library/2.0.3/ports.py rename to library/2.0.4/ports.py diff --git a/library/2.0.3/render.py b/library/2.0.4/render.py similarity index 85% rename from library/2.0.3/render.py rename to library/2.0.4/render.py index a2d70cfe89..8421a833c6 100644 --- a/library/2.0.3/render.py +++ b/library/2.0.4/render.py @@ -66,6 +66,14 @@ def render(self): "services": {c._name: c.render() for c in self._containers.values()}, } + # Make sure that after services are rendered + # there are no labels that target a non-existent container + # This is to prevent typos + for label in self.values.get("labels", []): + for c in label.get("containers", []): + if c not in self.container_names(): + raise RenderError(f"Label [{label['key']}] references container [{c}] which does not exist") + if self.volumes.has_volumes(): result["volumes"] = self.volumes.render() diff --git a/library/2.0.3/resources.py b/library/2.0.4/resources.py similarity index 100% rename from library/2.0.3/resources.py rename to library/2.0.4/resources.py diff --git a/library/2.0.3/restart.py b/library/2.0.4/restart.py similarity index 100% rename from library/2.0.3/restart.py rename to library/2.0.4/restart.py diff --git a/library/2.0.3/storage.py b/library/2.0.4/storage.py similarity index 100% rename from library/2.0.3/storage.py rename to library/2.0.4/storage.py diff --git a/library/2.0.3/tests/__init__.py b/library/2.0.4/tests/__init__.py similarity index 100% rename from library/2.0.3/tests/__init__.py rename to library/2.0.4/tests/__init__.py diff --git a/library/2.0.3/tests/test_build_image.py b/library/2.0.4/tests/test_build_image.py similarity index 100% rename from library/2.0.3/tests/test_build_image.py rename to library/2.0.4/tests/test_build_image.py diff --git a/library/2.0.3/tests/test_configs.py b/library/2.0.4/tests/test_configs.py similarity index 100% rename from library/2.0.3/tests/test_configs.py rename to library/2.0.4/tests/test_configs.py diff --git a/library/2.0.3/tests/test_container.py b/library/2.0.4/tests/test_container.py similarity index 100% rename from library/2.0.3/tests/test_container.py rename to library/2.0.4/tests/test_container.py diff --git a/library/2.0.3/tests/test_depends.py b/library/2.0.4/tests/test_depends.py similarity index 100% rename from library/2.0.3/tests/test_depends.py rename to library/2.0.4/tests/test_depends.py diff --git a/library/2.0.3/tests/test_deps.py b/library/2.0.4/tests/test_deps.py similarity index 100% rename from library/2.0.3/tests/test_deps.py rename to library/2.0.4/tests/test_deps.py diff --git a/library/2.0.3/tests/test_device.py b/library/2.0.4/tests/test_device.py similarity index 100% rename from library/2.0.3/tests/test_device.py rename to library/2.0.4/tests/test_device.py diff --git a/library/2.0.3/tests/test_dns.py b/library/2.0.4/tests/test_dns.py similarity index 100% rename from library/2.0.3/tests/test_dns.py rename to library/2.0.4/tests/test_dns.py diff --git a/library/2.0.3/tests/test_environment.py b/library/2.0.4/tests/test_environment.py similarity index 100% rename from library/2.0.3/tests/test_environment.py rename to library/2.0.4/tests/test_environment.py diff --git a/library/2.0.3/tests/test_functions.py b/library/2.0.4/tests/test_functions.py similarity index 100% rename from library/2.0.3/tests/test_functions.py rename to library/2.0.4/tests/test_functions.py diff --git a/library/2.0.3/tests/test_healthcheck.py b/library/2.0.4/tests/test_healthcheck.py similarity index 100% rename from library/2.0.3/tests/test_healthcheck.py rename to library/2.0.4/tests/test_healthcheck.py diff --git a/library/2.0.4/tests/test_labels.py b/library/2.0.4/tests/test_labels.py new file mode 100644 index 0000000000..ffa21eceac --- /dev/null +++ b/library/2.0.4/tests/test_labels.py @@ -0,0 +1,88 @@ +import pytest + +from render import Render + + +@pytest.fixture +def mock_values(): + return { + "images": { + "test_image": { + "repository": "nginx", + "tag": "latest", + } + }, + } + + +def test_add_disallowed_label(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + with pytest.raises(Exception): + c1.labels.add_label("com.docker.compose.service", "test_service") + + +def test_add_duplicate_label(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + c1.labels.add_label("my.custom.label", "test_value") + with pytest.raises(Exception): + c1.labels.add_label("my.custom.label", "test_value1") + + +def test_add_label_on_non_existing_container(mock_values): + mock_values["labels"] = [ + { + "key": "my.custom.label1", + "value": "test_value1", + "containers": ["test_container", "test_container2"], + }, + ] + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + with pytest.raises(Exception): + render.render() + + +def test_add_label(mock_values): + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c1.healthcheck.disable() + c1.labels.add_label("my.custom.label1", "test_value1") + c1.labels.add_label("my.custom.label2", "test_value2") + output = render.render() + assert output["services"]["test_container"]["labels"] == { + "my.custom.label1": "test_value1", + "my.custom.label2": "test_value2", + } + + +def test_auto_add_labels(mock_values): + mock_values["labels"] = [ + { + "key": "my.custom.label1", + "value": "test_value1", + "containers": ["test_container", "test_container2"], + }, + { + "key": "my.custom.label2", + "value": "test_value2", + "containers": ["test_container"], + }, + ] + render = Render(mock_values) + c1 = render.add_container("test_container", "test_image") + c2 = render.add_container("test_container2", "test_image") + c1.healthcheck.disable() + c2.healthcheck.disable() + output = render.render() + assert output["services"]["test_container"]["labels"] == { + "my.custom.label1": "test_value1", + "my.custom.label2": "test_value2", + } + assert output["services"]["test_container2"]["labels"] == { + "my.custom.label1": "test_value1", + } diff --git a/library/2.0.3/tests/test_notes.py b/library/2.0.4/tests/test_notes.py similarity index 100% rename from library/2.0.3/tests/test_notes.py rename to library/2.0.4/tests/test_notes.py diff --git a/library/2.0.3/tests/test_portal.py b/library/2.0.4/tests/test_portal.py similarity index 100% rename from library/2.0.3/tests/test_portal.py rename to library/2.0.4/tests/test_portal.py diff --git a/library/2.0.3/tests/test_ports.py b/library/2.0.4/tests/test_ports.py similarity index 100% rename from library/2.0.3/tests/test_ports.py rename to library/2.0.4/tests/test_ports.py diff --git a/library/2.0.3/tests/test_render.py b/library/2.0.4/tests/test_render.py similarity index 100% rename from library/2.0.3/tests/test_render.py rename to library/2.0.4/tests/test_render.py diff --git a/library/2.0.3/tests/test_resources.py b/library/2.0.4/tests/test_resources.py similarity index 100% rename from library/2.0.3/tests/test_resources.py rename to library/2.0.4/tests/test_resources.py diff --git a/library/2.0.3/tests/test_restart.py b/library/2.0.4/tests/test_restart.py similarity index 100% rename from library/2.0.3/tests/test_restart.py rename to library/2.0.4/tests/test_restart.py diff --git a/library/2.0.3/tests/test_volumes.py b/library/2.0.4/tests/test_volumes.py similarity index 100% rename from library/2.0.3/tests/test_volumes.py rename to library/2.0.4/tests/test_volumes.py diff --git a/library/2.0.3/validations.py b/library/2.0.4/validations.py similarity index 100% rename from library/2.0.3/validations.py rename to library/2.0.4/validations.py diff --git a/library/2.0.3/volume_mount.py b/library/2.0.4/volume_mount.py similarity index 100% rename from library/2.0.3/volume_mount.py rename to library/2.0.4/volume_mount.py diff --git a/library/2.0.3/volume_mount_types.py b/library/2.0.4/volume_mount_types.py similarity index 100% rename from library/2.0.3/volume_mount_types.py rename to library/2.0.4/volume_mount_types.py diff --git a/library/2.0.3/volume_sources.py b/library/2.0.4/volume_sources.py similarity index 100% rename from library/2.0.3/volume_sources.py rename to library/2.0.4/volume_sources.py diff --git a/library/2.0.3/volume_types.py b/library/2.0.4/volume_types.py similarity index 100% rename from library/2.0.3/volume_types.py rename to library/2.0.4/volume_types.py diff --git a/library/2.0.3/volumes.py b/library/2.0.4/volumes.py similarity index 100% rename from library/2.0.3/volumes.py rename to library/2.0.4/volumes.py diff --git a/library/hashes.yaml b/library/hashes.yaml index f48970ad26..006d1d119f 100644 --- a/library/hashes.yaml +++ b/library/hashes.yaml @@ -1,3 +1,3 @@ 0.0.1: f074617a82a86d2a6cc78a4c8a4296fc9d168e456f12713e50c696557b302133 1.1.4: 6e32ff5969906d9c3a10fea2b17fdd3197afb052d3432344da03188d8a907113 -2.0.3: 8ebbf2ffeaccfe0c0f608d93eb536667570974c415a4a75b0f0e8400f42fe427 +2.0.4: 0e79e3390d3ea73649ee2ac05a4af9ed944a02e95289b5c7e2eb047d475a4651