Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add skipping pulls by default if present, add --force flag and return on pull error #45

Merged
merged 1 commit into from
Aug 19, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 82 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use anyhow::{anyhow, Result};
use clap::{App, Arg};
use docker_api::api::{ContainerCreateOpts, PullOpts, RegistryAuth, RmContainerOpts};
use docker_api::Images;
use futures_util::{StreamExt, TryStreamExt};
use podman_api::api::Images as PodmanImages;
use podman_api::opts::ContainerCreateOpts as PodmanContainerCreateOpts;
use podman_api::opts::PullOpts as PodmanPullOpts;
use podman_api::opts::RegistryAuth as PodmanRegistryAuth;
Expand Down Expand Up @@ -33,6 +35,8 @@ pub struct Config {
username: String,
// Password for signing into a private registry
password: String,
// Force a pull even if the image is present locally
force_pull: bool,
}

pub fn get_args() -> Result<Config> {
Expand Down Expand Up @@ -96,12 +100,21 @@ pub fn get_args() -> Result<Config> {
.long("log-level")
.default_value("debug"),
)
.arg(
Arg::with_name("force-pull")
.value_name("FORCE-PULL")
.help("Force a pull even if the image is present locally")
.takes_value(false)
.long("force-pull")
.short("f")
)
.get_matches();

let image = matches.value_of("image").unwrap().to_string();
let download_path = matches.value_of("download-path").unwrap().to_string();
let content_path = matches.value_of("content-path").unwrap().to_string();
let write_to_stdout = matches.is_present("write-to-stdout");
let force_pull = matches.is_present("force-pull");
let log_level = matches.value_of("log-level").unwrap().to_string();
// TODO (tyslaton): Need to come up with a way for this to be extracted from the docker config to be more secure locally.
let username = matches.value_of("username").unwrap().to_string();
Expand All @@ -121,6 +134,7 @@ pub fn get_args() -> Result<Config> {
log_level,
username,
password,
force_pull,
})
}

Expand Down Expand Up @@ -166,18 +180,22 @@ pub async fn run(config: Config) -> Result<()> {
.build();
let pull_opts = PullOpts::builder().image(repo).tag(tag).auth(auth).build();

let images = docker.images();
let mut stream = images.pull(&pull_opts);

while let Some(pull_result) = stream.next().await {
match pull_result {
Ok(output) => {
debug!("🔧 {:?}", output);
}
Err(e) => {
error!("❌ pulling image {}", e);
let present_locally = image_present_locally_docker(docker.images(), image.image).await;
if config.force_pull || !present_locally {
let images = docker.images();
let mut stream = images.pull(&pull_opts);
while let Some(pull_result) = stream.next().await {
match pull_result {
Ok(output) => {
debug!("🔧 {:?}", output);
}
Err(e) => {
return Err(anyhow!("❌ error pulling image: {}", e));
exdx marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
} else {
debug!("✅ Skipping the pull process as the image was found locally");
}
} else {
let auth = PodmanRegistryAuth::builder()
Expand All @@ -188,18 +206,24 @@ pub async fn run(config: Config) -> Result<()> {
.reference(config.image.clone().trim())
.auth(auth)
.build();
let images = rt.podman.as_ref().unwrap().images();
let mut stream = images.pull(&pull_opts);

while let Some(pull_result) = stream.next().await {
match pull_result {
Ok(output) => {
debug!("🔧 {:?}", output);
}
Err(e) => {
error!("❌ pulling image: {}", e);
let present_locally =
image_present_locally_podman(rt.podman.as_ref().unwrap().images(), image.image).await;
if config.force_pull || !present_locally {
let images = rt.podman.as_ref().unwrap().images();
let mut stream = images.pull(&pull_opts);
while let Some(pull_result) = stream.next().await {
match pull_result {
Ok(output) => {
debug!("🔧 {:?}", output);
}
Err(e) => {
return Err(anyhow!("❌ error pulling image: {}", e));
}
}
}
} else {
debug!("✅ Skipping the pull process as the image was found locally");
}
}

Expand Down Expand Up @@ -270,7 +294,7 @@ pub async fn run(config: Config) -> Result<()> {
if let Some(docker) = &rt.docker {
let delete_opts = RmContainerOpts::builder().force(true).build();
if let Err(e) = docker.containers().get(&*id).remove(&delete_opts).await {
error!("❌ cleaning up container {}", e);
error!("❌ error cleaning up container {}", e);
Ok(())
} else {
debug!("📦 Cleaned up container {:?} successfully", id);
Expand All @@ -296,3 +320,41 @@ pub async fn run(config: Config) -> Result<()> {
}
}
}

// TODO (tyslaton): Refactor image_present_locally functions to be a single function
pub async fn image_present_locally_docker(images: Images, search_for_image: String) -> bool {
match images.list(&Default::default()).await {
Ok(images) => {
for image in images {
for repo_tag in image.repo_tags {
for tag in repo_tag {
print!("{} | {}", tag, search_for_image);
if tag == search_for_image {
return true;
}
}
}
}
}
Err(e) => error!("❌ error occurred while searching for image locally! {}", e),
}
return false;
}

pub async fn image_present_locally_podman(images: PodmanImages, search_for_image: String) -> bool {
match images.list(&Default::default()).await {
Ok(images) => {
for image in images {
for repo_tag in image.repo_tags {
for tag in repo_tag {
exdx marked this conversation as resolved.
Show resolved Hide resolved
if tag == search_for_image {
return true;
}
}
}
}
}
Err(e) => error!("❌ error occurred while searching for image locally! {}", e),
}
return false;
}