Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

library: add central port func for both "ports" and "expose" #1227

Merged
merged 7 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cspell.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ words:
- ddns
- ddnss
- desec
- dialout
- diskover
- diskoverdata
- dnsmasq
Expand Down Expand Up @@ -267,5 +268,6 @@ words:
- wtfismyip
- xattr
- zerotier
- zigbee
- zksync
- zoneedit
File renamed without changes.
File renamed without changes.
45 changes: 43 additions & 2 deletions library/2.1.6/container.py → library/2.1.7/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@
from .dns import Dns
from .environment import Environment
from .error import RenderError
from .expose import Expose
from .formatter import escape_dollar, get_image_with_hashed_data
from .healthcheck import Healthcheck
from .labels import Labels
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
from .validations import (
valid_network_mode_or_raise,
valid_cap_or_raise,
valid_pull_policy_or_raise,
valid_port_bind_mode_or_raise,
)
from .storage import Storage
from .sysctls import Sysctls
except ImportError:
Expand All @@ -28,12 +34,18 @@
from dns import Dns
from environment import Environment
from error import RenderError
from expose import Expose
from formatter import escape_dollar, get_image_with_hashed_data
from healthcheck import Healthcheck
from labels import Labels
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
from validations import (
valid_network_mode_or_raise,
valid_cap_or_raise,
valid_pull_policy_or_raise,
valid_port_bind_mode_or_raise,
)
from storage import Storage
from sysctls import Sysctls

Expand Down Expand Up @@ -75,6 +87,7 @@ def __init__(self, render_instance: "Render", name: str, image: str):
self.labels: Labels = Labels(self._render_instance)
self.restart: RestartPolicy = RestartPolicy(self._render_instance)
self.ports: Ports = Ports(self._render_instance)
self.expose: Expose = Expose(self._render_instance)

self._auto_set_network_mode()
self._auto_add_labels()
Expand Down Expand Up @@ -207,6 +220,28 @@ def remove_security_opt(self, opt: str):
def set_network_mode(self, mode: str):
self._network_mode = valid_network_mode_or_raise(mode, self._render_instance.container_names())

def add_port(self, port_config: dict | None = None, dev_config: dict | None = None):
port_config = port_config or {}
dev_config = dev_config or {}
# Merge port_config and dev_config (dev_config has precedence)
config = port_config | dev_config

bind_mode = valid_port_bind_mode_or_raise(config.get("bind_mode", ""))
# Skip port if its neither published nor exposed
if not bind_mode:
return

# Collect port config
host_port = config.get("port_number", 0)
container_port = config.get("container_port", 0) or host_port
protocol = config.get("protocol", "tcp")
host_ip = config.get("host_ip", "0.0.0.0")

if bind_mode == "published":
self.ports.add_port(host_port, container_port, {"protocol": protocol, "host_ip": host_ip})
elif bind_mode == "exposed":
self.expose.add_port(container_port, protocol)

def set_entrypoint(self, entrypoint: list[str]):
self._entrypoint = [escape_dollar(str(e)) for e in entrypoint]

Expand All @@ -220,6 +255,9 @@ def add_docker_socket(self, read_only: bool = True, mount_path: str = "/var/run/
self.add_group(999)
self._storage._add_docker_socket(read_only, mount_path)

def add_udev(self, read_only: bool = True, mount_path: str = "/run/udev"):
self._storage._add_udev(read_only, mount_path)

def add_tun_device(self):
self.devices._add_tun_device()

Expand Down Expand Up @@ -311,6 +349,9 @@ def render(self) -> dict[str, Any]:
if self.ports.has_ports():
result["ports"] = self.ports.render()

if self.expose.has_ports():
result["expose"] = self.expose.render()

if self._entrypoint:
result["entrypoint"] = self._entrypoint

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
31 changes: 31 additions & 0 deletions library/2.1.7/expose.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from render import Render

try:
from .error import RenderError
from .validations import valid_port_or_raise, valid_port_protocol_or_raise
except ImportError:
from error import RenderError
from validations import valid_port_or_raise, valid_port_protocol_or_raise


class Expose:
def __init__(self, render_instance: "Render"):
self._render_instance = render_instance
self._ports: set[str] = set()

def add_port(self, port: int, protocol: str = "tcp"):
port = valid_port_or_raise(port)
protocol = valid_port_protocol_or_raise(protocol)
key = f"{port}/{protocol}"
if key in self._ports:
raise RenderError(f"Exposed port [{port}/{protocol}] already added")
self._ports.add(key)

def has_ports(self):
return len(self._ports) > 0

def render(self):
return sorted(self._ports)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
8 changes: 4 additions & 4 deletions library/2.1.6/ports.py → library/2.1.7/ports.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
try:
from .error import RenderError
from .validations import (
valid_ip_or_raise,
valid_port_mode_or_raise,
valid_port_or_raise,
valid_port_protocol_or_raise,
valid_port_mode_or_raise,
valid_ip_or_raise,
)
except ImportError:
from error import RenderError
from validations import (
valid_ip_or_raise,
valid_port_mode_or_raise,
valid_port_or_raise,
valid_port_protocol_or_raise,
valid_port_mode_or_raise,
valid_ip_or_raise,
)


Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 9 additions & 0 deletions library/2.1.6/storage.py → library/2.1.7/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ def _add_docker_socket(self, read_only: bool = True, mount_path: str = ""):
}
self.add(mount_path, cfg)

