Skip to content

Latest commit

 

History

History
 
 

transport

transport

GoDoc

The distribution protocol is fairly simple, but correctly implementing authentication is hard.

This package implements an http.RoundTripper that transparently performs:

for registry clients.

Raison d'être

Why not just use the docker/distribution client?

Great question! Mostly, because I don't want to depend on prometheus/client_golang.

As a performance optimization, that client uses a cache to keep track of a mapping between blob digests and their descriptors. Unfortunately, the cache uses prometheus to track hits and misses, so if you want to use that client you have to pull in all of prometheus, which is pretty large.

docker/distribution

Why does it matter if you depend on prometheus? Who cares?

It's generally polite to your downstream to reduce the number of dependencies your package requires:

  • Downloading your package is faster, which helps our Australian friends and people on airplanes.
  • There is less code to compile, which speeds up builds and saves the planet from global warming.
  • You reduce the likelihood of inflicting dependency hell upon your consumers.
  • Tim Hockin prefers it based on his experience working on Kubernetes, and he's a pretty smart guy.

Okay, what about containerd/containerd?

Similar reasons! That ends up pulling in grpc, protobuf, and logrus.

containerd/containerd

Well... what about containers/image?

That just uses the the docker/distribution client... and more!

containers/image

Wow, what about this package?

Of course, this package isn't perfect either. transport depends on authn, which in turn depends on docker's config file parsing and handling package, which you don't strictly need but almost certainly want if you're going to be interacting with a registry.

google/go-containerregistry

These graphs were generated by kisielk/godepgraph.

Usage

This is heavily used by the remote package, which implements higher level image-centric functionality, but this package is useful if you want to interact directly with the registry to do something that remote doesn't support, e.g. to handle with schema 1 images.

This package also includes some error handling facilities in the form of CheckError, which will parse the response body into a structured error for unexpected http status codes.

Here's a "simple" program that writes the result of listing tags for gcr.io/google-containers/pause to stdout.

package main

import (
	"io"
	"net/http"
	"os"

	"github.com/google/go-containerregistry/pkg/authn"
	"github.com/google/go-containerregistry/pkg/name"
	"github.com/google/go-containerregistry/pkg/v1/remote/transport"
)

func main() {
	repo, err := name.NewRepository("gcr.io/google-containers/pause")
	if err != nil {
		panic(err)
	}

	// Fetch credentials based on your docker config file, which is $HOME/.docker/config.json or $DOCKER_CONFIG.
	auth, err := authn.DefaultKeychain.Resolve(repo.Registry)
	if err != nil {
		panic(err)
	}

	// Construct an http.Client that is authorized to pull from gcr.io/google-containers/pause.
	scopes := []string{repo.Scope(transport.PullScope)}
	t, err := transport.New(repo.Registry, auth, http.DefaultTransport, scopes)
	if err != nil {
		panic(err)
	}
	client := &http.Client{Transport: t}

	// Make the actual request.
	resp, err := client.Get("https://gcr.io/v2/google-containers/pause/tags/list")
	if err != nil {
		panic(err)
	}

	// Assert that we get a 200, otherwise attempt to parse body as a structured error.
	if err := transport.CheckError(resp, http.StatusOK); err != nil {
		panic(err)
	}

	// Write the response to stdout.
	if _, err := io.Copy(os.Stdout, resp.Body); err != nil {
		panic(err)
	}
}