mirror of
https://github.com/containers/bootc.git
synced 2026-02-05 06:45:13 +01:00
--transport docker-daemon support
So we can do things like: sudo bootc switch --transport docker-daemon localhost/bootc:latest Signed-off-by: Eric Curtin <eric.curtin@docker.com>
This commit is contained in:
committed by
Colin Walters
parent
a998bfc3f1
commit
086fa3bbd3
@@ -56,12 +56,15 @@ pub(crate) async fn initialize_composefs_repository(
|
||||
/// Ex
|
||||
/// docker://quay.io/some-image
|
||||
/// containers-storage:some-image
|
||||
/// docker-daemon:some-image-id
|
||||
pub(crate) fn get_imgref(transport: &str, image: &str) -> String {
|
||||
let img = image.strip_prefix(":").unwrap_or(&image);
|
||||
let transport = transport.strip_suffix(":").unwrap_or(&transport);
|
||||
|
||||
if transport == "registry" {
|
||||
format!("docker://{img}")
|
||||
} else if transport == "docker-daemon" {
|
||||
format!("docker-daemon:{img}")
|
||||
} else {
|
||||
format!("{transport}:{img}")
|
||||
}
|
||||
@@ -138,4 +141,12 @@ mod tests {
|
||||
format!("docker://{IMAGE_NAME}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_imgref_docker_daemon_transport() {
|
||||
assert_eq!(
|
||||
get_imgref("docker-daemon", IMAGE_NAME),
|
||||
format!("docker-daemon:{IMAGE_NAME}")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ pub(crate) struct SwitchOpts {
|
||||
#[clap(long = "soft-reboot")]
|
||||
pub(crate) soft_reboot: Option<SoftRebootMode>,
|
||||
|
||||
/// The transport; e.g. oci, oci-archive, containers-storage. Defaults to `registry`.
|
||||
/// The transport; e.g. registry, oci, oci-archive, docker-daemon, containers-storage. Defaults to `registry`.
|
||||
#[clap(long, default_value = "registry")]
|
||||
pub(crate) transport: String,
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ pub enum Transport {
|
||||
ContainerStorage,
|
||||
/// Local directory (`dir:`)
|
||||
Dir,
|
||||
/// Local Docker daemon (`docker-daemon:`)
|
||||
DockerDaemon,
|
||||
}
|
||||
|
||||
/// Combination of a remote image reference and transport.
|
||||
@@ -114,6 +116,7 @@ impl TryFrom<&str> for Transport {
|
||||
Self::DOCKER_ARCHIVE_STR => Self::DockerArchive,
|
||||
Self::CONTAINERS_STORAGE_STR => Self::ContainerStorage,
|
||||
Self::LOCAL_DIRECTORY_STR => Self::Dir,
|
||||
Self::DOCKER_DAEMON_STR => Self::DockerDaemon,
|
||||
o => return Err(anyhow!("Unknown transport '{}'", o)),
|
||||
})
|
||||
}
|
||||
@@ -126,6 +129,7 @@ impl Transport {
|
||||
const CONTAINERS_STORAGE_STR: &'static str = "containers-storage";
|
||||
const LOCAL_DIRECTORY_STR: &'static str = "dir";
|
||||
const REGISTRY_STR: &'static str = "registry";
|
||||
const DOCKER_DAEMON_STR: &'static str = "docker-daemon";
|
||||
|
||||
/// Retrieve an identifier that can then be re-parsed from [`Transport::try_from::<&str>`].
|
||||
pub fn serializable_name(&self) -> &'static str {
|
||||
@@ -136,6 +140,7 @@ impl Transport {
|
||||
Transport::DockerArchive => Self::DOCKER_ARCHIVE_STR,
|
||||
Transport::ContainerStorage => Self::CONTAINERS_STORAGE_STR,
|
||||
Transport::Dir => Self::LOCAL_DIRECTORY_STR,
|
||||
Transport::DockerDaemon => Self::DOCKER_DAEMON_STR,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,6 +263,7 @@ impl std::fmt::Display for Transport {
|
||||
Self::OciDir => "oci:",
|
||||
Self::ContainerStorage => "containers-storage:",
|
||||
Self::Dir => "dir:",
|
||||
Self::DockerDaemon => "docker-daemon:",
|
||||
};
|
||||
f.write_str(s)
|
||||
}
|
||||
|
||||
@@ -205,9 +205,13 @@ pub(crate) async fn fetch_layer<'a>(
|
||||
let (blob, driver, size);
|
||||
let media_type: oci_image::MediaType;
|
||||
match transport_src {
|
||||
Transport::ContainerStorage => {
|
||||
let layer_info = layer_info
|
||||
.ok_or_else(|| anyhow!("skopeo too old to pull from containers-storage"))?;
|
||||
// Both containers-storage and docker-daemon store layers uncompressed in their
|
||||
// local storage, even though the manifest may indicate they are compressed.
|
||||
// We need to use the actual media type from layer_info to avoid decompression errors.
|
||||
Transport::ContainerStorage | Transport::DockerDaemon => {
|
||||
let layer_info = layer_info.ok_or_else(|| {
|
||||
anyhow!("skopeo too old to pull from containers-storage or docker-daemon")
|
||||
})?;
|
||||
let n_layers = layer_info.len();
|
||||
let layer_blob = layer_info.get(layer_index).ok_or_else(|| {
|
||||
anyhow!("blobid position {layer_index} exceeds diffid count {n_layers}")
|
||||
|
||||
@@ -22,6 +22,11 @@ use crate::oci_spec::image as oci_image;
|
||||
/// TODO: change the skopeo code to shield us from this correctly
|
||||
const DOCKER_TYPE_LAYER_TAR: &str = "application/vnd.docker.image.rootfs.diff.tar";
|
||||
|
||||
/// The Docker MIME type for gzipped layers when stored in docker-daemon.
|
||||
/// Even though this indicates gzip compression, docker-daemon actually stores
|
||||
/// the layers uncompressed, so we need to treat this as uncompressed.
|
||||
const DOCKER_TYPE_LAYER_TAR_GZIP: &str = "application/vnd.docker.image.rootfs.diff.tar.gzip";
|
||||
|
||||
/// Extends the `Read` trait with another method to get mutable access to the inner reader
|
||||
trait ReadWithGetInnerMut: Read + Send + 'static {
|
||||
fn get_inner_mut(&mut self) -> &mut dyn Read;
|
||||
@@ -125,6 +130,9 @@ impl Decompressor {
|
||||
oci_image::MediaType::Other(t) if t.as_str() == DOCKER_TYPE_LAYER_TAR => {
|
||||
Box::new(TransparentDecompressor(src))
|
||||
}
|
||||
oci_image::MediaType::Other(t) if t.as_str() == DOCKER_TYPE_LAYER_TAR_GZIP => {
|
||||
Box::new(TransparentDecompressor(src))
|
||||
}
|
||||
o => anyhow::bail!("Unhandled layer type: {}", o),
|
||||
};
|
||||
Ok(Self {
|
||||
@@ -228,4 +236,17 @@ mod tests {
|
||||
assert_eq!(e.to_string(), "Unknown frame descriptor".to_string());
|
||||
drop(d)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_docker_tar_gzip_media_type_uses_transparent_decompressor() {
|
||||
// Test that the docker-daemon gzip media type is treated as uncompressed
|
||||
let data = b"test data";
|
||||
let media_type = oci_image::MediaType::Other(DOCKER_TYPE_LAYER_TAR_GZIP.to_string());
|
||||
let mut d = Decompressor::new(&media_type, &data[..]).unwrap();
|
||||
let mut buf = [0u8; 32];
|
||||
let n = d.read(&mut buf).unwrap();
|
||||
assert_eq!(n, data.len());
|
||||
assert_eq!(&buf[..n], data);
|
||||
drop(d)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user