Skip to content

Commit 086fa3b

Browse files
ericcurtincgwalters
authored andcommitted
--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>
1 parent a998bfc commit 086fa3b

File tree

5 files changed

+46
-4
lines changed

5 files changed

+46
-4
lines changed

crates/lib/src/bootc_composefs/repo.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,15 @@ pub(crate) async fn initialize_composefs_repository(
5656
/// Ex
5757
/// docker://quay.io/some-image
5858
/// containers-storage:some-image
59+
/// docker-daemon:some-image-id
5960
pub(crate) fn get_imgref(transport: &str, image: &str) -> String {
6061
let img = image.strip_prefix(":").unwrap_or(&image);
6162
let transport = transport.strip_suffix(":").unwrap_or(&transport);
6263

6364
if transport == "registry" {
6465
format!("docker://{img}")
66+
} else if transport == "docker-daemon" {
67+
format!("docker-daemon:{img}")
6568
} else {
6669
format!("{transport}:{img}")
6770
}
@@ -138,4 +141,12 @@ mod tests {
138141
format!("docker://{IMAGE_NAME}")
139142
);
140143
}
144+
145+
#[test]
146+
fn test_get_imgref_docker_daemon_transport() {
147+
assert_eq!(
148+
get_imgref("docker-daemon", IMAGE_NAME),
149+
format!("docker-daemon:{IMAGE_NAME}")
150+
);
151+
}
141152
}

crates/lib/src/cli.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ pub(crate) struct SwitchOpts {
123123
#[clap(long = "soft-reboot")]
124124
pub(crate) soft_reboot: Option<SoftRebootMode>,
125125

126-
/// The transport; e.g. oci, oci-archive, containers-storage. Defaults to `registry`.
126+
/// The transport; e.g. registry, oci, oci-archive, docker-daemon, containers-storage. Defaults to `registry`.
127127
#[clap(long, default_value = "registry")]
128128
pub(crate) transport: String,
129129

crates/ostree-ext/src/container/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ pub enum Transport {
6666
ContainerStorage,
6767
/// Local directory (`dir:`)
6868
Dir,
69+
/// Local Docker daemon (`docker-daemon:`)
70+
DockerDaemon,
6971
}
7072

7173
/// Combination of a remote image reference and transport.
@@ -114,6 +116,7 @@ impl TryFrom<&str> for Transport {
114116
Self::DOCKER_ARCHIVE_STR => Self::DockerArchive,
115117
Self::CONTAINERS_STORAGE_STR => Self::ContainerStorage,
116118
Self::LOCAL_DIRECTORY_STR => Self::Dir,
119+
Self::DOCKER_DAEMON_STR => Self::DockerDaemon,
117120
o => return Err(anyhow!("Unknown transport '{}'", o)),
118121
})
119122
}
@@ -126,6 +129,7 @@ impl Transport {
126129
const CONTAINERS_STORAGE_STR: &'static str = "containers-storage";
127130
const LOCAL_DIRECTORY_STR: &'static str = "dir";
128131
const REGISTRY_STR: &'static str = "registry";
132+
const DOCKER_DAEMON_STR: &'static str = "docker-daemon";
129133

130134
/// Retrieve an identifier that can then be re-parsed from [`Transport::try_from::<&str>`].
131135
pub fn serializable_name(&self) -> &'static str {
@@ -136,6 +140,7 @@ impl Transport {
136140
Transport::DockerArchive => Self::DOCKER_ARCHIVE_STR,
137141
Transport::ContainerStorage => Self::CONTAINERS_STORAGE_STR,
138142
Transport::Dir => Self::LOCAL_DIRECTORY_STR,
143+
Transport::DockerDaemon => Self::DOCKER_DAEMON_STR,
139144
}
140145
}
141146
}
@@ -258,6 +263,7 @@ impl std::fmt::Display for Transport {
258263
Self::OciDir => "oci:",
259264
Self::ContainerStorage => "containers-storage:",
260265
Self::Dir => "dir:",
266+
Self::DockerDaemon => "docker-daemon:",
261267
};
262268
f.write_str(s)
263269
}

crates/ostree-ext/src/container/unencapsulate.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,13 @@ pub(crate) async fn fetch_layer<'a>(
205205
let (blob, driver, size);
206206
let media_type: oci_image::MediaType;
207207
match transport_src {
208-
Transport::ContainerStorage => {
209-
let layer_info = layer_info
210-
.ok_or_else(|| anyhow!("skopeo too old to pull from containers-storage"))?;
208+
// Both containers-storage and docker-daemon store layers uncompressed in their
209+
// local storage, even though the manifest may indicate they are compressed.
210+
// We need to use the actual media type from layer_info to avoid decompression errors.
211+
Transport::ContainerStorage | Transport::DockerDaemon => {
212+
let layer_info = layer_info.ok_or_else(|| {
213+
anyhow!("skopeo too old to pull from containers-storage or docker-daemon")
214+
})?;
211215
let n_layers = layer_info.len();
212216
let layer_blob = layer_info.get(layer_index).ok_or_else(|| {
213217
anyhow!("blobid position {layer_index} exceeds diffid count {n_layers}")

crates/ostree-ext/src/generic_decompress.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ use crate::oci_spec::image as oci_image;
2222
/// TODO: change the skopeo code to shield us from this correctly
2323
const DOCKER_TYPE_LAYER_TAR: &str = "application/vnd.docker.image.rootfs.diff.tar";
2424

25+
/// The Docker MIME type for gzipped layers when stored in docker-daemon.
26+
/// Even though this indicates gzip compression, docker-daemon actually stores
27+
/// the layers uncompressed, so we need to treat this as uncompressed.
28+
const DOCKER_TYPE_LAYER_TAR_GZIP: &str = "application/vnd.docker.image.rootfs.diff.tar.gzip";
29+
2530
/// Extends the `Read` trait with another method to get mutable access to the inner reader
2631
trait ReadWithGetInnerMut: Read + Send + 'static {
2732
fn get_inner_mut(&mut self) -> &mut dyn Read;
@@ -125,6 +130,9 @@ impl Decompressor {
125130
oci_image::MediaType::Other(t) if t.as_str() == DOCKER_TYPE_LAYER_TAR => {
126131
Box::new(TransparentDecompressor(src))
127132
}
133+
oci_image::MediaType::Other(t) if t.as_str() == DOCKER_TYPE_LAYER_TAR_GZIP => {
134+
Box::new(TransparentDecompressor(src))
135+
}
128136
o => anyhow::bail!("Unhandled layer type: {}", o),
129137
};
130138
Ok(Self {
@@ -228,4 +236,17 @@ mod tests {
228236
assert_eq!(e.to_string(), "Unknown frame descriptor".to_string());
229237
drop(d)
230238
}
239+
240+
#[test]
241+
fn test_docker_tar_gzip_media_type_uses_transparent_decompressor() {
242+
// Test that the docker-daemon gzip media type is treated as uncompressed
243+
let data = b"test data";
244+
let media_type = oci_image::MediaType::Other(DOCKER_TYPE_LAYER_TAR_GZIP.to_string());
245+
let mut d = Decompressor::new(&media_type, &data[..]).unwrap();
246+
let mut buf = [0u8; 32];
247+
let n = d.read(&mut buf).unwrap();
248+
assert_eq!(n, data.len());
249+
assert_eq!(&buf[..n], data);
250+
drop(d)
251+
}
231252
}

0 commit comments

Comments
 (0)