730 lines
No EOL
22 KiB
Markdown
730 lines
No EOL
22 KiB
Markdown
# Calamares Module for bootc install
|
|
|
|
## Overview
|
|
|
|
This document outlines a focused plan for creating a single Calamares module that handles `bootc install` operations. This is the simplest and most direct approach, following the official bootc documentation patterns.
|
|
|
|
## Executive Summary
|
|
|
|
**Goal**: Create a single Calamares module that executes `bootc install` commands with proper configuration and error handling.
|
|
|
|
**Timeline**: 2-3 months (focused, single-purpose implementation)
|
|
**Complexity**: Low (direct tool integration)
|
|
**Target**: Debian 13 (Trixie) with bootc support
|
|
|
|
## Real-World Analysis
|
|
|
|
### How bootc install Actually Works
|
|
|
|
Based on the [official bootc documentation](https://bootc-dev.github.io/bootc//bootc-install.html) and [Fedora's bare metal documentation](https://docs.fedoraproject.org/en-US/bootc/bare-metal/):
|
|
|
|
#### 1. Core Commands
|
|
```bash
|
|
bootc install to-disk /dev/sda
|
|
bootc install to-filesystem /path/to/mounted/fs
|
|
bootc install to-existing-root
|
|
```
|
|
|
|
#### 2. Container Execution Pattern
|
|
From [Fedora's official documentation](https://docs.fedoraproject.org/en-US/bootc/bare-metal/):
|
|
```bash
|
|
podman run \
|
|
--rm --privileged \
|
|
--pid=host \
|
|
-v /dev:/dev \
|
|
-v /var/lib/containers:/var/lib/containers \
|
|
--security-opt label=type:unconfined_t \
|
|
<image> \
|
|
bootc install to-disk /path/to/disk
|
|
```
|
|
|
|
#### 3. Key Differences from Anaconda Approach
|
|
- **No kickstart files** - Direct container execution
|
|
- **No network during install** - Container is already pulled
|
|
- **Minimal configuration** - Basic installer built into bootc
|
|
- **Live ISO environment** - Typically run from Fedora CoreOS Live ISO
|
|
|
|
#### 3. Key OSTree Filesystem Nuances
|
|
|
|
**Critical differences from traditional Linux installations:**
|
|
|
|
- **Composefs by default**: [Fedora bootc uses composefs](https://docs.fedoraproject.org/en-US/bootc/filesystem/) for the root filesystem
|
|
- **Read-only root**: Filesystem is read-only at runtime (like `podman run --read-only`)
|
|
- **Special mount points**: `/etc` and `/var` are persistent, mutable bind mounts
|
|
- **Kernel location**: Kernel is in `/usr/lib/ostree-boot/` not `/boot/`
|
|
- **3-way merge**: `/etc` changes are merged across upgrades
|
|
- **Transient mountpoints**: Support for dynamic mountpoints with `transient-ro`
|
|
|
|
#### 4. Filesystem Layout
|
|
```
|
|
/usr/lib/ostree-boot/ # Kernel and initrd (not /boot/)
|
|
/etc/ # Persistent, mutable (bind mount)
|
|
/var/ # Persistent, mutable (bind mount)
|
|
/usr/ # Read-only (composefs)
|
|
/opt/ # Read-only (composefs)
|
|
```
|
|
|
|
#### 5. Authentication Patterns
|
|
From [Fedora's authentication documentation](https://docs.fedoraproject.org/en-US/bootc/authentication/):
|
|
- **Registry auth**: `/etc/ostree/auth.json` for container registries
|
|
- **SSH keys**: Via kickstart or bootc-image-builder config
|
|
- **User management**: systemd-sysusers for local users
|
|
- **nss-altfiles**: Static users in `/usr/lib/passwd` and `/usr/lib/group`
|
|
|
|
## Implementation Plan
|
|
|
|
### Phase 1: Core Module Development (Month 1)
|
|
|
|
#### 1.1 Module Structure
|
|
```cpp
|
|
class BootcInstallModule : public Calamares::Module
|
|
{
|
|
public:
|
|
void init() override;
|
|
QList<Calamares::job_ptr> jobs() const override;
|
|
|
|
private:
|
|
QString m_containerUrl;
|
|
QString m_targetDevice;
|
|
QString m_installType; // "to-disk", "to-filesystem", "to-existing-root"
|
|
bool m_authRequired;
|
|
QString m_authJson;
|
|
};
|
|
```
|
|
|
|
#### 1.2 Configuration Loading
|
|
```cpp
|
|
void BootcInstallModule::init()
|
|
{
|
|
auto config = Calamares::ModuleSystem::instance()->moduleConfiguration("bootc-install");
|
|
|
|
m_containerUrl = config.value("containerUrl").toString();
|
|
m_targetDevice = config.value("targetDevice").toString();
|
|
m_installType = config.value("installType", "to-disk").toString();
|
|
m_authRequired = config.value("authRequired", false).toBool();
|
|
m_authJson = config.value("authJson").toString();
|
|
}
|
|
```
|
|
|
|
#### 1.3 Job Implementation
|
|
```cpp
|
|
QList<Calamares::job_ptr> BootcInstallModule::jobs() const
|
|
{
|
|
QList<Calamares::job_ptr> jobs;
|
|
|
|
// Registry authentication job
|
|
if (m_authRequired) {
|
|
jobs.append(Calamares::job_ptr(new RegistryAuthJob(m_authJson)));
|
|
}
|
|
|
|
// Bootc installation job
|
|
jobs.append(Calamares::job_ptr(new BootcInstallJob(m_containerUrl, m_targetDevice, m_installType)));
|
|
|
|
// Post-install configuration job
|
|
jobs.append(Calamares::job_ptr(new BootcPostInstallJob(m_targetDevice, m_sshKey, m_username)));
|
|
|
|
return jobs;
|
|
}
|
|
```
|
|
|
|
### Phase 2: Job Implementations (Month 1-2)
|
|
|
|
#### 2.1 Registry Authentication Job
|
|
```cpp
|
|
class RegistryAuthJob : public Calamares::Job
|
|
{
|
|
public:
|
|
RegistryAuthJob(const QString& authJson) : m_authJson(authJson) {}
|
|
|
|
QString prettyName() const override { return "Configuring registry authentication"; }
|
|
Calamares::JobResult exec() override;
|
|
|
|
private:
|
|
QString m_authJson;
|
|
};
|
|
|
|
Calamares::JobResult RegistryAuthJob::exec()
|
|
{
|
|
// Create /etc/ostree directory (persistent bind mount)
|
|
if (!QDir("/etc/ostree").exists()) {
|
|
if (!QDir().mkpath("/etc/ostree")) {
|
|
return Calamares::JobResult::error("Failed to create /etc/ostree directory");
|
|
}
|
|
}
|
|
|
|
// Write auth.json (will persist across upgrades due to /etc bind mount)
|
|
QFile authFile("/etc/ostree/auth.json");
|
|
if (!authFile.open(QIODevice::WriteOnly)) {
|
|
return Calamares::JobResult::error("Failed to open /etc/ostree/auth.json for writing");
|
|
}
|
|
|
|
authFile.write(m_authJson.toUtf8());
|
|
authFile.close();
|
|
|
|
return Calamares::JobResult::ok();
|
|
}
|
|
```
|
|
|
|
#### 2.2 Bootc Install Job
|
|
```cpp
|
|
class BootcInstallJob : public Calamares::Job
|
|
{
|
|
public:
|
|
BootcInstallJob(const QString& containerUrl, const QString& targetDevice, const QString& installType)
|
|
: m_containerUrl(containerUrl), m_targetDevice(targetDevice), m_installType(installType) {}
|
|
|
|
QString prettyName() const override { return "Installing bootc container"; }
|
|
Calamares::JobResult exec() override;
|
|
|
|
private:
|
|
QString m_containerUrl;
|
|
QString m_targetDevice;
|
|
QString m_installType;
|
|
};
|
|
|
|
Calamares::JobResult BootcInstallJob::exec()
|
|
{
|
|
// Build podman command with OSTree-specific considerations
|
|
QStringList args;
|
|
args << "run" << "--rm" << "--privileged";
|
|
args << "--pid=host";
|
|
args << "-v" << "/dev:/dev";
|
|
args << "-v" << "/var/lib/containers:/var/lib/containers";
|
|
args << "--security-opt" << "label=type:unconfined_t";
|
|
|
|
// Add environment variables for OSTree filesystem
|
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
|
env.insert("OSTREE_NO_SIGNATURE_VERIFICATION", "1"); // For composefs unsigned mode
|
|
env.insert("LIBMOUNT_FORCE_MOUNT2", "always"); // For transient-ro support
|
|
|
|
args << m_containerUrl;
|
|
args << "bootc" << "install" << m_installType << m_targetDevice;
|
|
|
|
// Execute podman command
|
|
QProcess process;
|
|
process.setProcessEnvironment(env);
|
|
process.start("podman", args);
|
|
|
|
if (!process.waitForFinished(-1)) {
|
|
return Calamares::JobResult::error("bootc install command timed out");
|
|
}
|
|
|
|
if (process.exitCode() != 0) {
|
|
QString error = QString("bootc install failed: %1").arg(process.readAllStandardError());
|
|
return Calamares::JobResult::error(error);
|
|
}
|
|
|
|
return Calamares::JobResult::ok();
|
|
}
|
|
```
|
|
|
|
#### 2.3 Post-Install Configuration Job
|
|
```cpp
|
|
class BootcPostInstallJob : public Calamares::Job
|
|
{
|
|
public:
|
|
BootcPostInstallJob(const QString& targetDevice, const QString& sshKey, const QString& username)
|
|
: m_targetDevice(targetDevice), m_sshKey(sshKey), m_username(username) {}
|
|
|
|
QString prettyName() const override { return "Configuring bootc system"; }
|
|
Calamares::JobResult exec() override;
|
|
|
|
private:
|
|
QString m_targetDevice;
|
|
QString m_sshKey;
|
|
QString m_username;
|
|
|
|
bool configureBootloader();
|
|
bool createUserAccount();
|
|
bool setupSshKey();
|
|
};
|
|
|
|
Calamares::JobResult BootcPostInstallJob::exec()
|
|
{
|
|
// Mount the installed system
|
|
QString mountPoint = "/mnt/bootc-install";
|
|
if (!QDir().mkpath(mountPoint)) {
|
|
return Calamares::JobResult::error("Failed to create mount point");
|
|
}
|
|
|
|
// Mount the root partition
|
|
QProcess mount;
|
|
mount.start("mount", QStringList() << m_targetDevice << mountPoint);
|
|
if (!mount.waitForFinished() || mount.exitCode() != 0) {
|
|
return Calamares::JobResult::error("Failed to mount installed system");
|
|
}
|
|
|
|
// Configure bootloader (GRUB2 for OSTree)
|
|
if (!configureBootloader()) {
|
|
return Calamares::JobResult::error("Failed to configure bootloader");
|
|
}
|
|
|
|
// Create user account
|
|
if (!createUserAccount()) {
|
|
return Calamares::JobResult::error("Failed to create user account");
|
|
}
|
|
|
|
// Setup SSH key
|
|
if (!setupSshKey()) {
|
|
return Calamares::JobResult::error("Failed to setup SSH key");
|
|
}
|
|
|
|
// Unmount
|
|
QProcess umount;
|
|
umount.start("umount", QStringList() << mountPoint);
|
|
umount.waitForFinished();
|
|
|
|
return Calamares::JobResult::ok();
|
|
}
|
|
|
|
bool BootcPostInstallJob::configureBootloader()
|
|
{
|
|
// OSTree systems use GRUB2 with specific configuration
|
|
// Kernel is in /usr/lib/ostree-boot/, not /boot/
|
|
QString grubConfig = QString("/mnt/bootc-install/boot/grub2/grub.cfg");
|
|
|
|
// Check if GRUB2 configuration exists
|
|
if (!QFile::exists(grubConfig)) {
|
|
// Run grub2-mkconfig to generate configuration
|
|
QProcess grubMkconfig;
|
|
grubMkconfig.start("grub2-mkconfig", QStringList() << "-o" << grubConfig);
|
|
if (!grubMkconfig.waitForFinished() || grubMkconfig.exitCode() != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Install GRUB2 to the target device
|
|
QProcess grubInstall;
|
|
grubInstall.start("grub2-install", QStringList() << "--target=x86_64-efi" << m_targetDevice);
|
|
if (!grubInstall.waitForFinished() || grubInstall.exitCode() != 0) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BootcPostInstallJob::createUserAccount()
|
|
{
|
|
// OSTree systems use systemd-sysusers for user management
|
|
// Users are defined in /usr/lib/sysusers.d/ or /etc/sysusers.d/
|
|
QString sysusersConfig = "/mnt/bootc-install/etc/sysusers.d/calamares-user.conf";
|
|
|
|
QString userConfig = QString("u %1 1000 \"%1\" /home/%1\n").arg(m_username);
|
|
userConfig += QString("g %1 1000\n").arg(m_username);
|
|
userConfig += QString("m %1 %1\n").arg(m_username);
|
|
|
|
QFile file(sysusersConfig);
|
|
if (!file.open(QIODevice::WriteOnly)) {
|
|
return false;
|
|
}
|
|
|
|
file.write(userConfig.toUtf8());
|
|
file.close();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BootcPostInstallJob::setupSshKey()
|
|
{
|
|
if (m_sshKey.isEmpty()) return true;
|
|
|
|
// Create .ssh directory for root
|
|
QString sshDir = "/mnt/bootc-install/root/.ssh";
|
|
if (!QDir().mkpath(sshDir)) {
|
|
return false;
|
|
}
|
|
|
|
// Write SSH key
|
|
QString keyFile = sshDir + "/authorized_keys";
|
|
QFile file(keyFile);
|
|
if (!file.open(QIODevice::WriteOnly)) {
|
|
return false;
|
|
}
|
|
|
|
file.write(m_sshKey.toUtf8());
|
|
file.close();
|
|
|
|
// Set proper permissions
|
|
QFile::setPermissions(keyFile, QFile::ReadOwner | QFile::WriteOwner);
|
|
QFile::setPermissions(sshDir, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
|
|
|
|
return true;
|
|
}
|
|
```
|
|
|
|
### Phase 3: OSTree Filesystem Considerations (Month 2)
|
|
|
|
#### 3.1 Filesystem Layout Validation
|
|
```cpp
|
|
class OstreeFilesystemValidator
|
|
{
|
|
public:
|
|
static bool validateTargetDevice(const QString& device);
|
|
static bool checkComposefsSupport();
|
|
static bool validateMountPoints();
|
|
|
|
private:
|
|
static bool isOstreeBootPath(const QString& path);
|
|
static bool checkTransientRoSupport();
|
|
};
|
|
|
|
bool OstreeFilesystemValidator::validateTargetDevice(const QString& device)
|
|
{
|
|
// Check if target device can support OSTree layout
|
|
// - GPT partition table required
|
|
// - EFI system partition for UEFI
|
|
// - Root partition for OSTree deployment
|
|
// - Boot partition for kernel/initrd (not /boot/ but /usr/lib/ostree-boot/)
|
|
|
|
QProcess sfdisk;
|
|
sfdisk.start("sfdisk", QStringList() << "-l" << device);
|
|
if (!sfdisk.waitForFinished()) return false;
|
|
|
|
QString output = sfdisk.readAllStandardOutput();
|
|
return output.contains("GPT") && output.contains("EFI");
|
|
}
|
|
|
|
bool OstreeFilesystemValidator::checkComposefsSupport()
|
|
{
|
|
// Check if composefs is available in the container
|
|
QProcess composefs;
|
|
composefs.start("composefs", QStringList() << "--help");
|
|
return composefs.waitForFinished() && composefs.exitCode() == 0;
|
|
}
|
|
```
|
|
|
|
#### 3.2 Kernel and Initrd Handling
|
|
```cpp
|
|
class OstreeBootManager
|
|
{
|
|
public:
|
|
static QString getKernelPath();
|
|
static QString getInitrdPath();
|
|
static bool setupBootloader(const QString& device);
|
|
static bool regenerateInitramfs(const QString& mountPoint);
|
|
|
|
private:
|
|
static const QString OSTREE_BOOT_PATH; // "/usr/lib/ostree-boot/"
|
|
};
|
|
|
|
const QString OstreeBootManager::OSTREE_BOOT_PATH = "/usr/lib/ostree-boot/";
|
|
|
|
QString OstreeBootManager::getKernelPath()
|
|
{
|
|
// Kernel is in /usr/lib/ostree-boot/, not /boot/
|
|
return OSTREE_BOOT_PATH + "vmlinuz";
|
|
}
|
|
|
|
QString OstreeBootManager::getInitrdPath()
|
|
{
|
|
// Initrd is in /usr/lib/ostree-boot/, not /boot/
|
|
return OSTREE_BOOT_PATH + "initramfs.img";
|
|
}
|
|
|
|
bool OstreeBootManager::regenerateInitramfs(const QString& mountPoint)
|
|
{
|
|
// OSTree systems need initramfs regeneration after configuration changes
|
|
// This is critical for filesystem configuration changes
|
|
|
|
QProcess dracut;
|
|
dracut.start("dracut", QStringList()
|
|
<< "--force"
|
|
<< "--hostonly"
|
|
<< "--kver" << "5.15.0" // Get actual kernel version
|
|
<< mountPoint + "/boot/initramfs.img");
|
|
|
|
if (!dracut.waitForFinished()) {
|
|
return false;
|
|
}
|
|
|
|
return dracut.exitCode() == 0;
|
|
}
|
|
```
|
|
|
|
#### 3.3 Persistent State Management
|
|
```cpp
|
|
class OstreeStateManager
|
|
{
|
|
public:
|
|
static bool setupEtcBindMount();
|
|
static bool setupVarBindMount();
|
|
static bool configureTransientRo();
|
|
|
|
private:
|
|
static bool createBindMount(const QString& source, const QString& target);
|
|
};
|
|
|
|
bool OstreeStateManager::setupEtcBindMount()
|
|
{
|
|
// /etc is a persistent, mutable bind mount
|
|
// Changes here persist across upgrades via 3-way merge
|
|
return createBindMount("/etc", "/etc");
|
|
}
|
|
|
|
bool OstreeStateManager::configureTransientRo()
|
|
{
|
|
// Enable transient-ro for dynamic mountpoints
|
|
QString configPath = "/usr/lib/ostree/prepare-root.conf";
|
|
QString config = "[root]\ntransient-ro = true\n";
|
|
|
|
QFile file(configPath);
|
|
if (!file.open(QIODevice::WriteOnly)) return false;
|
|
|
|
file.write(config.toUtf8());
|
|
file.close();
|
|
|
|
// Regenerate initramfs after config change
|
|
QProcess dracut;
|
|
dracut.start("dracut", QStringList() << "--force");
|
|
return dracut.waitForFinished() && dracut.exitCode() == 0;
|
|
}
|
|
```
|
|
|
|
### Phase 4: UI Integration (Month 2-3)
|
|
|
|
#### 4.1 Configuration Page
|
|
```cpp
|
|
class BootcInstallPage : public QWidget
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit BootcInstallPage(QWidget* parent = nullptr);
|
|
|
|
QString containerUrl() const;
|
|
QString targetDevice() const;
|
|
QString installType() const;
|
|
bool authRequired() const;
|
|
QString authJson() const;
|
|
bool enableTransientRo() const;
|
|
|
|
private slots:
|
|
void onContainerUrlChanged();
|
|
void onTargetDeviceChanged();
|
|
void onInstallTypeChanged();
|
|
void onAuthRequiredChanged();
|
|
void validateOstreeRequirements();
|
|
|
|
private:
|
|
QLineEdit* m_containerUrlEdit;
|
|
QLineEdit* m_targetDeviceEdit;
|
|
QComboBox* m_installTypeCombo;
|
|
QCheckBox* m_authRequiredCheck;
|
|
QTextEdit* m_authJsonEdit;
|
|
QCheckBox* m_transientRoCheck;
|
|
QLabel* m_ostreeStatusLabel;
|
|
};
|
|
```
|
|
|
|
#### 4.2 OSTree-Aware Validation
|
|
```cpp
|
|
bool BootcInstallPage::validate()
|
|
{
|
|
if (m_containerUrlEdit->text().isEmpty()) {
|
|
Calamares::Branding::instance()->setValidationError("Container URL is required");
|
|
return false;
|
|
}
|
|
|
|
if (m_targetDeviceEdit->text().isEmpty()) {
|
|
Calamares::Branding::instance()->setValidationError("Target device is required");
|
|
return false;
|
|
}
|
|
|
|
// Validate OSTree filesystem requirements
|
|
if (!OstreeFilesystemValidator::validateTargetDevice(m_targetDeviceEdit->text())) {
|
|
Calamares::Branding::instance()->setValidationError("Target device must have GPT partition table and EFI support");
|
|
return false;
|
|
}
|
|
|
|
if (!OstreeFilesystemValidator::checkComposefsSupport()) {
|
|
Calamares::Branding::instance()->setValidationError("Composefs support not available in container");
|
|
return false;
|
|
}
|
|
|
|
if (m_authRequiredCheck->isChecked() && m_authJsonEdit->toPlainText().isEmpty()) {
|
|
Calamares::Branding::instance()->setValidationError("Authentication JSON is required");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BootcInstallPage::validateOstreeRequirements()
|
|
{
|
|
// Real-time validation of OSTree requirements
|
|
bool deviceValid = OstreeFilesystemValidator::validateTargetDevice(m_targetDeviceEdit->text());
|
|
bool composefsValid = OstreeFilesystemValidator::checkComposefsSupport();
|
|
|
|
QString status;
|
|
if (deviceValid && composefsValid) {
|
|
status = "✓ OSTree requirements satisfied";
|
|
m_ostreeStatusLabel->setStyleSheet("color: green;");
|
|
} else {
|
|
status = "✗ OSTree requirements not met";
|
|
m_ostreeStatusLabel->setStyleSheet("color: red;");
|
|
}
|
|
|
|
m_ostreeStatusLabel->setText(status);
|
|
}
|
|
```
|
|
|
|
### Phase 4: Testing and Polish (Month 2-3)
|
|
|
|
#### 4.1 Unit Tests
|
|
```cpp
|
|
class BootcInstallModuleTest : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
void testModuleInitialization();
|
|
void testJobCreation();
|
|
void testRegistryAuthJob();
|
|
void testBootcInstallJob();
|
|
void testValidation();
|
|
};
|
|
```
|
|
|
|
#### 4.2 Integration Tests
|
|
- [ ] Test with real bootc containers
|
|
- [ ] Test registry authentication
|
|
- [ ] Test different install types
|
|
- [ ] Test error handling
|
|
|
|
## Configuration
|
|
|
|
### Module Configuration
|
|
```yaml
|
|
# bootc-install.conf
|
|
module: bootc-install
|
|
config:
|
|
containerUrl: "quay.io/centos-bootc/centos-bootc:stream9"
|
|
targetDevice: "/dev/sda"
|
|
installType: "to-disk" # to-disk, to-filesystem, to-existing-root
|
|
authRequired: false
|
|
authJson: ""
|
|
|
|
# User account configuration
|
|
username: "admin"
|
|
sshKey: "" # SSH public key for root access
|
|
|
|
# OSTree-specific configuration
|
|
ostree:
|
|
enableComposefs: true
|
|
enableTransientRo: false
|
|
kernelPath: "/usr/lib/ostree-boot/vmlinuz"
|
|
initrdPath: "/usr/lib/ostree-boot/initramfs.img"
|
|
bootPath: "/usr/lib/ostree-boot/"
|
|
|
|
# Bootloader configuration
|
|
bootloader:
|
|
type: "grub2"
|
|
target: "x86_64-efi"
|
|
regenerateInitramfs: true
|
|
|
|
# Filesystem validation
|
|
validation:
|
|
requireGpt: true
|
|
requireEfi: true
|
|
checkComposefs: true
|
|
validateMountPoints: true
|
|
```
|
|
|
|
### Calamares Integration
|
|
```yaml
|
|
# calamares.conf
|
|
modules:
|
|
- bootc-install
|
|
|
|
bootc-install:
|
|
containerUrl: "quay.io/centos-bootc/centos-bootc:stream9"
|
|
targetDevice: "/dev/sda"
|
|
installType: "to-disk"
|
|
authRequired: false
|
|
|
|
# OSTree filesystem settings
|
|
ostree:
|
|
enableComposefs: true
|
|
enableTransientRo: false
|
|
```
|
|
|
|
## Technical Architecture
|
|
|
|
### File Structure
|
|
```
|
|
calamares-bootc-install/
|
|
├── src/
|
|
│ ├── BootcInstallModule.cpp
|
|
│ ├── BootcInstallJob.cpp
|
|
│ ├── RegistryAuthJob.cpp
|
|
│ ├── BootcInstallPage.cpp
|
|
│ └── BootcInstallPage.ui
|
|
├── config/
|
|
│ └── bootc-install.conf
|
|
├── tests/
|
|
│ ├── BootcInstallModuleTest.cpp
|
|
│ └── testdata/
|
|
└── CMakeLists.txt
|
|
```
|
|
|
|
### Dependencies
|
|
- **Calamares**: Module framework
|
|
- **Qt**: UI and core functionality
|
|
- **podman**: Container runtime
|
|
- **bootc**: Container installation tool
|
|
|
|
## Key Implementation Details
|
|
|
|
### 1. Simple and Focused
|
|
- **Single purpose**: Only handles `bootc install`
|
|
- **Direct integration**: Calls podman and bootc directly
|
|
- **Minimal complexity**: No pattern switching or hybrid approaches
|
|
|
|
### 2. Follow Official Patterns
|
|
- **Use exact podman command** from Fedora documentation
|
|
- **Follow bootc install syntax** from official docs
|
|
- **Handle registry auth** via `/etc/ostree/auth.json`
|
|
|
|
### 3. Error Handling
|
|
- **Validate inputs** before execution
|
|
- **Handle podman failures** gracefully
|
|
- **Provide clear error messages** to users
|
|
|
|
## Success Metrics
|
|
|
|
### Technical Metrics
|
|
- [ ] Module loads and initializes correctly
|
|
- [ ] Jobs execute successfully
|
|
- [ ] Registry authentication works
|
|
- [ ] bootc install completes successfully
|
|
|
|
### User Experience Metrics
|
|
- [ ] Clear configuration UI
|
|
- [ ] Proper validation and error messages
|
|
- [ ] Progress reporting during installation
|
|
- [ ] Integration with Calamares workflow
|
|
|
|
## Conclusion
|
|
|
|
This focused plan creates a single, purpose-built Calamares module for `bootc install` operations that properly accounts for the unique OSTree filesystem characteristics and the specific nuances of direct container installation. By following the [official bootc bare metal documentation](https://docs.fedoraproject.org/en-US/bootc/bare-metal/) exactly and incorporating the specific requirements for bootloader configuration, initramfs handling, and user account creation, we can create a reliable, maintainable solution.
|
|
|
|
### Key bootc install Considerations Addressed:
|
|
|
|
1. **Direct Container Execution** - Uses [podman run with privileged mode](https://docs.fedoraproject.org/en-US/bootc/bare-metal/) for installation
|
|
2. **Post-Install Configuration** - Handles bootloader setup, user creation, and SSH key configuration
|
|
3. **OSTree Filesystem Layout** - Kernel in `/usr/lib/ostree-boot/` not `/boot/`
|
|
4. **GRUB2 Configuration** - Proper bootloader setup for OSTree systems
|
|
5. **Initramfs Regeneration** - Critical for filesystem configuration changes
|
|
6. **User Account Management** - Uses systemd-sysusers for OSTree-compatible user creation
|
|
7. **SSH Key Setup** - Proper permissions and directory structure for root access
|
|
|
|
### Key Advantages:
|
|
|
|
1. **bootc install Native** - Follows the [official bootc install approach](https://docs.fedoraproject.org/en-US/bootc/bare-metal/) exactly
|
|
2. **Complete Installation** - Handles both installation and post-install configuration
|
|
3. **OSTree-Aware** - Properly manages filesystem layout and bootloader configuration
|
|
4. **User-Friendly** - Provides familiar Calamares interface for bootc installation
|
|
5. **Validation** - Real-time checking of OSTree and bootc requirements
|
|
6. **Extensibility** - Can be enhanced with additional features over time
|
|
|
|
This approach ensures that the Calamares module provides a complete, user-friendly interface for `bootc install` operations while properly handling all the OSTree-specific requirements for bootloader configuration, initramfs management, and user account creation. |