|
Packit |
a20ca0 |
#!/usr/bin/python3
|
|
Packit |
a20ca0 |
"""
|
|
Packit |
a20ca0 |
Execute commands on first-boot
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
Sequentially execute a list of commands on first-boot / instantiation.
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
This stage uses a logic similar to systemd's first-boot to execute a given
|
|
Packit |
a20ca0 |
script only the first time the image is booted.
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
An empty flag file /etc/osbuild-first-boot is written to /etc and a systemd
|
|
Packit |
a20ca0 |
service is enabled that is only run when the file exits, and will remove it
|
|
Packit |
a20ca0 |
before executing the given commands.
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
If the flag-file cannot be removed, the service fails without executing
|
|
Packit |
a20ca0 |
any further first-boot commands.
|
|
Packit |
a20ca0 |
"""
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
import os
|
|
Packit |
a20ca0 |
import sys
|
|
Packit |
a20ca0 |
|
|
Packit Service |
2d981f |
import osbuild.api
|
|
Packit Service |
2d981f |
|
|
Packit Service |
2d981f |
|
|
Packit |
a20ca0 |
SCHEMA = """
|
|
Packit |
a20ca0 |
"additionalProperties": false,
|
|
Packit |
a20ca0 |
"required": ["commands"],
|
|
Packit |
a20ca0 |
"properties": {
|
|
Packit |
a20ca0 |
"commands": {
|
|
Packit |
a20ca0 |
"type": "array",
|
|
Packit |
a20ca0 |
"description": "The command lines to execute",
|
|
Packit |
a20ca0 |
"items": {
|
|
Packit |
a20ca0 |
"type": "string"
|
|
Packit |
a20ca0 |
}
|
|
Packit |
a20ca0 |
},
|
|
Packit |
a20ca0 |
"wait_for_network": {
|
|
Packit |
a20ca0 |
"type": "boolean",
|
|
Packit |
a20ca0 |
"description": "Wait for the network to be up before executing",
|
|
Packit |
a20ca0 |
"default": false
|
|
Packit |
a20ca0 |
}
|
|
Packit |
a20ca0 |
}
|
|
Packit |
a20ca0 |
"""
|
|
Packit |
a20ca0 |
|
|
Packit Service |
2d981f |
|
|
Packit |
a20ca0 |
def add_first_boot(tree, commands, wait_for_network):
|
|
Packit |
a20ca0 |
if wait_for_network:
|
|
Packit |
a20ca0 |
network = """Wants=network-online.target
|
|
Packit |
a20ca0 |
After=network-online.target"""
|
|
Packit |
a20ca0 |
else:
|
|
Packit |
a20ca0 |
network = ""
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
execs = "\n"
|
|
Packit |
a20ca0 |
for command in commands:
|
|
Packit |
a20ca0 |
execs += f"ExecStart={command}\n"
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
service = f"""[Unit]
|
|
Packit |
a20ca0 |
Description=OSBuild First Boot Service
|
|
Packit |
a20ca0 |
ConditionPathExists=/etc/osbuild-first-boot
|
|
Packit |
a20ca0 |
{network}
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
[Service]
|
|
Packit |
a20ca0 |
Type=oneshot
|
|
Packit |
a20ca0 |
{execs}"""
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
os.makedirs(f"{tree}/usr/lib/systemd/system/default.target.wants", exist_ok=True)
|
|
Packit |
a20ca0 |
with open(f"{tree}/usr/lib/systemd/system/osbuild-first-boot.service", "w") as f:
|
|
Packit |
a20ca0 |
f.write(service)
|
|
Packit |
a20ca0 |
os.symlink("../osbuild-first-boot.service",
|
|
Packit |
a20ca0 |
f"{tree}/usr/lib/systemd/system/default.target.wants/osbuild-first-boot.service")
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
os.makedirs(f"{tree}/etc", exist_ok=True)
|
|
Packit |
a20ca0 |
open(f"{tree}/etc/osbuild-first-boot", 'a').close()
|
|
Packit |
a20ca0 |
|
|
Packit Service |
2d981f |
|
|
Packit |
a20ca0 |
def main(tree, options):
|
|
Packit |
a20ca0 |
commands = options["commands"]
|
|
Packit |
a20ca0 |
wait_for_network = options.get("wait_for_network", False)
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
commands = ["/usr/bin/rm /etc/osbuild-first-boot"] + commands
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
add_first_boot(tree, commands, wait_for_network)
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
return 0
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
|
|
Packit |
a20ca0 |
if __name__ == '__main__':
|
|
Packit Service |
2d981f |
args = osbuild.api.arguments()
|
|
Packit |
a20ca0 |
r = main(args["tree"], args["options"])
|
|
Packit |
a20ca0 |
sys.exit(r)
|