# `transport` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/transport?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/transport) The [distribution protocol](https://github.com/opencontainers/distribution-spec) is fairly simple, but correctly [implementing authentication](../../../authn/README.md) is **hard**. This package [implements](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote/transport#New) an [`http.RoundTripper`](https://godoc.org/net/http#RoundTripper) that transparently performs: * [Token Authentication](https://docs.docker.com/registry/spec/auth/token/) and * [OAuth2 Authentication](https://docs.docker.com/registry/spec/auth/oauth/) for registry clients. ## Raison d'ĂȘtre > Why not just use the [`docker/distribution`](https://godoc.org/github.com/docker/distribution/registry/client/auth) client? Great question! Mostly, because I don't want to depend on [`prometheus/client_golang`](https://github.com/prometheus/client_golang). As a performance optimization, that client uses [a cache](https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/registry/client/repository.go#L173) to keep track of a mapping between blob digests and their [descriptors](https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/blobs.go#L57-L86). Unfortunately, the cache [uses prometheus](https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/registry/storage/cache/cachedblobdescriptorstore.go#L44) 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](../../../../images/docker.dot.svg) > 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](https://twitter.com/thockin/status/958606077456654336) prefers it based on his experience working on Kubernetes, and he's a pretty smart guy. > Okay, what about [`containerd/containerd`](https://godoc.org/github.com/containerd/containerd/remotes/docker)? Similar reasons! That ends up pulling in grpc, protobuf, and logrus. ![containerd/containerd](../../../../images/containerd.dot.svg) > Well... what about [`containers/image`](https://godoc.org/github.com/containers/image/docker)? That just uses the the `docker/distribution` client... and more! ![containers/image](../../../../images/containers.dot.svg) > 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](../../../../images/ggcr.dot.svg) *These graphs were generated by [`kisielk/godepgraph`](https://github.com/kisielk/godepgraph).* ## Usage This is heavily used by the [`remote`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/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](https://github.com/google/go-containerregistry/pull/509). This package also includes some [error handling](https://github.com/opencontainers/distribution-spec/blob/60be706c34ee7805bdd1d3d11affec53b0dfb8fb/spec.md#errors) facilities in the form of [`CheckError`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote/transport#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](https://github.com/opencontainers/distribution-spec/blob/60be706c34ee7805bdd1d3d11affec53b0dfb8fb/spec.md#tags) for [`gcr.io/google-containers/pause`](https://gcr.io/google-containers/pause) to stdout. ```go 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) } } ```