Skip to content

Commit 0319427

Browse files
authored
Merge pull request #55 from cgwalters/install-to-self
Add `install-to-filesystem`
2 parents 44527ee + ca08d28 commit 0319427

File tree

13 files changed

+435
-31
lines changed

13 files changed

+435
-31
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@ prefix ?= /usr
33
all:
44
cargo build --release
55

6+
all-test:
7+
cargo build --release --all-features
8+
69
install:
710
install -D -t $(DESTDIR)$(prefix)/bin target/release/bootc
811

912
bin-archive: all
1013
$(MAKE) install DESTDIR=tmp-install && tar --zstd -C tmp-install -cf bootc.tar.zst . && rm tmp-install -rf
1114

15+
test-bin-archive: all-test
16+
$(MAKE) install DESTDIR=tmp-install && tar --zstd -C tmp-install -cf bootc.tar.zst . && rm tmp-install -rf
17+
1218
install-kola-tests:
1319
install -D -t $(DESTDIR)$(prefix)/lib/coreos-assembler/tests/kola/bootc tests/kolainst/*
1420

ci/Dockerfile.fcos

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
FROM quay.io/coreos-assembler/fcos-buildroot:testing-devel as builder
44
WORKDIR /src
55
COPY . .
6-
RUN make bin-archive
6+
RUN make test-bin-archive
77

88
FROM quay.io/fedora/fedora-coreos:testing-devel
99
COPY --from=builder /src/bootc.tar.zst /tmp

ci/run-kola.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ if test -z "${BASE_QEMU_IMAGE:-}"; then
3434
coreos-installer download -p qemu -f qcow2.xz --decompress
3535
BASE_QEMU_IMAGE="$(echo *.qcow2)"
3636
fi
37-
kola run --oscontainer ostree-unverified-registry:${TARGET_IMAGE} --qemu-image "./${BASE_QEMU_IMAGE}" "${kola_args[@]}" ext.bootc.'*'
37+
cosa kola run --oscontainer ostree-unverified-registry:${TARGET_IMAGE} --qemu-image "./${BASE_QEMU_IMAGE}" "${kola_args[@]}" ext.bootc.'*'
3838

3939
echo "ok kola bootc"

lib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ libc = "^0.2"
2323
once_cell = "1.9"
2424
openssl = "^0.10"
2525
nix = ">= 0.24, < 0.26"
26+
regex = "1.7.1"
2627
serde = { features = ["derive"], version = "1.0.125" }
2728
serde_json = "1.0.64"
2829
serde_with = ">= 1.9.4, < 2"

lib/src/blockdev.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use anyhow::{anyhow, Context, Result};
44
use camino::Utf8Path;
55
use fn_error_context::context;
66
use nix::errno::Errno;
7+
use once_cell::sync::Lazy;
8+
use regex::Regex;
79
use serde::Deserialize;
10+
use std::collections::HashMap;
811
use std::fs::File;
912
use std::os::unix::io::AsRawFd;
1013
use std::process::Command;
@@ -39,7 +42,7 @@ impl Device {
3942

4043
pub(crate) fn wipefs(dev: &Utf8Path) -> Result<()> {
4144
Task::new_and_run(
42-
&format!("Wiping device {dev}"),
45+
format!("Wiping device {dev}"),
4346
"wipefs",
4447
["-a", dev.as_str()],
4548
)
@@ -109,6 +112,67 @@ pub(crate) fn reread_partition_table(file: &mut File, retry: bool) -> Result<()>
109112
Ok(())
110113
}
111114

115+
/// Runs the provided Command object, captures its stdout, and swallows its stderr except on
116+
/// failure. Returns a Result<String> describing whether the command failed, and if not, its
117+
/// standard output. Output is assumed to be UTF-8. Errors are adequately prefixed with the full
118+
/// command.
119+
pub(crate) fn cmd_output(cmd: &mut Command) -> Result<String> {
120+
let result = cmd
121+
.output()
122+
.with_context(|| format!("running {:#?}", cmd))?;
123+
if !result.status.success() {
124+
eprint!("{}", String::from_utf8_lossy(&result.stderr));
125+
anyhow::bail!("{:#?} failed with {}", cmd, result.status);
126+
}
127+
String::from_utf8(result.stdout)
128+
.with_context(|| format!("decoding as UTF-8 output of `{:#?}`", cmd))
129+
}
130+
131+
/// Parse key-value pairs from lsblk --pairs.
132+
/// Newer versions of lsblk support JSON but the one in CentOS 7 doesn't.
133+
fn split_lsblk_line(line: &str) -> HashMap<String, String> {
134+
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#"([A-Z-_]+)="([^"]+)""#).unwrap());
135+
let mut fields: HashMap<String, String> = HashMap::new();
136+
for cap in REGEX.captures_iter(line) {
137+
fields.insert(cap[1].to_string(), cap[2].to_string());
138+
}
139+
fields
140+
}
141+
142+
/// This is a bit fuzzy, but... this function will return every block device in the parent
143+
/// hierarchy of `device` capable of containing other partitions. So e.g. parent devices of type
144+
/// "part" doesn't match, but "disk" and "mpath" does.
145+
pub(crate) fn find_parent_devices(device: &str) -> Result<Vec<String>> {
146+
let mut cmd = Command::new("lsblk");
147+
// Older lsblk, e.g. in CentOS 7.6, doesn't support PATH, but --paths option
148+
cmd.arg("--pairs")
149+
.arg("--paths")
150+
.arg("--inverse")
151+
.arg("--output")
152+
.arg("NAME,TYPE")
153+
.arg(device);
154+
let output = cmd_output(&mut cmd)?;
155+
let mut parents = Vec::new();
156+
// skip first line, which is the device itself
157+
for line in output.lines().skip(1) {
158+
let dev = split_lsblk_line(line);
159+
let name = dev
160+
.get("NAME")
161+
.with_context(|| format!("device in hierarchy of {device} missing NAME"))?;
162+
let kind = dev
163+
.get("TYPE")
164+
.with_context(|| format!("device in hierarchy of {device} missing TYPE"))?;
165+
if kind == "disk" {
166+
parents.push(name.clone());
167+
} else if kind == "mpath" {
168+
parents.push(name.clone());
169+
// we don't need to know what disks back the multipath
170+
break;
171+
}
172+
}
173+
Ok(parents)
174+
}
175+
112176
// create unsafe ioctl wrappers
113177
#[allow(clippy::missing_safety_doc)]
114178
mod ioctl {

lib/src/bootloader.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub(crate) const IGNITION_VARIABLE: &str = "$ignition_firstboot";
1515
const GRUB_BOOT_UUID_FILE: &str = "bootuuid.cfg";
1616
const STATIC_GRUB_CFG: &str = include_str!("grub.cfg");
1717
const STATIC_GRUB_CFG_EFI: &str = include_str!("grub-efi.cfg");
18+
/// The name of the mountpoint for efi (as a subdirectory of /boot, or at the toplevel)
19+
pub(crate) const EFI_DIR: &str = "efi";
1820

1921
fn install_grub2_efi(efidir: &Dir, uuid: &str) -> Result<()> {
2022
let mut vendordir = None;
@@ -64,7 +66,7 @@ pub(crate) fn install_via_bootupd(
6466
let bootfs = &rootfs.join("boot");
6567

6668
{
67-
let efidir = Dir::open_ambient_dir(&bootfs.join("efi"), cap_std::ambient_authority())?;
69+
let efidir = Dir::open_ambient_dir(bootfs.join("efi"), cap_std::ambient_authority())?;
6870
install_grub2_efi(&efidir, &grub2_uuid_contents)?;
6971
}
7072

lib/src/cli.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ pub(crate) enum TestingOpts {
8282
RunPrivilegedIntegration {},
8383
/// Execute integration tests that target a not-privileged ostree container
8484
RunContainerIntegration {},
85+
/// Block device setup for testing
86+
PrepTestInstallFilesystem { blockdev: Utf8PathBuf },
87+
/// e2e test of install-to-filesystem
88+
TestInstallFilesystem {
89+
image: String,
90+
blockdev: Utf8PathBuf,
91+
},
8592
}
8693

8794
/// Deploy and upgrade via bootable container images.
@@ -99,6 +106,9 @@ pub(crate) enum Opt {
99106
/// Install to the target block device
100107
#[cfg(feature = "install")]
101108
Install(crate::install::InstallOpts),
109+
/// Install to the target filesystem.
110+
#[cfg(feature = "install")]
111+
InstallToFilesystem(crate::install::InstallToFilesystemOpts),
102112
/// Internal integration testing helpers.
103113
#[clap(hide(true), subcommand)]
104114
#[cfg(feature = "internal-testing-api")]
@@ -336,6 +346,8 @@ where
336346
Opt::Switch(opts) => switch(opts).await,
337347
#[cfg(feature = "install")]
338348
Opt::Install(opts) => crate::install::install(opts).await,
349+
#[cfg(feature = "install")]
350+
Opt::InstallToFilesystem(opts) => crate::install::install_to_filesystem(opts).await,
339351
Opt::Status(opts) => super::status::status(opts).await,
340352
#[cfg(feature = "internal-testing-api")]
341353
Opt::InternalTests(opts) => crate::privtests::run(opts).await,

lib/src/ignition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ mod tests {
313313
(false, "sha512-cdaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f")
314314
];
315315
for (valid, hash_arg) in &hash_args {
316-
let hasher = IgnitionHash::from_str(&hash_arg).unwrap();
316+
let hasher = IgnitionHash::from_str(hash_arg).unwrap();
317317
let mut rd = std::io::Cursor::new(&input);
318318
assert!(hasher.validate(&mut rd).is_ok() == *valid);
319319
}

0 commit comments

Comments
 (0)