stages: externalize schemas

Copy all schemas into the relevant `.meta.json` files instead of having
them contained inside the stages.
This commit is contained in:
Simon de Vlieger 2024-03-14 10:34:18 +01:00 committed by Achilleas Koutsou
parent 02b6d696ef
commit bb58892571
296 changed files with 10435 additions and 9394 deletions

View file

@ -1,56 +1,9 @@
#!/usr/bin/python3
"""
Configure basic aspects of the anaconda installer
Create an anaconda configuration file `90-osbuild.conf` in
the folder `/etc/anaconda/conf.d` to configure anaconda.
Currently only the list of enabled kickstart modules is
configurable via the `kickstart-modules` option.
"""
import os
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": true,
"properties": {
"kickstart-modules": {
"type": "array",
"description": "Kick start modules to enable",
"items": {
"type": "string"
},
"minItems": 1
},
"activatable-modules": {
"type": "array",
"description": "Kick start modules to activate",
"items": {
"type": "string"
},
"minItems": 1
},
"forbidden-modules": {
"type": "array",
"description": "Kick start modules to forbid",
"items": {
"type": "string"
},
"minItems": 1
},
"optional-modules": {
"type": "array",
"description": "Kick start modules to activate but are allowed to fail",
"items": {
"type": "string"
},
"minItems": 1
}
}
"""
CONFIG = """
# osbuild customizations

View file

@ -0,0 +1,46 @@
{
"summary": "Configure basic aspects of the anaconda installer",
"description": [
"Create an anaconda configuration file `90-osbuild.conf` in",
"the folder `/etc/anaconda/conf.d` to configure anaconda.",
"Currently only the list of enabled kickstart modules is",
"configurable via the `kickstart-modules` option."
],
"schema": {
"additionalProperties": true,
"properties": {
"kickstart-modules": {
"type": "array",
"description": "Kick start modules to enable",
"items": {
"type": "string"
},
"minItems": 1
},
"activatable-modules": {
"type": "array",
"description": "Kick start modules to activate",
"items": {
"type": "string"
},
"minItems": 1
},
"forbidden-modules": {
"type": "array",
"description": "Kick start modules to forbid",
"items": {
"type": "string"
},
"minItems": 1
},
"optional-modules": {
"type": "array",
"description": "Kick start modules to activate but are allowed to fail",
"items": {
"type": "string"
},
"minItems": 1
}
}
}
}

View file

@ -1,26 +1,10 @@
#!/usr/bin/python3
"""
Configure authentication sources using authconfig.
Applies the default settings. Backups are cleared.
Notes:
- Requires 'chroot' in the buildroot.
- Runs the 'authconfig' binary from the image in the chroot.
"""
import shutil
import subprocess
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"description": "Configure authentication sources."
"""
def main(tree):
cmd = [

View file

@ -0,0 +1,13 @@
{
"summary": "Configure authentication sources using authconfig.",
"description": [
"Applies the default settings. Backups are cleared.",
"Notes:",
" - Requires 'chroot' in the buildroot.",
" - Runs the 'authconfig' binary from the image in the chroot."
],
"schema": {
"additionalProperties": false,
"description": "Configure authentication sources."
}
}

View file

@ -1,45 +1,9 @@
#!/usr/bin/python3
"""
Select system identity and authentication sources with authselect.
Sets system identity and authentication sources.
The stage calls `authselect select` to set authselect profile to 'profile'.
Optionally a list of profile features to enable may be provided using 'features'
option. The list of available profile features can be obtained by running
`authselect list-features <profile>`.
Notes:
- Requires 'chroot' in the buildroot.
- Runs the 'authselect' binary from the image in the chroot.
"""
import subprocess
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"required": ["profile"],
"description": "Select system identity and authentication sources.",
"properties": {
"profile": {
"type": "string",
"description": "Desired authselect profile to activate."
},
"features": {
"type": "array",
"description": "Features of the selected profile to activate.",
"minItems": 1,
"items": {
"type": "string"
}
}
}
"""
def main(tree, options):
profile = options["profile"]

View file

@ -0,0 +1,34 @@
{
"summary": "Select system identity and authentication sources with authselect.",
"description": [
"Sets system identity and authentication sources.",
"The stage calls `authselect select` to set authselect profile to 'profile'.",
"Optionally a list of profile features to enable may be provided using 'features'",
"option. The list of available profile features can be obtained by running",
"`authselect list-features <profile>`.",
"Notes:",
" - Requires 'chroot' in the buildroot.",
" - Runs the 'authselect' binary from the image in the chroot."
],
"schema": {
"additionalProperties": false,
"required": [
"profile"
],
"description": "Select system identity and authentication sources.",
"properties": {
"profile": {
"type": "string",
"description": "Desired authselect profile to activate."
},
"features": {
"type": "array",
"description": "Features of the selected profile to activate.",
"minItems": 1,
"items": {
"type": "string"
}
}
}
}
}

View file

@ -1,15 +1,4 @@
#!/usr/bin/python3
"""
Run bootc install to-filesystem
Note that this needs the disk.img in the inputs as one continous
devices so that bootupd can install grub to the mbr. It also needs
all relevant mount points for booting (e.g. /boot, /boot/efi) in
mounted in the "mounts" path.
Buildhost commands used: bootc
"""
import subprocess
import sys
import tempfile
@ -17,48 +6,6 @@ import tempfile
import osbuild.api
from osbuild.util import containers
CAPABILITIES = ["CAP_MAC_ADMIN"]
SCHEMA_2 = r"""
"inputs": {
"type": "object",
"additionalProperties": false,
"required": ["images"],
"properties": {
"images": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"properties": {
"root-ssh-authorized-keys": {
"description": "array of SSH Public Keys to add to roots authorized_keys",
"type": "array",
"items": {
"type": "string"
}
},
"kernel-args": {
"description": "array of additional kernel arguments",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
}
"""
def main(options, inputs, paths):
images = containers.parse_containers_input(inputs)

View file

@ -0,0 +1,54 @@
{
"summary": "Run bootc install to-filesystem",
"description": [
"Note that this needs the disk.img in the inputs as one continous",
"devices so that bootupd can install grub to the mbr. It also needs",
"all relevant mount points for booting (e.g. /boot, /boot/efi) in",
"mounted in the \"mounts\" path.",
"Buildhost commands used: bootc"
],
"capabilities": [
"CAP_MAC_ADMIN"
],
"schema_2": {
"inputs": {
"type": "object",
"additionalProperties": false,
"required": [
"images"
],
"properties": {
"images": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"properties": {
"root-ssh-authorized-keys": {
"description": "array of SSH Public Keys to add to roots authorized_keys",
"type": "array",
"items": {
"type": "string"
}
},
"kernel-args": {
"description": "array of additional kernel arguments",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
}
}
}

View file

@ -1,28 +1,4 @@
#!/usr/bin/python3
"""
Assemble a file system tree for a bootable iso
This stage prepares a file system tree for a bootable ISO, like the
Anaconda installer. It follows the convention used by Lorax to
create the boot isos: It takes an input `rootfs`, which will serve
as the root file system. This is copied into a file with a `ext4`
file system which in turn will be made into a squashfs file system.
Options for controlling the root file-system creation can be given
via `rootfs`, like it size and the compression to be used.
The boot loader is configured via the `isolinux` and `efi` options.
Which combination makes sense depends on the targeted platform and
architecture.
The kernel and initrd are taken from the tree given via the `kernel`
input, or if that was not specified, from `rootfs`. In either case
it will look for the specified kernel in the `/boot` directory.
Additionally kernel command line flags can passed via `kernel_opts`.
This stage has the `.mono` suffix to indicate that is a monolithic
stage that could, and in the future will be, broken up into smaller
pieces.
"""
import contextlib
import os
import re
@ -34,119 +10,6 @@ import tempfile
import osbuild.api
import osbuild.remoteloop as remoteloop
SCHEMA_2 = """
"options": {
"additionalProperties": false,
"required": ["product", "kernel", "isolabel"],
"properties": {
"product": {
"type": "object",
"additionalProperties": false,
"required": ["name", "version"],
"properties": {
"name": {"type": "string"},
"version": {"type": "string"}
}
},
"kernel": {
"type": "string"
},
"isolabel": {
"type": "string"
},
"efi": {
"type": "object",
"additionalProperties": false,
"required": ["architectures", "vendor"],
"properties": {
"architectures": {
"type": "array",
"items": {
"type": "string"
}
},
"vendor": {
"type": "string"
}
}
},
"isolinux": {
"type": "object",
"additionalProperties": false,
"required": ["enabled"],
"properties": {
"enabled": {
"type": "boolean"
},
"debug": {
"type": "boolean"
}
}
},
"kernel_opts": {
"description": "Additional kernel boot options",
"type": "string"
},
"templates": {
"type": "string",
"default": "99-generic"
},
"rootfs": {
"type": "object",
"additionalProperties": false,
"properties": {
"compression": {
"type": "object",
"additionalProperties": false,
"required": ["method"],
"properties": {
"method": {
"enum": ["gzip", "xz", "lz4"]
},
"options": {
"type": "object",
"additionalProperties": false,
"properties": {
"bcj": {
"enum": [
"x86",
"arm",
"armthumb",
"powerpc",
"sparc",
"ia64"
]
}
}
}
}
},
"size": {
"type": "integer",
"description": "size in MB",
"default": 3072
}
}
}
}
},
"inputs": {
"type": "object",
"additionalProperties": false,
"required": ["rootfs"],
"properties": {
"rootfs": {
"type": "object",
"additionalProperties": true
},
"kernel": {
"type": "object",
"additionalProperties": true
}
}
}
"""
LORAX_TEMPLATES = "/usr/share/lorax/templates.d"

View file

@ -0,0 +1,158 @@
{
"summary": "Assemble a file system tree for a bootable iso",
"description": [
"This stage prepares a file system tree for a bootable ISO, like the",
"Anaconda installer. It follows the convention used by Lorax to",
"create the boot isos: It takes an input `rootfs`, which will serve",
"as the root file system. This is copied into a file with a `ext4`",
"file system which in turn will be made into a squashfs file system.",
"Options for controlling the root file-system creation can be given",
"via `rootfs`, like it size and the compression to be used.",
"The boot loader is configured via the `isolinux` and `efi` options.",
"Which combination makes sense depends on the targeted platform and",
"architecture.",
"The kernel and initrd are taken from the tree given via the `kernel`",
"input, or if that was not specified, from `rootfs`. In either case",
"it will look for the specified kernel in the `/boot` directory.",
"Additionally kernel command line flags can passed via `kernel_opts`.",
"This stage has the `.mono` suffix to indicate that is a monolithic",
"stage that could, and in the future will be, broken up into smaller",
"pieces."
],
"schema_2": {
"options": {
"additionalProperties": false,
"required": [
"product",
"kernel",
"isolabel"
],
"properties": {
"product": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"version"
],
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"kernel": {
"type": "string"
},
"isolabel": {
"type": "string"
},
"efi": {
"type": "object",
"additionalProperties": false,
"required": [
"architectures",
"vendor"
],
"properties": {
"architectures": {
"type": "array",
"items": {
"type": "string"
}
},
"vendor": {
"type": "string"
}
}
},
"isolinux": {
"type": "object",
"additionalProperties": false,
"required": [
"enabled"
],
"properties": {
"enabled": {
"type": "boolean"
},
"debug": {
"type": "boolean"
}
}
},
"kernel_opts": {
"description": "Additional kernel boot options",
"type": "string"
},
"templates": {
"type": "string",
"default": "99-generic"
},
"rootfs": {
"type": "object",
"additionalProperties": false,
"properties": {
"compression": {
"type": "object",
"additionalProperties": false,
"required": [
"method"
],
"properties": {
"method": {
"enum": [
"gzip",
"xz",
"lz4"
]
},
"options": {
"type": "object",
"additionalProperties": false,
"properties": {
"bcj": {
"enum": [
"x86",
"arm",
"armthumb",
"powerpc",
"sparc",
"ia64"
]
}
}
}
}
},
"size": {
"type": "integer",
"description": "size in MB",
"default": 3072
}
}
}
}
},
"inputs": {
"type": "object",
"additionalProperties": false,
"required": [
"rootfs"
],
"properties": {
"rootfs": {
"type": "object",
"additionalProperties": true
},
"kernel": {
"type": "object",
"additionalProperties": true
}
}
}
}
}

View file

@ -1,14 +1,4 @@
#!/usr/bin/python3
"""
Install GRUB on both BIOS and UEFI systems,
ensuring that your bootloader stays up-to-date.
Bootupd supports updating GRUB and shim for
UEFI firmware on x86_64 and aarch64,
and GRUB for BIOS firmware on x86_64.
The project is deployed in Fedora CoreOS and derivatives
"""
import contextlib
import json
import os
@ -18,84 +8,6 @@ import sys
import osbuild.api
from osbuild.util import ostree
SCHEMA_2 = r"""
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
},
"options": {
"additionalProperties": false,
"properties": {
"deployment": {
"type": "object",
"additionalProperties": false,
"oneOf": [
{
"properties": {
"default": {"enum": [false]}
},
"required": ["osname", "ref"]
},
{
"properties": {
"default": {"enum": [true]}
},
"not": {
"anyOf": [
{"required": ["osname"]},
{"required": ["ref"]},
{"required": ["serial"]}
]
}
}
],
"properties": {
"osname": {
"description": "Name of the stateroot to be used in the deployment",
"type": "string"
},
"ref": {
"description": "OStree ref to create and use for deployment",
"type": "string"
},
"serial": {
"description": "The deployment serial (usually '0')",
"type": "number",
"default": 0
},
"default": {
"description": "Find and use the default ostree deployment",
"type": "boolean",
"default": false
}
}
},
"static-configs": {
"description": "Install the grub configs defined for Fedora CoreOS",
"type": "boolean"
},
"bios": {
"additionalProperties": false,
"type": "object",
"required": ["device"],
"properties": {
"device": {
"description": "Name of stage device to install GRUB for BIOS-based systems.",
"type": "string"
},
"partition": {
"description": "The partition on the stage device to install to, if installing to a partition",
"type": "number"
}
}
}
}
}
"""
@contextlib.contextmanager
def bind_mounts(sources, dest_root):

View file

@ -1,15 +1,4 @@
#!/usr/bin/python3
"""
Transforms /usr/lib/ostree-boot into a bootupd-compatible update payload.
Scrapes metadata (e.g. RPM versions) about shim/grub and puts them along with their component files in
`/usr/lib/bootupd/updates/`.
Notes:
- Requires 'chroot' in the buildroot.
- Runs the 'bootupctl' binary from the image in the chroot.
"""
import os
import subprocess
import sys
@ -17,12 +6,6 @@ import sys
import osbuild.api
from osbuild.util.mnt import MountGuard, MountPermissions
SCHEMA_2 = r"""
"options": {
"additionalProperties": false
}
"""
def main(tree):
with MountGuard() as mounter:

View file

@ -0,0 +1,15 @@
{
"summary": "Transforms /usr/lib/ostree-boot into a bootupd-compatible update payload.",
"description": [
"Scrapes metadata (e.g. RPM versions) about shim/grub and puts them along with their component files in",
"`/usr/lib/bootupd/updates/`.",
"Notes:",
" - Requires 'chroot' in the buildroot.",
" - Runs the 'bootupctl' binary from the image in the chroot."
],
"schema_2": {
"options": {
"additionalProperties": false
}
}
}

View file

@ -0,0 +1,111 @@
{
"summary": "Install GRUB on both BIOS and UEFI systems,\nensuring that your bootloader stays up-to-date.",
"description": [
"Bootupd supports updating GRUB and shim for",
"UEFI firmware on x86_64 and aarch64,",
"and GRUB for BIOS firmware on x86_64.",
"The project is deployed in Fedora CoreOS and derivatives"
],
"schema_2": {
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
},
"options": {
"additionalProperties": false,
"properties": {
"deployment": {
"type": "object",
"additionalProperties": false,
"oneOf": [
{
"properties": {
"default": {
"enum": [
false
]
}
},
"required": [
"osname",
"ref"
]
},
{
"properties": {
"default": {
"enum": [
true
]
}
},
"not": {
"anyOf": [
{
"required": [
"osname"
]
},
{
"required": [
"ref"
]
},
{
"required": [
"serial"
]
}
]
}
}
],
"properties": {
"osname": {
"description": "Name of the stateroot to be used in the deployment",
"type": "string"
},
"ref": {
"description": "OStree ref to create and use for deployment",
"type": "string"
},
"serial": {
"description": "The deployment serial (usually '0')",
"type": "number",
"default": 0
},
"default": {
"description": "Find and use the default ostree deployment",
"type": "boolean",
"default": false
}
}
},
"static-configs": {
"description": "Install the grub configs defined for Fedora CoreOS",
"type": "boolean"
},
"bios": {
"additionalProperties": false,
"type": "object",
"required": [
"device"
],
"properties": {
"device": {
"description": "Name of stage device to install GRUB for BIOS-based systems.",
"type": "string"
},
"partition": {
"description": "The partition on the stage device to install to, if installing to a partition",
"type": "number"
}
}
}
}
}
}
}

View file

@ -1,46 +1,10 @@
#!/usr/bin/python3
"""
Create subvolumes on a mounted btrfs partition.
See `btrfs`(8).
Buildhost commands used: `btrfs`.
"""
import os
import subprocess
import sys
import osbuild.api
SCHEMA_2 = r"""
"options": {
"additionalProperties": false,
"properties": {
"subvolumes": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["name"],
"properties": {
"name": {
"type": "string"
}
}
}
}
}
},
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
}
"""
def main(paths, options):
volume = paths["mounts"]

View file

@ -0,0 +1,36 @@
{
"summary": "Create subvolumes on a mounted btrfs partition.",
"description": [
"See `btrfs`(8).",
"Buildhost commands used: `btrfs`."
],
"schema_2": {
"options": {
"additionalProperties": false,
"properties": {
"subvolumes": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
}
}
}
}
}
},
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
}
}
}

View file

