Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

install: Optionally use host mounted /var/lib/containers #286

Merged
merged 1 commit into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 4 additions & 30 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,39 +116,13 @@ jobs:
run: sudo tar -C / -xvf bootc.tar.zst
- name: Integration tests
run: bootc internal-tests run-container-integration
build-skopeo-ubuntu:
name: "Build skopeo git main for ubuntu"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
repository: containers/skopeo
path: skopeo
- name: Install build deps
run: |
sudo sed -ie s,'# deb-src,deb-src,' /etc/apt/sources.list
sudo apt update
sudo apt build-dep -y skopeo
- uses: actions/setup-go@v4
with:
go-version: '>=1.20'
- name: Build skopeo
run: cd skopeo && make bin/skopeo PREFIX=/usr
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: skopeo-ubuntu
path: skopeo/bin/skopeo
privtest-alongside:
name: "Test install-alongside"
needs: [build-fedora, build-skopeo-ubuntu]
needs: [build-fedora]
runs-on: ubuntu-latest
steps:
- name: Download
uses: actions/download-artifact@v4
with:
name: skopeo-ubuntu
- run: chmod a+x skopeo && sudo mv skopeo /usr/bin
- name: Ensure host skopeo is disabled
run: sudo rm -f /bin/skopeo /usr/bin/skopeo
- name: Download
uses: actions/download-artifact@v3
with:
Expand All @@ -158,7 +132,7 @@ jobs:
- name: Integration tests
run: |
set -xeuo pipefail
sudo podman run --rm -ti --privileged -v /:/target -v ./usr/bin/bootc:/usr/bin/bootc --pid=host --security-opt label=disable \
sudo podman run --rm -ti --privileged --env RUST_LOG=debug -v /:/target -v /var/lib/containers:/var/lib/containers -v ./usr/bin/bootc:/usr/bin/bootc --pid=host --security-opt label=disable \
quay.io/centos-bootc/fedora-bootc-dev:eln bootc install to-filesystem \
--karg=foo=bar --disable-selinux --replace=alongside /target
ls -al /boot/loader/
Expand Down
15 changes: 9 additions & 6 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ inside the container.
There are two sub-commands: `bootc install to-disk` and `boot install to-filesystem`.

