Browse Source

split the base cookbook into base and coreos

- coreos: everything specific to Fedora CoreOS (virtiofs discovery and
  mount, qemu-guest-agent, SSH host key persistence, DNS configuration).
- base: everything else (install-tools, fastfetch, resolv.conf resync,
  base images, users and groups).

common.mk now injects both as implicit dependencies: coreos has none,
base depends on coreos and every other project depends on coreos and base.

default-butane-spec.sh skips ignition files whose content duplicates a
previous one: butane rejects duplicate merge entries, which happens with
empty "-examples" ignition files (e.g. coreos-examples and nftables-examples).

The virtiofs tags are now discovered in /run/quadlets/coreos/virtiofs
(seedbox mounts updated accordingly) and the tmpfiles.d file is named
coreos.conf so that make install/uninstall picks it up.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
main
Nicolas Massé 2 weeks ago
parent
commit
1e06ebfae9
  1. 11
      cookbooks/base/Makefile
  2. 20
      cookbooks/base/README.md
  3. 26
      cookbooks/base/overlay.bu
  4. 15
      cookbooks/coreos/Makefile
  5. 46
      cookbooks/coreos/README.md
  6. 2
      cookbooks/coreos/discover-virtiofs-tags.service
  7. 28
      cookbooks/coreos/overlay.bu
  8. 0
      cookbooks/coreos/rpm-ostree-install-qemu-guest-agent.service
  9. 0
      cookbooks/coreos/tmpfiles.d/coreos.conf
  10. 2
      cookbooks/coreos/var-lib-virtiofs-data.mount
  11. 2
      cookbooks/seedbox/var-lib-virtiofs-hdd.mount
  12. 2
      cookbooks/seedbox/var-lib-virtiofs-ssd.mount
  13. 32
      scripts/common.mk
  14. 18
      scripts/default-butane-spec.sh

11
cookbooks/base/Makefile

@ -1,6 +1,4 @@
SYSTEMD_MAIN_UNIT_NAMES := var-lib-virtiofs-data.mount
SYSTEMD_MAIN_UNIT_NAMES += rpm-ostree-install-qemu-guest-agent.service
SYSTEMD_MAIN_UNIT_NAMES += install-tools.service
# .path units are not picked up by common.mk's wildcard (only .service/.target/
# .timer/.mount), so install and enable/start it explicitly. It triggers the
@ -16,15 +14,6 @@ include ../../scripts/common.mk
$(TARGET_CHROOT)/etc/quadlets/base/install-tools.d:
install -d -m 0755 -o root -g root -D $@
pre-requisites::
@set -Eeuo pipefail; \
for tool in rpm-ostree; do \
if ! which $$tool &>/dev/null ; then \
echo "$$tool is not installed. Please install it first." >&2; \
exit 1; \
fi ; \
done
install-actions-pre::
@set -Eeuo pipefail; \
run() { echo $$*; "$$@"; }; \

20
cookbooks/base/README.md

