Skip to content

Commit 9e7511f

Browse files
committed
lib: add support for podman container runtime
1 parent e2eefae commit 9e7511f

File tree

4 files changed

+202
-32
lines changed

4 files changed

+202
-32
lines changed

Cargo.lock

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ tar = "0.4"
1414
log = "0.4"
1515
pretty_env_logger = "0.4"
1616
anyhow = "1.0"
17+
podman-api = "0.4"
1718

1819
[dev-dependencies]
1920
predicates = "2.1.1"

src/lib.rs

Lines changed: 107 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
use anyhow::{anyhow, Result};
22
use clap::{App, Arg};
33
use docker_api::api::{ContainerCreateOpts, PullOpts, RmContainerOpts};
4-
use docker_api::Docker;
54
use futures_util::{StreamExt, TryStreamExt};
5+
use podman_api::opts::ContainerCreateOpts as PodmanContainerCreateOpts;
6+
use podman_api::opts::PullOpts as PodmanPullOpts;
67
use std::path::PathBuf;
78
use tar::Archive;
89

910
mod image;
11+
mod runtime;
1012

1113
extern crate pretty_env_logger;
1214
#[macro_use]
1315
extern crate log;
1416

15-
const DOCKER_SOCKET: &str = "unix:///var/run/docker.sock";
1617
pub const VERSION: &str = "0.2.1";
1718

1819
#[derive(Debug)]
@@ -96,6 +97,7 @@ pub fn get_args() -> Result<Config> {
9697
}
9798

9899
/// Run runs a sequence of events with the provided image
100+
/// Run supports copying container filesystems running on both the docker and podman runtimes
99101
/// 1. Pull down the image
100102
/// 2. Create a container, receiving the container id as a response
101103
/// 3. Copy the container content to the specified directory
@@ -105,8 +107,6 @@ pub async fn run(config: Config) -> Result<()> {
105107
.parse_filters(&config.log_level.clone())
106108
.init();
107109

108-
let docker = Docker::new(DOCKER_SOCKET)?;
109-
110110
let image = image::Image {
111111
image: config.image.clone(),
112112
};
@@ -125,42 +125,97 @@ pub async fn run(config: Config) -> Result<()> {
125125
}
126126
}
127127