However, nothing *else* (external) is required to perform a basic installation
to disk. (The one exception to host requirements today is that the host must
have `skopeo` installed. This is a bug; more information in
[this issue](https://github.com/containers/bootc/issues/81).)
to disk - the container image itself comes with a baseline self-sufficient installer
that sets things up ready to boot.

This is motivated by experience gained from the Fedora CoreOS
project where today the expectation is that one boots from a pre-existing disk
Expand Down Expand Up @@ -58,7 +57,7 @@ to an existing system and install your container image. Failure to run
Here's an example of using `bootc install` (root/elevated permission required):

```bash
podman run --rm --privileged --pid=host --security-opt label=type:unconfined_t <image> bootc install to-disk /path/to/disk
podman run --rm --privileged --pid=host -v /var/lib/containers:/var/lib/containers --security-opt label=type:unconfined_t <image> bootc install to-disk /path/to/disk
```

Note that while `--privileged` is used, this command will not perform any
Expand All @@ -70,6 +69,10 @@ The `--pid=host --security-opt label=type:unconfined_t` today
make it more convenient for bootc to perform some privileged
operations; in the future these requirement may be dropped.

The `-v /var/lib/containers:/var/lib/containers` option is required in order
for the container to access its own underlying image, which is used by
the installation process.

Jump to the section for [`install to-filesystem`](#more-advanced-installation) later
in this document for additional information about that method.

Expand Down Expand Up @@ -219,7 +222,7 @@ via e.g.:

```bash
truncate -s 10G exampleos.raw
podman run --rm --privileged --pid=host --security-opt label=type:unconfined_t -v .:/output <yourimage> bootc install to-disk --generic-image --via-loopback /output/myimage.raw
podman run --rm --privileged --pid=host --security-opt label=type:unconfined_t -v /var/lib/containers:/var/lib/containers -v .:/output <yourimage> bootc install to-disk --generic-image --via-loopback /output/myimage.raw
```

Notice that we use `--generic-image` for this use case.
Expand All @@ -237,7 +240,7 @@ support the root storage setup already initialized.
The core command should look like this (root/elevated permission required):

```bash
podman run --rm --privileged -v /:/target \
podman run --rm --privileged -v /var/lib/containers:/var/lib/containers -v /:/target \
--pid=host --security-opt label=type:unconfined_t \
<image> \
bootc install to-filesystem --replace=alongside /target
Expand Down
39 changes: 33 additions & 6 deletions lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ pub(crate) struct SourceInfo {
pub(crate) selinux: bool,
/// Whether the source is available in the host mount namespace
pub(crate) in_host_mountns: bool,
/// Whether we were invoked with -v /var/lib/containers:/var/lib/containers
pub(crate) have_host_container_storage: bool,
}

// Shared read-only global state
Expand Down Expand Up @@ -387,23 +389,41 @@ impl SourceInfo {
};
let digest = crate::podman::imageid_to_digest(&container_info.imageid)?;

let root = Dir::open_ambient_dir("/", cap_std::ambient_authority())?;
let have_host_container_storage = Utf8Path::new(crate::podman::CONTAINER_STORAGE)
.try_exists()?
&& ostree_ext::mountutil::is_mountpoint(
&root,
crate::podman::CONTAINER_STORAGE.trim_start_matches('/'),
)?
.unwrap_or_default();

// Verify up front we can do the fetch
require_skopeo_with_containers_storage()?;
if have_host_container_storage {
tracing::debug!("Host container storage found");
} else {
tracing::debug!(
"No {} mount available, checking skopeo",
crate::podman::CONTAINER_STORAGE
);
require_skopeo_with_containers_storage()?;
}

Self::new(imageref, Some(digest), true)
Self::new(imageref, Some(digest), true, have_host_container_storage)
}

#[context("Creating source info from a given imageref")]
pub(crate) fn from_imageref(imageref: &str) -> Result<Self> {
let imageref = ostree_container::ImageReference::try_from(imageref)?;
Self::new(imageref, None, false)
Self::new(imageref, None, false, false)
}

/// Construct a new source information structure
fn new(
imageref: ostree_container::ImageReference,
digest: Option<String>,
in_host_mountns: bool,
have_host_container_storage: bool,
) -> Result<Self> {
let cancellable = ostree::gio::Cancellable::NONE;
let commit = Task::new("Reading ostree commit", "ostree")
Expand All @@ -424,6 +444,7 @@ impl SourceInfo {
digest,
selinux,
in_host_mountns,
have_host_container_storage,
})
}
}
Expand Down Expand Up @@ -630,10 +651,16 @@ async fn initialize_ostree_root_from_self(
}
};

// We need to fetch the container image from the root mount namespace
let skopeo_cmd = run_in_host_mountns("skopeo");
// We need to fetch the container image from the root mount namespace. If
// we don't have /var/lib/containers mounted in this image, fork off skopeo
// in the host mountnfs.
let skopeo_cmd = if !state.source.have_host_container_storage {
Some(run_in_host_mountns("skopeo"))
} else {
None
};
let proxy_cfg = ostree_container::store::ImageProxyConfig {
skopeo_cmd: Some(skopeo_cmd),
skopeo_cmd,
..Default::default()
};

Expand Down
4 changes: 4 additions & 0 deletions lib/src/podman.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ use serde::Deserialize;
use crate::install::run_in_host_mountns;
use crate::task::Task;

/// Where we look inside our container to find our own image
/// for use with `bootc install`.
pub(crate) const CONTAINER_STORAGE: &str = "/var/lib/containers";

#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
pub(crate) struct Inspect {
Expand Down
Loading