bootc-docs/status/bootc-status-technical-guide.md
robojerk 526f1c1afd Initial commit: Comprehensive Debian bootc documentation
- Complete documentation for all bootc commands and subcommands
- Debian-specific adaptations and workarounds
- Manual installation methods to bypass bootc reliability issues
- Technical guides with Rust source code analysis
- Flowcharts and external command references
- Hidden command documentation (bootc internals, state, etc.)
- Composefs integration analysis
- Base image creation guides (with and without bootc binary)
- Management scripts and automation
- Comprehensive troubleshooting and examples
2025-09-15 14:02:28 -07:00

20 KiB

bootc status - Technical Guide

Overview

bootc status is a command for displaying the current state of a bootc-managed system. It provides comprehensive information about deployments, boot order, image references, and system configuration. The command supports multiple output formats and can be used both interactively and programmatically.

Purpose

The status command serves several critical functions:

  1. System State Display: Show current bootc system state
  2. Deployment Information: Display staged, booted, and rollback deployments
  3. Image References: Show container image information and metadata
  4. Boot Order Status: Indicate current boot order configuration
  5. Programmatic Access: Provide structured data for automation
  6. Troubleshooting: Help diagnose system issues

Command Syntax

bootc status [OPTIONS...]

Basic Usage

# Show current system status
bootc status

# Show status in JSON format
bootc status --format=json

# Show detailed status with verbose output
bootc status --verbose

# Show only booted deployment status
bootc status --booted

# Show status in YAML format
bootc status --format=yaml

Command Options

Option Description Default Required
--format Output format (humanreadable, yaml, json) auto No
--format-version Format version (0, 1) 1 No
--booted Only display booted deployment false No
--verbose, -v Include additional fields false No
--json Output in JSON format (deprecated) false No

Output Formats

Format Description Use Case
humanreadable Human-friendly text output Interactive use
yaml YAML-formatted output Programmatic parsing
json JSON-formatted output Programmatic parsing

Architecture Overview

1. Status Command Structure

pub(crate) struct StatusOpts {
    pub(crate) json: bool,                    // JSON output (deprecated)
    pub(crate) format: Option<OutputFormat>,  // Output format
    pub(crate) format_version: Option<u32>,   // Format version
    pub(crate) booted: bool,                  // Only booted deployment
    pub(crate) verbose: bool,                 // Verbose output
}

2. Status Process Flow

pub(crate) async fn status(opts: StatusOpts) -> Result<()> {
    // 1. Validate format version
    match opts.format_version.unwrap_or_default() {
        0 | 1 => {} // Both 0 and 1 mean "v1"
        o => anyhow::bail!("Unsupported format version: {o}"),
    };
    
    // 2. Get system status
    let mut host = if !ostree_booted()? {
        Default::default()
    } else {
        let sysroot = super::cli::get_storage().await?;
        let ostree = sysroot.get_ostree()?;
        let booted_deployment = ostree.booted_deployment();
        let (_deployments, host) = get_status(&ostree, booted_deployment.as_ref())?;
        host
    };
    
    // 3. Filter to booted deployment if requested
    if opts.booted {
        host.filter_to_slot(Slot::Booted);
    }
    
    // 4. Determine output format
    let out = std::io::stdout();
    let mut out = out.lock();
    let legacy_opt = if opts.json {
        OutputFormat::Json
    } else if std::io::stdout().is_terminal() {
        OutputFormat::HumanReadable
    } else {
        OutputFormat::Yaml
    };
    let format = opts.format.unwrap_or(legacy_opt);
    
    // 5. Output in requested format
    match format {
        OutputFormat::Json => host.to_canon_json_writer(&mut out),
        OutputFormat::Yaml => serde_yaml::to_writer(&mut out, &host),
        OutputFormat::HumanReadable => {
            human_readable_output(&mut out, &host, opts.verbose)
        }
    }
}

3. Core Status Gathering