@ -1,47 +1,10 @@
#!/usr/bin/python3
"""
Create a /.buildstamp file describing the system
This will create a './buildstamp' with the specified parameters.
"""
import configparser
import datetime
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": true,
"required": ["arch", "product", "version", "final"],
"properties": {
"arch": {
"description": "Build architecture.",
"type": "string"
},
"product": {
"description": "The product name.",
"type": "string"
},
"version": {
"description": "The version .",
"type": "string"
},
"final": {
"description": "The product.",
"type": "boolean"
},
"variant": {
"description": "The variant of the product.",
"type": "string"
},
"bugurl": {
"description": "The bugurl of the product.",
"type": "string"
}
}
"""
def main(tree, options):
buildarch = options["arch"]

View file

@ -0,0 +1,41 @@
{
"summary": "Create a /.buildstamp file describing the system",
"description": [
"This will create a './buildstamp' with the specified parameters."
],
"schema": {
"additionalProperties": true,
"required": [
"arch",
"product",
"version",
"final"
],
"properties": {
"arch": {
"description": "Build architecture.",
"type": "string"
},
"product": {
"description": "The product name.",
"type": "string"
},
"version": {
"description": "The version .",
"type": "string"
},
"final": {
"description": "The product.",
"type": "boolean"
},
"variant": {
"description": "The variant of the product.",
"type": "string"
},
"bugurl": {
"description": "The bugurl of the product.",
"type": "string"
}
}
}
}

View file

@ -1,8 +1,4 @@
#!/usr/bin/python3
"""
Runs `chattr` to set file/directory attributes.
"""
import os
import subprocess
import sys
@ -11,38 +7,6 @@ from urllib.parse import ParseResult, urlparse
import osbuild.api
SCHEMA_2 = r"""
"options": {
"additionalProperties": false,
"properties": {
"items": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^mount:\/\/[^\/]+\/|^tree:\/\/\/": {
"type": "object",
"required": ["immutable"],
"properties": {
"immutable": {
"type": "boolean",
"description": "Make the file/directory immutable",
"default": true
}
}
}
}
}
}
},
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
}
"""
def parse_mount(url: ParseResult, args: Dict):
name = url.netloc

View file

@ -0,0 +1,37 @@
{
"summary": "Runs `chattr` to set file/directory attributes.",
"description": [],
"schema_2": {
"options": {
"additionalProperties": false,
"properties": {
"items": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^mount://[^/]+/|^tree:///": {
"type": "object",
"required": [
"immutable"
],
"properties": {
"immutable": {
"type": "boolean",
"description": "Make the file/directory immutable",
"default": true
}
}
}
}
}
}
},
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
}
}
}

View file

@ -1,10 +1,4 @@
#!/usr/bin/python3
"""
Change file mode bits
Change the file mode bits of one or more files or directories inside the tree.
"""
import os
import subprocess
import sys
@ -12,35 +6,6 @@ import sys
import osbuild.api
from osbuild.util.path import in_tree
SCHEMA_2 = r"""
"options": {
"additionalProperties": false,
"properties": {
"items": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^\\/(?!\\.\\.)((?!\\/\\.\\.\\/).)+$": {
"type": "object",
"required": ["mode"],
"properties": {
"mode": {
"type": "string",
"description": "Symbolic or numeric octal mode"
},
"recursive": {
"type": "boolean",
"description": "Change modes recursively",
"default": false
}
}
}
}
}
}
}
"""
def chmod(path: str, mode: str, recursive: bool):
arguments = [mode]

View file

@ -0,0 +1,36 @@
{
"summary": "Change file mode bits",
"description": [
"Change the file mode bits of one or more files or directories inside the tree."
],
"schema_2": {
"options": {
"additionalProperties": false,
"properties": {
"items": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^\\/(?!\\.\\.)((?!\\/\\.\\.\\/).)+$": {
"type": "object",
"required": [
"mode"
],
"properties": {
"mode": {
"type": "string",
"description": "Symbolic or numeric octal mode"
},
"recursive": {
"type": "boolean",
"description": "Change modes recursively",
"default": false
}
}
}
}
}
}
}
}
}

View file

@ -1,73 +1,9 @@
#!/usr/bin/python3
"""
Change file owner and group
Change the file user and/or group ownership of one or more files or directories inside the tree.
Notes:
- Requires 'chroot' in the buildroot.
- Runs the 'chown' binary from the image in the chroot.
"""
import subprocess
import sys
import osbuild.api
SCHEMA_2 = r"""
"options": {
"additionalProperties": false,
"properties": {
"items": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^\\/(?!\\.\\.)((?!\\/\\.\\.\\/).)+$": {
"type": "object",
"anyOf": [
{"required" : ["user"]},
{"required" : ["group"]}
],
"properties": {
"user": {
"oneOf": [
{
"type": "string",
"pattern": "^[A-Za-z0-9_.][A-Za-z0-9_.-]{0,31}$"
},
{
"type": "number",
"minimum": 0
}
],
"description": "User name or UID"
},
"group": {
"oneOf": [
{
"type": "string",
"pattern": "^[A-Za-z0-9_][A-Za-z0-9_-]{0,31}$"
},
{
"type": "number",
"minimum": 0
}
],
"description": "Group name or GID"
},
"recursive": {
"type": "boolean",
"default": false,
"description": "Change ownership recursively"
}
}
}
}
}
}
}
"""
def main(tree, options):
for path, cmdargs in options["items"].items():

View file

@ -0,0 +1,70 @@
{
"summary": "Change file owner and group",
"description": [
"Change the file user and/or group ownership of one or more files or directories inside the tree.",
"Notes:",
" - Requires 'chroot' in the buildroot.",
" - Runs the 'chown' binary from the image in the chroot."
],
"schema_2": {
"options": {
"additionalProperties": false,
"properties": {
"items": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^\\/(?!\\.\\.)((?!\\/\\.\\.\\/).)+$": {
"type": "object",
"anyOf": [
{
"required": [
"user"
]
},
{
"required": [
"group"
]
}
],
"properties": {
"user": {
"oneOf": [
{
"type": "string",
"pattern": "^[A-Za-z0-9_.][A-Za-z0-9_.-]{0,31}$"
},
{
"type": "number",
"minimum": 0
}
],
"description": "User name or UID"
},
"group": {
"oneOf": [
{
"type": "string",
"pattern": "^[A-Za-z0-9_][A-Za-z0-9_-]{0,31}$"
},
{
"type": "number",
"minimum": 0
}
],
"description": "Group name or GID"
},
"recursive": {
"type": "boolean",
"default": false,
"description": "Change ownership recursively"
}
}
}
}
}
}
}
}
}

View file

@ -1,100 +1,9 @@
#!/usr/bin/python3
"""
Configure chrony to set system time from the network.
Configures `chrony` by modifying `/etc/chrony.conf`.
Before new values are added to the chrony configuration, all lines starting with
"server", "pool" or "peer" are removed.
The 'timeservers' option provides a very high-level way of configuring chronyd
with specific timeservers. Its value is a list of strings representing the
hostname or IP address of the timeserver. For each list item, the following
line will be added to the configuration:
`server <HOSTNAME/IP> iburst`
The 'servers' option provides a direct mapping to the `server` directive from
chrony configuration. Its value is a list of dictionaries representing each
timeserver which should be added to the configuration. For each list item,
a `server` directive will be added the configuration. Currently supported
subset of options which can be specified for each timeserver item:
- 'hostname' (REQUIRED)
- 'minpoll'
- 'maxpoll'
- 'iburst' (defaults to true)
- 'prefer' (defaults to false)
The 'leapsectz' option configures chrony behavior related to automatic checking
of the next occurrence of the leap second, using the provided timezone. Its
value is a string representing a timezone from the system tz database (e.g.
'right/UTC'). If an empty string is provided, then all occurrences of
'leapsectz' directive are removed from the configuration.
Constraints:
- Exactly one of 'timeservers' or 'servers' options must be provided.
"""
import re
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"oneOf": [
{"required": ["timeservers"]},
{"required": ["servers"]}
],
"properties": {
"timeservers": {
"type": "array",
"items": { "type": "string" },
"description": "Array of NTP server addresses."
},
"servers": {
"type": "array",
"items": {
"additionalProperties": false,
"type": "object",
"required": ["hostname"],
"properties": {
"hostname": {
"type": "string",
"description": "Hostname or IP address of a NTP server."
},
"minpoll": {
"type": "integer",
"description": "Specifies the minimum interval between requests sent to the server as a power of 2 in seconds.",
"minimum": -6,
"maximum": 24
},
"maxpoll": {
"type": "integer",
"description": "Specifies the maximum interval between requests sent to the server as a power of 2 in seconds.",
"minimum": -6,
"maximum": 24
},
"iburst": {
"type": "boolean",
"default": true,
"description": "Configures chronyd behavior related to burst requests on startup."
},
"prefer": {
"type": "boolean",
"default": false,
"description": "Prefer this source over sources without the prefer option."
}
}
}
},
"leapsectz": {
"type": "string",
"description": "Timezone used by chronyd to determine when will the next leap second occur. Empty value will remove the option."
}
}
"""
DELETE_TIME_SOURCE_LINE_REGEX = re.compile(r"(server|pool|peer) ")
DELETE_LEAPSECTZ_LINE_REGEX = re.compile(r"leapsectz ")

View file

@ -0,0 +1,96 @@
{
"summary": "Configure chrony to set system time from the network.",
"description": [
"Configures `chrony` by modifying `/etc/chrony.conf`.",
"Before new values are added to the chrony configuration, all lines starting with",
"\"server\", \"pool\" or \"peer\" are removed.",
"The 'timeservers' option provides a very high-level way of configuring chronyd",
"with specific timeservers. Its value is a list of strings representing the",
"hostname or IP address of the timeserver. For each list item, the following",
"line will be added to the configuration:",
"`server <HOSTNAME/IP> iburst`",
"The 'servers' option provides a direct mapping to the `server` directive from",
"chrony configuration. Its value is a list of dictionaries representing each",
"timeserver which should be added to the configuration. For each list item,",
"a `server` directive will be added the configuration. Currently supported",
"subset of options which can be specified for each timeserver item:",
" - 'hostname' (REQUIRED)",
" - 'minpoll'",
" - 'maxpoll'",
" - 'iburst' (defaults to true)",
" - 'prefer' (defaults to false)",
"The 'leapsectz' option configures chrony behavior related to automatic checking",
"of the next occurrence of the leap second, using the provided timezone. Its",
"value is a string representing a timezone from the system tz database (e.g.",
"'right/UTC'). If an empty string is provided, then all occurrences of",
"'leapsectz' directive are removed from the configuration.",
"Constraints:",
" - Exactly one of 'timeservers' or 'servers' options must be provided."
],
"schema": {
"additionalProperties": false,
"oneOf": [
{
"required": [
"timeservers"
]
},
{
"required": [
"servers"
]
}
],
"properties": {
"timeservers": {
"type": "array",
"items": {
"type": "string"
},
"description": "Array of NTP server addresses."
},
"servers": {
"type": "array",
"items": {
"additionalProperties": false,
"type": "object",
"required": [
"hostname"
],
"properties": {
"hostname": {
"type": "string",
"description": "Hostname or IP address of a NTP server."
},
"minpoll": {
"type": "integer",
"description": "Specifies the minimum interval between requests sent to the server as a power of 2 in seconds.",
"minimum": -6,
"maximum": 24
},
"maxpoll": {
"type": "integer",
"description": "Specifies the maximum interval between requests sent to the server as a power of 2 in seconds.",
"minimum": -6,
"maximum": 24
},
"iburst": {
"type": "boolean",
"default": true,
"description": "Configures chronyd behavior related to burst requests on startup."
},
"prefer": {
"type": "boolean",
"default": false,
"description": "Prefer this source over sources without the prefer option."
}
}
}
},
"leapsectz": {
"type": "string",
"description": "Timezone used by chronyd to determine when will the next leap second occur. Empty value will remove the option."
}
}
}
}

View file

@ -1,49 +1,10 @@
#!/usr/bin/python3
"""
Bind a LUKS device using the specified policy.
Buildhost commands used: `clevis`, `clevis-luks`, `clevis-pin-*`.
"""
import os
import subprocess
import sys
import osbuild.api
SCHEMA_2 = r"""
"devices": {
"type": "object",
"additionalProperties": true,
"required": ["device"],
"properties": {
"device": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"required": ["passphrase", "pin", "policy"],
"properties": {
"passphrase": {
"description": "Passphrase to unlock the container",
"type": "string"
},
"pin": {
"description": "The pin to use",
"type": "string"
},
"policy": {
"description": "Policy to use with the given pin",
"type": "string"
}
}
}
"""
def main(devices, options):
device = devices["device"]

View file

@ -0,0 +1,43 @@
{
"summary": "Bind a LUKS device using the specified policy.",
"description": [
"Buildhost commands used: `clevis`, `clevis-luks`, `clevis-pin-*`."
],
"schema_2": {
"devices": {
"type": "object",
"additionalProperties": true,
"required": [
"device"
],
"properties": {
"device": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"required": [
"passphrase",
"pin",
"policy"
],
"properties": {
"passphrase": {
"description": "Passphrase to unlock the container",
"type": "string"
},
"pin": {
"description": "The pin to use",
"type": "string"
},
"policy": {
"description": "Policy to use with the given pin",
"type": "string"
}
}
}
}
}

View file

@ -1,146 +1,10 @@
#!/usr/bin/python3
"""
Configure cloud-init
The 'config' option allows to configure cloud-init by creating a
configuration file under `/etc/cloud/cloud.cfg.d` with the name
specified by `filename`.
Constrains:
- Each configuration file definition must contain at least one configuration
Currently supported subset of cloud-init configuration:
- 'system_info' section
- 'default_user' section
- 'name' option
"""
import sys
import yaml
import osbuild.api
SCHEMA = r"""
"definitions": {
"reporting_handlers": {
"type": "string",
"enum": ["log", "print", "webhook", "hyperv"]
}
},
"additionalProperties": false,
"required": ["config", "filename"],
"properties": {
"filename": {
"type": "string",
"description": "Name of the cloud-init configuration file.",
"pattern": "^[\\w.-]{1,251}\\.cfg$"
},
"config": {
"additionalProperties": false,
"type": "object",
"description": "cloud-init configuration",
"minProperties": 1,
"properties": {
"system_info": {
"additionalProperties": false,
"type": "object",
"description": "'system_info' configuration section.",
"minProperties": 1,
"properties": {
"default_user": {
"additionalProperties": false,
"type": "object",
"description": "Configuration of the 'default' user created by cloud-init.",
"minProperties": 1,
"properties": {
"name": {
"type": "string",
"description": "username of the 'default' user."
}
}
}
}
},
"reporting": {
"type": "object",
"additionalProperties": false,
"description": "Define reporting endpoints.",
"minProperties": 1,
"properties": {
"logging": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"$ref": "#/definitions/reporting_handlers"
}
}
},
"telemetry": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"$ref": "#/definitions/reporting_handlers"
}
}
}
}
},
"datasource_list": {
"type": "array",
"items": {
"type": "string",
"enum": ["Azure"]
}
},
"datasource": {
"type": "object",
"description": "Sources of configuration data for cloud-init.",
"minProperties": 1,
"properties": {
"Azure": {
"type": "object",
"minProperties": 1,
"properties": {
"apply_network_config": {
"type": "boolean",
"description": "Whether to use network configuration described by Azures IMDS endpoint",
"default": true
}
}
}
}
},
"output": {
"type": "object",
"minProperties": 1,
"properties": {
"init": {
"description": "Redirect the output of the init stage",
"type": "string"
},
"config": {
"description": "Redirect the output of the config stage",
"type": "string"
},
"final": {
"description": "Redirect the output of the final stage",
"type": "string"
},
"all": {
"description": "Redirect the output of all stages",
"type": "string"
}
}
}
}
}
}
"""
# Class representing the 'datasource_list' configuration option,
# allowing to define a custom YAML dumper for it.

View file

@ -0,0 +1,142 @@
{
"summary": "Configure cloud-init",
"description": [
"The 'config' option allows to configure cloud-init by creating a",
"configuration file under `/etc/cloud/cloud.cfg.d` with the name",
"specified by `filename`.",
"Constrains:",
" - Each configuration file definition must contain at least one configuration",
"Currently supported subset of cloud-init configuration:",
" - 'system_info' section",
" - 'default_user' section",
" - 'name' option"
],
"schema": {
"definitions": {
"reporting_handlers": {
"type": "string",
"enum": [
"log",
"print",
"webhook",
"hyperv"
]
}
},
"additionalProperties": false,
"required": [
"config",
"filename"
],
"properties": {
"filename": {
"type": "string",
"description": "Name of the cloud-init configuration file.",
"pattern": "^[\\w.-]{1,251}\\.cfg$"
},
"config": {
"additionalProperties": false,
"type": "object",
"description": "cloud-init configuration",
"minProperties": 1,
"properties": {
"system_info": {
"additionalProperties": false,
"type": "object",
"description": "'system_info' configuration section.",
"minProperties": 1,
"properties": {
"default_user": {
"additionalProperties": false,
"type": "object",
"description": "Configuration of the 'default' user created by cloud-init.",
"minProperties": 1,
"properties": {
"name": {
"type": "string",
"description": "username of the 'default' user."
}
}
}
}
},
"reporting": {
"type": "object",
"additionalProperties": false,
"description": "Define reporting endpoints.",
"minProperties": 1,
"properties": {
"logging": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"$ref": "#/definitions/reporting_handlers"
}
}
},
"telemetry": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"$ref": "#/definitions/reporting_handlers"
}
}
}
}
},
"datasource_list": {
"type": "array",
"items": {
"type": "string",
"enum": [
"Azure"
]
}
},
"datasource": {
"type": "object",
"description": "Sources of configuration data for cloud-init.",
"minProperties": 1,
"properties": {
"Azure": {
"type": "object",
"minProperties": 1,
"properties": {
"apply_network_config": {
"type": "boolean",
"description": "Whether to use network configuration described by Azure\u2019s IMDS endpoint",
"default": true
}
}
}
}
},
"output": {
"type": "object",
"minProperties": 1,
"properties": {
"init": {
"description": "Redirect the output of the init stage",
"type": "string"
},
"config": {
"description": "Redirect the output of the config stage",
"type": "string"
},
"final": {
"description": "Redirect the output of the final stage",
"type": "string"
},
"all": {
"description": "Redirect the output of all stages",
"type": "string"
}
}
}
}
}
}
}
}

View file

@ -1,9 +1,4 @@
#!/usr/bin/python3
"""
Deploy a container.
Buildhost commands used: podman skopeo
"""
import contextlib
import os
import random
@ -14,33 +9,6 @@ import sys
import osbuild.api
from osbuild.util import containers
SCHEMA_2 = r"""
"inputs": {
"type": "object",
"additionalProperties": false,
"required": ["images"],
"properties": {
"images": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"properties": {
"exclude": {
"type": "array",
"description": "Exclude paths from the deployment",
"minItems": 1,
"items": {
"type": "string"
}
}
}
}
"""
@contextlib.contextmanager
def mount_container(image_tag):

