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/golang/mock v1.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/slowjam v1.1.0
|
||||
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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
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.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q=
|
||||
github.com/google/go-containerregistry v0.17.0 h1:5p+zYs/R4VGHkhyvgWurWrpJ2hW4Vv9fQI+GzdcwXLk=
|
||||
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/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ type defaultKeychain struct {
|
|||
|
||||
var (
|
||||
// DefaultKeychain implements Keychain by interpreting the docker config file.
|
||||
DefaultKeychain = RefreshingKeychain(&defaultKeychain{}, 5*time.Minute)
|
||||
DefaultKeychain = &defaultKeychain{}
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -40,6 +40,24 @@ func Write(tag name.Tag, img v1.Image, options ...Option) (string, error) {
|
|||
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()
|
||||
go func() {
|
||||
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 {
|
||||
defer rc.Close()
|
||||
if hash.Hex == "" && renamer == nil {
|
||||
panic("writeBlob called an invalid hash and no renamer")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -402,7 +402,9 @@ func Time(img v1.Image, t time.Time) (v1.Image, error) {
|
|||
historyIdx++
|
||||
break
|
||||
}
|
||||
addendums[addendumIdx].Layer = newLayer
|
||||
if addendumIdx < len(addendums) {
|
||||
addendums[addendumIdx].Layer = newLayer
|
||||
}
|
||||
}
|
||||
|
||||
// add all leftover History entries
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ package remote
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"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.
|
||||
// This library doesn't have plans to support this legacy image format:
|
||||
// https://github.com/google/go-containerregistry/issues/377
|
||||
type ErrSchema1 struct {
|
||||
schema string
|
||||
}
|
||||
var ErrSchema1 = errors.New("see https://github.com/google/go-containerregistry/issues/377")
|
||||
|
||||
// newErrSchema1 returns an ErrSchema1 with the unexpected MediaType.
|
||||
func newErrSchema1(schema types.MediaType) error {
|
||||
return &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)
|
||||
return fmt.Errorf("unsupported MediaType: %q, %w", schema, ErrSchema1)
|
||||
}
|
||||
|
||||
// Descriptor provides access to metadata about remote artifact and accessors
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ import (
|
|||
"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.
|
||||
type fetcher struct {
|
||||
target resource
|
||||
|
|
@ -130,7 +136,7 @@ func (f *fetcher) fetchManifest(ctx context.Context, ref name.Reference, accepta
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
manifest, err := io.ReadAll(resp.Body)
|
||||
manifest, err := io.ReadAll(io.LimitReader(resp.Body, manifestLimit))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,8 @@ var defaultRetryStatusCodes = []int{
|
|||
http.StatusBadGateway,
|
||||
http.StatusServiceUnavailable,
|
||||
http.StatusGatewayTimeout,
|
||||
499,
|
||||
499, // nginx-specific, client closed request
|
||||
522, // Cloudflare-specific, connection timeout
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
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 {
|
||||
// Wrapped by bearerTransport.
|
||||
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
|
||||
// the registry with which we are interacting.
|
||||
// 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)
|
||||
in.Header.Set("Authorization", hdr)
|
||||
}
|
||||
|
|
@ -135,7 +200,36 @@ func (bt *bearerTransport) refresh(ctx context.Context) error {
|
|||
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 the secret being stored is an identity token,
|
||||
// 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)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Some registries don't have "token" in the response. See #54.
|
||||
type tokenResponse struct {
|
||||
Token string `json:"token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
// TODO: handle expiry?
|
||||
}
|
||||
|
||||
var response tokenResponse
|
||||
var response Token
|
||||
if err := json.Unmarshal(content, &response); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Some registries set access_token instead of token.
|
||||
if response.AccessToken != "" {
|
||||
response.Token = response.AccessToken
|
||||
if response.Token == "" && response.AccessToken == "" {
|
||||
return &response, fmt.Errorf("no token in bearer response:\n%s", content)
|
||||
}
|
||||
|
||||
// Find a token to turn into a Bearer authenticator
|
||||
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
|
||||
return &response, 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)
|
||||
canonicalURLHost := canonicalAddress(in.URL.Host, scheme)
|
||||
canonicalRegistryHost := canonicalAddress(reg.RegistryStr(), scheme)
|
||||
canonicalRegistryHost := canonicalAddress(host, scheme)
|
||||
return canonicalHeaderHost == canonicalRegistryHost || canonicalURLHost == canonicalRegistryHost
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,33 +28,22 @@ import (
|
|||
"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.
|
||||
var fallbackDelay = 300 * time.Millisecond
|
||||
|
||||
type pingResp struct {
|
||||
challenge challenge
|
||||
type Challenge struct {
|
||||
Scheme string
|
||||
|
||||
// Following the challenge there are often key/value pairs
|
||||
// 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.
|
||||
scheme string
|
||||
// Whether we had to use http to complete the Ping.
|
||||
Insecure bool
|
||||
}
|
||||
|
||||
func (c challenge) Canonical() challenge {
|
||||
return challenge(strings.ToLower(string(c)))
|
||||
}
|
||||
|
||||
func ping(ctx context.Context, reg name.Registry, t http.RoundTripper) (*pingResp, error) {
|
||||
// Ping does a GET /v2/ against the registry and returns the response.
|
||||
func Ping(ctx context.Context, reg name.Registry, t http.RoundTripper) (*Challenge, error) {
|
||||
// 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
|
||||
// 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)
|
||||
}
|
||||
|
||||
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}
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -86,27 +75,28 @@ func pingSingle(ctx context.Context, reg name.Registry, t http.RoundTripper, sch
|
|||
resp.Body.Close()
|
||||
}()
|
||||
|
||||
insecure := scheme == "http"
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
// If we get a 200, then no authentication is needed.
|
||||
return &pingResp{
|
||||
challenge: anonymous,
|
||||
scheme: scheme,
|
||||
return &Challenge{
|
||||
Insecure: insecure,
|
||||
}, nil
|
||||
case http.StatusUnauthorized:
|
||||
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.
|
||||
wac := pickFromMultipleChallenges(challenges)
|
||||
return &pingResp{
|
||||
challenge: challenge(wac.Scheme).Canonical(),
|
||||
parameters: wac.Parameters,
|
||||
scheme: scheme,
|
||||
return &Challenge{
|
||||
Scheme: wac.Scheme,
|
||||
Parameters: wac.Parameters,
|
||||
Insecure: insecure,
|
||||
}, nil
|
||||
}
|
||||
// Otherwise, just return the challenge without parameters.
|
||||
return &pingResp{
|
||||
challenge: challenge(resp.Header.Get("WWW-Authenticate")).Canonical(),
|
||||
scheme: scheme,
|
||||
return &Challenge{
|
||||
Scheme: resp.Header.Get("WWW-Authenticate"),
|
||||
Insecure: insecure,
|
||||
}, nil
|
||||
default:
|
||||
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.
|
||||
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{})
|
||||
defer close(returned)
|
||||
|
||||
type pingResult struct {
|
||||
*pingResp
|
||||
*Challenge
|
||||
error
|
||||
primary 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) {
|
||||
pr, err := pingSingle(ctx, reg, t, scheme)
|
||||
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:
|
||||
if pr != nil {
|
||||
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:
|
||||
if res.error == nil {
|
||||
return res.pingResp, nil
|
||||
return res.Challenge, nil
|
||||
}
|
||||
if res.primary {
|
||||
primary = res
|
||||
|
|
@ -164,7 +154,7 @@ func pingParallel(ctx context.Context, reg name.Registry, t http.RoundTripper, s
|
|||
fallback = res
|
||||
}
|
||||
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() {
|
||||
// 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
|
||||
// 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.
|
||||
if matchesHost(st.registry, in, st.scheme) {
|
||||
if matchesHost(st.registry.String(), in, st.scheme) {
|
||||
in.URL.Scheme = st.scheme
|
||||
}
|
||||
return st.inner.RoundTrip(in)
|
||||
|
|
|
|||
47
vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/transport.go
generated
vendored
47
vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/transport.go
generated
vendored
|
|
@ -16,8 +16,8 @@ package transport
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"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
|
||||
// (if one is even necessary).
|
||||
pr, err := ping(ctx, reg, t)
|
||||
pr, err := Ping(ctx, reg, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -69,39 +69,32 @@ func NewWithContext(ctx context.Context, reg name.Registry, auth authn.Authentic
|
|||
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.
|
||||
t = &schemeTransport{
|
||||
scheme: pr.scheme,
|
||||
scheme: scheme,
|
||||
registry: reg,
|
||||
inner: t,
|
||||
}
|
||||
|
||||
switch pr.challenge.Canonical() {
|
||||
case anonymous, basic:
|
||||
if strings.ToLower(pr.Scheme) != "bearer" {
|
||||
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{
|
||||
inner: t,
|
||||
basic: auth,
|
||||
realm: realm,
|
||||
registry: reg,
|
||||
service: service,
|
||||
scopes: scopes,
|
||||
scheme: pr.scheme,
|
||||
}
|
||||
if err := bt.refresh(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Wrapper{bt}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized challenge: %s", pr.challenge)
|
||||
}
|
||||
|
||||
bt, err := fromChallenge(reg, auth, t, pr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bt.scopes = scopes
|
||||
|
||||
if err := bt.refresh(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Wrapper{bt}, nil
|
||||
}
|
||||
|
||||
// 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")
|
||||
resp, err := w.client.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
if origin != "" && origin != w.repo.RegistryStr() {
|
||||
if from != "" {
|
||||
// https://github.com/google/go-containerregistry/issues/1679
|
||||
logs.Warn.Printf("retrying without mount: %v", err)
|
||||
return w.initiateUpload(ctx, "", "", "")
|
||||
|
|
@ -220,7 +220,7 @@ func (w *writer) initiateUpload(ctx context.Context, from, mount, origin string)
|
|||
defer resp.Body.Close()
|
||||
|
||||
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
|
||||
logs.Warn.Printf("retrying without mount: %v", err)
|
||||
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 {
|
||||
// We can't retry streaming layers.
|
||||
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")
|
||||
|
||||
|
|
@ -360,8 +365,16 @@ func (w *writer) uploadOne(ctx context.Context, l v1.Layer) error {
|
|||
if err := w.maybeUpdateScopes(ctx, ml); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
from = ml.Reference.Context().RepositoryStr()
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ func validateChildren(idx v1.ImageIndex, opt ...Option) error {
|
|||
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))
|
||||
}
|
||||
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:
|
||||
// Workaround for #819.
|
||||
if wl, ok := idx.(withLayer); ok {
|
||||
|
|
@ -173,3 +176,54 @@ func validateIndexManifest(idx v1.ImageIndex) error {
|
|||
|
||||
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/function
|
||||
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
|
||||
github.com/google/go-containerregistry/internal/and
|
||||
github.com/google/go-containerregistry/internal/compression
|
||||
|
|
|
|||
Loading…
Reference in New Issue