pub(crate) fn get_status(
    sysroot: &SysrootLock,
    booted_deployment: Option<&ostree::Deployment>,
) -> Result<(Deployments, Host)> {
    // 1. Get stateroot name
    let stateroot = booted_deployment.as_ref().map(|d| d.osname());
    
    // 2. Partition deployments
    let (mut related_deployments, other_deployments) = sysroot
        .deployments()
        .into_iter()
        .partition::<VecDeque<_>, _>(|d| Some(d.osname()) == stateroot);
    
    // 3. Find staged deployment
    let staged = related_deployments
        .iter()
        .position(|d| d.is_staged())
        .map(|i| related_deployments.remove(i).unwrap());
    
    // 4. Filter out booted deployment
    if let Some(booted) = booted_deployment.as_ref() {
        related_deployments.retain(|f| !f.equal(booted));
    }
    
    // 5. Get rollback deployment
    let rollback = related_deployments.pop_front();
    
    // 6. Determine rollback queued status
    let rollback_queued = match (booted_deployment.as_ref(), rollback.as_ref()) {
        (Some(booted), Some(rollback)) => rollback.index() < booted.index(),
        _ => false,
    };
    
    // 7. Determine boot order
    let boot_order = if rollback_queued {
        BootOrder::Rollback
    } else {
        BootOrder::Default
    };
    
    // 8. Create deployments structure
    let deployments = Deployments {
        staged,
        rollback,
        other: related_deployments,
    };
    
    // 9. Create boot entries
    let staged = deployments.staged
        .as_ref()
        .map(|d| boot_entry_from_deployment(sysroot, d))
        .transpose()
        .context("Staged deployment")?;
    
    let booted = booted_deployment
        .as_ref()
        .map(|d| boot_entry_from_deployment(sysroot, d))
        .transpose()
        .context("Booted deployment")?;
    
    let rollback = deployments.rollback
        .as_ref()
        .map(|d| boot_entry_from_deployment(sysroot, d))
        .transpose()
        .context("Rollback deployment")?;
    
    // 10. Create host specification
    let spec = staged
        .as_ref()
        .or(booted.as_ref())
        .and_then(|entry| entry.image.as_ref())
        .map(|img| HostSpec {
            image: Some(img.image.clone()),
            boot_order,
        })
        .unwrap_or_default();
    
    // 11. Determine host type
    let ty = if booted
        .as_ref()
        .map(|b| b.image.is_some())
        .unwrap_or_default()
    {
        Some(HostType::BootcHost)
    } else {
        None
    };
    
    // 12. Create host status
    let mut host = Host::new(spec);
    host.status = HostStatus {
        staged,
        booted,
        rollback,
        other_deployments: other_deployments,
        rollback_queued,
        ty,
    };
    
    Ok((deployments, host))
}

Data Structures

1. Host Structure

pub struct Host {
    pub resource: k8sapitypes::Resource,
    pub spec: HostSpec,
    pub status: HostStatus,
}

2. Host Specification

pub struct HostSpec {
    pub image: Option<ImageReference>,
    pub boot_order: BootOrder,
}

3. Host Status

pub struct HostStatus {
    pub staged: Option<BootEntry>,
    pub booted: Option<BootEntry>,
    pub rollback: Option<BootEntry>,
    pub other_deployments: Vec<BootEntry>,
    pub rollback_queued: bool,
    pub ty: Option<HostType>,
}

4. Boot Entry

pub struct BootEntry {
    pub deployment: DeploymentInfo,
    pub image: Option<ImageStatus>,
}

5. Image Status

pub struct ImageStatus {
    pub image: ImageReference,
    pub version: Option<String>,
    pub timestamp: Option<chrono::DateTime<chrono::Utc>>,
    pub image_digest: String,
    pub architecture: String,
}

Output Formats

1. Human Readable Format

Default for terminal output

System is deployed via bootc.

Image: quay.io/myorg/debian-bootc:v2.0
Version: 2.0.0
Digest: sha256:abc123def456...
Architecture: x86_64

Boot Order: Default
Rollback Queued: false

Deployments:
  Staged: None
  Booted: quay.io/myorg/debian-bootc:v2.0
  Rollback: quay.io/myorg/debian-bootc:v1.0

2. YAML Format

Default for non-terminal output

apiVersion: v1
kind: Host
metadata:
  name: localhost
spec:
  image:
    image: quay.io/myorg/debian-bootc:v2.0
    transport: registry
  bootOrder: Default
status:
  staged: null
  booted:
    deployment:
      id: abc123def456...
      osname: debian-bootc
      checksum: def456ghi789...
    image:
      image:
        image: quay.io/myorg/debian-bootc:v2.0
        transport: registry
      version: "2.0.0"
      timestamp: "2024-01-15T10:30:00Z"
      imageDigest: sha256:abc123def456...
      architecture: x86_64
  rollback:
    deployment:
      id: def456ghi789...
      osname: debian-bootc
      checksum: ghi789jkl012...
    image:
      image:
        image: quay.io/myorg/debian-bootc:v1.0
        transport: registry
      version: "1.0.0"
      timestamp: "2024-01-10T15:45:00Z"
      imageDigest: sha256:def456ghi789...
      architecture: x86_64
  otherDeployments: []
  rollbackQueued: false
  ty: BootcHost

3. JSON Format

For programmatic access