View file

@ -0,0 +1,34 @@
{
"summary": "Deploy a container.",
"description": [
"Buildhost commands used: podman skopeo"
],
"schema_2": {
"inputs": {
"type": "object",
"additionalProperties": false,
"required": [
"images"
],
"properties": {
"images": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"properties": {
"exclude": {
"type": "array",
"description": "Exclude paths from the deployment",
"minItems": 1,
"items": {
"type": "string"
}
}
}
}
}
}

View file

@ -1,12 +1,4 @@
#!/usr/bin/python3
"""
Edit containers-storage.conf(5) files.
This stage can be used to create or modify `containers-storage.conf`
configuration files. The default strategy is to merge the specified
options with the existing options.
"""
import contextlib
import os
import sys
@ -19,126 +11,6 @@ except ModuleNotFoundError:
import osbuild.api
SCHEMA = r"""
"definitions": {
"storage": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"driver": {
"description": "container storage driver.",
"type": "string",
"enum": [
"overlay",
"vfs",
"devmapper",
"aufs",
"btrfs",
"zfs"
]
},
"graphroot": {
"description": "container storage graph directory.",
"type": "string"
},
"runroot": {
"description": "container storage run directory.",
"type": "string"
},
"transient_store": {
"description": "Make the container store not persist across reboot.",
"type": "boolean"
},
"options": {
"$ref": "#/definitions/storage-options"
}
}
},
"storage-options": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"additionalimagestores": {
"type": "array",
"items": {
"type": "string"
}
},
"pull_options": {
"$ref": "#/definitions/storage-options-pulloptions"
},
"overlay": {
"$ref": "#/definitions/storage-options-overlay"
}
}
},
"storage-options-pulloptions": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"enable_partial_images": {
"type": "string",
"enum": ["true", "false"]
},
"use_hard_links": {
"type": "string",
"enum": ["true", "false"]
},
"convert_images": {
"type": "string",
"enum": ["true", "false"]
}
}
},
"storage-options-overlay": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"mountopt": {
"type": "string"
}
}
}
},
"additionalProperties": false,
"required": ["config"],
"properties": {
"filename": {
"type": "string",
"description": "location of the configuration file.",
"default": "/etc/containers/storage.conf",
"enum": [
"/etc/containers/storage.conf",
"/usr/share/containers/storage.conf"
]
},
"filebase": {
"type": "string",
"description": "Read the base configuration from this file."
},
"comment": {
"type": "array",
"items": {
"type": "string"
}
},
"config": {
"additionalProperties": false,
"type": "object",
"description": "storage configuration",
"minProperties": 1,
"properties": {
"storage": {
"$ref": "#/definitions/storage"
}
}
}
}
"""
DEFAULT_LOCATION = "/etc/containers/storage.conf"

View file

@ -0,0 +1,139 @@
{
"summary": "Edit containers-storage.conf(5) files.",
"description": [
"This stage can be used to create or modify `containers-storage.conf`",
"configuration files. The default strategy is to merge the specified",
"options with the existing options."
],
"schema": {
"definitions": {
"storage": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"driver": {
"description": "container storage driver.",
"type": "string",
"enum": [
"overlay",
"vfs",
"devmapper",
"aufs",
"btrfs",
"zfs"
]
},
"graphroot": {
"description": "container storage graph directory.",
"type": "string"
},
"runroot": {
"description": "container storage run directory.",
"type": "string"
},
"transient_store": {
"description": "Make the container store not persist across reboot.",
"type": "boolean"
},
"options": {
"$ref": "#/definitions/storage-options"
}
}
},
"storage-options": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"additionalimagestores": {
"type": "array",
"items": {
"type": "string"
}
},
"pull_options": {
"$ref": "#/definitions/storage-options-pulloptions"
},
"overlay": {
"$ref": "#/definitions/storage-options-overlay"
}
}
},
"storage-options-pulloptions": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"enable_partial_images": {
"type": "string",
"enum": [
"true",
"false"
]
},
"use_hard_links": {
"type": "string",
"enum": [
"true",
"false"
]
},
"convert_images": {
"type": "string",
"enum": [
"true",
"false"
]
}
}
},
"storage-options-overlay": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"mountopt": {
"type": "string"
}
}
}
},
"additionalProperties": false,
"required": [
"config"
],
"properties": {
"filename": {
"type": "string",
"description": "location of the configuration file.",
"default": "/etc/containers/storage.conf",
"enum": [
"/etc/containers/storage.conf",
"/usr/share/containers/storage.conf"
]
},
"filebase": {
"type": "string",
"description": "Read the base configuration from this file."
},
"comment": {
"type": "array",
"items": {
"type": "string"
}
},
"config": {
"additionalProperties": false,
"type": "object",
"description": "storage configuration",
"minProperties": 1,
"properties": {
"storage": {
"$ref": "#/definitions/storage"
}
}
}
}
}
}

View file

@ -1,24 +1,4 @@
#!/usr/bin/python3
"""
Copy items
Stage to copy items, that is files or trees, from inputs to mount
points or the tree. Multiple items can be copied. The source and
destination is an url. Supported locations ('schemes') are `tree`,
`mount` and `input`.
The path format follows the rsync convention that if the paths
ends with a slash `/` the content of that directory is copied not
the directory itself.
Note that the stage by default does not remove the destination
before copying. As a result, if the destination is an existing
symlink to a file, then this file will be overwritten, instead of
the symlink being replaced. If you want to replace the symlink
with a file, you need to set the `remove_destination` option to
`true`. This option works only for files, not directories or
symlinks to directories.
"""
import os
import subprocess
import sys
@ -27,79 +7,6 @@ from urllib.parse import ParseResult, urlparse
import osbuild.api
CAPABILITIES = ["CAP_MAC_ADMIN"]
SCHEMA_2 = r"""
"options": {
"additionalProperties": false,
"required": ["paths"],
"properties": {
"paths": {
"description": "Array of items to copy",
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["from", "to"],
"properties": {
"from": {
"oneOf": [
{
"type": "string",
"description": "The source, if an input",
"pattern": "^input:\/\/[^\/]+\/"
},
{
"type": "string",
"description": "The source, if a mount",
"pattern": "^mount:\/\/[^\/]+\/"
},
{
"type": "string",
"description": "The source, if the tree",
"pattern": "^tree:\/\/\/"
}
]
},
"to": {
"oneOf": [
{
"type": "string",
"description": "The destination, if a mount",
"pattern": "^mount:\/\/[^\/]+\/"
},
{
"type": "string",
"description": "The destination, if the tree",
"pattern": "^tree:\/\/\/"
}
]
},
"remove_destination": {
"type": "boolean",
"description": "Remove the destination before copying. Works only for files, not directories.",
"default": false
}
}
}
}
}
},
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
},
"inputs": {
"type": "object",
"additionalProperties": true
}
"""
def parse_mount(url: ParseResult, args: Dict):
name = url.netloc

View file

@ -0,0 +1,96 @@
{
"summary": "Copy items",
"description": [
"Stage to copy items, that is files or trees, from inputs to mount",
"points or the tree. Multiple items can be copied. The source and",
"destination is an url. Supported locations ('schemes') are `tree`,",
"`mount` and `input`.",
"The path format follows the rsync convention that if the paths",
"ends with a slash `/` the content of that directory is copied not",
"the directory itself.",
"Note that the stage by default does not remove the destination",
"before copying. As a result, if the destination is an existing",
"symlink to a file, then this file will be overwritten, instead of",
"the symlink being replaced. If you want to replace the symlink",
"with a file, you need to set the `remove_destination` option to",
"`true`. This option works only for files, not directories or",
"symlinks to directories."
],
"capabilities": [
"CAP_MAC_ADMIN"
],
"schema_2": {
"options": {
"additionalProperties": false,
"required": [
"paths"
],
"properties": {
"paths": {
"description": "Array of items to copy",
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"from",
"to"
],
"properties": {
"from": {
"oneOf": [
{
"type": "string",
"description": "The source, if an input",
"pattern": "^input://[^/]+/"
},
{
"type": "string",
"description": "The source, if a mount",
"pattern": "^mount://[^/]+/"
},
{
"type": "string",
"description": "The source, if the tree",
"pattern": "^tree:///"
}
]
},
"to": {
"oneOf": [
{
"type": "string",
"description": "The destination, if a mount",
"pattern": "^mount://[^/]+/"
},
{
"type": "string",
"description": "The destination, if the tree",
"pattern": "^tree:///"
}
]
},
"remove_destination": {
"type": "boolean",
"description": "Remove the destination before copying. Works only for files, not directories.",
"default": false
}
}
}
}
}
},
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
},
"inputs": {
"type": "object",
"additionalProperties": true
}
}
}

View file

@ -1,27 +1,4 @@
#!/usr/bin/python3
"""
Setup a CoreOS platform
In CoreOS we have the concept of a platform (i.e. AWS, GCP, Metal, QEMU)
where each platform has its own provided disk image with slightly
differing settings/behavior. This stage will perform the necessary
configuration for the given platform. This configuration boils down to
a few steps:
1. Locate the source of platform specific information that is provided
in the CoreOS filesystem tree already (the platforms.json).
2. Copy the platforms.json file into the /boot/ partition, which is
sometimes used by coreos-installer.
3. Read the platforms.json to fetch and platform specific kernel
arguments or grub configuration to set. These arguments/config
are primarily console settings.
4. Apply any platform specific kernel arguments along with the
`ignition.platform.id={platform-name}` kernel argument.
5. Create the grub console.cfg file and apply any platform
specific grub console configuration.
This stage is highly CoreOS specific and subject to change in the
future if/when we change the way platform specific information is
defined in our broader efforts to share more defaults with OSBuild.
"""
import json
import os
import shutil
@ -30,24 +7,6 @@ import sys
import osbuild.api
from osbuild.util import bls
SCHEMA_2 = r"""
"options": {
"additionalProperties": false,
"properties": {
"platform": {
"description": "The target platform name/ID",
"type": "string"
}
}
},
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
}
"""
# Constants
# For console.cfg see https://github.com/coreos/bootupd/pull/620

View file

@ -0,0 +1,42 @@
{
"summary": "Setup a CoreOS platform",
"description": [
"In CoreOS we have the concept of a platform (i.e. AWS, GCP, Metal, QEMU)",
"where each platform has its own provided disk image with slightly",
"differing settings/behavior. This stage will perform the necessary",
"configuration for the given platform. This configuration boils down to",
"a few steps:",
"1. Locate the source of platform specific information that is provided",
" in the CoreOS filesystem tree already (the platforms.json).",
"2. Copy the platforms.json file into the /boot/ partition, which is",
" sometimes used by coreos-installer.",
"3. Read the platforms.json to fetch and platform specific kernel",
" arguments or grub configuration to set. These arguments/config",
" are primarily console settings.",
"4. Apply any platform specific kernel arguments along with the",
" `ignition.platform.id={platform-name}` kernel argument.",
"5. Create the grub console.cfg file and apply any platform",
" specific grub console configuration.",
"This stage is highly CoreOS specific and subject to change in the",
"future if/when we change the way platform specific information is",
"defined in our broader efforts to share more defaults with OSBuild."
],
"schema_2": {
"options": {
"additionalProperties": false,
"properties": {
"platform": {
"description": "The target platform name/ID",
"type": "string"
}
}
},
"devices": {
"type": "object",
"additionalProperties": true
},
"mounts": {
"type": "array"
}
}
}

View file

@ -1,80 +1,10 @@
#!/usr/bin/python3
"""
Assembles the tree into a CPIO archive.
Uses the buildhost's `cpio` command, to create an archive
at `filename` with the contents of the input `tree`. If
`append` is `true`, its files will be added to an existing
archive. The default format is `newc` , the "new (SVR4)
portable format", which is also used by `dracut`(8).
Buildhost commands used: `cpio`
"""
import os
import subprocess
import sys
import osbuild.api
SCHEMA_2 = """
"options": {
"additionalProperties": false,
"required": ["filename"],
"properties": {
"filename": {
"description": "Filename for tar archive",
"type": "string"
},
"append": {
"type": "boolean",
"description": "Append to an existing archive.",
"default": false
},
"format": {
"description": "Archive format to use",
"type": "string",
"enum": ["bin", "odc", "newc", "crc", "tar", "ustar"],
"default": "newc"
},
"root-node": {
"description": "How to handle the root node: include or omit",
"enum": ["include", "omit"],
"default": "include"
},
"reproducible": {
"type": "boolean",
"description": "Produce device-independent, reproducible archives.",
"default": true
},
"owner": {
"type": "object",
"additionalProperties": false,
"required": ["user"],
"properties": {
"user": {
"type": "string"
},
"group": {
"type": "string"
}
}
}
}
},
"inputs": {
"type": "object",
"additionalProperties": false,
"required": ["tree"],
"properties": {
"tree": {
"type": "object",
"additionalProperties": true
}
}
}
"""
def iter_files(tree, include):
for root, _, files in os.walk(tree, topdown=True):

View file

@ -0,0 +1,84 @@
{
"summary": "Assembles the tree into a CPIO archive.",
"description": [
"Uses the buildhost's `cpio` command, to create an archive",
"at `filename` with the contents of the input `tree`. If",
"`append` is `true`, its files will be added to an existing",
"archive. The default format is `newc` , the \"new (SVR4)",
"portable format\", which is also used by `dracut`(8).",
"Buildhost commands used: `cpio`"
],
"schema_2": {
"options": {
"additionalProperties": false,
"required": [
"filename"
],
"properties": {
"filename": {
"description": "Filename for tar archive",
"type": "string"
},
"append": {
"type": "boolean",
"description": "Append to an existing archive.",
"default": false
},
"format": {
"description": "Archive format to use",
"type": "string",
"enum": [
"bin",
"odc",
"newc",
"crc",
"tar",
"ustar"
],
"default": "newc"
},
"root-node": {
"description": "How to handle the root node: include or omit",
"enum": [
"include",
"omit"
],
"default": "include"
},
"reproducible": {
"type": "boolean",
"description": "Produce device-independent, reproducible archives.",
"default": true
},
"owner": {
"type": "object",
"additionalProperties": false,
"required": [
"user"
],
"properties": {
"user": {
"type": "string"
},
"group": {
"type": "string"
}
}
}
}
},
"inputs": {
"type": "object",
"additionalProperties": false,
"required": [
"tree"
],
"properties": {
"tree": {
"type": "object",
"additionalProperties": true
}
}
}
}
}

View file

@ -1,59 +1,9 @@
#!/usr/bin/python3
"""
Run a script at regular intervals.
Execute a script at regular intervals. This uses the cron drop-in
directories in etc, which correspond to the supported intervals:
`cron.hourly/`, `cron.daily/`, `cron.weekly/`, `cron.monthly/`
NB: Does itself not create the directories so they must be created
via the package that provides the facility, like `cronie` or on
older systems `crontabs`.
"""
import os
import sys
import osbuild.api
SCHEMA_2 = r"""
"options": {
"additionalProperties": false,
"required": ["interval", "filename"],
"oneOf": [
{"required": ["simple"]}
],
"properties": {
"interval": {
"type": "string",
"enum": ["hourly", "daily", "weekly", "monthly"]
},
"filename": {
"type": "string",
"description": "Name of the cron script",
"pattern": "^[\\w.-]{1,255}$"
},
"simple": {
"type": "object",
"description": "A simple command to run.",
"required": ["command"],
"properties": {
"comment": {
"type": "array",
"items": {
"type": "string"
}
},
"command": {
"type": "string"
}
}
}
}
}
"""
def format_comment(comment):
lines = comment.split("\n")

View file

@ -0,0 +1,61 @@
{
"summary": "Run a script at regular intervals.",
"description": [
"Execute a script at regular intervals. This uses the cron drop-in",
"directories in etc, which correspond to the supported intervals:",
" `cron.hourly/`, `cron.daily/`, `cron.weekly/`, `cron.monthly/`",
"NB: Does itself not create the directories so they must be created",
"via the package that provides the facility, like `cronie` or on",
"older systems `crontabs`."
],
"schema_2": {
"options": {
"additionalProperties": false,
"required": [
"interval",
"filename"
],
"oneOf": [
{
"required": [
"simple"
]
}
],
"properties": {
"interval": {
"type": "string",
"enum": [
"hourly",
"daily",
"weekly",
"monthly"
]
},
"filename": {
"type": "string",
"description": "Name of the cron script",
"pattern": "^[\\w.-]{1,255}$"
},
"simple": {
"type": "object",
"description": "A simple command to run.",
"required": [
"command"
],
"properties": {
"comment": {
"type": "array",
"items": {
"type": "string"
}
},
"command": {
"type": "string"
}
}
}
}
}
}
}

View file

@ -1,66 +1,8 @@
#!/usr/bin/python3
"""
Create /etc/crypttab entries for encrypted block devices
See crypttab(5) for a detailed description of the format but in brief:
each item in the list of `volumes` describes an encrypted block device
and how it should it should be setup. The block device is identified
either by `uuid` or by `path` (device node path). The volume will be
named as `volume`, i.e. made available as `/dev/mapper/$volume`.
Additionally, a keyfile can (optionally) be specified via `keyfile`.
Specific device options can be specified via `options`.
This stage replaces /etc/crypttab, removing any existing entries.
"""
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"required": ["volumes"],
"properties": {
"volumes": {
"type": "array",
"description": "array of volume objects",
"items": {
"type": "object",
"oneOf": [{
"required": ["uuid", "volume"]
}, {
"required": ["path", "volume"]
}],
"properties": {
"volume": {
"description": "volume mountpoint",
"type": "string"
},
"uuid": {
"description": "device UUID",
"type": "string"
},
"path": {
"description": "device path",
"type": "string"
},
"keyfile": {
"description": "",
"type": "string",
"default": "none"
},
"options": {
"description": "options (comma-separated)",
"type": "string",
"default": ""
}
}
}
}
}
"""
def main(tree, options):
volumes = options["volumes"]

View file

@ -0,0 +1,66 @@
{
"summary": "Create /etc/crypttab entries for encrypted block devices",
"description": [
"See crypttab(5) for a detailed description of the format but in brief:",
"each item in the list of `volumes` describes an encrypted block device",
"and how it should it should be setup. The block device is identified",
"either by `uuid` or by `path` (device node path). The volume will be",
"named as `volume`, i.e. made available as `/dev/mapper/$volume`.",
"Additionally, a keyfile can (optionally) be specified via `keyfile`.",
"Specific device options can be specified via `options`.",
"This stage replaces /etc/crypttab, removing any existing entries."
],
"schema": {
"additionalProperties": false,
"required": [
"volumes"
],
"properties": {
"volumes": {
"type": "array",
"description": "array of volume objects",
"items": {
"type": "object",
"oneOf": [
{
"required": [
"uuid",
"volume"
]
},
{
"required": [
"path",
"volume"
]
}
],
"properties": {
"volume": {
"description": "volume mountpoint",
"type": "string"
},
"uuid": {
"description": "device UUID",
"type": "string"
},
"path": {
"description": "device path",
"type": "string"
},
"keyfile": {
"description": "",
"type": "string",
"default": "none"
},
"options": {
"description": "options (comma-separated)",
"type": "string",
"default": ""
}
}
}
}
}
}
}

View file

@ -1,30 +1,9 @@
#!/usr/bin/python3
"""
Set up an early root shell on a certain tty
Creates a systemd unit file at /etc/systemd/system/osbuild-debug-shell.service
which starts an early-boot root shell on the given `tty`.
Also symlinks the service file into /etc/systemd/system/sysinit.target.wants/.
"""
import os
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"required": ["tty"],
"properties": {
"tty": {
"type": "string",
"description": "Absolute path of the tty device to start a root shell on."
}
}
"""
def main(tree, options):
tty = options["tty"]

