1
0
Fork 0

Compare commits

..

No commits in common. "aa13b777585f80b8e88d46a4a50b832d0b37106e" and "fb6aadb63cf04061308616829a9a8179529dfb27" have entirely different histories.

3 changed files with 67 additions and 113 deletions

View file

@ -16,9 +16,6 @@
}, },
"tag": { "tag": {
"type": "string" "type": "string"
},
"command": {
"type": "string"
} }
}, },
"required": [ "required": [
@ -55,7 +52,11 @@
"type": "array", "type": "array",
"items": {} "items": {}
} }
} },
"required": [
"mode",
"options"
]
}, },
"dns": { "dns": {
"type": "object", "type": "object",
@ -71,7 +72,11 @@
} }
] ]
} }
} },
"required": [
"search",
"servers"
]
}, },
"ports": { "ports": {
"type": "object", "type": "object",
@ -92,7 +97,11 @@
} }
] ]
} }
} },
"required": [
"tcp",
"udp"
]
}, },
"env": { "env": {
"type": "object" "type": "object"
@ -123,7 +132,11 @@
} }
] ]
} }
} },
"required": [
"add",
"drop"
]
}, },
"accounting": { "accounting": {
"type": "object", "type": "object",

View file

@ -244,7 +244,7 @@ class Volume:
"""Container Volume.""" """Container Volume."""
name: str name: str
path: list path: str
@classmethod @classmethod
def from_json(cls, val: ConfigValue, logger: Log) -> list: def from_json(cls, val: ConfigValue, logger: Log) -> list:
@ -254,16 +254,7 @@ class Volume:
if not isinstance(val, dict): if not isinstance(val, dict):
logger.log_warning("Volume key is present, but malformed.") logger.log_warning("Volume key is present, but malformed.")
return [] return []
return [ return [cls(key, value) for key, value in val.items()]
Volume.from_json_entry(key, value) for key, value in val.items()
]
@classmethod
def from_json_entry(cls, key: str, value: str | list) -> Self:
"""Create from JSON entry."""
if isinstance(value, str):
return cls(key, [value])
return cls(key, value)
def is_host_volume(self) -> bool: def is_host_volume(self) -> bool:
"""Check if this Volume is a named or a host volume.""" """Check if this Volume is a named or a host volume."""
@ -271,10 +262,7 @@ class Volume:
def command(self) -> str: def command(self) -> str:
"""Option for podman container create.""" """Option for podman container create."""
cmd = "" return f"--volume {self.name}:{self.path}"
for path in self.path:
cmd += f"\t--volume {self.name}:{path} \\\n"
return cmd
def create(self) -> str: def create(self) -> str:
"""Create volume, if it does not exist.""" """Create volume, if it does not exist."""
@ -310,7 +298,7 @@ class Secret:
name: str name: str
secret_type: str secret_type: str
target: list target: str
options: str options: str
@classmethod @classmethod
@ -328,33 +316,26 @@ class Secret:
continue continue
name = key name = key
secret_type = maybe_or(val[key], "type", "") secret_type = maybe_or(val[key], "type", "")
target = maybe(val[key], "target") target = maybe_or(val[key], "target", "")
if isinstance(target, str):
target = [target]
if not isinstance(target, list):
target = []
options = maybe_or(val[key], "options", "") options = maybe_or(val[key], "options", "")
if options is None: if options is None:
options = "" options = ""
secrets.append(cls(name, str(secret_type), target, str(options))) secrets.append(
cls(name, str(secret_type), str(target), str(options))
)
return secrets return secrets
def command(self) -> str: def command(self) -> str:
"""Option for podman container create.""" """Option for podman container create."""
cmd = "" cmd = (
for target in self.target: f"--secret {self.name},:"
cmd += ( f"type={self.secret_type},"
f"\t--secret {self.name},:" f"target={self.target}"
f"type={self.secret_type}," )
f"target={target}" # Not a password, ruff...
) if self.secret_type == "mount" and self.options != "": # noqa: S105
# Not a password, ruff... cmd = f"{cmd},{self.options}"
has_option = self.secret_type == "mount" # noqa: S105
has_option = has_option and self.options != ""
if has_option:
cmd = f"{cmd},{self.options}"
cmd += " \\\n"
return cmd return cmd
@ -525,7 +506,6 @@ class Image:
registry: str registry: str
image: str image: str
tag: str tag: str
cmd: str
@classmethod @classmethod
def from_json(cls, val: ConfigValue, logger: Log) -> Self | None: def from_json(cls, val: ConfigValue, logger: Log) -> Self | None:
@ -536,13 +516,10 @@ class Image:
registry = maybe_or(val, "registry", "") registry = maybe_or(val, "registry", "")
image = maybe_or(val, "image", "") image = maybe_or(val, "image", "")
tag = maybe_or(val, "tag", "") tag = maybe_or(val, "tag", "")
cmd = maybe_or(val, "command", "") return cls(str(registry), str(image), str(tag))
return cls(str(registry), str(image), str(tag), cmd)
def command(self) -> str: def command(self) -> str:
"""Option for podman container create.""" """Option for podman container create."""
if self.cmd != "":
return f"{self.registry}/{self.image}:{self.tag} {self.cmd}"
return f"{self.registry}/{self.image}:{self.tag}" return f"{self.registry}/{self.image}:{self.tag}"
@ -608,69 +585,17 @@ class Dns:
return cmd return cmd
@dataclass
class ContainerOptions:
"""Container-Meta settings."""
privileged: bool = False
read_only: bool = False
replace: bool = False
restart: str = "no"
pull_policy: str = "always"
timezone: str = "local"
is_valid: bool = False
@classmethod
def from_json(cls, val: ConfigValue, logger: Log) -> Self:
"""Create from JSON."""
if val is None:
# Should not happen!
return cls()
if not isinstance(val, dict):
logger.log_error("Container config is invalid!")
return cls()
privileged = maybe_or(val, "privileged", _or=False)
read_only = maybe_or(val, "read_only", _or=False)
replace = maybe_or(val, "replace", _or=False)
restart = maybe_or(val, "restart", "no")
pull_policy = maybe_or(val, "pull_policy", "always")
timezone = maybe_or(val, "timezone", "local")
return cls(
privileged,
read_only,
replace,
restart,
pull_policy,
timezone,
is_valid=True,
)
def command(self) -> str:
"""Option for podman conainter create."""
cmd = ""
if self.privileged:
cmd += "\t--privileged \\\n"
if self.read_only:
cmd += "\t--read-only \\\n"
if self.replace:
cmd += "\t--replace \\\n"
if self.restart != "":
cmd += f"\t--restart={self.restart} \\\n"
if self.pull_policy != "":
cmd += f"\t--pull-policy={self.pull_policy} \\\n"
if self.timezone != "":
cmd += f"\t--tz={self.timezone} \\\n"
return ""
class Container: class Container:
"""Container.""" """Container."""
name: str name: str
image: Image image: Image
ct_opts: ContainerOptions privileged: bool
read_only: bool
replace: bool
restart: str
pull_policy: str
timezone: str
network: Network network: Network
dns: Dns dns: Dns
ports: Ports ports: Ports
@ -692,9 +617,12 @@ class Container:
if image is None: if image is None:
logger.log_error("No image set, aborting!") logger.log_error("No image set, aborting!")
return return
ct_opts = ContainerOptions.from_json(json, logger) privileged = maybe(json, "privileged")
if not ct_opts.is_valid: read_only = maybe(json, "read_only")
return replace = maybe(json, "replace")
pull_policy = maybe_or(json, "pull_policy", "always")
restart = maybe_or(json, "restart", "no")
timezone = maybe_or(json, "timezone", "local")
network = maybe(json, "network") network = maybe(json, "network")
dns = maybe(json, "dns") dns = maybe(json, "dns")
ports = maybe(json, "ports") ports = maybe(json, "ports")
@ -705,7 +633,12 @@ class Container:
accounting = maybe(json, "accounting") accounting = maybe(json, "accounting")
self.name = str(name) self.name = str(name)
self.image = Image.from_json(image, logger) self.image = Image.from_json(image, logger)
self.ct_opts = ct_opts self.privileged = privileged is not None and bool(privileged)
self.read_only = read_only is not None and bool(read_only)
self.replace = replace is not None and bool(replace)
self.pull_policy = str(pull_policy)
self.restart = str(restart)
self.timezone = str(timezone)
self.network = Network.from_json(network, logger) self.network = Network.from_json(network, logger)
self.dns = Dns.from_json(dns, logger) self.dns = Dns.from_json(dns, logger)
self.ports = Ports.from_json(ports, logger) self.ports = Ports.from_json(ports, logger)
@ -746,15 +679,23 @@ class Container:
cmd = f"# Create container {self.name}\n" cmd = f"# Create container {self.name}\n"
cmd += "podman container create \\\n" cmd += "podman container create \\\n"
cmd += f"\t--name={self.name} \\\n" cmd += f"\t--name={self.name} \\\n"
cmd += f"{self.ct_opts.command()}" if self.privileged:
cmd += "\t--privileged \\\n"
if self.replace:
cmd += "\t--replace \\\n"
if self.read_only:
cmd += "\t--read-only \\\n"
cmd += f"\t--restart={self.restart} \\\n"
cmd += f"\t--pull={self.pull_policy} \\\n"
cmd += f"\t--tz={self.timezone} \\\n"
cmd += f"{self.network.command()}" cmd += f"{self.network.command()}"
cmd += f"{self.dns.command()}" cmd += f"{self.dns.command()}"
cmd += f"{self.ports.command()}" cmd += f"{self.ports.command()}"
cmd += f"{self.env.command()}" cmd += f"{self.env.command()}"
for secret in self.secrets: for secret in self.secrets:
cmd += f"{secret.command()}" cmd += f"\t{secret.command()} \\\n"
for volume in self.volumes: for volume in self.volumes:
cmd += f"{volume.command()}" cmd += f"\t{volume.command()} \\\n"
for capability in self.capabilities: for capability in self.capabilities:
cmd += f"\t{capability.command()} \\\n" cmd += f"\t{capability.command()} \\\n"
cmd += f"{self.accounting.command()}" cmd += f"{self.accounting.command()}"

View file

@ -14,7 +14,7 @@ from pathlib import Path
from container import ConfigError, Container from container import ConfigError, Container
from log import Log from log import Log
GENERATE_VERSION = "0.0.11" GENERATE_VERSION = "0.0.9"
HEADER = f"""#!/bin/sh HEADER = f"""#!/bin/sh
# This script was generated by containerctl v{GENERATE_VERSION} # This script was generated by containerctl v{GENERATE_VERSION}
# Report bugs with _this script_ to <tenno+containerctl@suij.in> # Report bugs with _this script_ to <tenno+containerctl@suij.in>