github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/registry/client/client.go (about) 1 package client 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "strings" 8 9 manifesttypes "github.com/docker/cli/cli/manifest/types" 10 "github.com/docker/cli/cli/trust" 11 "github.com/docker/distribution" 12 "github.com/docker/distribution/reference" 13 distributionclient "github.com/docker/distribution/registry/client" 14 "github.com/docker/docker/api/types" 15 registrytypes "github.com/docker/docker/api/types/registry" 16 "github.com/opencontainers/go-digest" 17 "github.com/pkg/errors" 18 "github.com/sirupsen/logrus" 19 ) 20 21 // RegistryClient is a client used to communicate with a Docker distribution 22 // registry 23 type RegistryClient interface { 24 GetManifest(ctx context.Context, ref reference.Named) (manifesttypes.ImageManifest, error) 25 GetManifestList(ctx context.Context, ref reference.Named) ([]manifesttypes.ImageManifest, error) 26 MountBlob(ctx context.Context, source reference.Canonical, target reference.Named) error 27 PutManifest(ctx context.Context, ref reference.Named, manifest distribution.Manifest) (digest.Digest, error) 28 GetTags(ctx context.Context, ref reference.Named) ([]string, error) 29 } 30 31 // NewRegistryClient returns a new RegistryClient with a resolver 32 func NewRegistryClient(resolver AuthConfigResolver, userAgent string, insecure bool) RegistryClient { 33 return &client{ 34 authConfigResolver: resolver, 35 insecureRegistry: insecure, 36 userAgent: userAgent, 37 } 38 } 39 40 // AuthConfigResolver returns Auth Configuration for an index 41 type AuthConfigResolver func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig 42 43 // PutManifestOptions is the data sent to push a manifest 44 type PutManifestOptions struct { 45 MediaType string 46 Payload []byte 47 } 48 49 type client struct { 50 authConfigResolver AuthConfigResolver 51 insecureRegistry bool 52 userAgent string 53 } 54 55 // ErrBlobCreated returned when a blob mount request was created 56 type ErrBlobCreated struct { 57 From reference.Named 58 Target reference.Named 59 } 60 61 func (err ErrBlobCreated) Error() string { 62 return fmt.Sprintf("blob mounted from: %v to: %v", 63 err.From, err.Target) 64 } 65 66 // ErrHTTPProto returned if attempting to use TLS with a non-TLS registry 67 type ErrHTTPProto struct { 68 OrigErr string 69 } 70 71 func (err ErrHTTPProto) Error() string { 72 return err.OrigErr 73 } 74 75 var _ RegistryClient = &client{} 76 77 // MountBlob into the registry, so it can be referenced by a manifest 78 func (c *client) MountBlob(ctx context.Context, sourceRef reference.Canonical, targetRef reference.Named) error { 79 repoEndpoint, err := newDefaultRepositoryEndpoint(targetRef, c.insecureRegistry) 80 if err != nil { 81 return err 82 } 83 repo, err := c.getRepositoryForReference(ctx, targetRef, repoEndpoint) 84 if err != nil { 85 return err 86 } 87 lu, err := repo.Blobs(ctx).Create(ctx, distributionclient.WithMountFrom(sourceRef)) 88 switch err.(type) { 89 case distribution.ErrBlobMounted: 90 logrus.Debugf("mount of blob %s succeeded", sourceRef) 91 return nil 92 case nil: 93 default: 94 return errors.Wrapf(err, "failed to mount blob %s to %s", sourceRef, targetRef) 95 } 96 lu.Cancel(ctx) 97 logrus.Debugf("mount of blob %s created", sourceRef) 98 return ErrBlobCreated{From: sourceRef, Target: targetRef} 99 } 100 101 // PutManifest sends the manifest to a registry and returns the new digest 102 func (c *client) PutManifest(ctx context.Context, ref reference.Named, manifest distribution.Manifest) (digest.Digest, error) { 103 repoEndpoint, err := newDefaultRepositoryEndpoint(ref, c.insecureRegistry) 104 if err != nil { 105 return digest.Digest(""), err 106 } 107 108 repo, err := c.getRepositoryForReference(ctx, ref, repoEndpoint) 109 if err != nil { 110 return digest.Digest(""), err 111 } 112 113 manifestService, err := repo.Manifests(ctx) 114 if err != nil { 115 return digest.Digest(""), err 116 } 117 118 _, opts, err := getManifestOptionsFromReference(ref) 119 if err != nil { 120 return digest.Digest(""), err 121 } 122 123 dgst, err := manifestService.Put(ctx, manifest, opts...) 124 return dgst, errors.Wrapf(err, "failed to put manifest %s", ref) 125 } 126 127 func (c *client) GetTags(ctx context.Context, ref reference.Named) ([]string, error) { 128 repoEndpoint, err := newDefaultRepositoryEndpoint(ref, c.insecureRegistry) 129 if err != nil { 130 return nil, err 131 } 132 133 repo, err := c.getRepositoryForReference(ctx, ref, repoEndpoint) 134 if err != nil { 135 return nil, err 136 } 137 return repo.Tags(ctx).All(ctx) 138 } 139 140 func (c *client) getRepositoryForReference(ctx context.Context, ref reference.Named, repoEndpoint repositoryEndpoint) (distribution.Repository, error) { 141 repoName, err := reference.WithName(repoEndpoint.Name()) 142 if err != nil { 143 return nil, errors.Wrapf(err, "failed to parse repo name from %s", ref) 144 } 145 httpTransport, err := c.getHTTPTransportForRepoEndpoint(ctx, repoEndpoint) 146 if err != nil { 147 if !strings.Contains(err.Error(), "server gave HTTP response to HTTPS client") { 148 return nil, err 149 } 150 if !repoEndpoint.endpoint.TLSConfig.InsecureSkipVerify { 151 return nil, ErrHTTPProto{OrigErr: err.Error()} 152 } 153 // --insecure was set; fall back to plain HTTP 154 if url := repoEndpoint.endpoint.URL; url != nil && url.Scheme == "https" { 155 url.Scheme = "http" 156 httpTransport, err = c.getHTTPTransportForRepoEndpoint(ctx, repoEndpoint) 157 if err != nil { 158 return nil, err 159 } 160 } 161 } 162 return distributionclient.NewRepository(repoName, repoEndpoint.BaseURL(), httpTransport) 163 } 164 165 func (c *client) getHTTPTransportForRepoEndpoint(ctx context.Context, repoEndpoint repositoryEndpoint) (http.RoundTripper, error) { 166 httpTransport, err := getHTTPTransport( 167 c.authConfigResolver(ctx, repoEndpoint.info.Index), 168 repoEndpoint.endpoint, 169 repoEndpoint.Name(), 170 c.userAgent) 171 return httpTransport, errors.Wrap(err, "failed to configure transport") 172 } 173 174 // GetManifest returns an ImageManifest for the reference 175 func (c *client) GetManifest(ctx context.Context, ref reference.Named) (manifesttypes.ImageManifest, error) { 176 var result manifesttypes.ImageManifest 177 fetch := func(ctx context.Context, repo distribution.Repository, ref reference.Named) (bool, error) { 178 var err error 179 result, err = fetchManifest(ctx, repo, ref) 180 return result.Ref != nil, err 181 } 182 183 err := c.iterateEndpoints(ctx, ref, fetch) 184 return result, err 185 } 186 187 // GetManifestList returns a list of ImageManifest for the reference 188 func (c *client) GetManifestList(ctx context.Context, ref reference.Named) ([]manifesttypes.ImageManifest, error) { 189 result := []manifesttypes.ImageManifest{} 190 fetch := func(ctx context.Context, repo distribution.Repository, ref reference.Named) (bool, error) { 191 var err error 192 result, err = fetchList(ctx, repo, ref) 193 return len(result) > 0, err 194 } 195 196 err := c.iterateEndpoints(ctx, ref, fetch) 197 return result, err 198 } 199 200 func getManifestOptionsFromReference(ref reference.Named) (digest.Digest, []distribution.ManifestServiceOption, error) { 201 if tagged, isTagged := ref.(reference.NamedTagged); isTagged { 202 tag := tagged.Tag() 203 return "", []distribution.ManifestServiceOption{distribution.WithTag(tag)}, nil 204 } 205 if digested, isDigested := ref.(reference.Canonical); isDigested { 206 return digested.Digest(), []distribution.ManifestServiceOption{}, nil 207 } 208 return "", nil, errors.Errorf("%s no tag or digest", ref) 209 } 210 211 // GetRegistryAuth returns the auth config given an input image 212 func GetRegistryAuth(ctx context.Context, resolver AuthConfigResolver, imageName string) (*types.AuthConfig, error) { 213 distributionRef, err := reference.ParseNormalizedNamed(imageName) 214 if err != nil { 215 return nil, fmt.Errorf("Failed to parse image name: %s: %s", imageName, err) 216 } 217 imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, resolver, distributionRef.String()) 218 if err != nil { 219 return nil, fmt.Errorf("Failed to get imgRefAndAuth: %s", err) 220 } 221 return imgRefAndAuth.AuthConfig(), nil 222 }