from typing import TYPE_CHECKING if TYPE_CHECKING: from render import Render SHORT_LIVED = "short-lived" class Notes: def __init__(self, render_instance: "Render"): self._render_instance = render_instance self._app_name: str = "" self._app_train: str = "" self._warnings: list[str] = [] self._deprecations: list[str] = [] self._security: dict[str, list[str]] = {} self._header: str = "" self._body: str = "" self._footer: str = "" self._auto_set_app_name() self._auto_set_app_train() self._auto_set_header() self._auto_set_footer() def _is_enterprise_train(self): if self._app_train == "enterprise": return True def _auto_set_app_name(self): app_name = self._render_instance.values.get("ix_context", {}).get("app_metadata", {}).get("title", "") self._app_name = app_name or "" def _auto_set_app_train(self): app_train = self._render_instance.values.get("ix_context", {}).get("app_metadata", {}).get("train", "") self._app_train = app_train or "" def _auto_set_header(self): self._header = f"# {self._app_name}\n\n" def _auto_set_footer(self): url = "https://github.com/truenas/apps" if self._is_enterprise_train(): url = "https://ixsystems.atlassian.net" footer = "## Bug Reports and Feature Requests\n\n" footer += "If you find a bug in this app or have an idea for a new feature, please file an issue at\n" footer += f"{url}\n\n" self._footer = footer def add_warning(self, warning: str): self._warnings.append(warning) def _prepend_warning(self, warning: str): self._warnings.insert(0, warning) def add_deprecation(self, deprecation: str): self._deprecations.append(deprecation) def set_body(self, body: str): self._body = body def scan_containers(self): for name, c in self._render_instance._containers.items(): if self._security.get(name) is None: self._security[name] = [] if c.restart._policy == "on-failure": self._security[name].append(SHORT_LIVED) if c._privileged: self._security[name].append("Is running with privileged mode enabled") run_as = c._user.split(":") if c._user else [-1, -1] if run_as[0] in ["0", -1]: self._security[name].append(f"Is running as {'root' if run_as[0] == '0' else 'unknown'} user") if run_as[1] in ["0", -1]: self._security[name].append(f"Is running as {'root' if run_as[1] == '0' else 'unknown'} group") if any(x in c._group_add for x in ("root", 0)): self._security[name].append("Is running with supplementary root group") if c._ipc_mode == "host": self._security[name].append("Is running with host IPC namespace") if c._pid_mode == "host": self._security[name].append("Is running with host PID namespace") if c._cgroup == "host": self._security[name].append("Is running with host cgroup namespace") if "no-new-privileges=true" not in c._security_opt.render(): self._security[name].append("Is running without [no-new-privileges] security option") if c._tty: self._prepend_warning( f"Container [{name}] is running with a TTY, " "Logs will not appear correctly in the UI due to an [upstream bug]" "(https://github.com/docker/docker-py/issues/1394)" ) self._security = {k: v for k, v in self._security.items() if v} def render(self): self.scan_containers() result = self._header if self._warnings: result += "## Warnings\n\n" for warning in self._warnings: result += f"- {warning}\n" result += "\n" if self._deprecations: result += "## Deprecations\n\n" for deprecation in self._deprecations: result += f"- {deprecation}\n" result += "\n" if self._security: result += "## Security\n\n" for c_name, security in self._security.items(): if SHORT_LIVED in security and len(security) == 1: continue result += f"### Container: [{c_name}]" if SHORT_LIVED in security: result += "\n\n**This container is short-lived.**" result += "\n\n" for s in [s for s in security if s != "short-lived"]: result += f"- {s}\n" result += "\n" if self._body: result += self._body.strip() + "\n\n" result += self._footer return result