...
1# `transport`
2
3[](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/transport)
4
5The [distribution protocol](https://github.com/opencontainers/distribution-spec) is fairly simple, but correctly [implementing authentication](../../../authn/README.md) is **hard**.
6
7This package [implements](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote/transport#New) an [`http.RoundTripper`](https://godoc.org/net/http#RoundTripper)
8that transparently performs:
9* [Token
10Authentication](https://docs.docker.com/registry/spec/auth/token/) and
11* [OAuth2
12Authentication](https://docs.docker.com/registry/spec/auth/oauth/)
13
14for registry clients.
15
16## Raison d'ĂȘtre
17
18> Why not just use the [`docker/distribution`](https://godoc.org/github.com/docker/distribution/registry/client/auth) client?
19
20Great question! Mostly, because I don't want to depend on [`prometheus/client_golang`](https://github.com/prometheus/client_golang).
21
22As 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.
23
24
25
26> Why does it matter if you depend on prometheus? Who cares?
27
28It's generally polite to your downstream to reduce the number of dependencies your package requires:
29
30* Downloading your package is faster, which helps our Australian friends and people on airplanes.
31* There is less code to compile, which speeds up builds and saves the planet from global warming.
32* You reduce the likelihood of inflicting dependency hell upon your consumers.
33* [Tim Hockin](https://twitter.com/thockin/status/958606077456654336) prefers it based on his experience working on Kubernetes, and he's a pretty smart guy.
34
35> Okay, what about [`containerd/containerd`](https://godoc.org/github.com/containerd/containerd/remotes/docker)?
36
37Similar reasons! That ends up pulling in grpc, protobuf, and logrus.
38
39
40
41> Well... what about [`containers/image`](https://godoc.org/github.com/containers/image/docker)?
42
43That just uses the the `docker/distribution` client... and more!
44
45
46
47> Wow, what about this package?
48
49Of course, this package isn't perfect either. `transport` depends on `authn`,
50which in turn depends on docker's config file parsing and handling package,
51which you don't strictly need but almost certainly want if you're going to be
52interacting with a registry.
53
54
55
56*These graphs were generated by
57[`kisielk/godepgraph`](https://github.com/kisielk/godepgraph).*
58
59## Usage
60
61This is heavily used by the
62[`remote`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote)
63package, which implements higher level image-centric functionality, but this
64package is useful if you want to interact directly with the registry to do
65something that `remote` doesn't support, e.g. [to handle with schema 1
66images](https://github.com/google/go-containerregistry/pull/509).
67
68This package also includes some [error
69handling](https://github.com/opencontainers/distribution-spec/blob/60be706c34ee7805bdd1d3d11affec53b0dfb8fb/spec.md#errors)
70facilities in the form of
71[`CheckError`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote/transport#CheckError),
72which will parse the response body into a structured error for unexpected http
73status codes.
74
75Here's a "simple" program that writes the result of
76[listing tags](https://github.com/opencontainers/distribution-spec/blob/60be706c34ee7805bdd1d3d11affec53b0dfb8fb/spec.md#tags)
77for [`gcr.io/google-containers/pause`](https://gcr.io/google-containers/pause)
78to stdout.
79
80```go
81package main
82
83import (
84 "io"
85 "net/http"
86 "os"
87
88 "github.com/google/go-containerregistry/pkg/authn"
89 "github.com/google/go-containerregistry/pkg/name"
90 "github.com/google/go-containerregistry/pkg/v1/remote/transport"
91)
92
93func main() {
94 repo, err := name.NewRepository("gcr.io/google-containers/pause")
95 if err != nil {
96 panic(err)
97 }
98
99 // Fetch credentials based on your docker config file, which is $HOME/.docker/config.json or $DOCKER_CONFIG.
100 auth, err := authn.DefaultKeychain.Resolve(repo.Registry)
101 if err != nil {
102 panic(err)
103 }
104
105 // Construct an http.Client that is authorized to pull from gcr.io/google-containers/pause.
106 scopes := []string{repo.Scope(transport.PullScope)}
107 t, err := transport.New(repo.Registry, auth, http.DefaultTransport, scopes)
108 if err != nil {
109 panic(err)
110 }
111 client := &http.Client{Transport: t}
112
113 // Make the actual request.
114 resp, err := client.Get("https://gcr.io/v2/google-containers/pause/tags/list")
115 if err != nil {
116 panic(err)
117 }
118
119 // Assert that we get a 200, otherwise attempt to parse body as a structured error.
120 if err := transport.CheckError(resp, http.StatusOK); err != nil {
121 panic(err)
122 }
123
124 // Write the response to stdout.
125 if _, err := io.Copy(os.Stdout, resp.Body); err != nil {
126 panic(err)
127 }
128}
129```
View as plain text