stage/systemd-unit:stage to create systemd unit file

Add systemd unit files in osbuild stage

This stage creates systemd unit file in `/usr/lib/systemd/system/`.
The stage accepts filename which must end with `.service`.Section
`Unit` , `Service` , `Install` accepts various parameters as per
the systemd documentaion.`systemd-analyze verify` is be performed
after the .service file is created to check for potential errors.

Signed-off-by: Sayan Paul <paul.sayan@gmail.com>
This commit is contained in:
Sayan Paul 2024-02-08 15:26:28 +05:30 committed by Michael Vogt
parent f9e35c25da
commit e858dc72c3
6 changed files with 2388 additions and 0 deletions

View file

@ -0,0 +1,181 @@
#!/usr/bin/python3
"""
Create Systemd services unit file
This stage allows to create Systemd unit files in
`/usr/lib/systemd/system/`. The `filename` property specifies the
'.service' file to be added. These names are validated using the
same rules as specified by systemd.unit(5) and they must contain the
'.service' suffix (other types of unit files are not supported).
The Unit configuration can currently specify the following subset
of options:
- 'Unit' section
- 'Description' - string
- 'ConditionPathExists' - string
- 'DefaultDependencies' - bool
- 'Requires' - [strings]
- 'Wants' - [strings]
- 'Service' section
- 'Type' - string
- 'RemainAfterExit' - bool
- 'ExecStartPre' - [string]
- 'ExecStopPost' - [string]
- 'ExecStart' - [string]
- 'Install' section
- 'WantedBy' - [string]
- 'RequiredBy' - [string]
"""
import configparser
import subprocess
import sys
import osbuild.api
SCHEMA = r"""
"additionalProperties": false,
"required": [
"filename",
"config"
],
"properties": {
"filename": {
"type": "string",
"pattern": "^[\\w:.\\\\-]+[@]{0,1}[\\w:.\\\\-]*\\.service$"
},
"config": {
"additionalProperties": false,
"type": "object",
"required": ["Unit", "Service", "Install"],
"description": "Configuration for a '.service' 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"
}
},
"Requires": {
"type": "array",
"items": {
"type": "string"
}
},
"ConditionPathExists": {
"type": "array",
"items": {
"type": "string"
}
},
"DefaultDependencies": {
"type": "boolean"
}
}
},
"Service": {
"additionalProperties": false,
"type": "object",
"description": "'Service' configuration section of a unit file.",
"properties": {
"Type": {
"type": "string",
"enum": [
"simple",
"exec",
"forking",
"oneshot",
"dbus",
"notify",
"notify-reload",
"idle"
]
},
"RemainAfterExit": {
"type": "boolean"
},
"ExecStartPre": {
"type": "array",
"items": {
"type": "string"
}
},
"ExecStopPost": {
"type": "array",
"items": {
"type": "string"
}
},
"ExecStart": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"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"
}
}
}
}
}
}
}
"""
def main(tree, options):
filename = options["filename"]
# ensure the service name does not exceed maximum filename length
if len(filename) > 255:
raise ValueError(f"Error: the {filename} service exceeds the maximum filename length.")
cfg = options["config"]
config = configparser.ConfigParser(allow_no_value=True)
# 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:
config.set(section, str(option) + "=" + str(v))
else:
config.set(section, option, str(value))
systemd_dir = f"{tree}/usr/lib/systemd/system"
with open(f"{systemd_dir}/{filename}", "w", encoding="utf8") as f:
config.write(f, space_around_delimiters=False)
subprocess.run(["systemd-analyze", "verify", f"{systemd_dir}/{filename}"], check=True)
if __name__ == '__main__':
args = osbuild.api.arguments()
r = main(args["tree"], args["options"])
sys.exit(r)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,31 @@
version: '2'
pipelines:
- mpp-import-pipelines:
path: ../manifests/fedora-vars.ipp.yaml
- mpp-import-pipeline:
path: ../manifests/fedora-build-v2.ipp.yaml
id: build
runner:
mpp-format-string: org.osbuild.fedora{release}
- name: tree
build: name:build
stages:
- type: org.osbuild.rpm
inputs:
packages:
type: org.osbuild.files
origin: org.osbuild.source
mpp-depsolve:
architecture: $arch
module-platform-id: $module_platform_id
repos:
mpp-eval: repos
packages:
- nftables
- openssh-server
- systemd
options:
gpgkeys:
mpp-eval: gpgkeys
exclude:
docs: true

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
version: '2'
pipelines:
- mpp-import-pipelines:
path: ../manifests/fedora-vars.ipp.yaml
- mpp-import-pipeline:
path: ../manifests/fedora-build-v2.ipp.yaml
id: build
runner:
mpp-format-string: org.osbuild.fedora{release}
- name: tree
build: name:build
stages:
- type: org.osbuild.rpm
inputs:
packages:
type: org.osbuild.files
origin: org.osbuild.source
mpp-depsolve:
architecture: $arch
module-platform-id: $module_platform_id
repos:
mpp-eval: repos
packages:
- nftables
- openssh-server
- systemd
options:
gpgkeys:
mpp-eval: gpgkeys
exclude:
docs: true
- type: org.osbuild.systemd.unit.create
options:
filename: create-directory.service
config:
Unit:
Description: Create directory
DefaultDependencies: false
ConditionPathExists:
- "|!/etc/myfile"
Service:
Type: "oneshot"
RemainAfterExit: true
ExecStart:
- mkdir -p /etc/mydir
- touch /etc/myfile
Install:
WantedBy:
- local-fs.target
RequiredBy:
- multi-user.target

View file

@ -0,0 +1,7 @@
{
"added_files": [
"/usr/lib/systemd/system/create-directory.service"
],
"deleted_files": [],
"differences": {}
}