Skip to content

feat: add support for private docker registries #40

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

Merged
merged 2 commits into from
Aug 14, 2022
Merged
Show file tree
Hide file tree
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
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ By default, dcp will copy content to the current directory `.`. For example, let
try issuing the following command:

```
$ dcp tyslaton/sample-catalog:v0.0.4 -p configs
$ dcp tyslaton/sample-catalog:v0.0.4 -c configs
```

This command will copy the `configs` directory (specified via the `-p` flag) from the image to the current directory.
This command will copy the `configs` directory (specified via the `c` flag) from the image to the current directory.

For further configuration, lets try:

```
$ dcp tyslaton/sample-catalog:v0.0.4 -d output -p configs
$ dcp tyslaton/sample-catalog:v0.0.4 -d output -c configs
```

This command pulls down the requested image, only extracting
Expand All @@ -76,9 +76,22 @@ locally (specified via the `-d` flag).
Another example, for copying only the manifests directory:

```
$ dcp quay.io/tflannag/bundles:resolveset-v0.0.2 -p manifests
$ dcp quay.io/tflannag/bundles:resolveset-v0.0.2 -c manifests
```

Lastly, we can reference a private registry by providing a username
and password (specified via the `-u` and `-p` flags).

```
$ dcp quay.io/tyslaton/sample-catalog-private:latest -u <username> -p <password>
```

**Note**: This serves as a convenient way to connect to private
registries but is insecure locally as your credentials are saved in
your shell's history. If you would like to remain completely secure then
login via `<container_runtime> login` and pull the image locally. `dcp`
will then be able to notice the image locally pulled and process it.

## Testing

If you would like to run the test suite, you just need to run the standard cargo command. This will run all relevant
Expand Down
44 changes: 41 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use anyhow::{anyhow, Result};
use clap::{App, Arg};
use docker_api::api::{ContainerCreateOpts, PullOpts, RmContainerOpts};
use docker_api::api::{ContainerCreateOpts, PullOpts, RegistryAuth, RmContainerOpts};
use futures_util::{StreamExt, TryStreamExt};
use podman_api::opts::ContainerCreateOpts as PodmanContainerCreateOpts;
use podman_api::opts::PullOpts as PodmanPullOpts;
use podman_api::opts::RegistryAuth as PodmanRegistryAuth;
use std::path::PathBuf;
use tar::Archive;

Expand All @@ -28,6 +29,10 @@ pub struct Config {
write_to_stdout: bool,
// What level of logs to output
log_level: String,
// Username for singing into a private registry
username: String,
// Password for signing into a private registry
password: String,
}

pub fn get_args() -> Result<Config> {
Expand All @@ -53,7 +58,7 @@ pub fn get_args() -> Result<Config> {
Arg::with_name("content-path")
.value_name("CONTENT-PATH")
.help("Where in the container filesystem the content to extract is")
.short("p")
.short("c")
.default_value("/")
.long("content-path"),
)
Expand All @@ -65,6 +70,24 @@ pub fn get_args() -> Result<Config> {
.short("w")
.long("write-to-stdout"),
)
.arg(
Arg::with_name("username")
.value_name("USERNAME")
.help("Username used for singing into a private registry.")
.short("u")
.long("username")
.default_value(""),

)
.arg(
Arg::with_name("password")
.value_name("PASSWORD")
.help("Password used for signing into a private registry. * WARNING *: Writing credentials to your terminal is risky. Be sure you are okay with them showing up in your history")
.short("p")
.long("password")
.default_value(""),

)
.arg(
Arg::with_name("log-level")
.value_name("LOG-LEVEL")
Expand All @@ -80,6 +103,9 @@ pub fn get_args() -> Result<Config> {
let content_path = matches.value_of("content-path").unwrap().to_string();
let write_to_stdout = matches.is_present("write-to-stdout");
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();
let password = matches.value_of("password").unwrap().to_string();

if write_to_stdout {
return Err(anyhow!(
Expand All @@ -93,6 +119,8 @@ pub fn get_args() -> Result<Config> {
content_path,
write_to_stdout,
log_level,
username,
password,
})
}

Expand Down Expand Up @@ -132,7 +160,12 @@ pub async fn run(config: Config) -> Result<()> {
};

if let Some(docker) = &rt.docker {
let pull_opts = PullOpts::builder().image(repo).tag(tag).build();
let auth = RegistryAuth::builder()
.username(config.username)
.password(config.password)
.build();
let pull_opts = PullOpts::builder().image(repo).tag(tag).auth(auth).build();

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

Expand All @@ -147,8 +180,13 @@ pub async fn run(config: Config) -> Result<()> {
}
}
} else {
let auth = PodmanRegistryAuth::builder()
.username(config.username)
.password(config.password)
.build();
let pull_opts = PodmanPullOpts::builder()
.reference(config.image.clone().trim())
.auth(auth)
.build();
let images = rt.podman.as_ref().unwrap().images();
let mut stream = images.pull(&pull_opts);
Expand Down