{
  "apiVersion": "v1",
  "kind": "Host",
  "metadata": {
    "name": "localhost"
  },
  "spec": {
    "image": {
      "image": "quay.io/myorg/debian-bootc:v2.0",
      "transport": "registry"
    },
    "bootOrder": "Default"
  },
  "status": {
    "staged": null,
    "booted": {
      "deployment": {
        "id": "abc123def456...",
        "osname": "debian-bootc",
        "checksum": "def456ghi789..."
      },
      "image": {
        "image": {
          "image": "quay.io/myorg/debian-bootc:v2.0",
          "transport": "registry"
        },
        "version": "2.0.0",
        "timestamp": "2024-01-15T10:30:00Z",
        "imageDigest": "sha256:abc123def456...",
        "architecture": "x86_64"
      }
    },
    "rollback": {
      "deployment": {
        "id": "def456ghi789...",
        "osname": "debian-bootc",
        "checksum": "ghi789jkl012..."
      },
      "image": {
        "image": {
          "image": "quay.io/myorg/debian-bootc:v1.0",
          "transport": "registry"
        },
        "version": "1.0.0",
        "timestamp": "2024-01-10T15:45:00Z",
        "imageDigest": "sha256:def456ghi789...",
        "architecture": "x86_64"
      }
    },
    "otherDeployments": [],
    "rollbackQueued": false,
    "ty": "BootcHost"
  }
}

Deployment Detection

1. OSTree Boot Detection

if !ostree_booted()? {
    Default::default()
} else {
    // Get system status
}

Purpose: Check if system is booted via OSTree Process: Verify OSTree boot environment

2. Deployment Partitioning

let (mut related_deployments, other_deployments) = sysroot
    .deployments()
    .into_iter()
    .partition::<VecDeque<_>, _>(|d| Some(d.osname()) == stateroot);

Purpose: Separate related deployments from others Process: Filter deployments by stateroot name

3. Staged Deployment Detection

let staged = related_deployments
    .iter()
    .position(|d| d.is_staged())
    .map(|i| related_deployments.remove(i).unwrap());

Purpose: Find staged deployment for next boot Process: Look for deployment marked as staged

4. Rollback Detection

let rollback = related_deployments.pop_front();
let rollback_queued = match (booted_deployment.as_ref(), rollback.as_ref()) {
    (Some(booted), Some(rollback)) => rollback.index() < booted.index(),
    _ => false,
};

Purpose: Determine rollback deployment and queued status Process: Check deployment order and indices

Boot Order Management

1. Boot Order States

let boot_order = if rollback_queued {
    BootOrder::Rollback
} else {
    BootOrder::Default
};

States:

  • Default: Current deployment first, rollback second
  • Rollback: Rollback deployment first, current second

2. Rollback Queued Detection

let rollback_queued = match (booted_deployment.as_ref(), rollback.as_ref()) {
    (Some(booted), Some(rollback)) => rollback.index() < booted.index(),
    _ => false,
};

Purpose: Determine if rollback is queued for next boot Process: Compare deployment indices

Image Information Extraction

1. Image Status Creation

fn create_imagestatus(
    image: ImageReference,
    manifest_digest: &Digest,
    config: &ImageConfiguration,
) -> ImageStatus {
    let labels = labels_of_config(config);
    let timestamp = labels
        .and_then(|l| {
            l.get(oci_spec::image::ANNOTATION_CREATED)
                .map(|s| s.as_str())
        })
        .and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
        .map(|dt| dt.with_timezone(&chrono::Utc));
    
    let architecture = config.architecture().unwrap_or("unknown").to_string();
    
    ImageStatus {
        image,
        version: labels.and_then(|l| l.get("version").cloned()),
        timestamp,
        image_digest: manifest_digest.to_string(),
        architecture,
    }
}

Purpose: Extract image metadata and configuration Process: Parse OCI image configuration and labels

2. Boot Entry Creation

fn boot_entry_from_deployment(
    sysroot: &SysrootLock,
    deployment: &ostree::Deployment,
) -> Result<BootEntry> {
    let deployment_info = DeploymentInfo {
        id: deployment.csum().to_string(),
        osname: deployment.osname().to_string(),
        checksum: deployment.csum().to_string(),
    };
    
    let image = if let Ok(origin) = deployment.origin() {
        if let Some(refspec) = origin.optional_string("origin", "refspec")? {
            if let Ok(ostree_ref) = ostree::parse_refspec(&refspec) {
                if let Ok(image_ref) = ostree_container::OstreeImageReference::try_from(ostree_ref) {
                    let image_ref = ImageReference::from(image_ref);
                    let repo = &sysroot.repo();
                    let imgstate = ostree_container::store::query_image_commit(repo, &deployment.csum())?;
                    let cached = imgstate.cached_update
                        .map(|cached| create_imagestatus(image_ref, &cached.manifest_digest, &cached.config));
                    cached
                } else {
                    None
                }
            } else {
                None
            }
        } else {
            None
        }
    } else {
        None
    };
    
    Ok(BootEntry {
        deployment: deployment_info,
        image,
    })
}

Purpose: Create boot entry from OSTree deployment Process: Extract deployment info and image metadata

Format Detection

