Skip to content

Commit 78eecfb

Browse files
committed
install: Further clean up imageref handling
Prep for takeover installs. Rather than carrying the container env info around in `State`, carry just the source image reference and digest separately. This will make it easier to add code later for takeover installs to use an image already in an ostree repo. Signed-off-by: Colin Walters <[email protected]>
1 parent dc3e1fd commit 78eecfb

File tree

2 files changed

+48
-26
lines changed

2 files changed

+48
-26
lines changed

lib/src/install.rs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use ostree_ext::ostree;
2424
use ostree_ext::prelude::Cast;
2525
use serde::{Deserialize, Serialize};
2626

27-
use crate::containerenv::ContainerExecutionInfo;
2827
use crate::lsm::lsm_label;
2928
use crate::task::Task;
3029
use crate::utils::run_in_host_mountns;
@@ -234,7 +233,10 @@ pub(crate) struct InstallToFilesystemOpts {
234233

235234
// Shared read-only global state
236235
struct State {
237-
container_info: ContainerExecutionInfo,
236+
/// Image reference we'll pull from (today always containers-storage: type)
237+
source_imageref: ostree_container::ImageReference,
238+
/// The digest to use for pulls
239+
source_digest: String,
238240
/// Force SELinux off in target system
239241
override_disable_selinux: bool,
240242
config_opts: InstallConfigOpts,
@@ -416,20 +418,6 @@ async fn initialize_ostree_root_from_self(
416418
let opts = &state.target_opts;
417419
let cancellable = gio::Cancellable::NONE;
418420

419-
if !state.container_info.engine.starts_with("podman") {
420-
anyhow::bail!("Currently this command only supports being executed via podman");
421-
}
422-
if state.container_info.imageid.is_empty() {
423-
anyhow::bail!("Invalid empty imageid");
424-
}
425-
let digest = crate::podman::imageid_to_digest(&state.container_info.imageid)?;
426-
let src_image = crate::utils::digested_pullspec(&state.container_info.image, &digest);
427-
428-
let src_imageref = ostree_container::ImageReference {
429-
transport: ostree_container::Transport::ContainerStorage,
430-
name: src_image.clone(),
431-
};
432-
433421
// Parse the target CLI image reference options
434422
let target_sigverify = if opts.target_no_signature_verification {
435423
SignatureSource::ContainerPolicyAllowInsecure
@@ -451,10 +439,7 @@ async fn initialize_ostree_root_from_self(
451439
} else {
452440
ostree_container::OstreeImageReference {
453441
sigverify: target_sigverify,
454-
imgref: ostree_container::ImageReference {
455-
transport: ostree_container::Transport::Registry,
456-
name: state.container_info.image.clone(),
457-
},
442+
imgref: state.source_imageref.clone(),
458443
}
459444
};
460445

@@ -493,11 +478,17 @@ async fn initialize_ostree_root_from_self(
493478

494479
let mut temporary_dir = None;
495480
let src_imageref = if skopeo_supports_containers_storage()? {
496-
src_imageref
481+
// We always use exactly the digest of the running image to ensure predictability.
482+
let spec =
483+
crate::utils::digested_pullspec(&state.source_imageref.name, &state.source_digest);
484+
ostree_container::ImageReference {
485+
transport: ostree_container::Transport::ContainerStorage,
486+
name: spec,
487+
}
497488
} else {
498489
let td = tempfile::tempdir_in("/var/tmp")?;
499490
let path: &Utf8Path = td.path().try_into().unwrap();
500-
let r = copy_to_oci(&src_imageref, path)?;
491+
let r = copy_to_oci(&state.source_imageref, path)?;
501492
temporary_dir = Some(td);
502493
r
503494
};
@@ -555,7 +546,7 @@ async fn initialize_ostree_root_from_self(
555546
let uname = cap_std_ext::rustix::process::uname();
556547

557548
let aleph = InstallAleph {
558-
image: src_image,
549+
image: src_imageref.imgref.name.clone(),
559550
kernel: uname.release().to_str()?.to_string(),
560551
};
561552

@@ -903,6 +894,18 @@ async fn prepare_install(
903894

904895
// This command currently *must* be run inside a privileged container.
905896
let container_info = crate::containerenv::get_container_execution_info()?;
897+
if !container_info.engine.starts_with("podman") {
898+
anyhow::bail!("Currently this command only supports being executed via podman");
899+
}
900+
if container_info.imageid.is_empty() {
901+
anyhow::bail!("Invalid empty imageid");
902+
}
903+
let source_imageref = ostree_container::ImageReference {
904+
transport: ostree_container::Transport::ContainerStorage,
905+
name: container_info.image.clone(),
906+
};
907+
// Find the exact digested image we are running
908+
let source_digest = crate::podman::imageid_to_digest(&container_info.imageid)?;
906909

907910
// Even though we require running in a container, the mounts we create should be specific
908911
// to this process, so let's enter a private mountns to avoid leaking them.
@@ -943,7 +946,8 @@ async fn prepare_install(
943946
bind_mount_from_host("/var/tmp", "/var/tmp")?;
944947
let state = Arc::new(State {
945948
override_disable_selinux,
946-
container_info,
949+
source_imageref,
950+
source_digest,
947951
mntdir,
948952
devdir,
949953
config_opts,

lib/src/utils.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,27 @@ pub(crate) fn run_in_host_mountns(cmd: &str) -> Command {
5757
}
5858

5959
/// Given a possibly tagged image like quay.io/foo/bar:latest and a digest 0ab32..., return
60-
/// the digested form quay.io/foo/bar@sha256:0ab32...
60+
/// the digested form quay.io/foo/bar:latest@sha256:0ab32...
61+
/// If the image already has a digest, it will be replaced.
6162
#[allow(dead_code)]
6263
pub(crate) fn digested_pullspec(image: &str, digest: &str) -> String {
63-
let image = image.rsplit_once(':').map(|v| v.0).unwrap_or(image);
64+
let image = image.rsplit_once('@').map(|v| v.0).unwrap_or(image);
6465
format!("{image}@{digest}")
6566
}
67+
68+
#[test]
69+
fn test_digested_pullspec() {
70+
let digest = "ebe3bdccc041864e5a485f1e755e242535c3b83d110c0357fe57f110b73b143e";
71+
assert_eq!(
72+
digested_pullspec("quay.io/example/foo:bar", digest),
73+
format!("quay.io/example/foo:bar@{digest}")
74+
);
75+
assert_eq!(
76+
digested_pullspec("quay.io/example/foo@sha256:otherdigest", digest),
77+
format!("quay.io/example/foo@{digest}")
78+
);
79+
assert_eq!(
80+
digested_pullspec("quay.io/example/foo", digest),
81+
format!("quay.io/example/foo@{digest}")
82+
);
83+
}

0 commit comments

Comments
 (0)