Skip to content

Commit

Permalink
install: Optionally use host mounted /var/lib/containers
Browse files Browse the repository at this point in the history
I just keep hitting the host skopeo requirement in corner cases;
it's annoying because *otherwise* the container is self-sufficient.
Change our installation instructions to add a `/var/lib/containers`
bind mount.

For the time being of course we continue to support forking off
`skopeo` on the host.

One thing I still want to investigate is dropping some requirements
here and switch to *dynamically* setting up the mount points
inside the container as is mentioned in https://brauner.io/2023/02/28/mounting-into-mount-namespaces.html
but this currently requires relatively new host kernels.

As far as test coverage, this changes the Github Action that
uses ubuntu and needed to build a newer skopeo to stop doing
that, and in fact we explicitly *remove* skopeo to verify
it's not being used in the install process.

I didn't change the other install tests to verify they keep
working.

Closes: containers#81
Signed-off-by: Colin Walters <[email protected]>
  • Loading branch information
cgwalters committed Jan 26, 2024
1 parent 047ce72 commit 3009aed
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 42 deletions.
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

0 comments on commit 3009aed

Please sign in to comment.