Skip to content

Commit 6810f49

Browse files
committed
anaconda: Add bootc installation via anaconda
Implement bootc container installation using anaconda as the installation engine with kickstart processing. Uses the ephemeral VM infrastructure to run anaconda in an isolated environment with proper access to block devices and container storage. Key implementation details: - User must provide kickstart file with partitioning and locale settings - bcvk injects ostreecontainer directive with --transport=containers-storage - Inject %pre script to configure container storage with host overlay - Inject %post script running 'bootc switch --mutate-in-place' to repoint the installed system to the registry image (for bootc upgrade to work) - Handle SSH exit code 255 as success when VM powers off after installation - Share disk creation logic via qemu_img::create_disk() Options: - --kickstart (-k): Required kickstart file path - --target-imgref: Registry image for bootc origin (defaults to image arg) - --no-repoint: Skip %post repointing if user handles it themselves The anaconda installer container (localhost/anaconda-bootc) is based on fedora-bootc with anaconda-tui installed. Build instructions in containers/anaconda-bootc/. Assisted-by: OpenCode (claude-opus-4-5@20251101) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent 4e4c573 commit 6810f49

25 files changed

Lines changed: 2474 additions & 16 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Anaconda installer container for bcvk
2+
#
3+
# This container provides anaconda for installing bootc container images
4+
# using kickstart files. It's designed to run as an ephemeral VM that
5+
# boots into anaconda.target.
6+
#
7+
# Build: podman build -t localhost/anaconda-bootc:latest .
8+
9+
FROM quay.io/fedora/fedora-bootc:42
10+
11+
# Install anaconda and required packages
12+
COPY packages.txt /tmp/packages.txt
13+
RUN dnf install -y $(grep -v '^#' /tmp/packages.txt | tr '\n' ' ') && \
14+
dnf clean all && \
15+
rm /tmp/packages.txt
16+
17+
# Install bcvk setup service that runs before anaconda
18+
COPY bcvk-anaconda-setup.service /usr/lib/systemd/system/bcvk-anaconda-setup.service
19+
COPY bcvk-anaconda-setup.sh /usr/libexec/bcvk-anaconda-setup.sh
20+
RUN chmod +x /usr/libexec/bcvk-anaconda-setup.sh
21+
22+
# Set anaconda.target as default and enable our setup service
23+
RUN systemctl set-default anaconda.target && \
24+
systemctl enable bcvk-anaconda-setup.service
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Anaconda Installer Container for bcvk
2+
3+
This container provides the anaconda installer for installing bootc container
4+
images using kickstart files. It boots into `anaconda.target` and uses the
5+
upstream anaconda systemd services.
6+
7+
## Overview
8+
9+
The container is based on `quay.io/fedora/fedora-bootc:42` with anaconda-tui
10+
installed. It boots directly into `anaconda.target` and uses the upstream
11+
`anaconda-direct.service` with a bcvk setup service that runs beforehand.
12+
13+
## Building
14+
15+
```bash
16+
cd containers/anaconda-bootc
17+
podman build -t localhost/anaconda-bootc:latest .
18+
```
19+
20+
## How It Works
21+
22+
1. bcvk creates a target disk and generates a kickstart file
23+
2. bcvk starts the VM with:
24+
- Host container storage mounted read-only via virtiofs
25+
- Kickstart file mounted via virtiofs at `/run/virtiofs-mnt-kickstart/`
26+
- Target disk attached via virtio-blk
27+
- Kernel args: `inst.notmux inst.ks=file:///run/virtiofs-mnt-kickstart/anaconda.ks`
28+
3. The VM boots into `anaconda.target`
29+
4. `bcvk-anaconda-setup.service` runs first to:
30+
- Mount virtiofs shares for container storage and kickstart
31+
- Configure `/etc/containers/storage.conf` with additionalImageStores
32+
5. Upstream `anaconda-direct.service` runs anaconda (triggered by `inst.notmux`)
33+
6. Kickstart `poweroff` directive powers off the VM after anaconda completes
34+
35+
## Integration with Upstream Anaconda
36+
37+
This container leverages upstream anaconda systemd infrastructure:
38+
39+
| Component | Source | Purpose |
40+
|-----------|--------|---------|
41+
| `anaconda.target` | Upstream | Default boot target for installation |
42+
| `anaconda-direct.service` | Upstream | Runs anaconda without tmux |
43+
| `bcvk-anaconda-setup.service` | bcvk | Sets up virtiofs mounts before anaconda (conditional on `bcvk.anaconda` kernel arg) |
44+
45+
## Kickstart Requirements
46+
47+
The user provides a kickstart with partitioning and locale settings.
48+
bcvk injects:
49+
- `ostreecontainer --transport=containers-storage --url=<image>`
50+
- `%post` script to repoint bootc origin to the registry (unless `--no-repoint`)
51+
52+
**Important**: The target disk is available at `/dev/disk/by-id/virtio-output`.
53+
bcvk also attaches a swap disk, so use `ignoredisk` to target the correct disk.
54+
55+
Example kickstart for BIOS boot:
56+
```kickstart
57+
text
58+
lang en_US.UTF-8
59+
keyboard us
60+
timezone UTC --utc
61+
network --bootproto=dhcp --activate
62+
63+
# Target only the output disk
64+
ignoredisk --only-use=/dev/disk/by-id/virtio-output
65+
66+
zerombr
67+
clearpart --all --initlabel
68+
69+
# Create required boot partitions (biosboot + /boot)
70+
reqpart --add-boot
71+
part / --fstype=xfs --grow
72+
73+
rootpw --lock
74+
poweroff
75+
```
76+
77+
## Installed Packages
78+
79+
See `packages.txt` for the full list. Key packages:
80+
- **anaconda-tui**: Text-mode anaconda installer
81+
- **pykickstart**: Kickstart file processing
82+
- **Disk tools**: parted, gdisk, lvm2, cryptsetup
83+
- **Filesystem tools**: e2fsprogs, xfsprogs, btrfs-progs
84+
- **Container tools**: skopeo (bootc is in base image)
85+
86+
## Debugging
87+
88+
If installation fails, check the VM console output or journal:
89+
```bash
90+
# Run with console output
91+
bcvk anaconda install --console ...
92+
93+
# Inside VM, check logs
94+
journalctl -u bcvk-anaconda-setup
95+
journalctl -u anaconda-direct
96+
cat /tmp/anaconda.log
97+
```
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[Unit]
2+
Description=bcvk container storage setup for anaconda
3+
# Run before anaconda starts, after basic system is up and target disk is available
4+
Before=anaconda-direct.service anaconda.service
5+
After=basic.target dev-disk-by\x2did-virtio\x2doutput.device
6+
Requires=dev-disk-by\x2did-virtio\x2doutput.device
7+
# Only run in bcvk anaconda environment (bcvk passes this kernel arg)
8+
ConditionKernelCommandLine=bcvk.anaconda
9+
10+
[Service]
11+
Type=oneshot
12+
ExecStart=/usr/libexec/bcvk-anaconda-setup.sh
13+
RemainAfterExit=yes
14+
StandardOutput=journal+console
15+
StandardError=journal+console
16+
17+
[Install]
18+
WantedBy=anaconda.target
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/bin/bash
2+
# bcvk anaconda setup script
3+
#
4+
# This script runs before anaconda to set up:
5+
# - virtiofs mounts for host container storage and kickstart
6+
# - container storage configuration for additionalImageStores
7+
#
8+
# Anaconda itself is run by the upstream anaconda-direct.service
9+
set -euo pipefail
10+
11+
echo "bcvk: Setting up container storage for anaconda..."
12+
13+
# Mount host container storage via virtiofs
14+
AIS=/run/virtiofs-mnt-hoststorage
15+
if ! mountpoint -q "${AIS}" 2>/dev/null; then
16+
mkdir -p "${AIS}"
17+
mount -t virtiofs mount_hoststorage "${AIS}" || {
18+
echo "bcvk: ERROR: Failed to mount host container storage"
19+
exit 1
20+
}
21+
fi
22+
23+
# Mount kickstart directory via virtiofs
24+
KS_DIR=/run/virtiofs-mnt-kickstart
25+
if ! mountpoint -q "${KS_DIR}" 2>/dev/null; then
26+
mkdir -p "${KS_DIR}"
27+
mount -t virtiofs mount_kickstart "${KS_DIR}" || {
28+
echo "bcvk: ERROR: Failed to mount kickstart directory"
29+
exit 1
30+
}
31+
fi
32+
33+
# Configure containers to use host storage as additional image store
34+
mkdir -p /etc/containers
35+
cat > /etc/containers/storage.conf << 'EOF'
36+
[storage]
37+
driver = "overlay"
38+
[storage.options]
39+
additionalimagestores = ["/run/virtiofs-mnt-hoststorage"]
40+
EOF
41+
42+
# Verify kickstart exists
43+
if [ ! -f "${KS_DIR}/anaconda.ks" ]; then
44+
echo "bcvk: ERROR: Kickstart not found at ${KS_DIR}/anaconda.ks"
45+
exit 1
46+
fi
47+
48+
# Copy kickstart to where anaconda expects it
49+
# Anaconda looks for /run/install/ks.cfg when inst.ks is specified
50+
mkdir -p /run/install
51+
cp "${KS_DIR}/anaconda.ks" /run/install/ks.cfg
52+
echo "bcvk: Installed kickstart to /run/install/ks.cfg"
53+
54+
echo "bcvk: Setup complete. Kickstart: ${KS_DIR}/anaconda.ks"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Anaconda installer packages
2+
anaconda-tui
3+
python3-kickstart
4+
pykickstart
5+
6+
# Disk tools
7+
parted
8+
gdisk
9+
lvm2
10+
cryptsetup
11+
12+
# Filesystem tools
13+
e2fsprogs
14+
xfsprogs
15+
btrfs-progs
16+
dosfstools
17+
18+
# Container tools (bootc is in base image)
19+
skopeo

crates/integration-tests/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ pub(crate) use integration_tests::{
1515
};
1616

1717
mod tests {
18+
pub mod anaconda_install;
1819
pub mod libvirt_base_disks;
1920
pub mod libvirt_port_forward;
21+
pub mod libvirt_run_anaconda;
2022
pub mod libvirt_upload_disk;
2123
pub mod libvirt_verb;
2224
pub mod mount_feature;

0 commit comments

Comments
 (0)