def _add_udev(self, read_only: bool = True, mount_path: str = ""):
mount_path = valid_fs_path_or_raise(mount_path)
cfg: "IxStorage" = {
"type": "host_path",
"read_only": read_only,
"host_path_config": {"path": "/run/udev", "create_host_path": False},
}
self.add(mount_path, cfg)

def has_mounts(self) -> bool:
return bool(self._volume_mounts)

Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,24 @@ def test_command(mock_values):
c1.healthcheck.disable()
output = render.render()
assert output["services"]["test_container"]["command"] == ["echo", "hello $$MY_ENV"]


def test_add_ports(mock_values):
render = Render(mock_values)
c1 = render.add_container("test_container", "test_image")
c1.healthcheck.disable()
c1.add_port({"port_number": 8081, "container_port": 8080, "bind_mode": "published"})
c1.add_port({"port_number": 8082, "container_port": 8080, "bind_mode": "published", "protocol": "udp"})
c1.add_port({"port_number": 8083, "container_port": 8080, "bind_mode": "exposed"})
c1.add_port({"port_number": 8084, "container_port": 8080, "bind_mode": ""})
c1.add_port(
{"port_number": 9091, "container_port": 9091, "bind_mode": "published"},
{"container_port": 9092, "protocol": "udp"},
)
output = render.render()
assert output["services"]["test_container"]["ports"] == [
{"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress", "host_ip": "0.0.0.0"},
{"published": 8082, "target": 8080, "protocol": "udp", "mode": "ingress", "host_ip": "0.0.0.0"},
{"published": 9091, "target": 9092, "protocol": "udp", "mode": "ingress", "host_ip": "0.0.0.0"},
]
assert output["services"]["test_container"]["expose"] == ["8080/tcp"]
File renamed without changes.
File renamed without changes.
File renamed without changes.
46 changes: 46 additions & 0 deletions library/2.1.7/tests/test_expose.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import pytest


from render import Render


@pytest.fixture
def mock_values():
return {
"images": {
"test_image": {
"repository": "nginx",
"tag": "latest",
}
},
}


def test_add_expose_ports(mock_values):
render = Render(mock_values)
c1 = render.add_container("test_container", "test_image")
c1.healthcheck.disable()
c1.expose.add_port(8081)
c1.expose.add_port(8081, "udp")
c1.expose.add_port(8082, "udp")
output = render.render()
assert output["services"]["test_container"]["expose"] == ["8081/tcp", "8081/udp", "8082/udp"]


def test_add_duplicate_expose_ports(mock_values):
render = Render(mock_values)
c1 = render.add_container("test_container", "test_image")
c1.healthcheck.disable()
c1.expose.add_port(8081)
with pytest.raises(Exception):
c1.expose.add_port(8081)


def test_add_expose_ports_with_host_network(mock_values):
mock_values["network"] = {"host_network": True}
render = Render(mock_values)
c1 = render.add_container("test_container", "test_image")
c1.healthcheck.disable()
c1.expose.add_port(8081)
output = render.render()
assert "expose" not in output["services"]["test_container"]
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,40 @@ def test_anonymous_volume(mock_values):
assert "volumes" not in output


def test_add_udev(mock_values):
render = Render(mock_values)
c1 = render.add_container("test_container", "test_image")
c1.healthcheck.disable()
c1.add_udev()
output = render.render()
assert output["services"]["test_container"]["volumes"] == [
{
"type": "bind",
"source": "/run/udev",
"target": "/run/udev",
"read_only": True,
"bind": {"create_host_path": False, "propagation": "rprivate"},
}
]


def test_add_udev_not_read_only(mock_values):
render = Render(mock_values)
c1 = render.add_container("test_container", "test_image")
c1.healthcheck.disable()
c1.add_udev(read_only=False)
output = render.render()
assert output["services"]["test_container"]["volumes"] == [
{
"type": "bind",
"source": "/run/udev",
"target": "/run/udev",
"read_only": False,
"bind": {"create_host_path": False, "propagation": "rprivate"},
}
]


def test_add_docker_socket(mock_values):
render = Render(mock_values)
c1 = render.add_container("test_container", "test_image")
Expand Down
7 changes: 7 additions & 0 deletions library/2.1.6/validations.py → library/2.1.7/validations.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
)


def valid_port_bind_mode_or_raise(status: str):
valid_statuses = ("published", "exposed", "")
if status not in valid_statuses:
raise RenderError(f"Invalid port status [{status}]")
return status


def valid_pull_policy_or_raise(pull_policy: str):
valid_policies = ("missing", "always", "never", "build")
if pull_policy not in valid_policies:
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion library/hashes.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
0.0.1: f074617a82a86d2a6cc78a4c8a4296fc9d168e456f12713e50c696557b302133
2.1.6: 84c965e8b9bea696765ab62b8ee3238162fe7807d0f0a61cf9c153994a47fa90
2.1.7: c4c34d5b8aa0e8c3016b7cb3ee8a78ef09d4ffe80f34eb35107810656a43a615
Loading