1
0
Fork 0

Compare commits

..

7 commits

Author SHA1 Message Date
aa13b77758
generate: generate: Bump version to 0.0.11
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-08 16:20:55 +02:00
147e5630aa
generate: container: Create new ContainerOptions class for read_only and Co
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-08 16:20:12 +02:00
e4ec47401e
generate: generate: Bump version to 0.0.10
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-08 16:01:37 +02:00
45d2e3a3d1
containerrc: Update JSON schema
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-08 16:00:52 +02:00
7a794197f4
generate: container: Allow one volume to be mounted multiple times
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-08 15:58:53 +02:00
8b7221363c
generate: container: Allow one secret to be used multiple times
Secret a can now be used more than once, but currently only with the same
secret type.

Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-08 15:51:53 +02:00
486a38440f
generate: container: Add command section to image configuration
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-08 15:43:39 +02:00
3 changed files with 113 additions and 67 deletions

View file

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

View file

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

View file

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