View file

@ -0,0 +1,20 @@
{
"summary": "Set up an early root shell on a certain tty",
"description": [
"Creates a systemd unit file at /etc/systemd/system/osbuild-debug-shell.service",
"which starts an early-boot root shell on the given `tty`.",
"Also symlinks the service file into /etc/systemd/system/sysinit.target.wants/."
],
"schema": {
"additionalProperties": false,
"required": [
"tty"
],
"properties": {
"tty": {
"type": "string",
"description": "Absolute path of the tty device to start a root shell on."
}
}
}
}

View file

@ -1,31 +1,10 @@
#!/usr/bin/python3
"""
Create a `.discinfo` file describing disk
This will create a `.discinfo` file with the specified parameters.
"""
import os
import sys
import time
import osbuild.api
SCHEMA = """
"additionalProperties": true,
"required": ["basearch", "release"],
"properties": {
"basearch": {
"description": "Build architecture.",
"type": "string"
},
"release": {
"description": "The product name.",
"type": "string"
}
}
"""
def main(tree, options):
basearch = options["basearch"]

View file

@ -0,0 +1,23 @@
{
"summary": "Create a `.discinfo` file describing disk",
"description": [
"This will create a `.discinfo` file with the specified parameters."
],
"schema": {
"additionalProperties": true,
"required": [
"basearch",
"release"
],
"properties": {
"basearch": {
"description": "Build architecture.",
"type": "string"
},
"release": {
"description": "The product name.",
"type": "string"
}
}
}
}

View file

@ -1,56 +1,10 @@
#!/usr/bin/python3
"""
Change DNF Automatic configuration.
The stage changes persistent DNF Automatic configuration. Currently, only
a subset of options can be set:
- 'commands' section
- apply_updates
- upgrade_type
"""
import sys
import iniparse
import osbuild.api
SCHEMA = r"""
"definitions": {
"commands": {
"type": "object",
"additionalProperties": false,
"description": "'commands' configuration section.",
"properties": {
"apply_updates": {
"type": "boolean",
"description": "Whether packages comprising the available updates should be installed."
},
"upgrade_type": {
"type": "string",
"description": "What kind of upgrades to look at.",
"enum": ["default", "security"]
}
}
}
},
"additionalProperties": false,
"description": "DNF Automatic configuration.",
"properties": {
"config": {
"type": "object",
"additionalProperties": false,
"description": "configuration definition.",
"properties": {
"commands": {
"$ref": "#/definitions/commands"
}
}
}
}
"""
def bool_to_yes_no(b):
if b:

View file

@ -0,0 +1,47 @@
{
"summary": "Change DNF Automatic configuration.",
"description": [
"The stage changes persistent DNF Automatic configuration. Currently, only",
"a subset of options can be set:",
" - 'commands' section",
" - apply_updates",
" - upgrade_type"
],
"schema": {
"definitions": {
"commands": {
"type": "object",
"additionalProperties": false,
"description": "'commands' configuration section.",
"properties": {
"apply_updates": {
"type": "boolean",
"description": "Whether packages comprising the available updates should be installed."
},
"upgrade_type": {
"type": "string",
"description": "What kind of upgrades to look at.",
"enum": [
"default",
"security"
]
}
}
}
},
"additionalProperties": false,
"description": "DNF Automatic configuration.",
"properties": {
"config": {
"type": "object",
"additionalProperties": false,
"description": "configuration definition.",
"properties": {
"commands": {
"$ref": "#/definitions/commands"
}
}
}
}
}
}

View file

@ -1,15 +1,4 @@
#!/usr/bin/python3
"""
Change DNF configuration.
The stage changes persistent DNF configuration on the filesystem.
Allows defining dnf variables via the `variables` option and
specific configuration options via the `config` option. The
latter will be saved in `/etc/dnf/dnf.conf`. See `dnf.conf(5)`
for details about the configuration options.
"""
import os
import sys
@ -17,79 +6,6 @@ import iniparse
import osbuild.api
SCHEMA = r"""
"definitions": {
"variable": {
"type": "object",
"additionalProperties": false,
"required": ["name", "value"],
"description": "DNF variable to configure persistently.",
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z0-9_]+$",
"description": "Name of the variable."
},
"value": {
"type": "string",
"description": "Value of the variable."
}
}
},
"config_main": {
"type": "object",
"additionalProperties": false,
"properties": {
"ip_resolve": {
"type": "string",
"enum": ["4", "IPv4", "6", "IPv6"],
"description": "Determines how DNF resolves host names."
},
"tsflags": {
"type": "array",
"description": "Extra flags for the RPM transaction.",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string",
"enum": [
"noscripts",
"test",
"notriggers",
"nodocs",
"justdb",
"nocontexts",
"nocaps",
"nocrypto"
]
}
}
}
}
},
"additionalProperties": false,
"description": "DNF configuration.",
"properties": {
"variables": {
"type": "array",
"description": "DNF variables to configure persistently.",
"items": {
"$ref": "#/definitions/variable"
}
},
"config": {
"additionalProperties": false,
"type": "object",
"description": "DNF global configuration.",
"properties": {
"main": {
"$ref": "#/definitions/config_main"
}
}
}
}
"""
def configure_variable(tree, name, value):
"""

View file

@ -0,0 +1,90 @@
{
"summary": "Change DNF configuration.",
"description": [
"The stage changes persistent DNF configuration on the filesystem.",
"Allows defining dnf variables via the `variables` option and",
"specific configuration options via the `config` option. The",
"latter will be saved in `/etc/dnf/dnf.conf`. See `dnf.conf(5)`",
"for details about the configuration options."
],
"schema": {
"definitions": {
"variable": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"value"
],
"description": "DNF variable to configure persistently.",
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z0-9_]+$",
"description": "Name of the variable."
},
"value": {
"type": "string",
"description": "Value of the variable."
}
}
},
"config_main": {
"type": "object",
"additionalProperties": false,
"properties": {
"ip_resolve": {
"type": "string",
"enum": [
"4",
"IPv4",
"6",
"IPv6"
],
"description": "Determines how DNF resolves host names."
},
"tsflags": {
"type": "array",
"description": "Extra flags for the RPM transaction.",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string",
"enum": [
"noscripts",
"test",
"notriggers",
"nodocs",
"justdb",
"nocontexts",
"nocaps",
"nocrypto"
]
}
}
}
}
},
"additionalProperties": false,
"description": "DNF configuration.",
"properties": {
"variables": {
"type": "array",
"description": "DNF variables to configure persistently.",
"items": {
"$ref": "#/definitions/variable"
}
},
"config": {
"additionalProperties": false,
"type": "object",
"description": "DNF global configuration.",
"properties": {
"main": {
"$ref": "#/definitions/config_main"
}
}
}
}
}
}

View file

@ -1,43 +1,10 @@
#!/usr/bin/python3
"""
Mark packages in the DNF state database.
"""
import shutil
import subprocess
import sys
from osbuild import api
SCHEMA_2 = """
"options": {
"additionalProperties": false,
"properties": {
"packages": {
"type": "array",
"minItems": 1,
"description": "Packages and their marks.",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["name", "mark"],
"properties": {
"name": {
"type": "string",
"description": "Package name."
},
"mark": {
"type": "string",
"enum": ["install", "group"],
"description": "Package mark."
}
}
}
}
}
}
"""
def mark(tree, packages):
dnf_bin = shutil.which("dnf-3")

View file

@ -0,0 +1,38 @@
{
"summary": "Mark packages in the DNF state database.",
"description": [],
"schema_2": {
"options": {
"additionalProperties": false,
"properties": {
"packages": {
"type": "array",
"minItems": 1,
"description": "Packages and their marks.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"mark"
],
"properties": {
"name": {
"type": "string",
"description": "Package name."
},
"mark": {
"type": "string",
"enum": [
"install",
"group"
],
"description": "Package mark."
}
}
}
}
}
}
}
}

View file

@ -1,141 +1,9 @@
#!/usr/bin/python3
"""
Create (re-create) the initial RAM file-system
Uses `dracut` to re-create the initial RAM filesystem, see man dracut(8).
The kernels for which the initramfs should be generated need to be provided
via `kernel` matching their name on the disk, like "5.6.6-300.fc32.x86_64".
Supports most options also found in `dracut`(8). See the respective man
page and schema for this stage.
NB: needs chroot for now as well as `strip` for stripping the initrfams.
"""
import subprocess
import sys
import osbuild.api
SCHEMA = """
"required": ["kernel"],
"properties": {
"kernel": {
"description": "List of target kernel versions",
"type": "array",
"items": {
"type": "string",
"description": "A kernel version"
}
},
"compress": {
"description": "Compress the initramfs, passed via `--compress`",
"type": "string"
},
"modules": {
"description": "Exact list of dracut modules to use.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"add_modules": {
"description": "Additional dracut modules to include.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"omit_modules": {
"description": "Dracut modules to not include.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"drivers": {
"description": "Kernel modules to exclusively include.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension"
}
},
"add_drivers": {
"description": "Add a specific kernel modules.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension"
}
},
"omit_drivers": {
"description": "Omit specific kernel modules.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"force_drivers": {
"description": "Add driver and ensure that they are tried to be loaded.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension"
}
},
"filesystems": {
"description": "Kernel filesystem modules to exclusively include.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension"
}
},
"include": {
"description": "Add custom files to the initramfs.",
"type": "array",
"items": {
"type": "object",
"description": "What (keys) to include where (values)"
}
},
"install": {
"description": "Install the specified files.",
"type": "array",
"items": {
"type": "string"
}
},
"early_microcode": {
"description": "Combine early microcode with the initramfs.",
"type": "boolean",
"default": false
},
"reproducible": {
"description": "Create reproducible images.",
"type": "boolean"
},
"initoverlayfs": {
"description": "Use initoverlayfs rather than initramfs, requires initoverlayfs rpm to be installed",
"type": "boolean",
"default": false
},
"extra": {
"description": "Extra arguments to directly pass to dracut",
"type": "array",
"items": {
"type": "string",
"description": "Individual extra arguments"
}
}
}
"""
def yesno(name: str, value: bool) -> str:
prefix = "" if value else "no-"

View file

@ -1,138 +1,8 @@
#!/usr/bin/python3
"""
Configure dracut.
The 'config' option allows to create a dracut configuration file under
`/usr/lib/dracut/dracut.conf.d/` with the name `filename`. Only a subset
of configuration options is supported, with the intention to provide
functional parity with `org.osbuild.dracut` stage.
Constrains:
- At least one configuration option must be specified for each configuration
Supported configuration options:
- compress
- dracutmodules
- add_dracutmodules
- omit_dracutmodules
- drivers
- add_drivers
- omit_drivers
- force_drivers
- filesystems
- install_items
- early_microcode
- reproducible
"""
import sys
import osbuild.api
SCHEMA = r"""
"additionalProperties": false,
"required": ["config", "filename"],
"properties": {
"filename": {
"type": "string",
"description": "Name of the dracut configuration file.",
"pattern": "^[\\w.-]{1,250}\\.conf$"
},
"config": {
"additionalProperties": false,
"type": "object",
"description": "dracut configuration.",
"minProperties": 1,
"properties": {
"compress": {
"description": "Compress the generated initramfs using the passed compression program.",
"type": "string"
},
"dracutmodules": {
"description": "Exact list of dracut modules to use.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"add_dracutmodules": {
"description": "Additional dracut modules to include.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"omit_dracutmodules": {
"description": "Dracut modules to not include.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"drivers": {
"description": "Kernel modules to exclusively include.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"add_drivers": {
"description": "Add a specific kernel modules.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"omit_drivers": {
"description": "Omit specific kernel modules.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"force_drivers": {
"description": "Add driver and ensure that they are tried to be loaded.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"filesystems": {
"description": "Kernel filesystem modules to exclusively include.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"install_items": {
"description": "Install the specified files.",
"type": "array",
"items": {
"type": "string",
"description": "Specify additional files to include in the initramfs."
}
},
"early_microcode": {
"description": "Combine early microcode with the initramfs.",
"type": "boolean"
},
"reproducible": {
"description": "Create reproducible images.",
"type": "boolean"
}
}
}
}
"""
def bool_to_string(value):
return "yes" if value else "no"

View file

@ -0,0 +1,130 @@
{
"summary": "Configure dracut.",
"description": [
"The 'config' option allows to create a dracut configuration file under",
"`/usr/lib/dracut/dracut.conf.d/` with the name `filename`. Only a subset",
"of configuration options is supported, with the intention to provide",
"functional parity with `org.osbuild.dracut` stage.",
"Constrains:",
" - At least one configuration option must be specified for each configuration",
"Supported configuration options:",
" - compress",
" - dracutmodules",
" - add_dracutmodules",
" - omit_dracutmodules",
" - drivers",
" - add_drivers",
" - omit_drivers",
" - force_drivers",
" - filesystems",
" - install_items",
" - early_microcode",
" - reproducible"
],
"schema": {
"additionalProperties": false,
"required": [
"config",
"filename"
],
"properties": {
"filename": {
"type": "string",
"description": "Name of the dracut configuration file.",
"pattern": "^[\\w.-]{1,250}\\.conf$"
},
"config": {
"additionalProperties": false,
"type": "object",
"description": "dracut configuration.",
"minProperties": 1,
"properties": {
"compress": {
"description": "Compress the generated initramfs using the passed compression program.",
"type": "string"
},
"dracutmodules": {
"description": "Exact list of dracut modules to use.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"add_dracutmodules": {
"description": "Additional dracut modules to include.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"omit_dracutmodules": {
"description": "Dracut modules to not include.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"drivers": {
"description": "Kernel modules to exclusively include.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"add_drivers": {
"description": "Add a specific kernel modules.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"omit_drivers": {
"description": "Omit specific kernel modules.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"force_drivers": {
"description": "Add driver and ensure that they are tried to be loaded.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"filesystems": {
"description": "Kernel filesystem modules to exclusively include.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"install_items": {
"description": "Install the specified files.",
"type": "array",
"items": {
"type": "string",
"description": "Specify additional files to include in the initramfs."
}
},
"early_microcode": {
"description": "Combine early microcode with the initramfs.",
"type": "boolean"
},
"reproducible": {
"description": "Create reproducible images.",
"type": "boolean"
}
}
}
}
}
}

View file

@ -0,0 +1,131 @@
{
"summary": "Create (re-create) the initial RAM file-system",
"description": [
"Uses `dracut` to re-create the initial RAM filesystem, see man dracut(8).",
"The kernels for which the initramfs should be generated need to be provided",
"via `kernel` matching their name on the disk, like \"5.6.6-300.fc32.x86_64\".",
"Supports most options also found in `dracut`(8). See the respective man",
"page and schema for this stage.",
"NB: needs chroot for now as well as `strip` for stripping the initrfams."
],
"schema": {
"required": [
"kernel"
],
"properties": {
"kernel": {
"description": "List of target kernel versions",
"type": "array",
"items": {
"type": "string",
"description": "A kernel version"
}
},
"compress": {
"description": "Compress the initramfs, passed via `--compress`",
"type": "string"
},
"modules": {
"description": "Exact list of dracut modules to use.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"add_modules": {
"description": "Additional dracut modules to include.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"omit_modules": {
"description": "Dracut modules to not include.",
"type": "array",
"items": {
"type": "string",
"description": "A dracut module, e.g. base, nfs, network ..."
}
},
"drivers": {
"description": "Kernel modules to exclusively include.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension"
}
},
"add_drivers": {
"description": "Add a specific kernel modules.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension"
}
},
"omit_drivers": {
"description": "Omit specific kernel modules.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension."
}
},
"force_drivers": {
"description": "Add driver and ensure that they are tried to be loaded.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension"
}
},
"filesystems": {
"description": "Kernel filesystem modules to exclusively include.",
"type": "array",
"items": {
"type": "string",
"description": "A kernel module without the .ko extension"
}
},
"include": {
"description": "Add custom files to the initramfs.",
"type": "array",
"items": {
"type": "object",
"description": "What (keys) to include where (values)"
}
},
"install": {
"description": "Install the specified files.",
"type": "array",
"items": {
"type": "string"
}
},
"early_microcode": {
"description": "Combine early microcode with the initramfs.",
"type": "boolean",
"default": false
},
"reproducible": {
"description": "Create reproducible images.",
"type": "boolean"
},
"initoverlayfs": {
"description": "Use initoverlayfs rather than initramfs, requires initoverlayfs rpm to be installed",
"type": "boolean",
"default": false
},
"extra": {
"description": "Extra arguments to directly pass to dracut",
"type": "array",
"items": {
"type": "string",
"description": "Individual extra arguments"
}
}
}
}
}

View file

@ -1,76 +1,10 @@
#!/usr/bin/python3
"""
Create a file containing an erofs filesystem named `filename`.
See https://en.wikipedia.org/wiki/EROFS for details about the
filesystem.
Buildhost commands used: `mkfs.erofs`
"""
import os
import subprocess
import sys
import osbuild.api
SCHEMA_2 = """
"options": {
"additionalProperties": false,
"required": ["filename"],
"properties": {
"filename": {
"description": "Filename for the output",
"type": "string"
},
"compression": {
"type": "object",
"additionalProperties": false,
"required": ["method"],
"properties": {
"method": {
"description": "Compression method",
"enum": ["lz4", "lz4hc", "lzma"]
},
"level": {
"description": "Compression level. Note that different methods support different levels. See mkfs.erofs(1) for more details",
"type": "number"
}
}
},
"options": {
"description": "Extended options for the filesystem, see mkfs.erofs(1)",
"type": "array",
"minItems": 1,
"items:": {
"enum": [
"all-fragments",
"dedupe",
"force-inode-compact",
"force-inode-extended",
"force-inode-blockmap",
"force-chunk-indexes",
"fragments",
"noinline_data",
"ztailpacking"
]
}
}
}
},
"inputs": {
"type": "object",
"additionalProperties": false,
"required": ["tree"],
"properties": {
"tree": {
"type": "object",
"additionalProperties": true
}
}
}
"""
def main(inputs, output_dir, options):
source = inputs["tree"]["path"]

View file

@ -0,0 +1,74 @@
{
"summary": "Create a file containing an erofs filesystem named `filename`.",
"description": [
"See https://en.wikipedia.org/wiki/EROFS for details about the",
"filesystem.",
"Buildhost commands used: `mkfs.erofs`"
],
"schema_2": {
"options": {
"additionalProperties": false,
"required": [
"filename"
],
"properties": {
"filename": {
"description": "Filename for the output",
"type": "string"
},
"compression": {
"type": "object",
"additionalProperties": false,
"required": [
"method"
],
"properties": {
"method": {
"description": "Compression method",
"enum": [
"lz4",
"lz4hc",
"lzma"
]
},
"level": {
"description": "Compression level. Note that different methods support different levels. See mkfs.erofs(1) for more details",
"type": "number"
}
}
},
"options": {
"description": "Extended options for the filesystem, see mkfs.erofs(1)",
"type": "array",
"minItems": 1,
"items:": {
"enum": [
"all-fragments",
"dedupe",
"force-inode-compact",
"force-inode-extended",
"force-inode-blockmap",
"force-chunk-indexes",
"fragments",
"noinline_data",
"ztailpacking"
]
}
}
}
},
"inputs": {
"type": "object",
"additionalProperties": false,
"required": [
"tree"
],
"properties": {
"tree": {
"type": "object",
"additionalProperties": true
}
}
}
}
}

View file

@ -1,27 +1,8 @@
#!/usr/bin/python3
"""
Return an error
Error stage. Return the given error. Useful for testing, debugging, and
wasting time.
"""
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"properties": {
"returncode": {
"description": "What to return code to use",
"type": "number",
"default": 255
}
}
"""
def main(_tree, options):
errno = options.get("returncode", 255)

View file

@ -0,0 +1,17 @@
{
"summary": "Return an error",
"description": [
"Error stage. Return the given error. Useful for testing, debugging, and",
"wasting time."
],
"schema": {
"additionalProperties": false,
"properties": {
"returncode": {
"description": "What to return code to use",
"type": "number",
"default": 255
}
}
}
}

View file

@ -1,52 +1,10 @@
#!/usr/bin/python3
"""
Change OSTree configuration experimental options
NOTE: This stage is experimental and subject to changes
Change the configuration for an OSTree repository.
Currently only the following values are supported:
- `integrity.composefs`
See `ostree.repo-config(5)` for more information.
"""
import os
import sys
import osbuild.api
from osbuild.util import ostree
SCHEMA = """
"additionalProperties": false,
"required": ["repo"],
"properties": {
"repo": {
"description": "Location of the OSTree repo.",
"type": "string"
},
"config": {
"type": "object",
"additionalProperties": false,
"description": "OSTree configuration groups",
"properties": {
"integrity": {
"type": "object",
"additionalProperties": false,
"description": "Options concerning the sysroot",
"properties": {
"composefs": {
"description": "Enable composefs image generation on deploy.",
"type": "string",
"enum": ["true", "false", "maybe"]
}
}
}
}
}
}
"""
def main(tree, options):
repo = os.path.join(tree, options["repo"].lstrip("/"))

View file

@ -0,0 +1,45 @@
{
"summary": "Change OSTree configuration experimental options",
"description": [
"NOTE: This stage is experimental and subject to changes",
"Change the configuration for an OSTree repository.",
"Currently only the following values are supported:",
" - `integrity.composefs`",
"See `ostree.repo-config(5)` for more information."
],
"schema": {
"additionalProperties": false,
"required": [
"repo"
],
"properties": {
"repo": {
"description": "Location of the OSTree repo.",
"type": "string"
},
"config": {
"type": "object",
"additionalProperties": false,
"description": "OSTree configuration groups",
"properties": {
"integrity": {
"type": "object",
"additionalProperties": false,
"description": "Options concerning the sysroot",
"properties": {
"composefs": {
"description": "Enable composefs image generation on deploy.",
"type": "string",
"enum": [
"true",
"false",
"maybe"
]
}
}
}
}
}
}
}
}

View file

@ -1,37 +1,10 @@
#!/usr/bin/python3
"""
FDO initial DIUN certificates
FDO stage to write down the initial DIUN pub key root certificates
to be read by the manufacturer client
This will create a '/fdo_diun_root_certs.pem' with content
specified via the `rootcerts` input.
"""
import os
import shutil
import sys
import osbuild.api
SCHEMA_2 = r"""
"inputs": {
"type": "object",
"additionalProperties": false,
"required": ["rootcerts"],
"properties": {
"rootcerts": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false
}
"""
def parse_input(inputs):
image = inputs["rootcerts"]

View file

@ -0,0 +1,27 @@
{
"summary": "FDO initial DIUN certificates",
"description": [
"FDO stage to write down the initial DIUN pub key root certificates",
"to be read by the manufacturer client",
"This will create a '/fdo_diun_root_certs.pem' with content",
"specified via the `rootcerts` input."
],
"schema_2": {
"inputs": {
"type": "object",
"additionalProperties": false,
"required": [
"rootcerts"
],
"properties": {
"rootcerts": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false
}
}
}

View file

@ -1,98 +1,9 @@
#!/usr/bin/python3
"""
Configure firewall
Configure firewalld using the `firewall-offline-cmd` from inside the target.
This stage adds each of the given `ports` and `enabled_services` to the default
firewall zone using the `--port` and `--service` options, then removes the
services listed in `disabled_services` with `--remove-service`.
Ports should be specified as "portid:protocol" or "portid-portid:protocol",
where "portid" is a number (or a port name from `/etc/services`, like "ssh" or
"echo") and "protocol" is one of "tcp", "udp", "sctp", or "dccp".
Enabling or disabling a service that is already enabled or disabled will not
cause an error.
Attempting to enable/disable an unknown service name will cause this stage to
fail. Known service names are determined by the contents of firewalld's
configuration directories, usually `/{lib,etc}/firewalld/services/*.xml`, and
may vary from release to release.
WARNING: this stage uses `chroot` to run `firewall-offline-cmd` inside the
target tree, which means it may fail unexpectedly when the buildhost and target
are different arches or OSes.
"""
import subprocess
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"properties": {
"ports": {
"description": "Ports (or port ranges) to open",
"type": "array",
"items": {
"type": "string",
"description": "A port or port range: 'portid[-portid]:protocol'",
"pattern": ".:(tcp|udp|sctp|dccp)$"
}
},
"enabled_services": {
"description": "Network services to allow in the default firewall zone",
"type": "array",
"items": {
"type": "string",
"description": "Service name (from /{lib,etc}/firewalld/services/*.xml)"
}
},
"disabled_services": {
"description": "Network services to remove from the default firewall zone",
"type": "array",
"items": {
"type": "string",
"description": "Service name (from /{lib,etc}/firewalld/services/*.xml)"
}
},
"default_zone": {
"description": "Set default zone for connections and interfaces where no zone has been selected.",
"type": "string"
},
"zones": {
"description": "Bind a list of network sources to a zone to restrict traffic from those sources based on the settings of the zone.",
"type": "array",
"minItems": 1,
"items": {
"additionalProperties": false,
"type": "object",
"description": "configuration for each zone",
"required": ["name", "sources"],
"properties": {
"name": {
"type": "string",
"description": "name of the zone, if left empty the sources will apply to the default zone.",
"pattern": "^[a-zA-Z0-9_-]+$"
},
"sources": {
"type": "array",
"description": "list of sources for the zone",
"items": {
"additionalProperties": false,
"type": "string",
"description": "A source: <source>[/<mask>]|<MAC>|ipset:<ipset>"
}
}
}
}
}
}
"""
def main(tree, options):
# Takes a list of <port|application protocol>:<transport protocol> pairs

View file

@ -0,0 +1,85 @@
{
"summary": "Configure firewall",
"description": [
"Configure firewalld using the `firewall-offline-cmd` from inside the target.",
"This stage adds each of the given `ports` and `enabled_services` to the default",
"firewall zone using the `--port` and `--service` options, then removes the",
"services listed in `disabled_services` with `--remove-service`.",
"Ports should be specified as \"portid:protocol\" or \"portid-portid:protocol\",",
"where \"portid\" is a number (or a port name from `/etc/services`, like \"ssh\" or",
"\"echo\") and \"protocol\" is one of \"tcp\", \"udp\", \"sctp\", or \"dccp\".",
"Enabling or disabling a service that is already enabled or disabled will not",
"cause an error.",
"Attempting to enable/disable an unknown service name will cause this stage to",
"fail. Known service names are determined by the contents of firewalld's",
"configuration directories, usually `/{lib,etc}/firewalld/services/*.xml`, and",
"may vary from release to release.",
"WARNING: this stage uses `chroot` to run `firewall-offline-cmd` inside the",
"target tree, which means it may fail unexpectedly when the buildhost and target",
"are different arches or OSes."
],
"schema": {
"additionalProperties": false,
"properties": {
"ports": {
"description": "Ports (or port ranges) to open",
"type": "array",
"items": {
"type": "string",
"description": "A port or port range: 'portid[-portid]:protocol'",
"pattern": ".:(tcp|udp|sctp|dccp)$"
}
},
"enabled_services": {
"description": "Network services to allow in the default firewall zone",
"type": "array",
"items": {
"type": "string",
"description": "Service name (from /{lib,etc}/firewalld/services/*.xml)"
}
},
"disabled_services": {
"description": "Network services to remove from the default firewall zone",
"type": "array",
"items": {
"type": "string",
"description": "Service name (from /{lib,etc}/firewalld/services/*.xml)"
}
},
"default_zone": {
"description": "Set default zone for connections and interfaces where no zone has been selected.",
"type": "string"
},
"zones": {
"description": "Bind a list of network sources to a zone to restrict traffic from those sources based on the settings of the zone.",
"type": "array",
"minItems": 1,
"items": {
"additionalProperties": false,
"type": "object",
"description": "configuration for each zone",
"required": [
"name",
"sources"
],
"properties": {
"name": {
"type": "string",
"description": "name of the zone, if left empty the sources will apply to the default zone.",
"pattern": "^[a-zA-Z0-9_-]+$"
},
"sources": {
"type": "array",
"description": "list of sources for the zone",
"items": {
"additionalProperties": false,
"type": "string",
"description": "A source: <source>[/<mask>]|<MAC>|ipset:<ipset>"
}
}
}
}
}
}
}
}

View file

@ -1,45 +1,9 @@
#!/usr/bin/python3
"""
Execute commands on first-boot
Sequentially execute a list of commands on first-boot / instantiation.
This stage uses a logic similar to systemd's first-boot to execute a given
script only the first time the image is booted.
An empty flag file /etc/osbuild-first-boot is written to /etc and a systemd
service is enabled that is only run when the file exits, and will remove it
before executing the given commands.
If the flag-file cannot be removed, the service fails without executing
any further first-boot commands.
"""
import os
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"required": ["commands"],
"properties": {
"commands": {
"type": "array",
"description": "The command lines to execute",
"items": {
"type": "string"
}
},
"wait_for_network": {
"type": "boolean",
"description": "Wait for the network to be up before executing",
"default": false
}
}
"""
def add_first_boot(tree, commands, wait_for_network):
if wait_for_network:

View file

@ -0,0 +1,33 @@
{
"summary": "Execute commands on first-boot",
"description": [
"Sequentially execute a list of commands on first-boot / instantiation.",
"This stage uses a logic similar to systemd's first-boot to execute a given",
"script only the first time the image is booted.",
"An empty flag file /etc/osbuild-first-boot is written to /etc and a systemd",
"service is enabled that is only run when the file exits, and will remove it",
"before executing the given commands.",
"If the flag-file cannot be removed, the service fails without executing",
"any further first-boot commands."
],
"schema": {
"additionalProperties": false,
"required": [
"commands"
],
"properties": {
"commands": {
"type": "array",
"description": "The command lines to execute",
"items": {
"type": "string"
}
},
"wait_for_network": {
"type": "boolean",
"description": "Wait for the network to be up before executing",
"default": false
}
}
}
}

View file

@ -1,40 +1,10 @@
#!/usr/bin/python3
"""
Fix paths in /boot/loader/entries
Fixes paths in /boot/loader/entries that have incorrect paths for /boot.
This happens because some boot loader config tools (e.g. grub2-mkrelpath)
examine /proc/self/mountinfo to find the "real" path to /boot, and find the
path to the osbuild tree - which won't be valid at boot time for this image.
The paths in the Bootloader Specification are relative to the partition
they are located on, i.e. `/boot/loader/...` if `/boot` is on the root
file-system partition. If `/boot` is on a separate partition, the correct
path would be `/loader/.../` The `prefix` can be used to adjust for that.
By default it is `/boot`, i.e. assumes `/boot` is on the root file-system.
This stage reads and (re)writes all .conf files in /boot/loader/entries.
"""
import glob
import re
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"properties": {
"prefix": {
"description": "Prefix to use, normally `/boot`",
"type": "string",
"default": "/boot"
}
}
"""
def main(tree, options):
"""Fix broken paths in /boot/loader/entries.

View file

@ -0,0 +1,25 @@
{
"summary": "Fix paths in /boot/loader/entries",
"description": [
"Fixes paths in /boot/loader/entries that have incorrect paths for /boot.",
"This happens because some boot loader config tools (e.g. grub2-mkrelpath)",
"examine /proc/self/mountinfo to find the \"real\" path to /boot, and find the",
"path to the osbuild tree - which won't be valid at boot time for this image.",
"The paths in the Bootloader Specification are relative to the partition",
"they are located on, i.e. `/boot/loader/...` if `/boot` is on the root",
"file-system partition. If `/boot` is on a separate partition, the correct",
"path would be `/loader/.../` The `prefix` can be used to adjust for that.",
"By default it is `/boot`, i.e. assumes `/boot` is on the root file-system.",
"This stage reads and (re)writes all .conf files in /boot/loader/entries."
],
"schema": {
"additionalProperties": false,
"properties": {
"prefix": {
"description": "Prefix to use, normally `/boot`",
"type": "string",
"default": "/boot"
}
}
}
}

View file

@ -1,138 +1,9 @@
#!/usr/bin/python3
"""
Create /etc/fstab entries for filesystems
Each filesystem item must have at least the fs_spec, i.e `uuid`,
`label`, `partlabel` or `device` and a `path` (mount point).
This stage replaces /etc/fstab, removing any existing entries.
NB: The ostree configuration options are experimental and might
be replaced with a different mechanism in the near future.
"""
import sys
import osbuild.api
from osbuild.util import ostree
SCHEMA = """
"additionalProperties": false,
"required": ["filesystems"],
"properties": {
"ostree": {
"type": "object",
"additionalProperties": false,
"required": ["deployment"],
"properties": {
"deployment": {
"type": "object",
"additionalProperties": false,
"oneOf": [
{
"properties": {
"default": {"enum": [false]}
},
"required": ["osname", "ref"]
},
{
"properties": {
"default": {"enum": [true]}
},
"not": {
"anyOf": [
{"required": ["osname"]},
{"required": ["ref"]},
{"required": ["serial"]}
]
}
}
],
"properties": {
"osname": {
"description": "Name of the stateroot to be used in the deployment",
"type": "string"
},
"ref": {
"description": "OStree ref to create and use for deployment",
"type": "string"
},
"serial": {
"description": "The deployment serial (usually '0')",
"type": "number",
"default": 0
},
"default": {
"description": "Find and use the default ostree deployment",
"type": "boolean",
"default": false
}
}
}
}
},
"filesystems": {
"type": "array",
"description": "array of filesystem objects",
"items": {
"type": "object",
"oneOf": [{
"required": ["device", "path"]
}, {
"required": ["uuid", "path"]
}, {
"required": ["label", "path"]
}, {
"required": ["partlabel", "path"]
}],
"properties": {
"device": {
"description": "Device node",
"type": "string"
},
"uuid": {
"description": "Filesystem UUID",
"type": "string"
},
"label": {
"description": "Filesystem label",
"type": "string"
},
"partlabel": {
"description": "Partition label.",
"type": "string"
},
"path": {
"description": "Filesystem mountpoint",
"type": "string"
},
"vfs_type": {
"description": "Filesystem type",
"type": "string",
"default": "none"
},
"options": {
"description": "Filesystem options (comma-separated)",
"type": "string",
"default": "defaults"
},
"freq": {
"description": "dump(8) period in days",
"type": "number",
"default": 0
},
"passno": {
"description": "pass number on parallel fsck(8)",
"type": "number",
"default": 0
}
}
}
}
}
"""
def main(tree, options):
filesystems = options["filesystems"]

View file

@ -0,0 +1,169 @@
{
"summary": "Create /etc/fstab entries for filesystems",
"description": [
"Each filesystem item must have at least the fs_spec, i.e `uuid`,",
"`label`, `partlabel` or `device` and a `path` (mount point).",
"This stage replaces /etc/fstab, removing any existing entries.",
"NB: The ostree configuration options are experimental and might",
"be replaced with a different mechanism in the near future."
],
"schema": {
"additionalProperties": false,
"required": [
"filesystems"
],
"properties": {
"ostree": {
"type": "object",
"additionalProperties": false,
"required": [
"deployment"
],
"properties": {
"deployment": {
"type": "object",
"additionalProperties": false,
"oneOf": [
{
"properties": {
"default": {
"enum": [
false
]
}
},
"required": [
"osname",
"ref"
]
},
{
"properties": {
"default": {
"enum": [
true
]
}
},
"not": {
"anyOf": [
{
"required": [
"osname"
]
},
{
"required": [
"ref"
]
},
{
"required": [
"serial"
]
}
]
}
}
],
"properties": {
"osname": {
"description": "Name of the stateroot to be used in the deployment",
"type": "string"
},
"ref": {
"description": "OStree ref to create and use for deployment",
"type": "string"
},
"serial": {
"description": "The deployment serial (usually '0')",
"type": "number",
"default": 0
},
"default": {
"description": "Find and use the default ostree deployment",
"type": "boolean",
"default": false
}
}
}
}
},
"filesystems": {
"type": "array",
"description": "array of filesystem objects",
"items": {
"type": "object",
"oneOf": [
{
"required": [
"device",
"path"
]
},
{
"required": [
"uuid",
"path"
]
},
{
"required": [
"label",
"path"
]
},
{
"required": [
"partlabel",
"path"
]
}
],
"properties": {
"device": {
"description": "Device node",
"type": "string"
},
"uuid": {
"description": "Filesystem UUID",
"type": "string"
},
"label": {
"description": "Filesystem label",
"type": "string"
},
"partlabel": {
"description": "Partition label.",
"type": "string"
},
"path": {
"description": "Filesystem mountpoint",
"type": "string"
},
"vfs_type": {
"description": "Filesystem type",
"type": "string",
"default": "none"
},
"options": {
"description": "Filesystem options (comma-separated)",
"type": "string",
"default": "defaults"
},
"freq": {
"description": "dump(8) period in days",
"type": "number",
"default": 0
},
"passno": {
"description": "pass number on parallel fsck(8)",
"type": "number",
"default": 0
}
}
}
}
}
}
}

View file

@ -1,16 +1,4 @@
#!/usr/bin/python3
"""
Create or modify the GCP guest-agent config
Create or modify the GCP guest-agent config, depending on the
scope either at:
/etc/default/instance_configs.cfg.distro or
/etc/default/instance_configs.cfg
Configuration sections and options may contain any of these values:
https://github.com/GoogleCloudPlatform/guest-agent#configuration
"""
import os
import sys
@ -18,204 +6,6 @@ import iniparse
import osbuild.api
SCHEMA = r"""
"definitions": {
"Accounts": {
"type": "object",
"additionalProperties": false,
"description": "Accounts section.",
"minProperties": 1,
"properties": {
"deprovision_remove": {
"type": "boolean",
"description": "Makes deprovisioning a user destructive."
},
"groups": {
"type": "array",
"description": "List of groups for newly provisioned users."
},
"useradd_cmd": {
"type": "string",
"description": "Command string to create a new user."
},
"userdel_cmd": {
"type": "string",
"description": "Command string to delete a user."
},
"usermod_cmd": {
"type": "string",
"description": "Command string to modify a user's groups."
},
"gpasswd_add_cmd": {
"type": "string",
"description": "Command string to add a user to a group."
},
"gpasswd_remove_cmd": {
"type": "string",
"description": "Command string to remove a user from a group."
},
"groupadd_cmd": {
"type": "string",
"description": "Command string to create a new group."
}
}
},
"Daemons": {
"type": "object",
"additionalProperties": false,
"description": "Daemons section.",
"minProperties": 1,
"properties": {
"accounts_daemon": {
"type": "boolean",
"description": "Disables the accounts daemon."
},
"clock_skew_daemon": {
"type": "boolean",
"description": "Disables the clock skew daemon."
},
"network_daemon": {
"type": "boolean",
"description": "Disables the network daemon."
}
}
},
"InstanceSetup": {
"type": "object",
"additionalProperties": false,
"description": "InstanceSetup section.",
"minProperties": 1,
"properties": {
"host_key_types": {
"type": "array",
"description": "List of host key types to generate."
},
"optimize_local_ssd": {
"type": "boolean",
"description": "Prevents optimizing for local SSD."
},
"network_enabled": {
"type": "boolean",
"description": "Skips instance setup functions that require metadata."
},
"set_boto_config": {
"type": "boolean",
"description": "Skip setting up a boto config."
},
"set_host_keys": {
"type": "boolean",
"description": "Skips generating host keys on first boot."
},
"set_multiqueue": {
"type": "boolean",
"description": "Skips multiqueue driver support."
}
}
},
"IpForwarding": {
"type": "object",
"additionalProperties": false,
"description": "IpForwarding section.",
"minProperties": 1,
"properties": {
"ethernet_proto_id": {
"type": "string",
"description": "Protocol ID string for daemon added routes."
},
"ip_aliases": {
"type": "boolean",
"description": "Disables setting up alias IP routes."
},
"target_instance_ips": {
"type": "boolean",
"description": "Disables internal IP address load balancing."
}
}
},
"MetadataScripts": {
"type": "object",
"additionalProperties": false,
"description": "MetadataScripts section.",
"minProperties": 1,
"properties": {
"default_shell": {
"type": "string",
"description": "String with the default shell to execute scripts."
},
"run_dir": {
"type": "string",
"description": "String base directory where metadata scripts are executed."
},
"startup": {
"type": "boolean",
"description": "Disables startup script execution."
},
"shutdown": {
"type": "boolean",
"description": "Disables shutdown script execution."
}
}
},
"NetworkInterfaces": {
"type": "object",
"additionalProperties": false,
"description": "NetworkInterfaces section.",
"minProperties": 1,
"properties": {
"setup": {
"type": "boolean",
"description": "Skips network interface setup."
},
"ip_forwarding": {
"type": "boolean",
"description": "Skips IP forwarding."
},
"dhcp_command": {
"type": "string",
"description": "Path for alternate dhcp executable used to enable network interfaces."
}
}
}
},
"additionalProperties": false,
"required": ["config"],
"description": "Configure GCP guest-agent.",
"properties": {
"config_scope": {
"type": "string",
"description": "Create distro-wide or instance-specific configuration.",
"enum": ["distro", "instance"],
"default": "distro"
},
"config": {
"type": "object",
"additionalProperties": false,
"description": "GCP guest-agent configuration.",
"minProperties": 1,
"properties": {
"Accounts": {
"$ref": "#/definitions/Accounts"
},
"Daemons": {
"$ref": "#/definitions/Daemons"
},
"InstanceSetup": {
"$ref": "#/definitions/InstanceSetup"
},
"IpForwarding": {
"$ref": "#/definitions/IpForwarding"
},
"MetadataScripts": {
"$ref": "#/definitions/MetadataScripts"
},
"NetworkInterfaces": {
"$ref": "#/definitions/NetworkInterfaces"
}
}
}
}
"""
def option_value_to_str(value):
"""

View file

@ -0,0 +1,213 @@
{
"summary": "Create or modify the GCP guest-agent config",
"description": [
"Create or modify the GCP guest-agent config, depending on the",
"scope either at:",
" /etc/default/instance_configs.cfg.distro or",
" /etc/default/instance_configs.cfg",
"Configuration sections and options may contain any of these values:",
"https://github.com/GoogleCloudPlatform/guest-agent#configuration"
],
"schema": {
"definitions": {
"Accounts": {
"type": "object",
"additionalProperties": false,
"description": "Accounts section.",
"minProperties": 1,
"properties": {
"deprovision_remove": {
"type": "boolean",
"description": "Makes deprovisioning a user destructive."
},
"groups": {
"type": "array",
"description": "List of groups for newly provisioned users."
},
"useradd_cmd": {
"type": "string",
"description": "Command string to create a new user."
},
"userdel_cmd": {
"type": "string",
"description": "Command string to delete a user."
},
"usermod_cmd": {
"type": "string",
"description": "Command string to modify a user's groups."
},
"gpasswd_add_cmd": {
"type": "string",
"description": "Command string to add a user to a group."
},
"gpasswd_remove_cmd": {
"type": "string",
"description": "Command string to remove a user from a group."
},
"groupadd_cmd": {
"type": "string",
"description": "Command string to create a new group."
}
}
},
"Daemons": {
"type": "object",
"additionalProperties": false,
"description": "Daemons section.",
"minProperties": 1,
"properties": {
"accounts_daemon": {
"type": "boolean",
"description": "Disables the accounts daemon."
},
"clock_skew_daemon": {
"type": "boolean",
"description": "Disables the clock skew daemon."
},
"network_daemon": {
"type": "boolean",
"description": "Disables the network daemon."
}
}
},
"InstanceSetup": {
"type": "object",
"additionalProperties": false,
"description": "InstanceSetup section.",
"minProperties": 1,
"properties": {
"host_key_types": {
"type": "array",
"description": "List of host key types to generate."
},
"optimize_local_ssd": {
"type": "boolean",
"description": "Prevents optimizing for local SSD."
},
"network_enabled": {
"type": "boolean",
"description": "Skips instance setup functions that require metadata."
},
"set_boto_config": {
"type": "boolean",
"description": "Skip setting up a boto config."
},
"set_host_keys": {
"type": "boolean",
"description": "Skips generating host keys on first boot."
},
"set_multiqueue": {
"type": "boolean",
"description": "Skips multiqueue driver support."
}
}
},
"IpForwarding": {
"type": "object",
"additionalProperties": false,
"description": "IpForwarding section.",
"minProperties": 1,
"properties": {
"ethernet_proto_id": {
"type": "string",
"description": "Protocol ID string for daemon added routes."
},
"ip_aliases": {
"type": "boolean",
"description": "Disables setting up alias IP routes."
},
"target_instance_ips": {
"type": "boolean",
"description": "Disables internal IP address load balancing."
}
}
},
"MetadataScripts": {
"type": "object",
"additionalProperties": false,
"description": "MetadataScripts section.",
"minProperties": 1,
"properties": {
"default_shell": {
"type": "string",
"description": "String with the default shell to execute scripts."
},
"run_dir": {
"type": "string",
"description": "String base directory where metadata scripts are executed."
},
"startup": {
"type": "boolean",
"description": "Disables startup script execution."
},
"shutdown": {
"type": "boolean",
"description": "Disables shutdown script execution."
}
}
},
"NetworkInterfaces": {
"type": "object",
"additionalProperties": false,
"description": "NetworkInterfaces section.",
"minProperties": 1,
"properties": {
"setup": {
"type": "boolean",
"description": "Skips network interface setup."
},
"ip_forwarding": {
"type": "boolean",
"description": "Skips IP forwarding."
},
"dhcp_command": {
"type": "string",
"description": "Path for alternate dhcp executable used to enable network interfaces."
}
}
}
},
"additionalProperties": false,
"required": [
"config"
],
"description": "Configure GCP guest-agent.",
"properties": {
"config_scope": {
"type": "string",
"description": "Create distro-wide or instance-specific configuration.",
"enum": [
"distro",
"instance"
],
"default": "distro"
},
"config": {
"type": "object",
"additionalProperties": false,
"description": "GCP guest-agent configuration.",
"minProperties": 1,
"properties": {
"Accounts": {
"$ref": "#/definitions/Accounts"
},
"Daemons": {
"$ref": "#/definitions/Daemons"
},
"InstanceSetup": {
"$ref": "#/definitions/InstanceSetup"
},
"IpForwarding": {
"$ref": "#/definitions/IpForwarding"
},
"MetadataScripts": {
"$ref": "#/definitions/MetadataScripts"
},
"NetworkInterfaces": {
"$ref": "#/definitions/NetworkInterfaces"
}
}
}
}
}
}

View file

@ -1,35 +1,9 @@
#!/usr/bin/python3
"""
Configure greenboot
Update configuration of greenboot in /etc/greenboot/greenbot.conf.
"""
import fileinput
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"required": ["config"],
"properties": {
"config": {
"additionalProperties": false,
"description": "greenboot config options",
"type": "object",
"properties": {
"monitor_services": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
"""
def main(tree, options):
greenboot_conf = options.get("config", {})

View file

@ -0,0 +1,27 @@
{
"summary": "Configure greenboot",
"description": [
"Update configuration of greenboot in /etc/greenboot/greenbot.conf."
],
"schema": {
"additionalProperties": false,
"required": [
"config"
],
"properties": {
"config": {
"additionalProperties": false,
"description": "greenboot config options",
"type": "object",
"properties": {
"monitor_services": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}

View file

@ -1,43 +1,9 @@
#!/usr/bin/python3
"""
Create group accounts
Create group accounts, optionally assigning them static GIDs.
Runs `groupadd` from the buildhost to create the groups listed in `groups`.
If no `gid` is given, `groupadd` will choose one.
If the specified group name or GID is already in use, this stage will fail.
"""
import subprocess
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"properties": {
"groups": {
"type": "object",
"additionalProperties": false,
"description": "Keys are group names, values are objects with group info",
"patternProperties": {
"^[A-Za-z0-9_][A-Za-z0-9_-]{0,31}$": {
"type": "object",
"properties": {
"gid": {
"type": "number",
"description": "GID for this group"
}
}
}
}
}
}
"""
def groupadd(root, name, gid=None):
arguments = []

View file

@ -0,0 +1,30 @@
{
"summary": "Create group accounts",
"description": [
"Create group accounts, optionally assigning them static GIDs.",
"Runs `groupadd` from the buildhost to create the groups listed in `groups`.",
"If no `gid` is given, `groupadd` will choose one.",
"If the specified group name or GID is already in use, this stage will fail."
],
"schema": {
"additionalProperties": false,
"properties": {
"groups": {
"type": "object",
"additionalProperties": false,
"description": "Keys are group names, values are objects with group info",
"patternProperties": {
"^[A-Za-z0-9_][A-Za-z0-9_-]{0,31}$": {
"type": "object",
"properties": {
"gid": {
"type": "number",
"description": "GID for this group"
}
}
}
}
}
}
}
}

View file

@ -1,80 +1,4 @@
#!/usr/bin/python3
"""
Configure GRUB2 bootloader and set boot options
Configure the system to use GRUB2 as the bootloader, and set boot options.
Sets the GRUB2 boot/root filesystem to `rootfs`. If a separated boot
partition is used it can be specified via `bootfs`. The file-systems
can be identified either via uuid (`{"uuid": "<uuid>"}`) or label
(`{"label": "<label>"}`). The kernel boot argument will be composed
of the root file system id and additional options specified in
`{kernel_opts}`, if any.
Configures GRUB2 to boot via the Boot Loader Specification
(https://systemd.io/BOOT_LOADER_SPECIFICATION), which is the default
behavior in Fedora 30 and later.
This stage will overwrite `/etc/default/grub`, `/boot/grub2/grubenv`, and
`/boot/grub2/grub.cfg`. (Leading directories will be created if not present.)
If Legacy boot support is requested via `legacy` this stage will also
overwrite `/boot/grub2/grub.cfg` and will copy the GRUB2 files from the
buildhost into the target tree:
* `/usr/share/grub/unicode.pf2` -> `/boot/grub2/fonts/`
* `/usr/lib/grub/$platform/*.{mod,lst}` -> `/boot/grub2/$platform/`
* NOTE: skips `fdt.lst`, which is an empty file
The $platform variable (default: i386-pc) refers to target platform
that grub2 is mean to ran on (see grub-install(1)'s `--target`)
NB: with legacy support enabled, this stage will fail if the buildhost
doesn't have `/usr/lib/grub/$platform/` and `/usr/share/grub/unicode.pf2`.
If UEFI support is enabled via `uefi: {"vendor": "<vendor>"}` this stage will
also write the `grub.cfg` to `boot/efi/EFI/<vendor>/grub.cfg`. EFI binaries
and accompanying data can be installed from the built root via `uefi.install`.
Both UEFI and Legacy can be specified at the same time (hybrid boot).
If `uefi.unified` is specified or hybrid boot is enabled, the main grub config
will be written to `boot/grub2/grub.cfg` and a redirect config will be placed
in the EFI directory.
If the `saved_entry` option is present it will result in an entry in the
`grubenv` file of the same name. The grub config file contains logic so
that this variable will be used to select the next boot entry. This will
also make grub2-reboot and grub2-set-default tools work. It will also
prevent newly installed non-default kernels (like e.g. the debug kernel)
to be selected as default. The contents of variable needs to match the
corresponding loader entry, which currently is a combination of the
machine id and kernel NVRA, like e.g.:
`ffffffffffffffffffffffffffffffff-5.6.6-300.fc32.x86_64`
If `saved_entry` is set it is advisable to set `config.default` to
`saved` so that any re-creation of the grub configuration by the
user will preserve that functionality.
Support for "greenboot" can be turned on via the `greenboot` option.
Greenboot is the idea of automatically rolling back bad updates,
i.e. updates that do not boot successfully. The implementation
is split between the boot loader and a user space component.
The latter sets two variables `boot_counter`, which indicates
the maximum number of boot attempts and `boot_success` which
tells the boot laoder if a previous boot was successful. The
bootloader on the other hand will decrement the counter variable
and reset the success indicator one.
An implementation of the user space component for rpm-ostree is
called `greenboot`.
Support for ignition (https://github.com/coreos/ignition) can be turned
on via the `ignition` option. If enabled, a 'ignition_firstboot' variable
will be created, which is meant to be included in the kernel command line.
The grub.cfg will then contain the necessary code to detect and source
the '/boot/ignition.firstboot' file and configure said 'ignition_firstboot'
variable appropriately. See the 'org.osbuild.ignition' stage for more
information on that file.
"""
import os
import shutil
import string
@ -82,164 +6,6 @@ import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"oneOf": [{
"required": ["root_fs_uuid"]
}, {
"required": ["rootfs"]
}],
"definitions": {
"uuid": {
"description": "Identify the file system by UUID",
"type": "string",
"oneOf": [
{ "pattern": "^[0-9A-Za-z]{8}(-[0-9A-Za-z]{4}){3}-[0-9A-Za-z]{12}$",
"examples": ["9c6ae55b-cf88-45b8-84e8-64990759f39d"] },
{ "pattern": "^[0-9A-Za-z]{4}-[0-9A-Za-z]{4}$",
"examples": ["6699-AFB5"] }
]
},
"filesystem": {
"description": "Description of how to locate a file system",
"type": "object",
"oneOf": [{
"required": ["uuid"]
}, {
"required": ["label"]
}],
"properties": {
"label": {
"description": "Identify the file system by label",
"type": "string"
},
"uuid": { "$ref": "#/definitions/uuid" }
}
},
"terminal": {
"description": "Terminal device",
"type": "array",
"items": {
"type": "string"
}
}
},
"properties": {
"rootfs": { "$ref": "#/definitions/filesystem" },
"bootfs": { "$ref": "#/definitions/filesystem" },
"root_fs_uuid": { "$ref": "#/definitions/uuid" },
"boot_fs_uuid": { "$ref": "#/definitions/uuid" },
"kernel_opts": {
"description": "Additional kernel boot options",
"type": "string",
"default": ""
},
"legacy": {
"description": "Include legacy boot support",
"oneOf": [
{"type": "boolean", "default": false},
{"type": "string"}
]
},
"uefi": {
"description": "Include UEFI boot support",
"type": "object",
"required": ["vendor"],
"properties": {
"vendor": {
"type": "string",
"description": "The vendor of the UEFI binaries (this is us)",
"examples": ["fedora"],
"pattern": "^(.+)$"
},
"efi_src_dir": {
"type": "string",
"description": "The source path to use for the EFI/ binaries when installing is set to True",
"default": "/boot/efi/EFI"
},
"install": {
"description": "Install EFI binaries and data from the build root",
"type": "boolean",
"default": false
},
"unified": {
"description": "Main grub config in 'boot/grub2/grub.cfg'",
"type": "boolean",
"default": false
}
}
},
"saved_entry": {
"description": "Set the variable of the same name in `grubenv`.",
"type": "string"
},
"write_defaults": {
"description": "Whether to write /etc/defaults/grub",
"type": "boolean",
"default": true
},
"write_cmdline": {
"description": "Include the kernel command line in `grubenv`",
"type": "boolean",
"default": true
},
"ignition": {
"description": "Include ignition support in the grub.cfg",
"type": "boolean",
"default": false
},
"greenboot": {
"description": "Include support for fallback counting",
"type": "boolean",
"default": false
},
"config": {
"description": "Configuration options for grub itself",
"type": "object",
"additionalProperties": false,
"properties": {
"default": {
"description": "Default boot entry",
"type": "string"
},
"disable_recovery": {
"type": "boolean"
},
"disable_submenu": {
"type": "boolean"
},
"distributor": {
"description": "Name of the distributor",
"type": "string"
},
"terminal": {
"$ref": "#/definitions/terminal"
},
"terminal_input": {
"$ref": "#/definitions/terminal"
},
"terminal_output": {
"$ref": "#/definitions/terminal"
},
"timeout": {
"description": "Timeout in seconds",
"type": "integer",
"minimum": 0,
"default": 0
},
"timeout_style": {
"type": "string",
"enum": ["hidden", "menu", "countdown"]
},
"serial": {
"description": "The command to configure the serial console",
"type": "string"
}
}
}
}
"""
# The main grub2 configuration file template. Used for UEFI and legacy
# boot. The parameters are currently:

View file

@ -1,46 +1,4 @@
#!/usr/bin/python3
"""
Install the grub2 boot loader for non-UEFI systems or hybrid boot
This stage can be used to generate a grub2 core image and install
it to the correct location to enable booting of non-UEFI systems,
i.e. x86 legacy and PPC64LE (Open Firmware).
On x86, the core image can be installed into the MBR gap or to a
dedicated BIOS boot partition when the partition label is GTP. On
ppc64le with Open Firmware a dedicated 'PrEP partition' is used.
x86 / MBR gap:
For historic and performance reasons the first partition
is aligned to a specific sector number (used to be 64,
now it is 2048), which leaves a gap between it and the MBR,
where the core image can be embedded in
x86 / BIOS boot:
A dedicated partition with a specific GUID[1] is used.
ppc64le / Open Firmware:
A dedicated partition with a specified GUID[2] is used.
On ppc64le with Open Firmware a special partition called
'PrEP partition' is used the store the grub2 core; the
firmware looks for this partition and directly loads and
executes the core form it.
On x86, a "boot image", aka grub stage 1, is installed into the
master boot record (MBR) of the partition (even in the case the
partition layout is GPT). It main purpose is to load the second
stage (core image). Therefore the location of the core image is
patched into the boot image.
On ppc64le, the firmware itself directly loads the complete core
image and transfers control to it.
[1] 21686148-6449-6E6F-744E-656564454649
[2] 9E1A2D38-C612-4316-AA26-8B49521E5A8B
"""
import os
import shutil
import struct
@ -50,92 +8,6 @@ from typing import BinaryIO, Dict
import osbuild.api
SCHEMA = r"""
"definitions": {
"core-mkimage": {
"type": "object",
"description": "Generate the core image via grub-mkimage",
"additionalProperties": false,
"required": ["type", "partlabel", "filesystem"],
"properties": {
"type": {
"enum": ["mkimage"]
},
"partlabel": {
"type": "string",
"enum": ["gpt", "dos"]
},
"filesystem": {
"type": "string",
"enum": ["ext4", "xfs", "btrfs"]
},
"binary": {
"description": "grub-mkimage binary name",
"type": "string",
"default": "grub2-mkimage"
}
}
},
"prefix-partition": {
"type": "object",
"description": "Grub2 config on a specific partition, e.g. (,gpt3)/boot",
"additionalProperties": false,
"required": ["type", "partlabel", "number", "path"],
"properties": {
"type": {
"enum": ["partition"]
},
"partlabel": {
"type": "string",
"enum": ["gpt", "dos"]
},
"number": {
"description": "The partition number, starting at zero",
"type": "number"
},
"path": {
"description": "location of grub config inside the partition",
"type": "string",
"pattern": "\/.*"
}
}
}
},
"additionalProperties": false,
"required": ["filename", "platform", "location", "core", "prefix"],
"properties": {
"filename": {
"type": "string",
"description": "filename of the disk image"
},
"platform": {
"type": "string",
"description": "Platform of the target system"
},
"location": {
"type": "integer",
"description": "Location of the stage 2 (in sectors)"
},
"core": {
"description": "How to obtain the GRUB core image",
"oneOf": [
{"$ref": "#/definitions/core-mkimage"}
]
},
"prefix": {
"description": "location of grub config",
"oneOf": [
{"$ref": "#/definitions/prefix-partition"}
]
},
"sector-size": {
"type": "number",
"description": "Sector size (in bytes)",
"default": 512
}
}
"""
def grub2_partition_id(label):
"""grub2 partition identifier for the partition table"""

View file

@ -0,0 +1,151 @@
{
"summary": "Install the grub2 boot loader for non-UEFI systems or hybrid boot",
"description": [
"This stage can be used to generate a grub2 core image and install",
"it to the correct location to enable booting of non-UEFI systems,",
"i.e. x86 legacy and PPC64LE (Open Firmware).",
"On x86, the core image can be installed into the MBR gap or to a",
"dedicated BIOS boot partition when the partition label is GTP. On",
"ppc64le with Open Firmware a dedicated 'PrEP partition' is used.",
"x86 / MBR gap:",
" For historic and performance reasons the first partition",
" is aligned to a specific sector number (used to be 64,",
" now it is 2048), which leaves a gap between it and the MBR,",
" where the core image can be embedded in",
"x86 / BIOS boot:",
" A dedicated partition with a specific GUID[1] is used.",
"ppc64le / Open Firmware:",
" A dedicated partition with a specified GUID[2] is used.",
" On ppc64le with Open Firmware a special partition called",
" 'PrEP partition' is used the store the grub2 core; the",
" firmware looks for this partition and directly loads and",
" executes the core form it.",
"On x86, a \"boot image\", aka grub stage 1, is installed into the",
"master boot record (MBR) of the partition (even in the case the",
"partition layout is GPT). It main purpose is to load the second",
"stage (core image). Therefore the location of the core image is",
"patched into the boot image.",
"On ppc64le, the firmware itself directly loads the complete core",
"image and transfers control to it.",
"[1] 21686148-6449-6E6F-744E-656564454649",
"[2] 9E1A2D38-C612-4316-AA26-8B49521E5A8B"
],
"schema": {
"definitions": {
"core-mkimage": {
"type": "object",
"description": "Generate the core image via grub-mkimage",
"additionalProperties": false,
"required": [
"type",
"partlabel",
"filesystem"
],
"properties": {
"type": {
"enum": [
"mkimage"
]
},
"partlabel": {
"type": "string",
"enum": [
"gpt",
"dos"
]
},
"filesystem": {
"type": "string",
"enum": [
"ext4",
"xfs",
"btrfs"
]
},
"binary": {
"description": "grub-mkimage binary name",
"type": "string",
"default": "grub2-mkimage"
}
}
},
"prefix-partition": {
"type": "object",
"description": "Grub2 config on a specific partition, e.g. (,gpt3)/boot",
"additionalProperties": false,
"required": [
"type",
"partlabel",
"number",
"path"
],
"properties": {
"type": {
"enum": [
"partition"
]
},
"partlabel": {
"type": "string",
"enum": [
"gpt",
"dos"
]
},
"number": {
"description": "The partition number, starting at zero",
"type": "number"
},
"path": {
"description": "location of grub config inside the partition",
"type": "string",
"pattern": "/.*"
}
}
}
},
"additionalProperties": false,
"required": [
"filename",
"platform",
"location",
"core",
"prefix"
],
"properties": {
"filename": {
"type": "string",
"description": "filename of the disk image"
},
"platform": {
"type": "string",
"description": "Platform of the target system"
},
"location": {
"type": "integer",
"description": "Location of the stage 2 (in sectors)"
},
"core": {
"description": "How to obtain the GRUB core image",
"oneOf": [
{
"$ref": "#/definitions/core-mkimage"
}
]
},
"prefix": {
"description": "location of grub config",
"oneOf": [
{
"$ref": "#/definitions/prefix-partition"
}
]
},
"sector-size": {
"type": "number",
"description": "Sector size (in bytes)",
"default": 512
}
}
}
}

View file

@ -1,9 +1,4 @@
#!/usr/bin/python3
"""
Create a boot filesystem tree, can be consumed to create
an efiboot.img.
"""
import os
import shutil
import string
@ -11,65 +6,6 @@ import sys
import osbuild.api
SCHEMA_2 = """
"options": {
"additionalProperties": false,
"required": ["product", "kernel", "isolabel"],
"properties": {
"product": {
"type": "object",
"additionalProperties": false,
"required": ["name", "version"],
"properties": {
"name": {"type": "string"},
"version": {"type": "string"}
}
},
"kernel": {
"type": "object",
"required": ["dir"],
"properties": {
"dir": {
"type": "string"
},
"opts": {
"description": "Array of group names for this user",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"isolabel": {
"type": "string"
},
"architectures": {
"type": "array",
"items": {
"type": "string"
}
},
"vendor": {
"type": "string"
},
"config": {
"description": "Configuration options for grub itself",
"type": "object",
"additionalProperties": false,
"properties": {
"timeout": {
"description": "Timeout in seconds",
"type": "integer",
"minimum": 0,
"default": 60
}
}
}
}
}
"""
# The main grub2 configuration file template. Used for UEFI.
GRUB2_EFI_CFG_TEMPLATE = """

View file

@ -0,0 +1,75 @@
{
"summary": "Create a boot filesystem tree, can be consumed to create\nan efiboot.img.",
"description": [],
"schema_2": {
"options": {
"additionalProperties": false,
"required": [
"product",
"kernel",
"isolabel"
],
"properties": {
"product": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"version"
],
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"kernel": {
"type": "object",
"required": [
"dir"
],
"properties": {
"dir": {
"type": "string"
},
"opts": {
"description": "Array of group names for this user",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"isolabel": {
"type": "string"
},
"architectures": {
"type": "array",
"items": {
"type": "string"
}
},
"vendor": {
"type": "string"
},
"config": {
"description": "Configuration options for grub itself",
"type": "object",
"additionalProperties": false,
"properties": {
"timeout": {
"description": "Timeout in seconds",
"type": "integer",
"minimum": 0,
"default": 60
}
}
}
}
}
}
}

View file

@ -1,43 +1,4 @@
#!/usr/bin/python3
"""
Configure GRUB2 bootloader and set boot options (legacy, i.e. non-BLS)
This stage creates traditional menu entries for systems that are not
capable of using the Booloader Specific (BLS).
Sets the GRUB2 boot/root filesystem to `rootfs`. If a separated boot
partition is used it can be specified via `bootfs`. The file-systems
can be identified either via
- uuid (`{"uuid": "<uuid>"}`)
- label (`{"label": "<label>"}`)
- device (`{"device": "<device>"}`, only for the root file system)
The kernel boot argument will be composed of the root file system id
and additional options specified in `config.cmdline`, if any.
This stage will overwrite `/etc/default/grub`, `/boot/grub2/grubenv`;
leading directories will be created if not present.
The stage supports configuring grub for BIOS boot and UEFI systems:
If BIOS boot support is requested via `bios` this stage will also
overwrite `/boot/grub2/grub.cfg` and will copy the GRUB2 files from the
buildhost into the target tree:
* `/usr/share/grub/unicode.pf2` -> `/boot/grub2/fonts/`
* `/usr/lib/grub/$platform/*.{mod,lst}` -> `/boot/grub2/$platform/`
* NOTE: skips `fdt.lst`, which is an empty file
NB: with bios support enabled, this stage will fail if the buildhost
doesn't have `/usr/lib/grub/$platform/` and `/usr/share/grub/unicode.pf2`.
If UEFI support is enabled via `uefi: {"vendor": "<vendor>"}` this stage will
also write the `grub.cfg` to `boot/efi/EFI/<vendor>/grub.cfg`. EFI binaries
and accompanying data can be installed from the built root via `uefi.install`.
Both UEFI and Legacy can be specified at the same time (hybrid boot).
"""
import os
import shutil
import string
@ -45,181 +6,6 @@ import sys
import osbuild.api
SCHEMA = """
"definitions": {
"filesystem": {
"description": "Description of how to locate a file system",
"type": "object",
"oneOf": [{
"required": ["uuid"]
}, {
"required": ["label"]
}, {
"required": ["device"]
}],
"properties": {
"device": {
"description": "Identify the file system by device node",
"type": "string"
},
"label": {
"description": "Identify the file system by label",
"type": "string"
},
"uuid": {
"description": "Identify the file system by UUID",
"type": "string",
"oneOf": [
{ "pattern": "^[0-9A-Za-z]{8}(-[0-9A-Za-z]{4}){3}-[0-9A-Za-z]{12}$",
"examples": ["9c6ae55b-cf88-45b8-84e8-64990759f39d"] },
{ "pattern": "^[0-9A-Za-z]{4}-[0-9A-Za-z]{4}$",
"examples": ["6699-AFB5"] }
]
}
}
},
"terminal": {
"description": "Terminal device",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": ["rootfs", "entries"],
"anyOf": [{
"required": ["bios"]
}, {
"required": ["uefi"]
}],
"properties": {
"rootfs": { "$ref": "#/definitions/filesystem" },
"bootfs": { "$ref": "#/definitions/filesystem" },
"bios": {
"description": "Include bios boot support",
"type": "object",
"required": ["platform"],
"properties": {
"platform": {
"type": "string",
"enum": ["i386-pc", "powerpc-ieee1275"]
}
}
},
"uefi": {
"description": "Include UEFI boot support",
"type": "object",
"required": ["vendor"],
"properties": {
"vendor": {
"type": "string",
"description": "The vendor of the UEFI binaries (this is us)",
"examples": ["fedora"],
"pattern": "^(.+)$"
},
"install": {
"description": "Install EFI binaries and data from the build root",
"type": "boolean",
"default": false
}
}
},
"write_defaults": {
"description": "Whether to write /etc/defaults/grub",
"type": "boolean",
"default": true
},
"entries": {
"description": "List of entries to add to the boot menu",
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "product", "kernel"],
"properties": {
"default": {
"type": "boolean",
"description": "Make this entry the default entry"
},
"id": {
"description": "UUID for the entry (grub uses the root fs uuid)",
"type": "string"
},
"product": {
"type": "object",
"additionalProperties": false,
"required": ["name", "version"],
"properties": {
"name": {"type": "string"},
"nick": {"type": "string"},
"version": {"type": "string"}
}
},
"kernel": {
"description": "The kernel (EVRA)",
"type": "string"
}
}
}
},
"config": {
"description": "Configuration options for grub itself",
"type": "object",
"additionalProperties": false,
"properties": {
"cmdline": {
"description": "Additional kernel command line options",
"type": "string"
},
"default": {
"description": "Default boot entry",
"type": "string",
"default": "saved"
},
"disable_recovery": {
"type": "boolean",
"default": true
},
"disable_submenu": {
"type": "boolean",
"default": true
},
"distributor": {
"description": "Name of the distributor",
"type": "string"
},
"terminal": {
"$ref": "#/definitions/terminal"
},
"terminal_input": {
"$ref": "#/definitions/terminal"
},
"terminal_output": {
"$ref": "#/definitions/terminal"
},
"timeout": {
"description": "Timeout in seconds",
"type": "integer",
"minimum": 0,
"default": 0
},
"timeout_style": {
"type": "string",
"enum": ["hidden", "menu", "countdown"],
"default": "countdown"
},
"serial": {
"description": "The command to configure the serial console",
"type": "string"
}
}
}
}
"""
# The main grub2 configuration file template. Used for UEFI and legacy
GRUB_CFG_TEMPLATE = """

View file

@ -0,0 +1,261 @@
{
"summary": "Configure GRUB2 bootloader and set boot options (legacy, i.e. non-BLS)",
"description": [
"This stage creates traditional menu entries for systems that are not",
"capable of using the Booloader Specific (BLS).",
"Sets the GRUB2 boot/root filesystem to `rootfs`. If a separated boot",
"partition is used it can be specified via `bootfs`. The file-systems",
"can be identified either via",
" - uuid (`{\"uuid\": \"<uuid>\"}`)",
" - label (`{\"label\": \"<label>\"}`)",
" - device (`{\"device\": \"<device>\"}`, only for the root file system)",
"The kernel boot argument will be composed of the root file system id",
"and additional options specified in `config.cmdline`, if any.",
"This stage will overwrite `/etc/default/grub`, `/boot/grub2/grubenv`;",
"leading directories will be created if not present.",
"The stage supports configuring grub for BIOS boot and UEFI systems:",
"If BIOS boot support is requested via `bios` this stage will also",
"overwrite `/boot/grub2/grub.cfg` and will copy the GRUB2 files from the",
"buildhost into the target tree:",
"* `/usr/share/grub/unicode.pf2` -> `/boot/grub2/fonts/`",
"* `/usr/lib/grub/$platform/*.{mod,lst}` -> `/boot/grub2/$platform/`",
" * NOTE: skips `fdt.lst`, which is an empty file",
"NB: with bios support enabled, this stage will fail if the buildhost",
"doesn't have `/usr/lib/grub/$platform/` and `/usr/share/grub/unicode.pf2`.",
"If UEFI support is enabled via `uefi: {\"vendor\": \"<vendor>\"}` this stage will",
"also write the `grub.cfg` to `boot/efi/EFI/<vendor>/grub.cfg`. EFI binaries",
"and accompanying data can be installed from the built root via `uefi.install`.",
"Both UEFI and Legacy can be specified at the same time (hybrid boot)."
],
"schema": {
"definitions": {
"filesystem": {
"description": "Description of how to locate a file system",
"type": "object",
"oneOf": [
{
"required": [
"uuid"
]
},
{
"required": [
"label"
]
},
{
"required": [
"device"
]
}
],
"properties": {
"device": {
"description": "Identify the file system by device node",
"type": "string"
},
"label": {
"description": "Identify the file system by label",
"type": "string"
},
"uuid": {
"description": "Identify the file system by UUID",
"type": "string",
"oneOf": [
{
"pattern": "^[0-9A-Za-z]{8}(-[0-9A-Za-z]{4}){3}-[0-9A-Za-z]{12}$",
"examples": [
"9c6ae55b-cf88-45b8-84e8-64990759f39d"
]
},
{
"pattern": "^[0-9A-Za-z]{4}-[0-9A-Za-z]{4}$",
"examples": [
"6699-AFB5"
]
}
]
}
}
},
"terminal": {
"description": "Terminal device",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"rootfs",
"entries"
],
"anyOf": [
{
"required": [
"bios"
]
},
{
"required": [
"uefi"
]
}
],
"properties": {
"rootfs": {
"$ref": "#/definitions/filesystem"
},
"bootfs": {
"$ref": "#/definitions/filesystem"
},
"bios": {
"description": "Include bios boot support",
"type": "object",
"required": [
"platform"
],
"properties": {
"platform": {
"type": "string",
"enum": [
"i386-pc",
"powerpc-ieee1275"
]
}
}
},
"uefi": {
"description": "Include UEFI boot support",
"type": "object",
"required": [
"vendor"
],
"properties": {
"vendor": {
"type": "string",
"description": "The vendor of the UEFI binaries (this is us)",
"examples": [
"fedora"
],
"pattern": "^(.+)$"
},
"install": {
"description": "Install EFI binaries and data from the build root",
"type": "boolean",
"default": false
}
}
},
"write_defaults": {
"description": "Whether to write /etc/defaults/grub",
"type": "boolean",
"default": true
},
"entries": {
"description": "List of entries to add to the boot menu",
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"product",
"kernel"
],
"properties": {
"default": {
"type": "boolean",
"description": "Make this entry the default entry"
},
"id": {
"description": "UUID for the entry (grub uses the root fs uuid)",
"type": "string"
},
"product": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"version"
],
"properties": {
"name": {
"type": "string"
},
"nick": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"kernel": {
"description": "The kernel (EVRA)",
"type": "string"
}
}
}
},
"config": {
"description": "Configuration options for grub itself",
"type": "object",
"additionalProperties": false,
"properties": {
"cmdline": {
"description": "Additional kernel command line options",
"type": "string"
},
"default": {
"description": "Default boot entry",
"type": "string",
"default": "saved"
},
"disable_recovery": {
"type": "boolean",
"default": true
},
"disable_submenu": {
"type": "boolean",
"default": true
},
"distributor": {
"description": "Name of the distributor",
"type": "string"
},
"terminal": {
"$ref": "#/definitions/terminal"
},
"terminal_input": {
"$ref": "#/definitions/terminal"
},
"terminal_output": {
"$ref": "#/definitions/terminal"
},
"timeout": {
"description": "Timeout in seconds",
"type": "integer",
"minimum": 0,
"default": 0
},
"timeout_style": {
"type": "string",
"enum": [
"hidden",
"menu",
"countdown"
],
"default": "countdown"
},
"serial": {
"description": "The command to configure the serial console",
"type": "string"
}
}
}
}
}
}

View file

@ -0,0 +1,266 @@
{
"summary": "Configure GRUB2 bootloader and set boot options",
"description": [
"Configure the system to use GRUB2 as the bootloader, and set boot options.",
"Sets the GRUB2 boot/root filesystem to `rootfs`. If a separated boot",
"partition is used it can be specified via `bootfs`. The file-systems",
"can be identified either via uuid (`{\"uuid\": \"<uuid>\"}`) or label",
"(`{\"label\": \"<label>\"}`). The kernel boot argument will be composed",
"of the root file system id and additional options specified in",
"`{kernel_opts}`, if any.",
"Configures GRUB2 to boot via the Boot Loader Specification",
"(https://systemd.io/BOOT_LOADER_SPECIFICATION), which is the default",
"behavior in Fedora 30 and later.",
"This stage will overwrite `/etc/default/grub`, `/boot/grub2/grubenv`, and",
"`/boot/grub2/grub.cfg`. (Leading directories will be created if not present.)",
"If Legacy boot support is requested via `legacy` this stage will also",
"overwrite `/boot/grub2/grub.cfg` and will copy the GRUB2 files from the",
"buildhost into the target tree:",
"* `/usr/share/grub/unicode.pf2` -> `/boot/grub2/fonts/`",
"* `/usr/lib/grub/$platform/*.{mod,lst}` -> `/boot/grub2/$platform/`",
" * NOTE: skips `fdt.lst`, which is an empty file",
"The $platform variable (default: i386-pc) refers to target platform",
"that grub2 is mean to ran on (see grub-install(1)'s `--target`)",
"NB: with legacy support enabled, this stage will fail if the buildhost",
"doesn't have `/usr/lib/grub/$platform/` and `/usr/share/grub/unicode.pf2`.",
"If UEFI support is enabled via `uefi: {\"vendor\": \"<vendor>\"}` this stage will",
"also write the `grub.cfg` to `boot/efi/EFI/<vendor>/grub.cfg`. EFI binaries",
"and accompanying data can be installed from the built root via `uefi.install`.",
"Both UEFI and Legacy can be specified at the same time (hybrid boot).",
"If `uefi.unified` is specified or hybrid boot is enabled, the main grub config",
"will be written to `boot/grub2/grub.cfg` and a redirect config will be placed",
"in the EFI directory.",
"If the `saved_entry` option is present it will result in an entry in the",
"`grubenv` file of the same name. The grub config file contains logic so",
"that this variable will be used to select the next boot entry. This will",
"also make grub2-reboot and grub2-set-default tools work. It will also",
"prevent newly installed non-default kernels (like e.g. the debug kernel)",
"to be selected as default. The contents of variable needs to match the",
"corresponding loader entry, which currently is a combination of the",
"machine id and kernel NVRA, like e.g.:",
" `ffffffffffffffffffffffffffffffff-5.6.6-300.fc32.x86_64`",
"If `saved_entry` is set it is advisable to set `config.default` to",
"`saved` so that any re-creation of the grub configuration by the",
"user will preserve that functionality.",
"Support for \"greenboot\" can be turned on via the `greenboot` option.",
"Greenboot is the idea of automatically rolling back bad updates,",
"i.e. updates that do not boot successfully. The implementation",
"is split between the boot loader and a user space component.",
"The latter sets two variables `boot_counter`, which indicates",
"the maximum number of boot attempts and `boot_success` which",
"tells the boot laoder if a previous boot was successful. The",
"bootloader on the other hand will decrement the counter variable",
"and reset the success indicator one.",
"An implementation of the user space component for rpm-ostree is",
"called `greenboot`.",
"Support for ignition (https://github.com/coreos/ignition) can be turned",
"on via the `ignition` option. If enabled, a 'ignition_firstboot' variable",
"will be created, which is meant to be included in the kernel command line.",
"The grub.cfg will then contain the necessary code to detect and source",
"the '/boot/ignition.firstboot' file and configure said 'ignition_firstboot'",
"variable appropriately. See the 'org.osbuild.ignition' stage for more",
"information on that file."
],
"schema": {
"additionalProperties": false,
"oneOf": [
{
"required": [
"root_fs_uuid"
]
},
{
"required": [
"rootfs"
]
}
],
"definitions": {
"uuid": {
"description": "Identify the file system by UUID",
"type": "string",
"oneOf": [
{
"pattern": "^[0-9A-Za-z]{8}(-[0-9A-Za-z]{4}){3}-[0-9A-Za-z]{12}$",
"examples": [
"9c6ae55b-cf88-45b8-84e8-64990759f39d"
]
},
{
"pattern": "^[0-9A-Za-z]{4}-[0-9A-Za-z]{4}$",
"examples": [
"6699-AFB5"
]
}
]
},
"filesystem": {
"description": "Description of how to locate a file system",
"type": "object",
"oneOf": [
{
"required": [
"uuid"
]
},
{
"required": [
"label"
]
}
],
"properties": {
"label": {
"description": "Identify the file system by label",
"type": "string"
},
"uuid": {
"$ref": "#/definitions/uuid"
}
}
},
"terminal": {
"description": "Terminal device",
"type": "array",
"items": {
"type": "string"
}
}
},
"properties": {
"rootfs": {
"$ref": "#/definitions/filesystem"
},
"bootfs": {
"$ref": "#/definitions/filesystem"
},
"root_fs_uuid": {
"$ref": "#/definitions/uuid"
},
"boot_fs_uuid": {
"$ref": "#/definitions/uuid"
},
"kernel_opts": {
"description": "Additional kernel boot options",
"type": "string",
"default": ""
},
"legacy": {
"description": "Include legacy boot support",
"oneOf": [
{
"type": "boolean",
"default": false
},
{
"type": "string"
}
]
},
"uefi": {
"description": "Include UEFI boot support",
"type": "object",
"required": [
"vendor"
],
"properties": {
"vendor": {
"type": "string",
"description": "The vendor of the UEFI binaries (this is us)",
"examples": [
"fedora"
],
"pattern": "^(.+)$"
},
"efi_src_dir": {
"type": "string",
"description": "The source path to use for the EFI/ binaries when installing is set to True",
"default": "/boot/efi/EFI"
},
"install": {
"description": "Install EFI binaries and data from the build root",
"type": "boolean",
"default": false
},
"unified": {
"description": "Main grub config in 'boot/grub2/grub.cfg'",
"type": "boolean",
"default": false
}
}
},
"saved_entry": {
"description": "Set the variable of the same name in `grubenv`.",
"type": "string"
},
"write_defaults": {
"description": "Whether to write /etc/defaults/grub",
"type": "boolean",
"default": true
},
"write_cmdline": {
"description": "Include the kernel command line in `grubenv`",
"type": "boolean",
"default": true
},
"ignition": {
"description": "Include ignition support in the grub.cfg",
"type": "boolean",
"default": false
},
"greenboot": {
"description": "Include support for fallback counting",
"type": "boolean",
"default": false
},
"config": {
"description": "Configuration options for grub itself",
"type": "object",
"additionalProperties": false,
"properties": {
"default": {
"description": "Default boot entry",
"type": "string"
},
"disable_recovery": {
"type": "boolean"
},
"disable_submenu": {
"type": "boolean"
},
"distributor": {
"description": "Name of the distributor",
"type": "string"
},
"terminal": {
"$ref": "#/definitions/terminal"
},
"terminal_input": {
"$ref": "#/definitions/terminal"
},
"terminal_output": {
"$ref": "#/definitions/terminal"
},
"timeout": {
"description": "Timeout in seconds",
"type": "integer",
"minimum": 0,
"default": 0
},
"timeout_style": {
"type": "string",
"enum": [
"hidden",
"menu",
"countdown"
]
},
"serial": {
"description": "The command to configure the serial console",
"type": "string"
}
}
}
}
}
}

View file

@ -1,40 +1,10 @@
#!/usr/bin/python3
"""
Extract a gzipped file
Buildhost commands used: `gunzip`.
"""
import os
import subprocess
import sys
import osbuild.api
SCHEMA_2 = r"""
"inputs": {
"type": "object",
"additionalProperties": false,
"required": ["file"],
"properties": {
"file": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"required": ["path"],
"properties": {
"path": {
"description": "Unzip here.",
"type": "string"
}
}
}
"""
def parse_input(inputs):
image = inputs["file"]

View file

@ -0,0 +1,33 @@
{
"summary": "Extract a gzipped file",
"description": [
"Buildhost commands used: `gunzip`."
],
"schema_2": {
"inputs": {
"type": "object",
"additionalProperties": false,
"required": [
"file"
],
"properties": {
"file": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"required": [
"path"
],
"properties": {
"path": {
"description": "Unzip here.",
"type": "string"
}
}
}
}
}

View file

@ -1,47 +1,10 @@
#!/usr/bin/python3
"""
Compress a file using gzip
Buildhost commands used: `gzip`.
"""
import os
import subprocess
import sys
import osbuild.api
SCHEMA_2 = r"""
"inputs": {
"type": "object",
"additionalProperties": false,
"required": ["file"],
"properties": {
"file": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"required": ["filename"],
"properties": {
"filename": {
"description": "Filename to use for the compressed file",
"type": "string"
},
"level": {
"description": "Compression level",
"type": "integer",
"minimum": 1,
"maximum": 9,
"default": 1
}
}
}
"""
def parse_input(inputs):
image = inputs["file"]

View file

@ -0,0 +1,40 @@
{
"summary": "Compress a file using gzip",
"description": [
"Buildhost commands used: `gzip`."
],
"schema_2": {
"inputs": {
"type": "object",
"additionalProperties": false,
"required": [
"file"
],
"properties": {
"file": {
"type": "object",
"additionalProperties": true
}
}
},
"options": {
"additionalProperties": false,
"required": [
"filename"
],
"properties": {
"filename": {
"description": "Filename to use for the compressed file",
"type": "string"
},
"level": {
"description": "Compression level",
"type": "integer",
"minimum": 1,
"maximum": 9,
"default": 1
}
}
}
}
}

View file

@ -1,32 +1,10 @@
#!/usr/bin/python3
"""
Set system hostname
Sets system hostname.
Deletes /etc/hostname if present, then runs `systemd-firstboot` from the
buildhost with `--hostname={hostname}`, which checks the validity of the
hostname and writes it to /etc/hostname.
"""
import os
import subprocess
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"required": ["hostname"],
"properties": {
"hostname": {
"type": "string",
"description": "hostname for the target system"
}
}
"""
def main(tree, options):
hostname = options["hostname"]

View file

@ -0,0 +1,21 @@
{
"summary": "Set system hostname",
"description": [
"Sets system hostname.",
"Deletes /etc/hostname if present, then runs `systemd-firstboot` from the",
"buildhost with `--hostname={hostname}`, which checks the validity of the",
"hostname and writes it to /etc/hostname."
],
"schema": {
"additionalProperties": false,
"required": [
"hostname"
],
"properties": {
"hostname": {
"type": "string",
"description": "hostname for the target system"
}
}
}
}

View file

@ -1,39 +1,8 @@
#!/usr/bin/python3
"""
Setup ignition so it will be triggered on first boot.
Create the file '/boot/ignition.firstboot' that will be used by grub,
if the necessary ignition support is enabled, to create a variable to
be used in the kernel command line ('ignition_firstboot'). Via this
variable, if included in the actual kernel command line, the run of
ignition during early boot can be controlled: if grub detects the
aforementioned file to be present it will set 'ignition_firstboot'
to "ignition.firstboot" which is the trigger for ignition to run.
The "ignition-firstboot-complete.service" will remove said file and
thus preventing ignition to be run on the next boot.
The `network` option can be used to overwrite the default network
configuration, in case that ignition is run.
"""
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"properties": {
"network": {
"type": "array",
"description": "Overwrite default network connection",
"items": {
"type": "string"
}
}
}
"""
def main(tree, options):
network = options.get("network", [])

View file

@ -0,0 +1,28 @@
{
"summary": "Setup ignition so it will be triggered on first boot.",
"description": [
"Create the file '/boot/ignition.firstboot' that will be used by grub,",
"if the necessary ignition support is enabled, to create a variable to",
"be used in the kernel command line ('ignition_firstboot'). Via this",
"variable, if included in the actual kernel command line, the run of",
"ignition during early boot can be controlled: if grub detects the",
"aforementioned file to be present it will set 'ignition_firstboot'",
"to \"ignition.firstboot\" which is the trigger for ignition to run.",
"The \"ignition-firstboot-complete.service\" will remove said file and",
"thus preventing ignition to be run on the next boot.",
"The `network` option can be used to overwrite the default network",
"configuration, in case that ignition is run."
],
"schema": {
"additionalProperties": false,
"properties": {
"network": {
"type": "array",
"description": "Overwrite default network connection",
"items": {
"type": "string"
}
}
}
}
}

View file

@ -1,28 +1,10 @@
#!/usr/bin/python3
"""
Implant an MD5 checksum in an ISO9660 image
This stage is using implantisomd5(1) to implant MD5 checksums into an iso
image. This is needed for the check media feature used in the installer.
"""
import os
import subprocess
import sys
import osbuild.api
SCHEMA = """
"additionalProperties": false,
"required": ["filename"],
"properties": {
"filename": {
"type": "string",
"description": "Path to where the iso to implant md5s is located."
}
}
"""
def main(tree, options):
filename = options["filename"].lstrip("/")

View file

@ -0,0 +1,19 @@
{
"summary": "Implant an MD5 checksum in an ISO9660 image",
"description": [
"This stage is using implantisomd5(1) to implant MD5 checksums into an iso",
"image. This is needed for the check media feature used in the installer."
],
"schema": {
"additionalProperties": false,
"required": [
"filename"
],
"properties": {
"filename": {
"type": "string",
"description": "Path to where the iso to implant md5s is located."
}
}
}
}

View file

@ -1,8 +1,4 @@
#!/usr/bin/python3
"""
Create an isolinux bootloader
"""
import os
import shutil
import string
@ -10,51 +6,6 @@ import sys
import osbuild.api
SCHEMA_2 = """
"options": {
"additionalProperties": false,
"required": ["product", "kernel"],
"properties": {
"product": {
"type": "object",
"additionalProperties": false,
"required": ["name", "version"],
"properties": {
"name": {"type": "string"},
"version": {"type": "string"}
}
},
"kernel": {
"type": "object",
"required": ["dir"],
"properties": {
"dir": {
"type": "string"
},
"opts": {
"description": "Array of group names for this user",
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"inputs": {
"type": "object",
"additionalProperties": false,
"required": ["data"],
"properties": {
"data": {
"type": "object",
"additionalProperties": true
}
}
}
"""
# The isolinux configuration template
ISOLINUX_CFG_TEMPLATE = """

View file

@ -0,0 +1,62 @@
{
"summary": "Create an isolinux bootloader",
"description": [],
"schema_2": {
"options": {
"additionalProperties": false,
"required": [
"product",
"kernel"
],
"properties": {
"product": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"version"
],
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"kernel": {
"type": "object",
"required": [
"dir"
],
"properties": {
"dir": {
"type": "string"
},
"opts": {
"description": "Array of group names for this user",
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"inputs": {
"type": "object",
"additionalProperties": false,
"required": [
"data"
],
"properties": {
"data": {
"type": "object",
"additionalProperties": true
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show more