...

Text file src/github.com/google/go-containerregistry/pkg/v1/remote/transport/README.md

Documentation: github.com/google/go-containerregistry/pkg/v1/remote/transport

     1# `transport`
     2
     3[![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)
     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![docker/distribution](../../../../images/docker.dot.svg)
    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![containerd/containerd](../../../../images/containerd.dot.svg)
    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![containers/image](../../../../images/containers.dot.svg)
    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![google/go-containerregistry](../../../../images/ggcr.dot.svg)
    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