forked from osbuild/osbuild
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add org.osbuild.containers.unit.create stage
This is essentially org.osbuild.systemd.unit.create but creates file where podman looks for quadlets instead. For now only container, volume and network is supported. Not all quadlet options are supported, but at least the most usef ones, and enough for the automotive sample-images.
- Loading branch information
1 parent
a2f46cb
commit 9a03607
Showing
2 changed files
with
391 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#!/usr/bin/python3 | ||
import configparser | ||
import sys | ||
|
||
import osbuild.api | ||
|
||
|
||
def validate(filename, cfg): | ||
# ensure the service name does not exceed maximum filename length | ||
if len(filename) > 255: | ||
raise ValueError(f"Error: the {filename} unit exceeds the maximum filename length.") | ||
|
||
# Filename extension must match the config: | ||
# .service requires a Service section | ||
# .mount requires a Mount section | ||
if filename.endswith(".container") and "Container" not in cfg: | ||
raise ValueError(f"Error: {filename} unit requires Container section") | ||
if filename.endswith(".volume") and "Volume" not in cfg: | ||
raise ValueError(f"Error: {filename} unit requires Volume section") | ||
if filename.endswith(".network") and "Network" not in cfg: | ||
raise ValueError(f"Error: {filename} unit requires Network section") | ||
|
||
|
||
def main(tree, options): | ||
filename = options["filename"] | ||
cfg = options["config"] | ||
validate(filename, cfg) | ||
|
||
# We trick configparser into letting us write multiple instances of the same option by writing them as keys with no | ||
# value, so we enable allow_no_value | ||
config = configparser.ConfigParser(allow_no_value=True, interpolation=None) | ||
# prevent conversion of the option name to lowercase | ||
config.optionxform = lambda option: option | ||
|
||
for section, opts in cfg.items(): | ||
if not config.has_section(section): | ||
config.add_section(section) | ||
for option, value in opts.items(): | ||
if isinstance(value, list): | ||
for v in value: | ||
if option == "Environment": | ||
# Option value becomes "KEY=VALUE" (quoted) | ||
v = '"' + v["key"] + "=" + str(v["value"]) + '"' | ||
config.set(section, str(option) + "=" + str(v)) | ||
else: | ||
config.set(section, option, str(value)) | ||
persistent = options.get("unit-path", "usr") | ||
systemd_dir = str() | ||
if persistent == "usr": | ||
systemd_dir = f"{tree}/usr/share/containers/systemd" | ||
elif persistent == "etc": | ||
systemd_dir = f"{tree}/etc/containers/systemd" | ||
|
||
with open(f"{systemd_dir}/{filename}", "w", encoding="utf8") as f: | ||
config.write(f, space_around_delimiters=False) | ||
|
||
|
||
if __name__ == '__main__': | ||
args = osbuild.api.arguments() | ||
r = main(args["tree"], args["options"]) | ||
sys.exit(r) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,330 @@ | ||
{ | ||
"summary": "Create a podman systemd unit file", | ||
"description": [ | ||
"This stage allows to create Podman systemd (quadlet) unit files. The `filename` property", | ||
"specifies the, '.service' or '.mount' file to be added. These names are", | ||
"validated using the, same rules as specified by podman-systemd.unit(5) and they", | ||
"must contain the, '.container', '.volume' or '.network' suffix (other types of unit files", | ||
"are not supported). 'unit-path' determines determine the unit load path.", | ||
"", | ||
"The Unit configuration can currently specify the following subset", | ||
"of options:", | ||
" - 'Unit' section", | ||
" - 'Description' - string", | ||
" - 'ConditionPathExists' - string", | ||
" - 'ConditionPathIsDirectory' - string", | ||
" - 'DefaultDependencies' - bool", | ||
" - 'Requires' - [strings]", | ||
" - 'Wants' - [strings]", | ||
" - 'After' - [strings]", | ||
" - 'Before' - [strings]", | ||
" - 'Service' section", | ||
" - 'Restart' - string", | ||
" - 'Container' section", | ||
" - 'Image' - string", | ||
" - 'Exec' - string", | ||
" - 'Volume' - [string]", | ||
" - 'User' - string", | ||
" - 'Group' - string", | ||
" - 'AddDevice' - string", | ||
" - 'Environment' - [object]", | ||
" - 'Network' - string", | ||
" - 'WorkingDir' - string", | ||
" - 'Volume' section", | ||
" - 'VolumeName' - string", | ||
" - 'Driver' - string", | ||
" - 'Image' - string", | ||
" - 'User' - string", | ||
" - 'Group' - string", | ||
" - 'Network' section", | ||
" - 'Gateway' - string", | ||
" - 'DNS' - string", | ||
" - 'IPRange' - string", | ||
" - 'Subnet' - string", | ||
" - 'Driver' - string", | ||
" - 'NetworkName' - string", | ||
" - 'Install' section", | ||
" - 'WantedBy' - [string]", | ||
" - 'RequiredBy' - [string]" | ||
], | ||
"schema": { | ||
"additionalProperties": false, | ||
"required": [ | ||
"filename", | ||
"config" | ||
], | ||
"properties": { | ||
"filename": { | ||
"type": "string", | ||
"pattern": "^[\\w:.\\\\-]+[@]{0,1}[\\w:.\\\\-]*\\.(container|volume|network)$" | ||
}, | ||
"unit-path": { | ||
"type": "string", | ||
"enum": [ | ||
"usr", | ||
"etc" | ||
], | ||
"default": "usr", | ||
"description": "Define the system load path" | ||
}, | ||
"config": { | ||
"additionalProperties": false, | ||
"type": "object", | ||
"oneOf": [ | ||
{ | ||
"required": [ | ||
"Unit", | ||
"Container", | ||
"Install" | ||
], | ||
"not": { | ||
"required": [ | ||
"Service", | ||
"Volume", | ||
"Network" | ||
] | ||
} | ||
}, | ||
{ | ||
"required": [ | ||
"Volume" | ||
], | ||
"not": { | ||
"required": [ | ||
"Container", | ||
"Network", | ||
"Service" | ||
] | ||
} | ||
}, | ||
{ | ||
"required": [ | ||
"Network" | ||
], | ||
"not": { | ||
"required": [ | ||
"Service", | ||
"Container", | ||
"Volume" | ||
] | ||
} | ||
} | ||
], | ||
"description": "Configuration for a '.container' unit.", | ||
"properties": { | ||
"Unit": { | ||
"additionalProperties": false, | ||
"type": "object", | ||
"description": "'Unit' configuration section of a unit file.", | ||
"properties": { | ||
"Description": { | ||
"type": "string" | ||
}, | ||
"Wants": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"After": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"Before": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"Requires": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"ConditionPathExists": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"ConditionPathIsDirectory": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"DefaultDependencies": { | ||
"type": "boolean" | ||
} | ||
} | ||
}, | ||
"Service": { | ||
"additionalProperties": false, | ||
"type": "object", | ||
"description": "'Service' configuration section of a unit file.", | ||
"properties": { | ||
"Restart": { | ||
"type": "string", | ||
"enum": [ | ||
"no", | ||
"on-success", | ||
"on-failure", | ||
"on-abnormal", | ||
"on-watchdog", | ||
"on-abort", | ||
"always" | ||
] | ||
} | ||
} | ||
}, | ||
"Container": { | ||
"additionalProperties": false, | ||
"type": "object", | ||
"description": "'Container' configuration section of a unit file.", | ||
"required": [ | ||
"Image" | ||
], | ||
"properties": { | ||
"Environment": { | ||
"type": "array", | ||
"description": "Sets environment variables for executed process.", | ||
"items": { | ||
"type": "object", | ||
"description": "Sets environment variables for executed process.", | ||
"additionalProperties": false, | ||
"properties": { | ||
"key": { | ||
"type": "string", | ||
"pattern": "^[A-Za-z_][A-Za-z0-9_]*" | ||
}, | ||
"value": { | ||
"type": "string" | ||
} | ||
} | ||
} | ||
}, | ||
"Image": { | ||
"description": "Container Image to use", | ||
"type": "string" | ||
}, | ||
"Exec": { | ||
"description": "Command to execute in container", | ||
"type": "string" | ||
}, | ||
"Volume": { | ||
"description": "Volumes to use", | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"User": { | ||
"description": "Run as user", | ||
"type": "string" | ||
}, | ||
"Group": { | ||
"description": "Run as group", | ||
"type": "string" | ||
}, | ||
"AddDevice": { | ||
"description": "Add device to container", | ||
"type": "string" | ||
}, | ||
"Network": { | ||
"description": "What network option to use", | ||
"type": "string" | ||
}, | ||
"WorkingDir": { | ||
"description": "Working directory for initial process", | ||
"type": "string" | ||
} | ||
} | ||
}, | ||
"Volume": { | ||
"additionalProperties": false, | ||
"type": "object", | ||
"description": "'Volume' configuration section of a unit file.", | ||
"required": [ | ||
"What" | ||
], | ||
"properties": { | ||
"VolumeName": { | ||
"description": "Override volume name", | ||
"type": "string" | ||
}, | ||
"Driver": { | ||
"description": "What volume driver to use", | ||
"type": "string" | ||
}, | ||
"Image": { | ||
"description": "Image to use if driver is image", | ||
"type": "string" | ||
}, | ||
"User": { | ||
"description": "User to use as owner of the volume", | ||
"type": "string" | ||
}, | ||
"Group": { | ||
"description": "Group to use as owner of the volume", | ||
"type": "string" | ||
} | ||
} | ||
}, | ||
"Network": { | ||
"additionalProperties": false, | ||
"type": "object", | ||
"description": "'Network' configuration section of a unit file.", | ||
"properties": { | ||
"Gateway": { | ||
"description": "Addres of gaterway", | ||
"type": "boolean" | ||
}, | ||
"DNS": { | ||
"description": "Address of DNS server", | ||
"type": "boolean" | ||
}, | ||
"IPRange": { | ||
"description": "Range to allocate IPs from", | ||
"type": "boolean" | ||
}, | ||
"Subnet": { | ||
"description": "Subnet in CIDR notation", | ||
"type": "boolean" | ||
}, | ||
"Driver": { | ||
"description": "What network driver to use", | ||
"type": "boolean" | ||
}, | ||
"NetworkName": { | ||
"description": "Override network name", | ||
"type": "boolean" | ||
} | ||
} | ||
}, | ||
"Install": { | ||
"additionalProperties": false, | ||
"type": "object", | ||
"description": "'Install' configuration section of a unit file.", | ||
"properties": { | ||
"WantedBy": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
}, | ||
"RequiredBy": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |