OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / truenas-app / templates / library / base_v2_3_8 / render.py

107 lines · python ref:
import copy

try:
    from .configs import Configs
    from .container import Container
    from .deps import Deps
    from .docker_client import DockerClient
    from .error import RenderError
    from .functions import Functions
    from .networks import Networks
    from .notes import Notes
    from .portals import Portals
    from .truenas_client import TNClient
    from .volumes import Volumes
except ImportError:
    from configs import Configs
    from container import Container
    from deps import Deps
    from docker_client import DockerClient
    from error import RenderError
    from functions import Functions
    from networks import Networks
    from notes import Notes
    from portals import Portals
    from truenas_client import TNClient
    from volumes import Volumes


class Render(object):
    def __init__(self, values):
        self._containers: dict[str, Container] = {}
        self.values = values
        # Make a copy after we inject the images
        self._original_values: dict = copy.deepcopy(self.values)

        self.deps: Deps = Deps(self)

        self.client: TNClient = TNClient(render_instance=self)
        self.docker: DockerClient = DockerClient(render_instance=self)

        self.configs = Configs(render_instance=self)
        self.funcs = Functions(render_instance=self).func_map()
        self.portals: Portals = Portals(render_instance=self)
        self.notes: Notes = Notes(render_instance=self)
        self.networks: Networks = Networks(render_instance=self)
        self.volumes = Volumes(render_instance=self)

        self._auto_add_networks()

    def _auto_add_networks(self):
        networks = self.values.get("network", {}).get("networks", [])
        if not networks:
            return

        for network in networks:
            self.networks.register(network["name"])

    def container_names(self):
        return list(self._containers.keys())

    def add_container(self, name: str, image: str):
        name = name.strip()
        if not name:
            raise RenderError("Container name cannot be empty")
        container = Container(self, name, image)
        if name in self._containers:
            raise RenderError(f"Container {name} already exists.")
        self._containers[name] = container
        return container

    def render(self):
        if self.values != self._original_values:
            raise RenderError("Values have been modified since the renderer was created.")

        if not self._containers:
            raise RenderError("No containers added.")

        action_required = False
        if self.notes.has_deprecations() or self.notes.has_warnings():
            action_required = True

        result: dict = {
            "x-notes": self.notes.render(),
            "x-portals": self.portals.render(),
            "x-action-required": action_required,
            "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()

        if self.configs.has_configs():
            result["configs"] = self.configs.render()

        if self.networks.has_items():
            result["networks"] = self.networks.render()

        return result