chore(deps): bump github.com/google/go-containerregistry (#2924)
Bumps [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) from 0.15.2 to 0.17.0. - [Release notes](https://github.com/google/go-containerregistry/releases) - [Changelog](https://github.com/google/go-containerregistry/blob/main/.goreleaser.yml) - [Commits](https://github.com/google/go-containerregistry/compare/v0.15.2...v0.17.0) --- updated-dependencies: - dependency-name: github.com/google/go-containerregistry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									4636916c77
								
							
						
					
					
						commit
						a8afc79f5e
					
				
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							|  | @ -22,7 +22,7 @@ require ( | ||||||
| 	github.com/go-git/go-git/v5 v5.11.0 | 	github.com/go-git/go-git/v5 v5.11.0 | ||||||
| 	github.com/golang/mock v1.6.0 | 	github.com/golang/mock v1.6.0 | ||||||
| 	github.com/google/go-cmp v0.6.0 | 	github.com/google/go-cmp v0.6.0 | ||||||
| 	github.com/google/go-containerregistry v0.15.2 | 	github.com/google/go-containerregistry v0.17.0 | ||||||
| 	github.com/google/go-github v17.0.0+incompatible | 	github.com/google/go-github v17.0.0+incompatible | ||||||
| 	github.com/google/slowjam v1.1.0 | 	github.com/google/slowjam v1.1.0 | ||||||
| 	github.com/karrick/godirwalk v1.16.1 | 	github.com/karrick/godirwalk v1.16.1 | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										4
									
								
								go.sum
								
								
								
								
							|  | @ -329,8 +329,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ | ||||||
| github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
| github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||||||
| github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||||
| github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE= | github.com/google/go-containerregistry v0.17.0 h1:5p+zYs/R4VGHkhyvgWurWrpJ2hW4Vv9fQI+GzdcwXLk= | ||||||
| github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= | github.com/google/go-containerregistry v0.17.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= | ||||||
| github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= | github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= | ||||||
| github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= | github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= | ||||||
| github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= | ||||||
|  |  | ||||||
|  | @ -53,7 +53,7 @@ type defaultKeychain struct { | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	// DefaultKeychain implements Keychain by interpreting the docker config file.
 | 	// DefaultKeychain implements Keychain by interpreting the docker config file.
 | ||||||
| 	DefaultKeychain = RefreshingKeychain(&defaultKeychain{}, 5*time.Minute) | 	DefaultKeychain = &defaultKeychain{} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  |  | ||||||
|  | @ -40,6 +40,24 @@ func Write(tag name.Tag, img v1.Image, options ...Option) (string, error) { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// If we already have this image by this image ID, we can skip loading it.
 | ||||||
|  | 	id, err := img.ConfigName() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", fmt.Errorf("computing image ID: %w", err) | ||||||
|  | 	} | ||||||
|  | 	if resp, _, err := o.client.ImageInspectWithRaw(o.ctx, id.String()); err == nil { | ||||||
|  | 		want := tag.String() | ||||||
|  | 
 | ||||||
|  | 		// If we already have this tag, we can skip tagging it.
 | ||||||
|  | 		for _, have := range resp.RepoTags { | ||||||
|  | 			if have == want { | ||||||
|  | 				return "", nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return "", o.client.ImageTag(o.ctx, id.String(), want) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	pr, pw := io.Pipe() | 	pr, pw := io.Pipe() | ||||||
| 	go func() { | 	go func() { | ||||||
| 		pw.CloseWithError(tarball.Write(tag, img, pw)) | 		pw.CloseWithError(tarball.Write(tag, img, pw)) | ||||||
|  |  | ||||||
							
								
								
									
										137
									
								
								vendor/github.com/google/go-containerregistry/pkg/v1/layout/gc.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										137
									
								
								vendor/github.com/google/go-containerregistry/pkg/v1/layout/gc.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,137 @@ | ||||||
|  | // Copyright 2018 Google LLC All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||||
|  | // you may not use this file except in compliance with the License.
 | ||||||
|  | // You may obtain a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //    http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||||
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||||
|  | // See the License for the specific language governing permissions and
 | ||||||
|  | // limitations under the License.
 | ||||||
|  | 
 | ||||||
|  | // This is an EXPERIMENTAL package, and may change in arbitrary ways without notice.
 | ||||||
|  | package layout | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/fs" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	v1 "github.com/google/go-containerregistry/pkg/v1" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // GarbageCollect removes unreferenced blobs from the oci-layout
 | ||||||
|  | //
 | ||||||
|  | //	This is an experimental api, and not subject to any stability guarantees
 | ||||||
|  | //	We may abandon it at any time, without prior notice.
 | ||||||
|  | //	Deprecated: Use it at your own risk!
 | ||||||
|  | func (l Path) GarbageCollect() ([]v1.Hash, error) { | ||||||
|  | 	idx, err := l.ImageIndex() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	blobsToKeep := map[string]bool{} | ||||||
|  | 	if err := l.garbageCollectImageIndex(idx, blobsToKeep); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	blobsDir := l.path("blobs") | ||||||
|  | 	removedBlobs := []v1.Hash{} | ||||||
|  | 
 | ||||||
|  | 	err = filepath.WalkDir(blobsDir, func(path string, d fs.DirEntry, err error) error { | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if d.IsDir() { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		rel, err := filepath.Rel(blobsDir, path) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		hashString := strings.Replace(rel, "/", ":", 1) | ||||||
|  | 		if present := blobsToKeep[hashString]; !present { | ||||||
|  | 			h, err := v1.NewHash(hashString) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			removedBlobs = append(removedBlobs, h) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return removedBlobs, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l Path) garbageCollectImageIndex(index v1.ImageIndex, blobsToKeep map[string]bool) error { | ||||||
|  | 	idxm, err := index.IndexManifest() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	h, err := index.Digest() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	blobsToKeep[h.String()] = true | ||||||
|  | 
 | ||||||
|  | 	for _, descriptor := range idxm.Manifests { | ||||||
|  | 		if descriptor.MediaType.IsImage() { | ||||||
|  | 			img, err := index.Image(descriptor.Digest) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			if err := l.garbageCollectImage(img, blobsToKeep); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} else if descriptor.MediaType.IsIndex() { | ||||||
|  | 			idx, err := index.ImageIndex(descriptor.Digest) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			if err := l.garbageCollectImageIndex(idx, blobsToKeep); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			return fmt.Errorf("gc: unknown media type: %s", descriptor.MediaType) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l Path) garbageCollectImage(image v1.Image, blobsToKeep map[string]bool) error { | ||||||
|  | 	h, err := image.Digest() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	blobsToKeep[h.String()] = true | ||||||
|  | 
 | ||||||
|  | 	h, err = image.ConfigName() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	blobsToKeep[h.String()] = true | ||||||
|  | 
 | ||||||
|  | 	ls, err := image.Layers() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	for _, l := range ls { | ||||||
|  | 		h, err := l.Digest() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		blobsToKeep[h.String()] = true | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | @ -196,6 +196,7 @@ func (l Path) WriteBlob(hash v1.Hash, r io.ReadCloser) error { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l Path) writeBlob(hash v1.Hash, size int64, rc io.ReadCloser, renamer func() (v1.Hash, error)) error { | func (l Path) writeBlob(hash v1.Hash, size int64, rc io.ReadCloser, renamer func() (v1.Hash, error)) error { | ||||||
|  | 	defer rc.Close() | ||||||
| 	if hash.Hex == "" && renamer == nil { | 	if hash.Hex == "" && renamer == nil { | ||||||
| 		panic("writeBlob called an invalid hash and no renamer") | 		panic("writeBlob called an invalid hash and no renamer") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -402,8 +402,10 @@ func Time(img v1.Image, t time.Time) (v1.Image, error) { | ||||||
| 			historyIdx++ | 			historyIdx++ | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
|  | 		if addendumIdx < len(addendums) { | ||||||
| 			addendums[addendumIdx].Layer = newLayer | 			addendums[addendumIdx].Layer = newLayer | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// add all leftover History entries
 | 	// add all leftover History entries
 | ||||||
| 	for ; historyIdx < len(ocf.History); historyIdx, addendumIdx = historyIdx+1, addendumIdx+1 { | 	for ; historyIdx < len(ocf.History); historyIdx, addendumIdx = historyIdx+1, addendumIdx+1 { | ||||||
|  |  | ||||||
|  | @ -16,6 +16,7 @@ package remote | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 
 | 
 | ||||||
| 	"github.com/google/go-containerregistry/pkg/logs" | 	"github.com/google/go-containerregistry/pkg/logs" | ||||||
|  | @ -33,20 +34,11 @@ var allManifestMediaTypes = append(append([]types.MediaType{ | ||||||
| // ErrSchema1 indicates that we received a schema1 manifest from the registry.
 | // ErrSchema1 indicates that we received a schema1 manifest from the registry.
 | ||||||
| // This library doesn't have plans to support this legacy image format:
 | // This library doesn't have plans to support this legacy image format:
 | ||||||
| // https://github.com/google/go-containerregistry/issues/377
 | // https://github.com/google/go-containerregistry/issues/377
 | ||||||
| type ErrSchema1 struct { | var ErrSchema1 = errors.New("see https://github.com/google/go-containerregistry/issues/377") | ||||||
| 	schema string |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // newErrSchema1 returns an ErrSchema1 with the unexpected MediaType.
 | // newErrSchema1 returns an ErrSchema1 with the unexpected MediaType.
 | ||||||
| func newErrSchema1(schema types.MediaType) error { | func newErrSchema1(schema types.MediaType) error { | ||||||
| 	return &ErrSchema1{ | 	return fmt.Errorf("unsupported MediaType: %q, %w", schema, ErrSchema1) | ||||||
| 		schema: string(schema), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Error implements error.
 |  | ||||||
| func (e *ErrSchema1) Error() string { |  | ||||||
| 	return fmt.Sprintf("unsupported MediaType: %q, see https://github.com/google/go-containerregistry/issues/377", e.schema) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Descriptor provides access to metadata about remote artifact and accessors
 | // Descriptor provides access to metadata about remote artifact and accessors
 | ||||||
|  |  | ||||||
|  | @ -32,6 +32,12 @@ import ( | ||||||
| 	"github.com/google/go-containerregistry/pkg/v1/types" | 	"github.com/google/go-containerregistry/pkg/v1/types" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | const ( | ||||||
|  | 	kib           = 1024 | ||||||
|  | 	mib           = 1024 * kib | ||||||
|  | 	manifestLimit = 100 * mib | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| // fetcher implements methods for reading from a registry.
 | // fetcher implements methods for reading from a registry.
 | ||||||
| type fetcher struct { | type fetcher struct { | ||||||
| 	target resource | 	target resource | ||||||
|  | @ -130,7 +136,7 @@ func (f *fetcher) fetchManifest(ctx context.Context, ref name.Reference, accepta | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	manifest, err := io.ReadAll(resp.Body) | 	manifest, err := io.ReadAll(io.LimitReader(resp.Body, manifestLimit)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -96,7 +96,8 @@ var defaultRetryStatusCodes = []int{ | ||||||
| 	http.StatusBadGateway, | 	http.StatusBadGateway, | ||||||
| 	http.StatusServiceUnavailable, | 	http.StatusServiceUnavailable, | ||||||
| 	http.StatusGatewayTimeout, | 	http.StatusGatewayTimeout, | ||||||
| 	499, | 	499, // nginx-specific, client closed request
 | ||||||
|  | 	522, // Cloudflare-specific, connection timeout
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  |  | ||||||
							
								
								
									
										137
									
								
								vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/bearer.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										137
									
								
								vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/bearer.go
								
								
									generated
								
								
									vendored
								
								
							|  | @ -32,6 +32,71 @@ import ( | ||||||
| 	"github.com/google/go-containerregistry/pkg/name" | 	"github.com/google/go-containerregistry/pkg/name" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type Token struct { | ||||||
|  | 	Token        string `json:"token"` | ||||||
|  | 	AccessToken  string `json:"access_token,omitempty"` | ||||||
|  | 	RefreshToken string `json:"refresh_token"` | ||||||
|  | 	ExpiresIn    int    `json:"expires_in"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Exchange requests a registry Token with the given scopes.
 | ||||||
|  | func Exchange(ctx context.Context, reg name.Registry, auth authn.Authenticator, t http.RoundTripper, scopes []string, pr *Challenge) (*Token, error) { | ||||||
|  | 	if strings.ToLower(pr.Scheme) != "bearer" { | ||||||
|  | 		// TODO: Pretend token for basic?
 | ||||||
|  | 		return nil, fmt.Errorf("challenge scheme %q is not bearer", pr.Scheme) | ||||||
|  | 	} | ||||||
|  | 	bt, err := fromChallenge(reg, auth, t, pr, scopes...) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	authcfg, err := auth.Authorization() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	tok, err := bt.Refresh(ctx, authcfg) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return tok, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // FromToken returns a transport given a Challenge + Token.
 | ||||||
|  | func FromToken(reg name.Registry, auth authn.Authenticator, t http.RoundTripper, pr *Challenge, tok *Token) (http.RoundTripper, error) { | ||||||
|  | 	if strings.ToLower(pr.Scheme) != "bearer" { | ||||||
|  | 		return &Wrapper{&basicTransport{inner: t, auth: auth, target: reg.RegistryStr()}}, nil | ||||||
|  | 	} | ||||||
|  | 	bt, err := fromChallenge(reg, auth, t, pr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if tok.Token != "" { | ||||||
|  | 		bt.bearer.RegistryToken = tok.Token | ||||||
|  | 	} | ||||||
|  | 	return &Wrapper{bt}, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func fromChallenge(reg name.Registry, auth authn.Authenticator, t http.RoundTripper, pr *Challenge, scopes ...string) (*bearerTransport, error) { | ||||||
|  | 	// We require the realm, which tells us where to send our Basic auth to turn it into Bearer auth.
 | ||||||
|  | 	realm, ok := pr.Parameters["realm"] | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, fmt.Errorf("malformed www-authenticate, missing realm: %v", pr.Parameters) | ||||||
|  | 	} | ||||||
|  | 	service := pr.Parameters["service"] | ||||||
|  | 	scheme := "https" | ||||||
|  | 	if pr.Insecure { | ||||||
|  | 		scheme = "http" | ||||||
|  | 	} | ||||||
|  | 	return &bearerTransport{ | ||||||
|  | 		inner:    t, | ||||||
|  | 		basic:    auth, | ||||||
|  | 		realm:    realm, | ||||||
|  | 		registry: reg, | ||||||
|  | 		service:  service, | ||||||
|  | 		scopes:   scopes, | ||||||
|  | 		scheme:   scheme, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type bearerTransport struct { | type bearerTransport struct { | ||||||
| 	// Wrapped by bearerTransport.
 | 	// Wrapped by bearerTransport.
 | ||||||
| 	inner http.RoundTripper | 	inner http.RoundTripper | ||||||
|  | @ -73,7 +138,7 @@ func (bt *bearerTransport) RoundTrip(in *http.Request) (*http.Response, error) { | ||||||
| 		// we are redirected, only set it when the authorization header matches
 | 		// we are redirected, only set it when the authorization header matches
 | ||||||
| 		// the registry with which we are interacting.
 | 		// the registry with which we are interacting.
 | ||||||
| 		// In case of redirect http.Client can use an empty Host, check URL too.
 | 		// In case of redirect http.Client can use an empty Host, check URL too.
 | ||||||
| 		if matchesHost(bt.registry, in, bt.scheme) { | 		if matchesHost(bt.registry.RegistryStr(), in, bt.scheme) { | ||||||
| 			hdr := fmt.Sprintf("Bearer %s", bt.bearer.RegistryToken) | 			hdr := fmt.Sprintf("Bearer %s", bt.bearer.RegistryToken) | ||||||
| 			in.Header.Set("Authorization", hdr) | 			in.Header.Set("Authorization", hdr) | ||||||
| 		} | 		} | ||||||
|  | @ -135,7 +200,36 @@ func (bt *bearerTransport) refresh(ctx context.Context) error { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var content []byte | 	response, err := bt.Refresh(ctx, auth) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Some registries set access_token instead of token. See #54.
 | ||||||
|  | 	if response.AccessToken != "" { | ||||||
|  | 		response.Token = response.AccessToken | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Find a token to turn into a Bearer authenticator
 | ||||||
|  | 	if response.Token != "" { | ||||||
|  | 		bt.bearer.RegistryToken = response.Token | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If we obtained a refresh token from the oauth flow, use that for refresh() now.
 | ||||||
|  | 	if response.RefreshToken != "" { | ||||||
|  | 		bt.basic = authn.FromConfig(authn.AuthConfig{ | ||||||
|  | 			IdentityToken: response.RefreshToken, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (bt *bearerTransport) Refresh(ctx context.Context, auth *authn.AuthConfig) (*Token, error) { | ||||||
|  | 	var ( | ||||||
|  | 		content []byte | ||||||
|  | 		err     error | ||||||
|  | 	) | ||||||
| 	if auth.IdentityToken != "" { | 	if auth.IdentityToken != "" { | ||||||
| 		// If the secret being stored is an identity token,
 | 		// If the secret being stored is an identity token,
 | ||||||
| 		// the Username should be set to <token>, which indicates
 | 		// the Username should be set to <token>, which indicates
 | ||||||
|  | @ -152,48 +246,25 @@ func (bt *bearerTransport) refresh(ctx context.Context) error { | ||||||
| 		content, err = bt.refreshBasic(ctx) | 		content, err = bt.refreshBasic(ctx) | ||||||
| 	} | 	} | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Some registries don't have "token" in the response. See #54.
 | 	var response Token | ||||||
| 	type tokenResponse struct { |  | ||||||
| 		Token        string `json:"token"` |  | ||||||
| 		AccessToken  string `json:"access_token"` |  | ||||||
| 		RefreshToken string `json:"refresh_token"` |  | ||||||
| 		// TODO: handle expiry?
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var response tokenResponse |  | ||||||
| 	if err := json.Unmarshal(content, &response); err != nil { | 	if err := json.Unmarshal(content, &response); err != nil { | ||||||
| 		return err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Some registries set access_token instead of token.
 | 	if response.Token == "" && response.AccessToken == "" { | ||||||
| 	if response.AccessToken != "" { | 		return &response, fmt.Errorf("no token in bearer response:\n%s", content) | ||||||
| 		response.Token = response.AccessToken |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Find a token to turn into a Bearer authenticator
 | 	return &response, nil | ||||||
| 	if response.Token != "" { |  | ||||||
| 		bt.bearer.RegistryToken = response.Token |  | ||||||
| 	} else { |  | ||||||
| 		return fmt.Errorf("no token in bearer response:\n%s", content) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// If we obtained a refresh token from the oauth flow, use that for refresh() now.
 |  | ||||||
| 	if response.RefreshToken != "" { |  | ||||||
| 		bt.basic = authn.FromConfig(authn.AuthConfig{ |  | ||||||
| 			IdentityToken: response.RefreshToken, |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func matchesHost(reg name.Registry, in *http.Request, scheme string) bool { | func matchesHost(host string, in *http.Request, scheme string) bool { | ||||||
| 	canonicalHeaderHost := canonicalAddress(in.Host, scheme) | 	canonicalHeaderHost := canonicalAddress(in.Host, scheme) | ||||||
| 	canonicalURLHost := canonicalAddress(in.URL.Host, scheme) | 	canonicalURLHost := canonicalAddress(in.URL.Host, scheme) | ||||||
| 	canonicalRegistryHost := canonicalAddress(reg.RegistryStr(), scheme) | 	canonicalRegistryHost := canonicalAddress(host, scheme) | ||||||
| 	return canonicalHeaderHost == canonicalRegistryHost || canonicalURLHost == canonicalRegistryHost | 	return canonicalHeaderHost == canonicalRegistryHost || canonicalURLHost == canonicalRegistryHost | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -28,33 +28,22 @@ import ( | ||||||
| 	"github.com/google/go-containerregistry/pkg/name" | 	"github.com/google/go-containerregistry/pkg/name" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type challenge string |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	anonymous challenge = "anonymous" |  | ||||||
| 	basic     challenge = "basic" |  | ||||||
| 	bearer    challenge = "bearer" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // 300ms is the default fallback period for go's DNS dialer but we could make this configurable.
 | // 300ms is the default fallback period for go's DNS dialer but we could make this configurable.
 | ||||||
| var fallbackDelay = 300 * time.Millisecond | var fallbackDelay = 300 * time.Millisecond | ||||||
| 
 | 
 | ||||||
| type pingResp struct { | type Challenge struct { | ||||||
| 	challenge challenge | 	Scheme string | ||||||
| 
 | 
 | ||||||
| 	// Following the challenge there are often key/value pairs
 | 	// Following the challenge there are often key/value pairs
 | ||||||
| 	// e.g. Bearer service="gcr.io",realm="https://auth.gcr.io/v36/tokenz"
 | 	// e.g. Bearer service="gcr.io",realm="https://auth.gcr.io/v36/tokenz"
 | ||||||
| 	parameters map[string]string | 	Parameters map[string]string | ||||||
| 
 | 
 | ||||||
| 	// The registry's scheme to use. Communicates whether we fell back to http.
 | 	// Whether we had to use http to complete the Ping.
 | ||||||
| 	scheme string | 	Insecure bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c challenge) Canonical() challenge { | // Ping does a GET /v2/ against the registry and returns the response.
 | ||||||
| 	return challenge(strings.ToLower(string(c))) | func Ping(ctx context.Context, reg name.Registry, t http.RoundTripper) (*Challenge, error) { | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func ping(ctx context.Context, reg name.Registry, t http.RoundTripper) (*pingResp, error) { |  | ||||||
| 	// This first attempts to use "https" for every request, falling back to http
 | 	// This first attempts to use "https" for every request, falling back to http
 | ||||||
| 	// if the registry matches our localhost heuristic or if it is intentionally
 | 	// if the registry matches our localhost heuristic or if it is intentionally
 | ||||||
| 	// set to insecure via name.NewInsecureRegistry.
 | 	// set to insecure via name.NewInsecureRegistry.
 | ||||||
|  | @ -68,9 +57,9 @@ func ping(ctx context.Context, reg name.Registry, t http.RoundTripper) (*pingRes | ||||||
| 	return pingParallel(ctx, reg, t, schemes) | 	return pingParallel(ctx, reg, t, schemes) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func pingSingle(ctx context.Context, reg name.Registry, t http.RoundTripper, scheme string) (*pingResp, error) { | func pingSingle(ctx context.Context, reg name.Registry, t http.RoundTripper, scheme string) (*Challenge, error) { | ||||||
| 	client := http.Client{Transport: t} | 	client := http.Client{Transport: t} | ||||||
| 	url := fmt.Sprintf("%s://%s/v2/", scheme, reg.Name()) | 	url := fmt.Sprintf("%s://%s/v2/", scheme, reg.RegistryStr()) | ||||||
| 	req, err := http.NewRequest(http.MethodGet, url, nil) | 	req, err := http.NewRequest(http.MethodGet, url, nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|  | @ -86,27 +75,28 @@ func pingSingle(ctx context.Context, reg name.Registry, t http.RoundTripper, sch | ||||||
| 		resp.Body.Close() | 		resp.Body.Close() | ||||||
| 	}() | 	}() | ||||||
| 
 | 
 | ||||||
|  | 	insecure := scheme == "http" | ||||||
|  | 
 | ||||||
| 	switch resp.StatusCode { | 	switch resp.StatusCode { | ||||||
| 	case http.StatusOK: | 	case http.StatusOK: | ||||||
| 		// If we get a 200, then no authentication is needed.
 | 		// If we get a 200, then no authentication is needed.
 | ||||||
| 		return &pingResp{ | 		return &Challenge{ | ||||||
| 			challenge: anonymous, | 			Insecure: insecure, | ||||||
| 			scheme:    scheme, |  | ||||||
| 		}, nil | 		}, nil | ||||||
| 	case http.StatusUnauthorized: | 	case http.StatusUnauthorized: | ||||||
| 		if challenges := authchallenge.ResponseChallenges(resp); len(challenges) != 0 { | 		if challenges := authchallenge.ResponseChallenges(resp); len(challenges) != 0 { | ||||||
| 			// If we hit more than one, let's try to find one that we know how to handle.
 | 			// If we hit more than one, let's try to find one that we know how to handle.
 | ||||||
| 			wac := pickFromMultipleChallenges(challenges) | 			wac := pickFromMultipleChallenges(challenges) | ||||||
| 			return &pingResp{ | 			return &Challenge{ | ||||||
| 				challenge:  challenge(wac.Scheme).Canonical(), | 				Scheme:     wac.Scheme, | ||||||
| 				parameters: wac.Parameters, | 				Parameters: wac.Parameters, | ||||||
| 				scheme:     scheme, | 				Insecure:   insecure, | ||||||
| 			}, nil | 			}, nil | ||||||
| 		} | 		} | ||||||
| 		// Otherwise, just return the challenge without parameters.
 | 		// Otherwise, just return the challenge without parameters.
 | ||||||
| 		return &pingResp{ | 		return &Challenge{ | ||||||
| 			challenge: challenge(resp.Header.Get("WWW-Authenticate")).Canonical(), | 			Scheme:   resp.Header.Get("WWW-Authenticate"), | ||||||
| 			scheme:    scheme, | 			Insecure: insecure, | ||||||
| 		}, nil | 		}, nil | ||||||
| 	default: | 	default: | ||||||
| 		return nil, CheckError(resp, http.StatusOK, http.StatusUnauthorized) | 		return nil, CheckError(resp, http.StatusOK, http.StatusUnauthorized) | ||||||
|  | @ -114,12 +104,12 @@ func pingSingle(ctx context.Context, reg name.Registry, t http.RoundTripper, sch | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Based on the golang happy eyeballs dialParallel impl in net/dial.go.
 | // Based on the golang happy eyeballs dialParallel impl in net/dial.go.
 | ||||||
| func pingParallel(ctx context.Context, reg name.Registry, t http.RoundTripper, schemes []string) (*pingResp, error) { | func pingParallel(ctx context.Context, reg name.Registry, t http.RoundTripper, schemes []string) (*Challenge, error) { | ||||||
| 	returned := make(chan struct{}) | 	returned := make(chan struct{}) | ||||||
| 	defer close(returned) | 	defer close(returned) | ||||||
| 
 | 
 | ||||||
| 	type pingResult struct { | 	type pingResult struct { | ||||||
| 		*pingResp | 		*Challenge | ||||||
| 		error | 		error | ||||||
| 		primary bool | 		primary bool | ||||||
| 		done    bool | 		done    bool | ||||||
|  | @ -130,7 +120,7 @@ func pingParallel(ctx context.Context, reg name.Registry, t http.RoundTripper, s | ||||||
| 	startRacer := func(ctx context.Context, scheme string) { | 	startRacer := func(ctx context.Context, scheme string) { | ||||||
| 		pr, err := pingSingle(ctx, reg, t, scheme) | 		pr, err := pingSingle(ctx, reg, t, scheme) | ||||||
| 		select { | 		select { | ||||||
| 		case results <- pingResult{pingResp: pr, error: err, primary: scheme == "https", done: true}: | 		case results <- pingResult{Challenge: pr, error: err, primary: scheme == "https", done: true}: | ||||||
| 		case <-returned: | 		case <-returned: | ||||||
| 			if pr != nil { | 			if pr != nil { | ||||||
| 				logs.Debug.Printf("%s lost race", scheme) | 				logs.Debug.Printf("%s lost race", scheme) | ||||||
|  | @ -156,7 +146,7 @@ func pingParallel(ctx context.Context, reg name.Registry, t http.RoundTripper, s | ||||||
| 
 | 
 | ||||||
| 		case res := <-results: | 		case res := <-results: | ||||||
| 			if res.error == nil { | 			if res.error == nil { | ||||||
| 				return res.pingResp, nil | 				return res.Challenge, nil | ||||||
| 			} | 			} | ||||||
| 			if res.primary { | 			if res.primary { | ||||||
| 				primary = res | 				primary = res | ||||||
|  | @ -164,7 +154,7 @@ func pingParallel(ctx context.Context, reg name.Registry, t http.RoundTripper, s | ||||||
| 				fallback = res | 				fallback = res | ||||||
| 			} | 			} | ||||||
| 			if primary.done && fallback.done { | 			if primary.done && fallback.done { | ||||||
| 				return nil, multierrs([]error{primary.error, fallback.error}) | 				return nil, multierrs{primary.error, fallback.error} | ||||||
| 			} | 			} | ||||||
| 			if res.primary && fallbackTimer.Stop() { | 			if res.primary && fallbackTimer.Stop() { | ||||||
| 				// Primary failed and we haven't started the fallback,
 | 				// Primary failed and we haven't started the fallback,
 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/schemer.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										2
									
								
								vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/schemer.go
								
								
									generated
								
								
									vendored
								
								
							|  | @ -37,7 +37,7 @@ func (st *schemeTransport) RoundTrip(in *http.Request) (*http.Response, error) { | ||||||
| 	// based on which scheme was successful. That is only valid for the
 | 	// based on which scheme was successful. That is only valid for the
 | ||||||
| 	// registry server and not e.g. a separate token server or blob storage,
 | 	// registry server and not e.g. a separate token server or blob storage,
 | ||||||
| 	// so we should only override the scheme if the host is the registry.
 | 	// so we should only override the scheme if the host is the registry.
 | ||||||
| 	if matchesHost(st.registry, in, st.scheme) { | 	if matchesHost(st.registry.String(), in, st.scheme) { | ||||||
| 		in.URL.Scheme = st.scheme | 		in.URL.Scheme = st.scheme | ||||||
| 	} | 	} | ||||||
| 	return st.inner.RoundTrip(in) | 	return st.inner.RoundTrip(in) | ||||||
|  |  | ||||||
							
								
								
									
										37
									
								
								vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/transport.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										37
									
								
								vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/transport.go
								
								
									generated
								
								
									vendored
								
								
							|  | @ -16,8 +16,8 @@ package transport | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" |  | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/google/go-containerregistry/pkg/authn" | 	"github.com/google/go-containerregistry/pkg/authn" | ||||||
| 	"github.com/google/go-containerregistry/pkg/name" | 	"github.com/google/go-containerregistry/pkg/name" | ||||||
|  | @ -59,7 +59,7 @@ func NewWithContext(ctx context.Context, reg name.Registry, auth authn.Authentic | ||||||
| 
 | 
 | ||||||
| 	// First we ping the registry to determine the parameters of the authentication handshake
 | 	// First we ping the registry to determine the parameters of the authentication handshake
 | ||||||
| 	// (if one is even necessary).
 | 	// (if one is even necessary).
 | ||||||
| 	pr, err := ping(ctx, reg, t) | 	pr, err := Ping(ctx, reg, t) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | @ -69,39 +69,32 @@ func NewWithContext(ctx context.Context, reg name.Registry, auth authn.Authentic | ||||||
| 		t = NewUserAgent(t, "") | 		t = NewUserAgent(t, "") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	scheme := "https" | ||||||
|  | 	if pr.Insecure { | ||||||
|  | 		scheme = "http" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Wrap t in a transport that selects the appropriate scheme based on the ping response.
 | 	// Wrap t in a transport that selects the appropriate scheme based on the ping response.
 | ||||||
| 	t = &schemeTransport{ | 	t = &schemeTransport{ | ||||||
| 		scheme:   pr.scheme, | 		scheme:   scheme, | ||||||
| 		registry: reg, | 		registry: reg, | ||||||
| 		inner:    t, | 		inner:    t, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	switch pr.challenge.Canonical() { | 	if strings.ToLower(pr.Scheme) != "bearer" { | ||||||
| 	case anonymous, basic: |  | ||||||
| 		return &Wrapper{&basicTransport{inner: t, auth: auth, target: reg.RegistryStr()}}, nil | 		return &Wrapper{&basicTransport{inner: t, auth: auth, target: reg.RegistryStr()}}, nil | ||||||
| 	case bearer: |  | ||||||
| 		// We require the realm, which tells us where to send our Basic auth to turn it into Bearer auth.
 |  | ||||||
| 		realm, ok := pr.parameters["realm"] |  | ||||||
| 		if !ok { |  | ||||||
| 			return nil, fmt.Errorf("malformed www-authenticate, missing realm: %v", pr.parameters) |  | ||||||
| 	} | 	} | ||||||
| 		service := pr.parameters["service"] | 
 | ||||||
| 		bt := &bearerTransport{ | 	bt, err := fromChallenge(reg, auth, t, pr) | ||||||
| 			inner:    t, | 	if err != nil { | ||||||
| 			basic:    auth, | 		return nil, err | ||||||
| 			realm:    realm, |  | ||||||
| 			registry: reg, |  | ||||||
| 			service:  service, |  | ||||||
| 			scopes:   scopes, |  | ||||||
| 			scheme:   pr.scheme, |  | ||||||
| 	} | 	} | ||||||
|  | 	bt.scopes = scopes | ||||||
|  | 
 | ||||||
| 	if err := bt.refresh(ctx); err != nil { | 	if err := bt.refresh(ctx); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	return &Wrapper{bt}, nil | 	return &Wrapper{bt}, nil | ||||||
| 	default: |  | ||||||
| 		return nil, fmt.Errorf("unrecognized challenge: %s", pr.challenge) |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Wrapper results in *not* wrapping supplied transport with additional logic such as retries, useragent and debug logging
 | // Wrapper results in *not* wrapping supplied transport with additional logic such as retries, useragent and debug logging
 | ||||||
|  |  | ||||||
|  | @ -210,7 +210,7 @@ func (w *writer) initiateUpload(ctx context.Context, from, mount, origin string) | ||||||
| 	req.Header.Set("Content-Type", "application/json") | 	req.Header.Set("Content-Type", "application/json") | ||||||
| 	resp, err := w.client.Do(req.WithContext(ctx)) | 	resp, err := w.client.Do(req.WithContext(ctx)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if origin != "" && origin != w.repo.RegistryStr() { | 		if from != "" { | ||||||
| 			// https://github.com/google/go-containerregistry/issues/1679
 | 			// https://github.com/google/go-containerregistry/issues/1679
 | ||||||
| 			logs.Warn.Printf("retrying without mount: %v", err) | 			logs.Warn.Printf("retrying without mount: %v", err) | ||||||
| 			return w.initiateUpload(ctx, "", "", "") | 			return w.initiateUpload(ctx, "", "", "") | ||||||
|  | @ -220,7 +220,7 @@ func (w *writer) initiateUpload(ctx context.Context, from, mount, origin string) | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
| 
 | 
 | ||||||
| 	if err := transport.CheckError(resp, http.StatusCreated, http.StatusAccepted); err != nil { | 	if err := transport.CheckError(resp, http.StatusCreated, http.StatusAccepted); err != nil { | ||||||
| 		if origin != "" && origin != w.repo.RegistryStr() { | 		if from != "" { | ||||||
| 			// https://github.com/google/go-containerregistry/issues/1404
 | 			// https://github.com/google/go-containerregistry/issues/1404
 | ||||||
| 			logs.Warn.Printf("retrying without mount: %v", err) | 			logs.Warn.Printf("retrying without mount: %v", err) | ||||||
| 			return w.initiateUpload(ctx, "", "", "") | 			return w.initiateUpload(ctx, "", "", "") | ||||||
|  | @ -280,6 +280,11 @@ func (w *writer) streamBlob(ctx context.Context, layer v1.Layer, streamLocation | ||||||
| 	if _, ok := layer.(*stream.Layer); !ok { | 	if _, ok := layer.(*stream.Layer); !ok { | ||||||
| 		// We can't retry streaming layers.
 | 		// We can't retry streaming layers.
 | ||||||
| 		req.GetBody = getBody | 		req.GetBody = getBody | ||||||
|  | 
 | ||||||
|  | 		// If we know the size, set it.
 | ||||||
|  | 		if size, err := layer.Size(); err == nil { | ||||||
|  | 			req.ContentLength = size | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	req.Header.Set("Content-Type", "application/octet-stream") | 	req.Header.Set("Content-Type", "application/octet-stream") | ||||||
| 
 | 
 | ||||||
|  | @ -360,8 +365,16 @@ func (w *writer) uploadOne(ctx context.Context, l v1.Layer) error { | ||||||
| 			if err := w.maybeUpdateScopes(ctx, ml); err != nil { | 			if err := w.maybeUpdateScopes(ctx, ml); err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
| 			from = ml.Reference.Context().RepositoryStr() | 			from = ml.Reference.Context().RepositoryStr() | ||||||
| 			origin = ml.Reference.Context().RegistryStr() | 			origin = ml.Reference.Context().RegistryStr() | ||||||
|  | 
 | ||||||
|  | 			// This keeps breaking with DockerHub.
 | ||||||
|  | 			// https://github.com/google/go-containerregistry/issues/1741
 | ||||||
|  | 			if w.repo.RegistryStr() == name.DefaultRegistry && origin != w.repo.RegistryStr() { | ||||||
|  | 				from = "" | ||||||
|  | 				origin = "" | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		location, mounted, err := w.initiateUpload(ctx, from, mount, origin) | 		location, mounted, err := w.initiateUpload(ctx, from, mount, origin) | ||||||
|  |  | ||||||
|  | @ -79,6 +79,9 @@ func validateChildren(idx v1.ImageIndex, opt ...Option) error { | ||||||
| 			if err := validateMediaType(img, desc.MediaType); err != nil { | 			if err := validateMediaType(img, desc.MediaType); err != nil { | ||||||
| 				errs = append(errs, fmt.Sprintf("failed to validate image MediaType[%d](%s): %v", i, desc.Digest, err)) | 				errs = append(errs, fmt.Sprintf("failed to validate image MediaType[%d](%s): %v", i, desc.Digest, err)) | ||||||
| 			} | 			} | ||||||
|  | 			if err := validatePlatform(img, desc.Platform); err != nil { | ||||||
|  | 				errs = append(errs, fmt.Sprintf("failed to validate image platform[%d](%s): %v", i, desc.Digest, err)) | ||||||
|  | 			} | ||||||
| 		default: | 		default: | ||||||
| 			// Workaround for #819.
 | 			// Workaround for #819.
 | ||||||
| 			if wl, ok := idx.(withLayer); ok { | 			if wl, ok := idx.(withLayer); ok { | ||||||
|  | @ -173,3 +176,54 @@ func validateIndexManifest(idx v1.ImageIndex) error { | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func validatePlatform(img v1.Image, want *v1.Platform) error { | ||||||
|  | 	if want == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cf, err := img.ConfigFile() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	got := cf.Platform() | ||||||
|  | 
 | ||||||
|  | 	if got == nil { | ||||||
|  | 		return fmt.Errorf("config file missing platform fields") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if got.Equals(*want) { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	errs := []string{} | ||||||
|  | 
 | ||||||
|  | 	if got.OS != want.OS { | ||||||
|  | 		errs = append(errs, fmt.Sprintf("mismatched OS: %s != %s", got.OS, want.OS)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if got.Architecture != want.Architecture { | ||||||
|  | 		errs = append(errs, fmt.Sprintf("mismatched Architecture: %s != %s", got.Architecture, want.Architecture)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if got.OSVersion != want.OSVersion { | ||||||
|  | 		errs = append(errs, fmt.Sprintf("mismatched OSVersion: %s != %s", got.OSVersion, want.OSVersion)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if got.OSVersion != want.OSVersion { | ||||||
|  | 		errs = append(errs, fmt.Sprintf("mismatched OSVersion: %s != %s", got.OSVersion, want.OSVersion)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(errs) == 0 { | ||||||
|  | 		// If we got here, some features might be mismatched. Just add those...
 | ||||||
|  | 		if len(got.Features) != 0 || len(want.Features) != 0 { | ||||||
|  | 			errs = append(errs, fmt.Sprintf("mismatched Features: %v, %v", got.Features, want.Features)) | ||||||
|  | 		} | ||||||
|  | 		if len(got.OSFeatures) != 0 || len(want.OSFeatures) != 0 { | ||||||
|  | 			errs = append(errs, fmt.Sprintf("mismatched OSFeatures: %v, %v", got.OSFeatures, want.OSFeatures)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return errors.New(strings.Join(errs, "\n")) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -681,7 +681,7 @@ github.com/google/go-cmp/cmp/internal/diff | ||||||
| github.com/google/go-cmp/cmp/internal/flags | github.com/google/go-cmp/cmp/internal/flags | ||||||
| github.com/google/go-cmp/cmp/internal/function | github.com/google/go-cmp/cmp/internal/function | ||||||
| github.com/google/go-cmp/cmp/internal/value | github.com/google/go-cmp/cmp/internal/value | ||||||
| # github.com/google/go-containerregistry v0.15.2 | # github.com/google/go-containerregistry v0.17.0 | ||||||
| ## explicit; go 1.18 | ## explicit; go 1.18 | ||||||
| github.com/google/go-containerregistry/internal/and | github.com/google/go-containerregistry/internal/and | ||||||
| github.com/google/go-containerregistry/internal/compression | github.com/google/go-containerregistry/internal/compression | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue