summaryrefslogtreecommitdiffstats
path: root/pkg/v1/remote/transport/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/v1/remote/transport/README.md')
-rw-r--r--pkg/v1/remote/transport/README.md129
1 files changed, 129 insertions, 0 deletions
diff --git a/pkg/v1/remote/transport/README.md b/pkg/v1/remote/transport/README.md
new file mode 100644
index 0000000..bd4d957
--- /dev/null
+++ b/pkg/v1/remote/transport/README.md
@@ -0,0 +1,129 @@
+# `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)
+ }
+}
+```