This project creates a small CLI that only pulls OCI container images. The project goal was to create a feature-capable and robust image puller with a minimalist code base.
The project can be used as a CLI, or, as a library.
Table of Contents
After git cloning the project, build the CLI:
make imgpull
This creates the binary imgpull
in the bin
directory. Verify:
bin/imgpull --version
Success looks like (date and version will differ):
imgpull version: 1.1.0 build date: 2024-12-14T00:37:34.83Z
Two positional parameters are required to pull an image tarball: 1) an image reference and, 2) a tar file:
bin/imgpull [image ref] [tar file]
Example:
bin/imgpull docker.io/hello-world:latest hello-world-latest.tar
That's the simplest use case! Several options are supported:
-o|--os [operating system]
Specifies the operating system of the image to pull. Most images in public registries are multi-platform images. Usually you want an image for your operating system. Therefore if you omit this, the CLI uses runtime.GOOS
to determine your OS and will pull the image for that OS. Generally, this will be used in conjunction with the --arch
param.
Example:
bin/imgpull docker.io/hello-world:latest hello-world-latest.tar --os linux --arch amd64
-a|--arch [architecture]
Same idea as --os
except specifies the architecture. If omitted, the code uses runtime.GOARCH
to determine your architecture and will pull the image for that architecture. Generally, this will be used in conjunction with the --os
param.
Example:
bin/imgpull docker.io/hello-world:latest hello-world-latest.tar --os linux --arch amd64
-n|--ns [namespace]
Specifies a namespace. The word namespace in this context refers to a registry host name like docker.io
, registry.k8s.io
, quay.io
, etc. This supports pulling from mirroring / pull-through registries and is intended to pull the same way containerd
does when you've configured containerd
mirroring.
Example:
bin/imgpull localhost:8080/curl/curl:8.10.1 quay.io-curl-8.1.0.1.tar --ns quay.io
In this scenario, you have a pull-through or mirroring registry running on localhost:8080
. When the CLI interacts with the registry via the V2 OCI Distribution Server REST API, it appends the namespace to the various REST API calls as a query param. In other words, internally the CLI makes calls like:
https://localhost:8080/v2/curl/curl/manifests/8.10.1?ns=quay.io
Note the query param: ?ns=quay.io
. The pull-through mirroring registry running on localhost:8080
will check to see if quay.io/curl/curl:8.10.1
is cached and if not, will pull and cache from the upstream quay.io
registry. (Or the mirror will fail it only supports mirroring, and the desired image is not already cached.)
For a highly configurable, performant, and feature-rich caching pull-through OCI distribution server that supports this functionality, see: my distribution server: https://github.com/aceeric/ociregistry.
-u|--user [username]
-p|--password [password]
Specifies the username and password for basic auth.
Example:
bin/imgpull docker.io/hello-world:latest hello-world-latest.tar\
--user jqpubli --password mypass
-s|--scheme [scheme]
Specifies the scheme. The CLI defaults to https
. Valid values are http
and https
.
Example:
bin/imgpull my.inhouse.http.registry/hello-world:latest hello-world-latest.tar\
--scheme http
The example pulls from a registry over HTTP, rather than HTTPS.
-c|--cert [tls cert]
-k|--key [tls key]
Configures mTLS. By default, the CLI performs 1-way TLS using the OS trust store. You use these options to configure mTLS. The option values are paths to a PEM-encoded cert and key.
Example:
bin/imgpull my.mtls.registry/hello-world:latest hello-world-latest.tar\
--cert /path/to/cert.pem --key /path/to/key.pem
-x|--cacert [tls ca cert]
Configures verification of the server cert using a specified CA. By default, server certs are validated using the OS trust store. This parameter supports cases where the server presents certs that are not signed by the OS trust store. The option value is a path to a PEM-encoded CA or CA bundle file.
Example:
bin/imgpull my.private.registry/hello-world:latest hello-world-latest.tar\
--ca /path/to/ca.pem
-i|--insecure
Does not verify the server certs presented by the OCI distribution server if the connection to the server is over HTTPS (the default.) Use this option when the registry presents a server cert that is not signed by the OS trust store, and you are unable to provide a CA using the --cacert
option.
Example:
bin/imgpull my.private.registry/hello-world:latest hello-world-latest.tar --insecure
-m|--manifest [type]
Displays the manifest to the console rather than downloading the image tarball. Valid values are list
for the image list manifest, and image
for the image manifest. If you supply this param then the tarball positional param is ignored and can be omitted.
Example:
bin/imgpull docker.io/hello-world:latest --manifest list
Not every image repository provides an image list manifest. If the image is not multi-platform then an image list manifest won't be available. In that case if you ask for an image list manifest (and it's not provided by the server) the CLI will display an error message to this effect.
-v|--version
Prints the version and exits.
Example:
bin/imgpull --version
-h|--help
Prints the help and exits.
Example:
bin/imgpull --help
--parsed
This is a developer troubleshooting option that prints the parsed command line without acting on the params, and then exits.
Example:
bin/imgpull my.inhouse.http.registry/hello-world:latest hello-world-latest.tar --cacert /path/to/ca.pem --parsed
- Pull the image for
linux/amd64
tohello-world.tar
in the working directory:bin/imgpull docker.io/hello-world:latest ./hello-world.tar -o linux -a amd64
- Show the image manifest for the system OS and architecture
bin/imgpull docker.io/hello-world:latest --manifest image
- Pull with mTLS
bin/imgpull docker.io/hello-world:latest --cert ~/mycert.pem --key ~/mykey.pem
- Pull with a custom CA
bin/imgpull docker.io/hello-world:latest --cacert ~/myca.pem
- Pull without server cert verification
bin/imgpull docker.io/hello-world:latest --insecure
- Pull with basic auth
bin/imgpull docker.io/hello-world:latest --user foobar --password frobozz
- Pull using http
bin/imgpull localhost:8080/hello-world:latest --scheme http
- Pull from a mirror
bin/imgpull localhost:8080/hello-world:latest --ns docker.io
The project was designed for the code to be easily used as a library. The two primary abstractions are:
PullerOpts
- Configures how you'll interact with the upstream distribution server.Puller
- Contains all the logic to actually pull the image.
For example:
package main
import (
"fmt"
"imgpull/pkg/imgpull"
)
func main() {
image := "docker.io/hello-world:latest"
puller, err := imgpull.NewPullerWith(imgpull.NewPullerOpts(image))
if err != nil {
fmt.Println(err)
return
} else if err := puller.PullTar("./hello-world.tar"); err != nil {
fmt.Println(err)
}
}
The NewPullerOpts
function in the example above creates aPullerOpts
struct as follows:
- Uses the image URL you provide.
- Defaults the scheme to
https
. - Validates the upstream distribution server cert using the host OS trust store.
- Tries to pull an image matching the OS and architecture for your system.
This is the most common use case.
Or, suppose you're pulling from a private registry that presents a cert signed by a CA that isn't included in the OS trust store. You can initialize a PullerOpts
struct directly:
...
opts := imgpull.PullerOpts{
Url: "docker.io/hello-world:latest",
Scheme: "https",
OStype: "linux",
ArchType: "amd64",
CaCert: "/path/to/ca.pem",
}
p, err := imgpull.NewPullerWith(opts)
You can see that the PullerOpts
struct is the key to configuring the puller to interface with the upstream registry. In fact the CLI options directly map to the fields in the PullerOpts
struct as shown by the table below.
See the Examples directory for a couple of examples on using the code as a library.
Struct Member | Command line option | Setting via the struct | Setting via the CLI |
---|---|---|---|
Url |
Positional param one | Url: "docker.io/hello-world:latest" |
imgpull docker.io/hello-world:latest |
Scheme |
-s|--scheme [scheme] |
Scheme: "http" |
--scheme http |
OStype |
-o|--os [operating system] |
OStype: "linux" |
--os linux |
ArchType |
-a|--arch [architecture] |
ArchType: "amd64" |
--arch amd64 |
Username |
-u|--user [username] |
Username: "foo" |
--user foo |
Password |
-p|--password [password] |
Password: "bar" |
--password bar |
TlsCert |
-c|--cert [tls cert] |
TlsCert: "/path/to/client-cert.pem" |
--cert /path/to/client-cert.pem |
TlsKey |
-k|--key [tls key] |
TlsKey: "/path/to/client-key.pem" |
--key /path/to/client-key.pem |
CaCert |
-x|--cacert [tls ca cert] |
CaCert: "/path/to/ca-cert.pem" |
--cacert /path/to/ca-cert.pem |
Insecure |
-i|--insecure |
Insecure: true |
--insecure |
Namespace |
-n|--ns [namespace] |
Namespace: "docker.io" |
--ns docker.io |