1. Automatic Format Selection

let legacy_opt = if opts.json {
    OutputFormat::Json
} else if std::io::stdout().is_terminal() {
    OutputFormat::HumanReadable
} else {
    OutputFormat::Yaml
};

Logic:

  • JSON: If --json flag specified
  • Human Readable: If output is a terminal
  • YAML: If output is not a terminal

2. Format Version Support

match opts.format_version.unwrap_or_default() {
    0 | 1 => {} // Both 0 and 1 mean "v1"
    o => anyhow::bail!("Unsupported format version: {o}"),
};

Purpose: Support multiple format versions Current: Version 1 (exposed as both 0 and 1)

Filtering Options

1. Booted Deployment Filter

if opts.booted {
    host.filter_to_slot(Slot::Booted);
}

Purpose: Show only booted deployment information Use Case: Focus on current deployment

2. Verbose Output

human_readable_output(&mut out, &host, opts.verbose)

Purpose: Include additional fields in human readable format Use Case: Detailed troubleshooting information

Error Handling

1. OSTree Boot Detection Errors

if !ostree_booted()? {
    Default::default()
}

Error: System not booted via OSTree Response: Return default empty status

2. Deployment Access Errors

let (_deployments, host) = get_status(&ostree, booted_deployment.as_ref())?;

Error: Cannot access deployments Response: Propagate error up the call stack

3. Format Version Errors

match opts.format_version.unwrap_or_default() {
    0 | 1 => {}
    o => anyhow::bail!("Unsupported format version: {o}"),
};

Error: Unsupported format version Response: Return error with message

Usage Patterns

1. Interactive Use

# Check system status
bootc status

# Check with verbose output
bootc status --verbose

# Check only booted deployment
bootc status --booted

2. Programmatic Use

# Get JSON output for parsing
bootc status --format=json

# Get YAML output for parsing
bootc status --format=yaml

# Check if system is bootc compatible
bootc status --format=json | jq '.status.booted != null'

3. Automation Scripts

#!/bin/bash
# Check if rollback is queued
if bootc status --format=json | jq -r '.status.rollbackQueued' | grep -q true; then
    echo "Rollback is queued for next boot"
fi

# Get current image version
CURRENT_VERSION=$(bootc status --format=json | jq -r '.status.booted.image.version // "unknown"')
echo "Current version: $CURRENT_VERSION"

Integration with Other Commands

1. Relationship to bootc upgrade

Status Before Upgrade:

bootc status
bootc upgrade --apply
bootc status

Status After Upgrade:

  • Shows new staged deployment
  • Shows updated booted deployment
  • Shows previous deployment as rollback

2. Relationship to bootc switch

Status Before Switch:

bootc status
bootc switch quay.io/myorg/debian-bootc:v2.0
bootc status

Status After Switch:

  • Shows new image reference
  • Shows staged deployment
  • Shows updated boot order

3. Relationship to bootc rollback

Status Before Rollback:

bootc status
bootc rollback
bootc status

Status After Rollback:

  • Shows rollback queued status
  • Shows updated boot order
  • Shows deployment reordering

Troubleshooting

1. Common Issues

System Not Bootc Compatible

# Check if system is bootc compatible
bootc status --format=json | jq '.status.booted != null'

# Check OSTree status
ostree admin status

No Deployments Found

# Check deployment status
ostree admin status

# Check system logs
journalctl -u bootc-fetch-apply-updates.service

Format Parsing Errors

# Use specific format
bootc status --format=json

# Check format version
bootc status --format-version=1

2. Debug Commands

# Enable debug logging
RUST_LOG=debug bootc status

# Check system logs
journalctl -u bootc-fetch-apply-updates.service

# Check OSTree status
ostree admin status --verbose

Best Practices

1. Programmatic Usage

  • Use JSON/YAML: For programmatic parsing
  • Check Format Version: Specify format version explicitly
  • Handle Errors: Check for null values and errors
  • Validate Output: Verify expected fields exist

2. Interactive Usage

  • Use Human Readable: For interactive use
  • Use Verbose: For detailed information
  • Check Booted: Focus on current deployment
  • Monitor Changes: Track status over time

3. Automation

  • Parse JSON: Use jq for JSON parsing
  • Check Compatibility: Verify bootc compatibility
  • Monitor Status: Track deployment changes
  • Handle Errors: Implement proper error handling

Future Enhancements

1. Planned Features

  • Additional Formats: Support for more output formats
  • Filtering Options: More granular filtering
  • Status History: Track status changes over time
  • Health Checks: Include system health information

2. Integration Improvements

  • API Support: REST API for status queries
  • Web Interface: Web-based status display
  • Monitoring Integration: Integration with monitoring systems
  • Alerting: Status-based alerting

This technical guide provides comprehensive understanding of the bootc status system's architecture, implementation, and usage patterns.