128-
let pull_opts = PullOpts::builder().image(repo).tag(tag).build();
129-
let images = docker.images();
130-
let mut stream = images.pull(&pull_opts);
128+
let mut rt = runtime::Runtime {
129+
docker: &None,
130+
podman: &None,
131+
};
132+
match runtime::set() {
133+
Some(docker) => rt = docker,
134+
Some(podman) => rt = podman,
135+
None => {
136+
error!("❌ neither docker or podman sockets were found running on this host");
137+
return Err(anyhow!("no valid container runtime"));
138+
}
139+
};
140+
141+
if let Some(docker) = rt.docker {
142+
let pull_opts = PullOpts::builder().image(repo).tag(tag).build();
143+
let images = docker.images();
144+
let mut stream = images.pull(&pull_opts);
131145

132-
while let Some(pull_result) = stream.next().await {
133-
match pull_result {
134-
Ok(output) => {
135-
debug!("🔧 {:?}", output);
146+
while let Some(pull_result) = stream.next().await {
147+
match pull_result {
148+
Ok(output) => {
149+
debug!("🔧 {:?}", output);
150+
}
151+
Err(e) => {
152+
error!("❌ {}", e);
153+
}
136154
}
137-
Err(e) => {
138-
error!("❌ {}", e);
155+
}
156+
} else {
157+
// TODO set tag
158+
let pull_opts = PodmanPullOpts::builder().reference(repo).build();
159+
let images = rt.podman.as_ref().unwrap().images();
160+
let mut stream = images.pull(&pull_opts);
161+
162+
while let Some(pull_result) = stream.next().await {
163+
match pull_result {
164+
Ok(output) => {
165+
debug!("🔧 {:?}", output);
166+
}
167+
Err(e) => {
168+
error!("❌ {}", e);
169+
}
139170
}
140171
}
141172
}
142173

143174
// note(tflannag): Use a "dummy" command "FROM SCRATCH" container images.
144175
let cmd = vec![""];
145-
let create_opts = ContainerCreateOpts::builder(config.image.clone())
146-
.cmd(&cmd)
147-
.build();
148-
let container = docker.containers().create(&create_opts).await?;
149-
let id = container.id();
150-
debug!("📦 Created container with id: {:?}", id);
176+
let mut id = String::new();
177+
if let Some(docker) = rt.docker {
178+
let create_opts = ContainerCreateOpts::builder(config.image.clone())
179+
.cmd(&cmd)
180+
.build();
181+
let container = docker.containers().create(&create_opts).await?;
182+
id = container.id().to_string();
183+
debug!("📦 Created container with id: {:?}", id);
184+
} else {
185+
let create_opts = PodmanContainerCreateOpts::builder()
186+
.image(config.image.clone())
187+
.command(&cmd)
188+
.build();
189+
let container = rt.podman.as_ref().unwrap().containers().create(&create_opts).await?;
190+
id = container.id;
191+
debug!("📦 Created container with id: {:?}", id);
192+
}
151193

152194
let mut content_path = PathBuf::new();
153195
content_path.push(&config.content_path);
154196

155197
let mut download_path = PathBuf::new();
156198
download_path.push(&config.download_path);
157199

158-
let bytes = docker
159-
.containers()
160-
.get(&*id)
161-
.copy_from(&content_path)
162-
.try_concat()
163-
.await?;
200+
let mut bytes = Vec::new();
201+
if let Some(docker) = rt.docker {
202+
bytes = docker
203+
.containers()
204+
.get(&*id)
205+
.copy_from(&content_path)
206+
.try_concat()
207+
.await?;
208+
} else {
209+
bytes = rt
210+
.podman
211+
.as_ref()
212+
.unwrap()
213+
.containers()
214+
.get(&*id)
215+
.copy_from(&content_path)
216+
.try_concat()
217+
.await?;
218+
}
164219

165220
let mut archive = Archive::new(&bytes[..]);
166221
if config.write_to_stdout {
@@ -174,12 +229,32 @@ pub async fn run(config: Config) -> Result<()> {
174229
download_path.display()
175230
);
176231

177-
let delete_opts = RmContainerOpts::builder().force(true).build();
178-
if let Err(e) = docker.containers().get(&*id).remove(&delete_opts).await {
179-
error!("❌ Error cleaning up container {}", e);
232+
if let Some(docker) = rt.docker {
233+
let delete_opts = RmContainerOpts::builder().force(true).build();
234+
if let Err(e) = docker.containers().get(&*id).remove(&delete_opts).await {
235+
error!("❌ Error cleaning up container {}", e);
236+
Ok(())
237+
} else {
238+
debug!("📦 Cleaned up container {:?} successfully", id);
239+
Ok(())
240+
}
241+
} else {
242+
match rt
243+
.podman
244+
.as_ref()
245+
.unwrap()
246+
.containers()
247+
.prune(&Default::default())
248+
.await
249+
{
250+
Ok(_) => {
251+
debug!("📦 Cleaned up container {:?} successfully", id);
252+
Ok(())
253+
}
254+
Err(e) => {
255+
eprintln!("{}", e);
256+
Ok(())
257+
}
258+
}
180259
}
181-
182-
debug!("📦 Cleaned up container {:?} successfully", id);
183-
184-
Ok(())
185260
}

src/runtime.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use docker_api::Docker;
2+
use podman_api::Podman;
3+
4+
const DOCKER_SOCKET: &str = "unix:///var/run/docker.sock";
5+
const PODMAN_SOCKET: &str = "unix:///run/podman/podman.sock";
6+
7+
pub enum RuntimeOptions {
8+
Docker(docker_api::Docker),
9+
Podman(podman_api::Podman),
10+
}
11+
12+
pub struct Runtime<'a> {
13+
pub docker: &'a Option<docker_api::Docker>,
14+
pub podman: &'a Option<podman_api::Podman>,
15+
}
16+
17+
pub fn set() -> Option<Runtime<'static>> {
18+
match Docker::new(DOCKER_SOCKET) {
19+
Ok(Docker) => return Some(Runtime{docker: &Some(Docker), podman: &None}),
20+
Err(e) => {
21+
// Fallback to podman config
22+
debug!("🔧 docker socket not found: falling back to podman configuration");
23+
match Podman::new(PODMAN_SOCKET) {
24+
Ok(Podman) => return Some(Runtime{docker: &None, podman: &Some(Podman)}),
25+
Err(e) => {
26+
error!("❌ neither docker or podman sockets were found running on this host: {}", e);
27+
return None;
28+
}
29+
}
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)