@ -2,20 +2,24 @@
## Overview
The base cookbook provides foundational configuration for Fedora CoreOS systems. It includes:
The base cookbook provides the foundational configuration shared by all cookbooks, whatever the underlying operating system. It includes:
- **fastfetch**: A system information tool displayed on login, with color-coded output (red for root, blue for regular users).
- **tmpfiles configuration**: Sets up required directories such as `/var/lib/virtiofs/data`.
- **QEMU guest agent**: Optional installation for better VM integration.
- **SSH key persistence**: Backs up and restores SSH host keys across reboots.
- **resolv.conf resync**: Keeps `Network=host` containers in sync with the host's DNS.
- **Tools installation**: the **install-tools.service** oneshot runs every script found in `/etc/quadlets/base/install-tools.d/`.
Versions are pinned in `/etc/quadlets/base/install-tools.env`.
The service is skipped when this file is absent.
- **fastfetch**: a system information tool installed by **install-tools.service** and displayed on login, with color-coded output (red for root, blue for regular users).
- **resolv.conf resync**: keeps `Network=host` containers in sync with the host's DNS.
With `Network=host`, Podman snapshots `/etc/resolv.conf` at container creation and bind-mounts
the copy, so a later DNS change (e.g. a DHCP renewal) never reaches running containers. A
`systemd.path` unit watches `/etc/resolv.conf` and a oneshot service rewrites each host-network
container's snapshot **in place** (same inode, no restart); bridged containers are left untouched.
See [issue #1](https://github.com/nmasse-itix/podman-quadlet-cookbook/issues/1).
- **Container teardown drop-in** (example): a `container.d` drop-in that makes `systemctl stop` wait until the container has actually been removed.
- **Base images**: `fedora.image` and `centos-stream10.image` pull the base images used by other cookbooks to build their own images.
- **Users and groups**: creates the `itix-svc` service account (uid/gid 10000) and, in the Butane spec, the `itix` group (gid 1000) and removes the default `core` user.
This cookbook is used as an implicit dependency for other cookbooks that run on Fedora CoreOS.
This cookbook is an implicit dependency of every other cookbook, except [coreos](../coreos/).
It depends itself implicitly on the [coreos](../coreos/) cookbook, which holds everything specific to Fedora CoreOS.
## Usage
@ -31,7 +35,7 @@ Install the base configuration.
sudo make clean install
```
You should see the **install-fastfetch.service** downloading and installing fastfetch.
You should see the **install-tools.service** downloading and installing fastfetch.
On next login, fastfetch will display system information.
To verify the installation:

26
cookbooks/base/overlay.bu

@ -1,31 +1,5 @@
variant: fcos
version: 1.4.0
storage:
directories:
- path: /var/lib/private/sshd
mode: 0700
files:
- path: /etc/resolv.conf
overwrite: true
contents:
inline: "" # will be updated by NetworkManager
- path: /etc/NetworkManager/conf.d/itix.conf
contents:
inline: |
[main]
# test
rc-manager=file
systemd:
units:
- name: systemd-resolved.service
enabled: false
mask: true
- name: sshd-keygen@.service
dropins:
- name: backup-restore.conf
contents: |
[Service]
ExecStartPost=/bin/bash -c 'if [ -f /var/lib/private/sshd/ssh_host_%i_key ]; then cp -Z /var/lib/private/sshd/ssh_host_%i_key{,.pub} /etc/ssh/; elif [ -f /etc/ssh/ssh_host_%i_key ]; then cp -a /etc/ssh/ssh_host_%i_key{,.pub} /var/lib/private/sshd/; fi'
passwd:
users:
- name: core

15
cookbooks/coreos/Makefile

@ -0,0 +1,15 @@
SYSTEMD_MAIN_UNIT_NAMES := var-lib-virtiofs-data.mount
SYSTEMD_MAIN_UNIT_NAMES += rpm-ostree-install-qemu-guest-agent.service
# Include common Makefile
include ../../scripts/common.mk
pre-requisites::
@set -Eeuo pipefail; \
for tool in rpm-ostree; do \
if ! which $$tool &>/dev/null ; then \
echo "$$tool is not installed. Please install it first." >&2; \
exit 1; \
fi ; \
done

46
cookbooks/coreos/README.md

@ -0,0 +1,46 @@
# Podman Quadlet: CoreOS
## Overview
The coreos cookbook provides the configuration specific to Fedora CoreOS systems. It includes:
- **virtiofs data mount**: the **discover-virtiofs-tags.service** lists the virtiofs tags exposed by the hypervisor.
When a `data` tag is present, **var-lib-virtiofs-data.mount** mounts it on `/var/lib/virtiofs/data` with the `container_file_t` SELinux context, so containers can use it.
- **tmpfiles configuration**: creates the `/var/lib/virtiofs` directory.
- **QEMU guest agent**: when running on KVM, **rpm-ostree-install-qemu-guest-agent.service** layers `qemu-guest-agent` with `rpm-ostree`, applies it live and starts it.
- **SSH key persistence** (Butane spec only): backs up the SSH host keys in `/var/lib/private/sshd` and restores them on reinstall, so the host keeps its identity.
- **DNS configuration** (Butane spec only): masks `systemd-resolved` and lets NetworkManager write `/etc/resolv.conf` directly (`rc-manager=file`).
This cookbook is an implicit dependency of every other cookbook, including [base](../base/).
It has no dependencies itself.
## Usage
This cookbook requires `rpm-ostree`, so it can only be installed on Fedora CoreOS.
In a separate terminal, follow the logs.
```sh
sudo make tail-logs
```
Install the CoreOS configuration.
```sh
sudo make clean install
```
On a KVM virtual machine, you should see the **rpm-ostree-install-qemu-guest-agent.service** installing the QEMU guest agent.
To verify the installation:
```sh
systemctl status qemu-guest-agent.service
findmnt /var/lib/virtiofs/data
```
Finally, remove the configuration and its data.
```sh
sudo make uninstall clean
```

2
cookbooks/base/discover-virtiofs-tags.service → cookbooks/coreos/discover-virtiofs-tags.service

@ -12,5 +12,5 @@ DefaultDependencies=no
[Service]
Type=oneshot
ExecStart=/bin/bash -Eeuo pipefail -c 'shopt -s nullglob; mkdir -p /run/quadlets/base/virtiofs; for tagfile in /sys/fs/virtiofs/*/tag; do tag=$(<"$tagfile"); echo -n > "/run/quadlets/base/virtiofs/$tag"; done'
ExecStart=/bin/bash -Eeuo pipefail -c 'shopt -s nullglob; mkdir -p /run/quadlets/coreos/virtiofs; for tagfile in /sys/fs/virtiofs/*/tag; do tag=$(<"$tagfile"); echo -n > "/run/quadlets/coreos/virtiofs/$tag"; done'
RemainAfterExit=yes

28
cookbooks/coreos/overlay.bu

@ -0,0 +1,28 @@
variant: fcos
version: 1.4.0
storage:
directories:
- path: /var/lib/private/sshd
mode: 0700
files:
- path: /etc/resolv.conf
overwrite: true
contents:
inline: "" # will be updated by NetworkManager
- path: /etc/NetworkManager/conf.d/itix.conf
contents:
inline: |
[main]
# test
rc-manager=file
systemd:
units:
- name: systemd-resolved.service
enabled: false
mask: true
- name: sshd-keygen@.service
dropins:
- name: backup-restore.conf
contents: |
[Service]
ExecStartPost=/bin/bash -c 'if [ -f /var/lib/private/sshd/ssh_host_%i_key ]; then cp -Z /var/lib/private/sshd/ssh_host_%i_key{,.pub} /etc/ssh/; elif [ -f /etc/ssh/ssh_host_%i_key ]; then cp -a /etc/ssh/ssh_host_%i_key{,.pub} /var/lib/private/sshd/; fi'

0
cookbooks/base/rpm-ostree-install-qemu-guest-agent.service → cookbooks/coreos/rpm-ostree-install-qemu-guest-agent.service

0
cookbooks/base/tmpfiles.d/base.conf → cookbooks/coreos/tmpfiles.d/coreos.conf

2
cookbooks/base/var-lib-virtiofs-data.mount → cookbooks/coreos/var-lib-virtiofs-data.mount

@ -4,7 +4,7 @@ Requires=discover-virtiofs-tags.service
After=discover-virtiofs-tags.service
# Only run if the "data" tag is present (as discovered by discover-virtiofs-tags.service)
ConditionPathExists=/run/quadlets/base/virtiofs/data
ConditionPathExists=/run/quadlets/coreos/virtiofs/data
[Mount]
What=data

2
cookbooks/seedbox/var-lib-virtiofs-hdd.mount

@ -4,7 +4,7 @@ Requires=discover-virtiofs-tags.service
After=discover-virtiofs-tags.service
# Only run if the "hdd" tag is present (as discovered by discover-virtiofs-tags.service)
ConditionPathExists=/run/quadlets/base/virtiofs/hdd
ConditionPathExists=/run/quadlets/coreos/virtiofs/hdd
[Mount]
What=hdd

2
cookbooks/seedbox/var-lib-virtiofs-ssd.mount

@ -4,7 +4,7 @@ Requires=discover-virtiofs-tags.service
After=discover-virtiofs-tags.service
# Only run if the "ssd" tag is present (as discovered by discover-virtiofs-tags.service)
ConditionPathExists=/run/quadlets/base/virtiofs/ssd
ConditionPathExists=/run/quadlets/coreos/virtiofs/ssd
[Mount]
What=ssd

32
scripts/common.mk

@ -11,6 +11,7 @@ help:
@echo " Persistent Storage directory: /var/lib/quadlets/$(PROJECT_NAME)"
@echo " UID & GID for project files: $(PROJECT_UID):$(PROJECT_GID)"
@echo " Dependencies: $(if $(DEPENDENCIES),$(DEPENDENCIES),none)"
@echo " Implicit dependencies: $(if $(IMPLICIT_DEPENDENCIES),$(IMPLICIT_DEPENDENCIES),none)"
@echo
@echo "Available targets:"
@echo
@ -132,15 +133,26 @@ TARGET_EXAMPLE_FILES += $(TARGET_EXAMPLES_CONFIG_FILES) $(TARGET_EXAMPLES_TMPFIL
# List here the names of other projects (directories at the top-level) that this project depends on.
DEPENDENCIES ?=
# Implicit dependencies, injected in every project's butane specs.
# "coreos" holds everything related to Fedora CoreOS and "base" everything else.
# "base" depends on "coreos", so the order matters and neither one depends on itself.
ifeq ($(PROJECT_NAME),coreos)
IMPLICIT_DEPENDENCIES :=
else ifeq ($(PROJECT_NAME),base)
IMPLICIT_DEPENDENCIES := coreos
else
IMPLICIT_DEPENDENCIES := coreos base
endif
# Set this variable to "yes" to skip the confirmation prompt when running "make clean".
I_KNOW_WHAT_I_AM_DOING ?=
# List of all ignition files corresponding to the dependencies
# Here, we inject the "base" project as a dependency. It can therefore be assumed to always be embeddable in project's butane specs.
DEPENDENCIES_IGNITION_FILES := $(shell for dep in $$(if [ "$(PROJECT_NAME)" != "base" ]; then echo base; fi) $(DEPENDENCIES); do echo $(COOKBOOKS_DIR)/$$dep/build/$$dep.ign; done)
# Here, we inject the "coreos" and "base" projects as dependencies. They can therefore be assumed to always be embeddable in project's butane specs.
DEPENDENCIES_IGNITION_FILES := $(foreach dep,$(IMPLICIT_DEPENDENCIES) $(DEPENDENCIES),$(COOKBOOKS_DIR)/$(dep)/build/$(dep).ign)
# Variation of the previous variable with the built-in examples.
DEPENDENCIES_IGNITION_EXAMPLES_FILES := $(shell for dep in $$(if [ "$(PROJECT_NAME)" != "base" ]; then echo base; fi) $(DEPENDENCIES); do echo $(COOKBOOKS_DIR)/$$dep/build/$$dep.ign $(COOKBOOKS_DIR)/$$dep/build/$$dep-examples.ign; done)
DEPENDENCIES_IGNITION_EXAMPLES_FILES := $(foreach dep,$(IMPLICIT_DEPENDENCIES) $(DEPENDENCIES),$(COOKBOOKS_DIR)/$(dep)/build/$(dep).ign $(COOKBOOKS_DIR)/$(dep)/build/$(dep)-examples.ign)
# User and group IDs to own the project files and directories.
PROJECT_UID ?= 0
@ -152,7 +164,7 @@ include $(HOOKS)
# Dump makefile variables for debugging purposes
debug:
$(foreach v, $(filter %_FILES SYSTEMD_% QUADLET% PROJECT_% DEPENDENCIES% %_DIR, $(.VARIABLES)), $(info $(v): $($(v))))
$(foreach v, $(filter %_FILES SYSTEMD_% QUADLET% PROJECT_% DEPENDENCIES% IMPLICIT_DEPENDENCIES %_DIR, $(.VARIABLES)), $(info $(v): $($(v))))
@echo "TARGET_CHROOT: $(TARGET_CHROOT)"
@echo "BUTANE_BLOCKLIST: $(BUTANE_BLOCKLIST)"
@echo "BUTANE_START_TS: $(BUTANE_START_TS)"
@ -427,7 +439,7 @@ build/$(PROJECT_NAME).tar.gz build/$(PROJECT_NAME).bu build/$(PROJECT_NAME)-exam
export ALL_DEPENDENCIES="$(shell $(MAKE) -s list-dependencies 2>/dev/null)"; \
set -Eeuo pipefail; \
if [ build/$(PROJECT_NAME).bu -ot "$(BUTANE_START_TS)" ] || [ build/$(PROJECT_NAME)-examples.bu -ot "$(BUTANE_START_TS)" ]; then \
for dep in base $(DEPENDENCIES); do \
for dep in $(IMPLICIT_DEPENDENCIES) $(DEPENDENCIES); do \
if [[ "$$dep" == "$(PROJECT_NAME)" ]]; then \
# Avoid building the current project as its own dependency. \
continue; \
@ -476,12 +488,18 @@ $(TOP_LEVEL_DIR)/build/local.ign: $(TOP_LEVEL_DIR)/local.bu
# Generate the Butane specs for development and testing by merging the current project's spec with those of the dependencies.
# The development spec also includes the examples of the current project and of its dependencies.
# Whereas the testing spec only includes the main specs of the current project and of its dependencies.
build/fcos-dev.bu build/fcos-test.bu: DEPS := $(if $(filter-out base,$(PROJECT_NAME)),base $(DEPENDENCIES),$(DEPENDENCIES))
build/fcos-dev.bu build/fcos-test.bu: DEPS := $(IMPLICIT_DEPENDENCIES) $(DEPENDENCIES)
build/fcos-dev.bu: DEPS := $(DEPS) $(addsuffix -examples,$(DEPS))
build/fcos-test.bu: SELF := $(PROJECT_NAME)
build/fcos-dev.bu: SELF := $(PROJECT_NAME) $(PROJECT_NAME)-examples
# The ignition files are needed to detect duplicates (see default-butane-spec.sh).
build/fcos-dev.bu: build/$(PROJECT_NAME).ign build/$(PROJECT_NAME)-examples.ign $(DEPENDENCIES_IGNITION_EXAMPLES_FILES)
build/fcos-test.bu: build/$(PROJECT_NAME).ign $(DEPENDENCIES_IGNITION_FILES)
build/fcos-dev.bu build/fcos-test.bu: %.bu: Makefile $(SCRIPTS_DIR)/default-butane-spec.sh build
$(SCRIPTS_DIR)/default-butane-spec.sh $(SELF) $(DEPS) > $@
$(SCRIPTS_DIR)/default-butane-spec.sh $(foreach name,$(SELF) $(DEPS),$(call ignition_path,$(name))) > $@
# Path of the ignition file of the current project or of one of its dependencies (with or without the "-examples" suffix).
ignition_path = $(if $(filter $(PROJECT_NAME) $(PROJECT_NAME)-examples,$(1)),build/$(1).ign,$(COOKBOOKS_DIR)/$(patsubst %-examples,%,$(1))/build/$(1).ign)
# Generate the final Fedora CoreOS ignition files (dev & test) by merging the Butane spec with the local and project-specific ignition files, as well as those of the dependencies.
build/fcos-dev.ign: $(TOP_LEVEL_DIR)/build/local.ign build/$(PROJECT_NAME).ign build/$(PROJECT_NAME)-examples.ign $(DEPENDENCIES_IGNITION_EXAMPLES_FILES)

18
scripts/default-butane-spec.sh

@ -2,6 +2,13 @@
set -Eeuo pipefail
# Usage: default-butane-spec.sh <ignition file>...
#
# Butane inlines the "local" files as data URLs and rejects duplicate entries.
# So, ignition files whose content is identical to a previous one (typically
# the empty "-examples" ignition files) are skipped. Merging the same config
# twice would be a no-op anyway.
cat <<'EOF'
variant: fcos
version: 1.4.0
@ -9,7 +16,14 @@ ignition:
config:
merge:
EOF
for dep in "$@"; do
echo " - local: ${dep}.ign"
declare -A seen=()
for ign in "$@"; do
checksum="$(sha256sum "$ign" | cut -d' ' -f1)"
if [ -n "${seen[$checksum]:-}" ]; then
echo "Skipping $(basename "$ign"): same content as ${seen[$checksum]}." >&2
continue
fi
seen[$checksum]="$(basename "$ign")"
echo " - local: $(basename "$ign")"
done
echo " - local: local.ign"

Loading…
Cancel
Save