From 164768c3b9d05c2410f4f672a80c631d21b8cd02 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 11 May 2020 13:39:59 +0000 Subject: Bump github.com/containers/image/v5 from 5.4.3 to 5.4.4 Bumps [github.com/containers/image/v5](https://github.com/containers/image) from 5.4.3 to 5.4.4. - [Release notes](https://github.com/containers/image/releases) - [Commits](https://github.com/containers/image/compare/v5.4.3...v5.4.4) Signed-off-by: dependabot-preview[bot] Signed-off-by: Daniel J Walsh --- vendor/github.com/containers/image/v5/copy/copy.go | 11 +- .../containers/image/v5/docker/docker_client.go | 6 ++ .../containers/image/v5/docker/docker_image.go | 4 +- .../image/v5/docker/docker_image_dest.go | 53 +++++---- .../containers/image/v5/docker/docker_image_src.go | 39 +++---- .../v5/internal/pkg/platform/platform_matcher.go | 91 ++++++++-------- .../v5/internal/uploadreader/upload_reader.go | 61 +++++++++++ .../containers/image/v5/manifest/common.go | 118 +++++++++++++++++++++ .../containers/image/v5/manifest/docker_schema2.go | 97 ++++------------- .../image/v5/manifest/docker_schema2_list.go | 2 +- .../containers/image/v5/manifest/list.go | 24 ----- .../containers/image/v5/manifest/manifest.go | 10 -- .../github.com/containers/image/v5/manifest/oci.go | 106 ++++-------------- .../containers/image/v5/manifest/oci_index.go | 5 +- .../containers/image/v5/version/version.go | 2 +- vendor/github.com/vbauerster/mpb/v5/bar.go | 12 ++- vendor/github.com/vbauerster/mpb/v5/bar_filler.go | 4 +- vendor/github.com/vbauerster/mpb/v5/go.mod | 4 +- vendor/github.com/vbauerster/mpb/v5/go.sum | 8 +- .../vbauerster/mpb/v5/internal/percentage.go | 3 + 20 files changed, 351 insertions(+), 309 deletions(-) create mode 100644 vendor/github.com/containers/image/v5/internal/uploadreader/upload_reader.go create mode 100644 vendor/github.com/containers/image/v5/manifest/common.go (limited to 'vendor/github.com') diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index e8610254c..9fc0e5123 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -798,7 +798,6 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error { // copyGroup is used to determine if all layers are copied copyGroup := sync.WaitGroup{} - copyGroup.Add(numLayers) // copySemaphore is used to limit the number of parallel downloads to // avoid malicious images causing troubles and to be nice to servers. @@ -850,18 +849,22 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error { if err := func() error { // A scope for defer progressPool, progressCleanup := ic.c.newProgressPool(ctx) - defer progressCleanup() + defer func() { + // Wait for all layers to be copied. progressCleanup() must not be called while any of the copyLayerHelpers interact with the progressPool. + copyGroup.Wait() + progressCleanup() + }() for i, srcLayer := range srcInfos { err = copySemaphore.Acquire(ctx, 1) if err != nil { return errors.Wrapf(err, "Can't acquire semaphore") } + copyGroup.Add(1) go copyLayerHelper(i, srcLayer, encLayerBitmap[i], progressPool) } - // Wait for all layers to be copied - copyGroup.Wait() + // A call to copyGroup.Wait() is done at this point by the defer above. return nil }(); err != nil { return err diff --git a/vendor/github.com/containers/image/v5/docker/docker_client.go b/vendor/github.com/containers/image/v5/docker/docker_client.go index c5c49b90b..9461bc91a 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_client.go +++ b/vendor/github.com/containers/image/v5/docker/docker_client.go @@ -613,6 +613,9 @@ func (c *dockerClient) getBearerTokenOAuth2(ctx context.Context, challenge chall params.Add("client_id", "containers/image") authReq.Body = ioutil.NopCloser(bytes.NewBufferString(params.Encode())) + if c.sys != nil && c.sys.DockerRegistryUserAgent != "" { + authReq.Header.Add("User-Agent", c.sys.DockerRegistryUserAgent) + } authReq.Header.Add("Content-Type", "application/x-www-form-urlencoded") logrus.Debugf("%s %s", authReq.Method, authReq.URL.String()) res, err := c.client.Do(authReq) @@ -665,6 +668,9 @@ func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge, if c.auth.Username != "" && c.auth.Password != "" { authReq.SetBasicAuth(c.auth.Username, c.auth.Password) } + if c.sys != nil && c.sys.DockerRegistryUserAgent != "" { + authReq.Header.Add("User-Agent", c.sys.DockerRegistryUserAgent) + } logrus.Debugf("%s %s", authReq.Method, authReq.URL.String()) res, err := c.client.Do(authReq) diff --git a/vendor/github.com/containers/image/v5/docker/docker_image.go b/vendor/github.com/containers/image/v5/docker/docker_image.go index 483581dbc..479effa59 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image.go @@ -37,7 +37,7 @@ func newImage(ctx context.Context, sys *types.SystemContext, ref dockerReference // SourceRefFullName returns a fully expanded name for the repository this image is in. func (i *Image) SourceRefFullName() string { - return i.src.ref.ref.Name() + return i.src.logicalRef.ref.Name() } // GetRepositoryTags list all tags available in the repository. The tag @@ -45,7 +45,7 @@ func (i *Image) SourceRefFullName() string { // backward-compatible shim method which calls the module-level // GetRepositoryTags) func (i *Image) GetRepositoryTags(ctx context.Context) ([]string, error) { - return GetRepositoryTags(ctx, i.src.c.sys, i.src.ref) + return GetRepositoryTags(ctx, i.src.c.sys, i.src.logicalRef) } // GetRepositoryTags list all tags available in the repository. The tag diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go index ab74e1607..979100ee3 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go @@ -16,6 +16,7 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/internal/iolimits" + "github.com/containers/image/v5/internal/uploadreader" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/pkg/blobinfocache/none" "github.com/containers/image/v5/types" @@ -162,20 +163,31 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, digester := digest.Canonical.Digester() sizeCounter := &sizeCounter{} - tee := io.TeeReader(stream, io.MultiWriter(digester.Hash(), sizeCounter)) - res, err = d.c.makeRequestToResolvedURL(ctx, "PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, inputInfo.Size, v2Auth, nil) + uploadLocation, err = func() (*url.URL, error) { // A scope for defer + uploadReader := uploadreader.NewUploadReader(io.TeeReader(stream, io.MultiWriter(digester.Hash(), sizeCounter))) + // This error text should never be user-visible, we terminate only after makeRequestToResolvedURL + // returns, so there isn’t a way for the error text to be provided to any of our callers. + defer uploadReader.Terminate(errors.New("Reading data from an already terminated upload")) + res, err = d.c.makeRequestToResolvedURL(ctx, "PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, uploadReader, inputInfo.Size, v2Auth, nil) + if err != nil { + logrus.Debugf("Error uploading layer chunked %v", err) + return nil, err + } + defer res.Body.Close() + if !successStatus(res.StatusCode) { + return nil, errors.Wrapf(client.HandleErrorResponse(res), "Error uploading layer chunked") + } + uploadLocation, err := res.Location() + if err != nil { + return nil, errors.Wrap(err, "Error determining upload URL") + } + return uploadLocation, nil + }() if err != nil { - logrus.Debugf("Error uploading layer chunked, response %#v", res) return types.BlobInfo{}, err } - defer res.Body.Close() computedDigest := digester.Digest() - uploadLocation, err = res.Location() - if err != nil { - return types.BlobInfo{}, errors.Wrap(err, "Error determining upload URL") - } - // FIXME: DELETE uploadLocation on failure (does not really work in docker/distribution servers, which incorrectly require the "delete" action in the token's scope) locationQuery := uploadLocation.Query() @@ -469,17 +481,17 @@ func (d *dockerImageDestination) PutSignatures(ctx context.Context, signatures [ } switch { case d.c.signatureBase != nil: - return d.putSignaturesToLookaside(signatures, instanceDigest) + return d.putSignaturesToLookaside(signatures, *instanceDigest) case d.c.supportsSignatures: - return d.putSignaturesToAPIExtension(ctx, signatures, instanceDigest) + return d.putSignaturesToAPIExtension(ctx, signatures, *instanceDigest) default: return errors.Errorf("X-Registry-Supports-Signatures extension not supported, and lookaside is not configured") } } // putSignaturesToLookaside implements PutSignatures() from the lookaside location configured in s.c.signatureBase, -// which is not nil. -func (d *dockerImageDestination) putSignaturesToLookaside(signatures [][]byte, instanceDigest *digest.Digest) error { +// which is not nil, for a manifest with manifestDigest. +func (d *dockerImageDestination) putSignaturesToLookaside(signatures [][]byte, manifestDigest digest.Digest) error { // FIXME? This overwrites files one at a time, definitely not atomic. // A failure when updating signatures with a reordered copy could lose some of them. @@ -490,7 +502,7 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures [][]byte, i // NOTE: Keep this in sync with docs/signature-protocols.md! for i, signature := range signatures { - url := signatureStorageURL(d.c.signatureBase, *instanceDigest, i) + url := signatureStorageURL(d.c.signatureBase, manifestDigest, i) if url == nil { return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } @@ -505,7 +517,7 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures [][]byte, i // is enough for dockerImageSource to stop looking for other signatures, so that // is sufficient. for i := len(signatures); ; i++ { - url := signatureStorageURL(d.c.signatureBase, *instanceDigest, i) + url := signatureStorageURL(d.c.signatureBase, manifestDigest, i) if url == nil { return errors.Errorf("Internal error: signatureStorageURL with non-nil base returned nil") } @@ -564,8 +576,9 @@ func (c *dockerClient) deleteOneSignature(url *url.URL) (missing bool, err error } } -// putSignaturesToAPIExtension implements PutSignatures() using the X-Registry-Supports-Signatures API extension. -func (d *dockerImageDestination) putSignaturesToAPIExtension(ctx context.Context, signatures [][]byte, instanceDigest *digest.Digest) error { +// putSignaturesToAPIExtension implements PutSignatures() using the X-Registry-Supports-Signatures API extension, +// for a manifest with manifestDigest. +func (d *dockerImageDestination) putSignaturesToAPIExtension(ctx context.Context, signatures [][]byte, manifestDigest digest.Digest) error { // Skip dealing with the manifest digest, or reading the old state, if not necessary. if len(signatures) == 0 { return nil @@ -575,7 +588,7 @@ func (d *dockerImageDestination) putSignaturesToAPIExtension(ctx context.Context // always adds signatures. Eventually we should also allow removing signatures, // but the X-Registry-Supports-Signatures API extension does not support that yet. - existingSignatures, err := d.c.getExtensionsSignatures(ctx, d.ref, *instanceDigest) + existingSignatures, err := d.c.getExtensionsSignatures(ctx, d.ref, manifestDigest) if err != nil { return err } @@ -600,7 +613,7 @@ sigExists: if err != nil || n != 16 { return errors.Wrapf(err, "Error generating random signature len %d", n) } - signatureName = fmt.Sprintf("%s@%032x", instanceDigest.String(), randBytes) + signatureName = fmt.Sprintf("%s@%032x", manifestDigest.String(), randBytes) if _, ok := existingSigNames[signatureName]; !ok { break } @@ -616,7 +629,7 @@ sigExists: return err } - path := fmt.Sprintf(extensionsSignaturePath, reference.Path(d.ref.ref), d.manifestDigest.String()) + path := fmt.Sprintf(extensionsSignaturePath, reference.Path(d.ref.ref), manifestDigest.String()) res, err := d.c.makeRequest(ctx, "PUT", path, nil, bytes.NewReader(body), v2Auth, nil) if err != nil { return err diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_src.go b/vendor/github.com/containers/image/v5/docker/docker_image_src.go index 9c0c20c64..10aff615e 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_src.go @@ -24,8 +24,9 @@ import ( ) type dockerImageSource struct { - ref dockerReference - c *dockerClient + logicalRef dockerReference // The reference the user requested. + physicalRef dockerReference // The actual reference we are accessing (possibly a mirror) + c *dockerClient // State cachedManifest []byte // nil if not loaded yet cachedManifestMIMEType string // Only valid if cachedManifest != nil @@ -49,7 +50,6 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef } } - primaryDomain := reference.Domain(ref.ref) // Check all endpoints for the manifest availability. If we find one that does // contain the image, it will be used for all future pull actions. Always try the // non-mirror original location last; this both transparently handles the case @@ -66,7 +66,7 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef attempts := []attempt{} for _, pullSource := range pullSources { logrus.Debugf("Trying to access %q", pullSource.Reference) - s, err := newImageSourceAttempt(ctx, sys, pullSource, primaryDomain) + s, err := newImageSourceAttempt(ctx, sys, ref, pullSource) if err == nil { return s, nil } @@ -95,32 +95,33 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef } // newImageSourceAttempt is an internal helper for newImageSource. Everyone else must call newImageSource. -// Given a pullSource and primaryDomain, return a dockerImageSource if it is reachable. +// Given a logicalReference and a pullSource, return a dockerImageSource if it is reachable. // The caller must call .Close() on the returned ImageSource. -func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, pullSource sysregistriesv2.PullSource, primaryDomain string) (*dockerImageSource, error) { - ref, err := newReference(pullSource.Reference) +func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, logicalRef dockerReference, pullSource sysregistriesv2.PullSource) (*dockerImageSource, error) { + physicalRef, err := newReference(pullSource.Reference) if err != nil { return nil, err } endpointSys := sys // sys.DockerAuthConfig does not explicitly specify a registry; we must not blindly send the credentials intended for the primary endpoint to mirrors. - if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(ref.ref) != primaryDomain { + if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(physicalRef.ref) != reference.Domain(logicalRef.ref) { copy := *endpointSys copy.DockerAuthConfig = nil copy.DockerBearerRegistryToken = "" endpointSys = © } - client, err := newDockerClientFromRef(endpointSys, ref, false, "pull") + client, err := newDockerClientFromRef(endpointSys, physicalRef, false, "pull") if err != nil { return nil, err } client.tlsClientConfig.InsecureSkipVerify = pullSource.Endpoint.Insecure s := &dockerImageSource{ - ref: ref, - c: client, + logicalRef: logicalRef, + physicalRef: physicalRef, + c: client, } if err := s.ensureManifestIsLoaded(ctx); err != nil { @@ -132,7 +133,7 @@ func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, pullSo // Reference returns the reference used to set up this source, _as specified by the user_ // (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. func (s *dockerImageSource) Reference() types.ImageReference { - return s.ref + return s.logicalRef } // Close removes resources associated with an initialized ImageSource, if any. @@ -181,7 +182,7 @@ func (s *dockerImageSource) GetManifest(ctx context.Context, instanceDigest *dig } func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest string) ([]byte, string, error) { - path := fmt.Sprintf(manifestPath, reference.Path(s.ref.ref), tagOrDigest) + path := fmt.Sprintf(manifestPath, reference.Path(s.physicalRef.ref), tagOrDigest) headers := map[string][]string{ "Accept": manifest.DefaultRequestedManifestMIMETypes, } @@ -191,7 +192,7 @@ func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest strin } defer res.Body.Close() if res.StatusCode != http.StatusOK { - return nil, "", errors.Wrapf(client.HandleErrorResponse(res), "Error reading manifest %s in %s", tagOrDigest, s.ref.ref.Name()) + return nil, "", errors.Wrapf(client.HandleErrorResponse(res), "Error reading manifest %s in %s", tagOrDigest, s.physicalRef.ref.Name()) } manblob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxManifestBodySize) @@ -213,7 +214,7 @@ func (s *dockerImageSource) ensureManifestIsLoaded(ctx context.Context) error { return nil } - reference, err := s.ref.tagOrDigest() + reference, err := s.physicalRef.tagOrDigest() if err != nil { return err } @@ -271,7 +272,7 @@ func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca return s.getExternalBlob(ctx, info.URLs) } - path := fmt.Sprintf(blobsPath, reference.Path(s.ref.ref), info.Digest.String()) + path := fmt.Sprintf(blobsPath, reference.Path(s.physicalRef.ref), info.Digest.String()) logrus.Debugf("Downloading %s", path) res, err := s.c.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil) if err != nil { @@ -280,7 +281,7 @@ func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca if err := httpResponseToError(res, "Error fetching blob"); err != nil { return nil, 0, err } - cache.RecordKnownLocation(s.ref.Transport(), bicTransportScope(s.ref), info.Digest, newBICLocationReference(s.ref)) + cache.RecordKnownLocation(s.physicalRef.Transport(), bicTransportScope(s.physicalRef), info.Digest, newBICLocationReference(s.physicalRef)) return res.Body, getBlobSize(res), nil } @@ -308,7 +309,7 @@ func (s *dockerImageSource) manifestDigest(ctx context.Context, instanceDigest * if instanceDigest != nil { return *instanceDigest, nil } - if digested, ok := s.ref.ref.(reference.Digested); ok { + if digested, ok := s.physicalRef.ref.(reference.Digested); ok { d := digested.Digest() if d.Algorithm() == digest.Canonical { return d, nil @@ -398,7 +399,7 @@ func (s *dockerImageSource) getSignaturesFromAPIExtension(ctx context.Context, i return nil, err } - parsedBody, err := s.c.getExtensionsSignatures(ctx, s.ref, manifestDigest) + parsedBody, err := s.c.getExtensionsSignatures(ctx, s.physicalRef, manifestDigest) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go b/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go index c23457642..c4d42f3eb 100644 --- a/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go +++ b/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go @@ -115,12 +115,23 @@ func getCPUVariant(os string, arch string) string { return "" } +// compatibility contains, for a specified architecture, a list of known variants, in the +// order from most capable (most restrictive) to least capable (most compatible). +// Architectures that don’t have variants should not have an entry here. var compatibility = map[string][]string{ - "arm": {"v7", "v6", "v5"}, + "arm": {"v8", "v7", "v6", "v5"}, "arm64": {"v8"}, } -// Returns all compatible platforms with the platform specifics possibly overriden by user, +// baseVariants contains, for a specified architecture, a variant that is known to be +// supported by _all_ machines using that architecture. +// Architectures that don’t have variants, or where there are possible versions without +// an established variant name, should not have an entry here. +var baseVariants = map[string]string{ + "arm64": "v8", +} + +// WantedPlatforms returns all compatible platforms with the platform specifics possibly overriden by user, // the most compatible platform is first. // If some option (arch, os, variant) is not present, a value from current platform is detected. func WantedPlatforms(ctx *types.SystemContext) ([]imgspecv1.Platform, error) { @@ -145,59 +156,45 @@ func WantedPlatforms(ctx *types.SystemContext) ([]imgspecv1.Platform, error) { wantedOS = ctx.OSChoice } - var wantedPlatforms []imgspecv1.Platform - if wantedVariant != "" && compatibility[wantedArch] != nil { - wantedPlatforms = make([]imgspecv1.Platform, 0, len(compatibility[wantedArch])) - wantedIndex := -1 - for i, v := range compatibility[wantedArch] { - if wantedVariant == v { - wantedIndex = i - break + var variants []string = nil + if wantedVariant != "" { + if compatibility[wantedArch] != nil { + variantOrder := compatibility[wantedArch] + for i, v := range variantOrder { + if wantedVariant == v { + variants = variantOrder[i:] + break + } } } - // user wants a variant which we know nothing about - not even compatibility - if wantedIndex == -1 { - wantedPlatforms = []imgspecv1.Platform{ - { - OS: wantedOS, - Architecture: wantedArch, - Variant: wantedVariant, - }, - } - } else { - for i := wantedIndex; i < len(compatibility[wantedArch]); i++ { - v := compatibility[wantedArch][i] - wantedPlatforms = append(wantedPlatforms, imgspecv1.Platform{ - OS: wantedOS, - Architecture: wantedArch, - Variant: v, - }) - } + if variants == nil { + // user wants a variant which we know nothing about - not even compatibility + variants = []string{wantedVariant} } + variants = append(variants, "") } else { - wantedPlatforms = []imgspecv1.Platform{ - { - OS: wantedOS, - Architecture: wantedArch, - Variant: wantedVariant, - }, + variants = append(variants, "") // No variant specified, use a “no variant specified” image if present + if baseVariant, ok := baseVariants[wantedArch]; ok { + // But also accept an image with the “base” variant for the architecture, if it exists. + variants = append(variants, baseVariant) } } - return wantedPlatforms, nil + res := make([]imgspecv1.Platform, 0, len(variants)) + for _, v := range variants { + res = append(res, imgspecv1.Platform{ + OS: wantedOS, + Architecture: wantedArch, + Variant: v, + }) + } + return res, nil } +// MatchesPlatform returns true if a platform descriptor from a multi-arch image matches +// an item from the return value of WantedPlatforms. func MatchesPlatform(image imgspecv1.Platform, wanted imgspecv1.Platform) bool { - if image.Architecture != wanted.Architecture { - return false - } - if image.OS != wanted.OS { - return false - } - - if wanted.Variant == "" || image.Variant == wanted.Variant { - return true - } - - return false + return image.Architecture == wanted.Architecture && + image.OS == wanted.OS && + image.Variant == wanted.Variant } diff --git a/vendor/github.com/containers/image/v5/internal/uploadreader/upload_reader.go b/vendor/github.com/containers/image/v5/internal/uploadreader/upload_reader.go new file mode 100644 index 000000000..6aa9ead68 --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/uploadreader/upload_reader.go @@ -0,0 +1,61 @@ +package uploadreader + +import ( + "io" + "sync" +) + +// UploadReader is a pass-through reader for use in sending non-trivial data using the net/http +// package (http.NewRequest, http.Post and the like). +// +// The net/http package uses a separate goroutine to upload data to a HTTP connection, +// and it is possible for the server to return a response (typically an error) before consuming +// the full body of the request. In that case http.Client.Do can return with an error while +// the body is still being read — regardless of of the cancellation, if any, of http.Request.Context(). +// +// As a result, any data used/updated by the io.Reader() provided as the request body may be +// used/updated even after http.Client.Do returns, causing races. +// +// To fix this, UploadReader provides a synchronized Terminate() method, which can block for +// a not-completely-negligible time (for a duration of the underlying Read()), but guarantees that +// after Terminate() returns, the underlying reader is never used any more (unlike calling +// the cancellation callback of context.WithCancel, which returns before any recipients may have +// reacted to the cancellation). +type UploadReader struct { + mutex sync.Mutex + // The following members can only be used with mutex held + reader io.Reader + terminationError error // nil if not terminated yet +} + +// NewUploadReader returns an UploadReader for an "underlying" reader. +func NewUploadReader(underlying io.Reader) *UploadReader { + return &UploadReader{ + reader: underlying, + terminationError: nil, + } +} + +// Read returns the error set by Terminate, if any, or calls the underlying reader. +// It is safe to call this from a different goroutine than Terminate. +func (ur *UploadReader) Read(p []byte) (int, error) { + ur.mutex.Lock() + defer ur.mutex.Unlock() + + if ur.terminationError != nil { + return 0, ur.terminationError + } + return ur.reader.Read(p) +} + +// Terminate waits for in-progress Read calls, if any, to finish, and ensures that after +// this function returns, any Read calls will fail with the provided error, and the underlying +// reader will never be used any more. +// +// It is safe to call this from a different goroutine than Read. +func (ur *UploadReader) Terminate(err error) { + ur.mutex.Lock() // May block for some time if ur.reader.Read() is in progress + defer ur.mutex.Unlock() + + ur.terminationError = err +} diff --git a/vendor/github.com/containers/image/v5/manifest/common.go b/vendor/github.com/containers/image/v5/manifest/common.go new file mode 100644 index 000000000..fa2b39e0e --- /dev/null +++ b/vendor/github.com/containers/image/v5/manifest/common.go @@ -0,0 +1,118 @@ +package manifest + +import ( + "fmt" + + "github.com/containers/image/v5/pkg/compression" + "github.com/containers/image/v5/types" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// dupStringSlice returns a deep copy of a slice of strings, or nil if the +// source slice is empty. +func dupStringSlice(list []string) []string { + if len(list) == 0 { + return nil + } + dup := make([]string, len(list)) + copy(dup, list) + return dup +} + +// dupStringStringMap returns a deep copy of a map[string]string, or nil if the +// passed-in map is nil or has no keys. +func dupStringStringMap(m map[string]string) map[string]string { + if len(m) == 0 { + return nil + } + result := make(map[string]string) + for k, v := range m { + result[k] = v + } + return result +} + +// layerInfosToStrings converts a list of layer infos, presumably obtained from a Manifest.LayerInfos() +// method call, into a format suitable for inclusion in a types.ImageInspectInfo structure. +func layerInfosToStrings(infos []LayerInfo) []string { + layers := make([]string, len(infos)) + for i, info := range infos { + layers[i] = info.Digest.String() + } + return layers +} + +// compressionMIMETypeSet describes a set of MIME type “variants” that represent differently-compressed +// versions of “the same kind of content”. +// The map key is the return value of compression.Algorithm.Name(), or mtsUncompressed; +// the map value is a MIME type, or mtsUnsupportedMIMEType to mean "recognized but unsupported". +type compressionMIMETypeSet map[string]string + +const mtsUncompressed = "" // A key in compressionMIMETypeSet for the uncompressed variant +const mtsUnsupportedMIMEType = "" // A value in compressionMIMETypeSet that means “recognized but unsupported” + +// compressionVariantMIMEType returns a variant of mimeType for the specified algorithm (which may be nil +// to mean "no compression"), based on variantTable. +func compressionVariantMIMEType(variantTable []compressionMIMETypeSet, mimeType string, algorithm *compression.Algorithm) (string, error) { + if mimeType == mtsUnsupportedMIMEType { // Prevent matching against the {algo:mtsUnsupportedMIMEType} entries + return "", fmt.Errorf("cannot update unknown MIME type") + } + for _, variants := range variantTable { + for _, mt := range variants { + if mt == mimeType { // Found the variant + name := mtsUncompressed + if algorithm != nil { + name = algorithm.Name() + } + if res, ok := variants[name]; ok { + if res != mtsUnsupportedMIMEType { + return res, nil + } + if name != mtsUncompressed { + return "", fmt.Errorf("%s compression is not supported", name) + } + return "", errors.New("uncompressed variant is not supported") + } + if name != mtsUncompressed { + return "", fmt.Errorf("unknown compression algorithm %s", name) + } + // We can't very well say “the idea of no compression is unknown” + return "", errors.New("uncompressed variant is not supported") + } + } + } + if algorithm != nil { + return "", fmt.Errorf("unsupported MIME type for compression: %s", mimeType) + } + return "", fmt.Errorf("unsupported MIME type for decompression: %s", mimeType) +} + +// updatedMIMEType returns the result of applying edits in updated (MediaType, CompressionOperation) to +// mimeType, based on variantTable. It may use updated.Digest for error messages. +func updatedMIMEType(variantTable []compressionMIMETypeSet, mimeType string, updated types.BlobInfo) (string, error) { + // Note that manifests in containers-storage might be reporting the + // wrong media type since the original manifests are stored while layers + // are decompressed in storage. Hence, we need to consider the case + // that an already {de}compressed layer should be {de}compressed; + // compressionVariantMIMEType does that by not caring whether the original is + // {de}compressed. + switch updated.CompressionOperation { + case types.PreserveOriginal: + // Keep the original media type. + return mimeType, nil + + case types.Decompress: + return compressionVariantMIMEType(variantTable, mimeType, nil) + + case types.Compress: + if updated.CompressionAlgorithm == nil { + logrus.Debugf("Error preparing updated manifest: blob %q was compressed but does not specify by which algorithm: falling back to use the original blob", updated.Digest) + return mimeType, nil + } + return compressionVariantMIMEType(variantTable, mimeType, updated.CompressionAlgorithm) + + default: + return "", fmt.Errorf("unknown compression operation (%d)", updated.CompressionOperation) + } +} diff --git a/vendor/github.com/containers/image/v5/manifest/docker_schema2.go b/vendor/github.com/containers/image/v5/manifest/docker_schema2.go index ff0780fe3..8d8bb9e01 100644 --- a/vendor/github.com/containers/image/v5/manifest/docker_schema2.go +++ b/vendor/github.com/containers/image/v5/manifest/docker_schema2.go @@ -10,7 +10,6 @@ import ( "github.com/containers/image/v5/types" "github.com/opencontainers/go-digest" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // Schema2Descriptor is a “descriptor” in docker/distribution schema 2. @@ -213,26 +212,17 @@ func (m *Schema2) LayerInfos() []LayerInfo { return blobs } -// isSchema2ForeignLayer is a convenience wrapper to check if a given mime type -// is a compressed or decompressed schema 2 foreign layer. -func isSchema2ForeignLayer(mimeType string) bool { - switch mimeType { - case DockerV2Schema2ForeignLayerMediaType, DockerV2Schema2ForeignLayerMediaTypeGzip: - return true - default: - return false - } -} - -// isSchema2Layer is a convenience wrapper to check if a given mime type is a -// compressed or decompressed schema 2 layer. -func isSchema2Layer(mimeType string) bool { - switch mimeType { - case DockerV2SchemaLayerMediaTypeUncompressed, DockerV2Schema2LayerMediaType: - return true - default: - return false - } +var schema2CompressionMIMETypeSets = []compressionMIMETypeSet{ + { + mtsUncompressed: DockerV2Schema2ForeignLayerMediaType, + compression.Gzip.Name(): DockerV2Schema2ForeignLayerMediaTypeGzip, + compression.Zstd.Name(): mtsUnsupportedMIMEType, + }, + { + mtsUncompressed: DockerV2SchemaLayerMediaTypeUncompressed, + compression.Gzip.Name(): DockerV2Schema2LayerMediaType, + compression.Zstd.Name(): mtsUnsupportedMIMEType, + }, } // UpdateLayerInfos replaces the original layers with the specified BlobInfos (size+digest+urls), in order (the root layer first, and then successive layered layers) @@ -243,67 +233,16 @@ func (m *Schema2) UpdateLayerInfos(layerInfos []types.BlobInfo) error { original := m.LayersDescriptors m.LayersDescriptors = make([]Schema2Descriptor, len(layerInfos)) for i, info := range layerInfos { + mimeType := original[i].MediaType // First make sure we support the media type of the original layer. - if err := SupportedSchema2MediaType(original[i].MediaType); err != nil { - return fmt.Errorf("Error preparing updated manifest: unknown media type of original layer: %q", original[i].MediaType) + if err := SupportedSchema2MediaType(mimeType); err != nil { + return fmt.Errorf("Error preparing updated manifest: unknown media type of original layer %q: %q", info.Digest, mimeType) } - - // Set the correct media types based on the specified compression - // operation, the desired compression algorithm AND the original media - // type. - // - // Note that manifests in containers-storage might be reporting the - // wrong media type since the original manifests are stored while layers - // are decompressed in storage. Hence, we need to consider the case - // that an already {de}compressed layer should be {de}compressed, which - // is being addressed in `isSchema2{Foreign}Layer`. - switch info.CompressionOperation { - case types.PreserveOriginal: - // Keep the original media type. - m.LayersDescriptors[i].MediaType = original[i].MediaType - - case types.Decompress: - // Decompress the original media type and check if it was - // non-distributable one or not. - mimeType := original[i].MediaType - switch { - case isSchema2ForeignLayer(mimeType): - m.LayersDescriptors[i].MediaType = DockerV2Schema2ForeignLayerMediaType - case isSchema2Layer(mimeType): - m.LayersDescriptors[i].MediaType = DockerV2SchemaLayerMediaTypeUncompressed - default: - return fmt.Errorf("Error preparing updated manifest: unsupported media type for decompression: %q", original[i].MediaType) - } - - case types.Compress: - if info.CompressionAlgorithm == nil { - logrus.Debugf("Preparing updated manifest: blob %q was compressed but does not specify by which algorithm: falling back to use the original blob", info.Digest) - m.LayersDescriptors[i].MediaType = original[i].MediaType - break - } - // Compress the original media type and set the new one based on - // that type (distributable or not) and the specified compression - // algorithm. Throw an error if the algorithm is not supported. - switch info.CompressionAlgorithm.Name() { - case compression.Gzip.Name(): - mimeType := original[i].MediaType - switch { - case isSchema2ForeignLayer(mimeType): - m.LayersDescriptors[i].MediaType = DockerV2Schema2ForeignLayerMediaTypeGzip - case isSchema2Layer(mimeType): - m.LayersDescriptors[i].MediaType = DockerV2Schema2LayerMediaType - default: - return fmt.Errorf("Error preparing updated manifest: unsupported media type for compression: %q", original[i].MediaType) - } - case compression.Zstd.Name(): - return fmt.Errorf("Error preparing updated manifest: zstd compression is not supported for docker images") - default: - return fmt.Errorf("Error preparing updated manifest: unknown compression algorithm %q for layer %q", info.CompressionAlgorithm.Name(), info.Digest) - } - - default: - return fmt.Errorf("Error preparing updated manifest: unknown compression operation (%d) for layer %q", info.CompressionOperation, info.Digest) + mimeType, err := updatedMIMEType(schema2CompressionMIMETypeSets, mimeType, info) + if err != nil { + return errors.Wrapf(err, "Error preparing updated manifest, layer %q", info.Digest) } + m.LayersDescriptors[i].MediaType = mimeType m.LayersDescriptors[i].Digest = info.Digest m.LayersDescriptors[i].Size = info.Size m.LayersDescriptors[i].URLs = info.URLs diff --git a/vendor/github.com/containers/image/v5/manifest/docker_schema2_list.go b/vendor/github.com/containers/image/v5/manifest/docker_schema2_list.go index 5f96a981a..bfedff69c 100644 --- a/vendor/github.com/containers/image/v5/manifest/docker_schema2_list.go +++ b/vendor/github.com/containers/image/v5/manifest/docker_schema2_list.go @@ -107,7 +107,7 @@ func (list *Schema2List) ChooseInstance(ctx *types.SystemContext) (digest.Digest } } } - return "", fmt.Errorf("no image found in manifest list for architecture %s, variant %s, OS %s", wantedPlatforms[0].Architecture, wantedPlatforms[0].Variant, wantedPlatforms[0].OS) + return "", fmt.Errorf("no image found in manifest list for architecture %s, variant %q, OS %s", wantedPlatforms[0].Architecture, wantedPlatforms[0].Variant, wantedPlatforms[0].OS) } // Serialize returns the list in a blob format. diff --git a/vendor/github.com/containers/image/v5/manifest/list.go b/vendor/github.com/containers/image/v5/manifest/list.go index c7d741dc2..58982597e 100644 --- a/vendor/github.com/containers/image/v5/manifest/list.go +++ b/vendor/github.com/containers/image/v5/manifest/list.go @@ -59,30 +59,6 @@ type ListUpdate struct { MediaType string } -// dupStringSlice returns a deep copy of a slice of strings, or nil if the -// source slice is empty. -func dupStringSlice(list []string) []string { - if len(list) == 0 { - return nil - } - dup := make([]string, len(list)) - copy(dup, list) - return dup -} - -// dupStringStringMap returns a deep copy of a map[string]string, or nil if the -// passed-in map is nil or has no keys. -func dupStringStringMap(m map[string]string) map[string]string { - if len(m) == 0 { - return nil - } - result := make(map[string]string) - for k, v := range m { - result[k] = v - } - return result -} - // ListFromBlob parses a list of manifests. func ListFromBlob(manifest []byte, manifestMIMEType string) (List, error) { normalized := NormalizedMIMEType(manifestMIMEType) diff --git a/vendor/github.com/containers/image/v5/manifest/manifest.go b/vendor/github.com/containers/image/v5/manifest/manifest.go index 033b8d951..7b0758873 100644 --- a/vendor/github.com/containers/image/v5/manifest/manifest.go +++ b/vendor/github.com/containers/image/v5/manifest/manifest.go @@ -256,13 +256,3 @@ func FromBlob(manblob []byte, mt string) (Manifest, error) { // Note that this may not be reachable, NormalizedMIMEType has a default for unknown values. return nil, fmt.Errorf("Unimplemented manifest MIME type %s (normalized as %s)", mt, nmt) } - -// layerInfosToStrings converts a list of layer infos, presumably obtained from a Manifest.LayerInfos() -// method call, into a format suitable for inclusion in a types.ImageInspectInfo structure. -func layerInfosToStrings(infos []LayerInfo) []string { - layers := make([]string, len(infos)) - for i, info := range infos { - layers[i] = info.Digest.String() - } - return layers -} diff --git a/vendor/github.com/containers/image/v5/manifest/oci.go b/vendor/github.com/containers/image/v5/manifest/oci.go index aafe6693b..40c40dee8 100644 --- a/vendor/github.com/containers/image/v5/manifest/oci.go +++ b/vendor/github.com/containers/image/v5/manifest/oci.go @@ -12,7 +12,6 @@ import ( "github.com/opencontainers/image-spec/specs-go" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // BlobInfoFromOCI1Descriptor returns a types.BlobInfo based on the input OCI1 descriptor. @@ -95,26 +94,17 @@ func (m *OCI1) LayerInfos() []LayerInfo { return blobs } -// isOCI1NonDistributableLayer is a convenience wrapper to check if a given mime -// type is a compressed or decompressed OCI v1 non-distributable layer. -func isOCI1NonDistributableLayer(mimeType string) bool { - switch mimeType { - case imgspecv1.MediaTypeImageLayerNonDistributable, imgspecv1.MediaTypeImageLayerNonDistributableGzip, imgspecv1.MediaTypeImageLayerNonDistributableZstd: - return true - default: - return false - } -} - -// isOCI1Layer is a convenience wrapper to check if a given mime type is a -// compressed or decompressed OCI v1 layer. -func isOCI1Layer(mimeType string) bool { - switch mimeType { - case imgspecv1.MediaTypeImageLayer, imgspecv1.MediaTypeImageLayerGzip, imgspecv1.MediaTypeImageLayerZstd: - return true - default: - return false - } +var oci1CompressionMIMETypeSets = []compressionMIMETypeSet{ + { + mtsUncompressed: imgspecv1.MediaTypeImageLayerNonDistributable, + compression.Gzip.Name(): imgspecv1.MediaTypeImageLayerNonDistributableGzip, + compression.Zstd.Name(): imgspecv1.MediaTypeImageLayerNonDistributableZstd, + }, + { + mtsUncompressed: imgspecv1.MediaTypeImageLayer, + compression.Gzip.Name(): imgspecv1.MediaTypeImageLayerGzip, + compression.Zstd.Name(): imgspecv1.MediaTypeImageLayerZstd, + }, } // UpdateLayerInfos replaces the original layers with the specified BlobInfos (size+digest+urls+mediatype), in order (the root layer first, and then successive layered layers) @@ -133,79 +123,19 @@ func (m *OCI1) UpdateLayerInfos(layerInfos []types.BlobInfo) error { } mimeType = decMimeType } - - // Set the correct media types based on the specified compression - // operation, the desired compression algorithm AND the original media - // type. - // - // Note that manifests in containers-storage might be reporting the - // wrong media type since the original manifests are stored while layers - // are decompressed in storage. Hence, we need to consider the case - // that an already {de}compressed layer should be {de}compressed, which - // is being addressed in `isSchema2{Foreign}Layer`. - switch info.CompressionOperation { - case types.PreserveOriginal: - // Keep the original media type. - m.Layers[i].MediaType = mimeType - - case types.Decompress: - // Decompress the original media type and check if it was - // non-distributable one or not. - switch { - case isOCI1NonDistributableLayer(mimeType): - m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayerNonDistributable - case isOCI1Layer(mimeType): - m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayer - default: - return fmt.Errorf("Error preparing updated manifest: unsupported media type for decompression: %q", mimeType) - } - - case types.Compress: - if info.CompressionAlgorithm == nil { - logrus.Debugf("Error preparing updated manifest: blob %q was compressed but does not specify by which algorithm: falling back to use the original blob", info.Digest) - m.Layers[i].MediaType = mimeType - break - } - // Compress the original media type and set the new one based on - // that type (distributable or not) and the specified compression - // algorithm. Throw an error if the algorithm is not supported. - switch info.CompressionAlgorithm.Name() { - case compression.Gzip.Name(): - switch { - case isOCI1NonDistributableLayer(mimeType): - m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayerNonDistributableGzip - case isOCI1Layer(mimeType): - m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayerGzip - default: - return fmt.Errorf("Error preparing updated manifest: unsupported media type for compression: %q", mimeType) - } - - case compression.Zstd.Name(): - switch { - case isOCI1NonDistributableLayer(mimeType): - m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayerNonDistributableZstd - case isOCI1Layer(mimeType): - m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayerZstd - default: - return fmt.Errorf("Error preparing updated manifest: unsupported media type for compression: %q", mimeType) - } - - default: - return fmt.Errorf("Error preparing updated manifest: unknown compression algorithm %q for layer %q", info.CompressionAlgorithm.Name(), info.Digest) - } - - default: - return fmt.Errorf("Error preparing updated manifest: unknown compression operation (%d) for layer %q", info.CompressionOperation, info.Digest) + mimeType, err := updatedMIMEType(oci1CompressionMIMETypeSets, mimeType, info) + if err != nil { + return errors.Wrapf(err, "Error preparing updated manifest, layer %q", info.Digest) } - if info.CryptoOperation == types.Encrypt { - encMediaType, err := getEncryptedMediaType(m.Layers[i].MediaType) + encMediaType, err := getEncryptedMediaType(mimeType) if err != nil { - return fmt.Errorf("error preparing updated manifest: encryption specified but no counterpart for mediatype: %q", m.Layers[i].MediaType) + return fmt.Errorf("error preparing updated manifest: encryption specified but no counterpart for mediatype: %q", mimeType) } - m.Layers[i].MediaType = encMediaType + mimeType = encMediaType } + m.Layers[i].MediaType = mimeType m.Layers[i].Digest = info.Digest m.Layers[i].Size = info.Size m.Layers[i].Annotations = info.Annotations diff --git a/vendor/github.com/containers/image/v5/manifest/oci_index.go b/vendor/github.com/containers/image/v5/manifest/oci_index.go index 18cc8135c..7bdea8fb2 100644 --- a/vendor/github.com/containers/image/v5/manifest/oci_index.go +++ b/vendor/github.com/containers/image/v5/manifest/oci_index.go @@ -79,6 +79,9 @@ func (index *OCI1Index) ChooseInstance(ctx *types.SystemContext) (digest.Digest, } for _, wantedPlatform := range wantedPlatforms { for _, d := range index.Manifests { + if d.Platform == nil { + continue + } imagePlatform := imgspecv1.Platform{ Architecture: d.Platform.Architecture, OS: d.Platform.OS, @@ -97,7 +100,7 @@ func (index *OCI1Index) ChooseInstance(ctx *types.SystemContext) (digest.Digest, return d.Digest, nil } } - return "", fmt.Errorf("no image found in image index for architecture %s, variant %s, OS %s", wantedPlatforms[0].Architecture, wantedPlatforms[0].Variant, wantedPlatforms[0].OS) + return "", fmt.Errorf("no image found in image index for architecture %s, variant %q, OS %s", wantedPlatforms[0].Architecture, wantedPlatforms[0].Variant, wantedPlatforms[0].OS) } // Serialize returns the index in a blob format. diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index 717c2b1b3..67f57e03e 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -8,7 +8,7 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 4 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 3 + VersionPatch = 4 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "" diff --git a/vendor/github.com/vbauerster/mpb/v5/bar.go b/vendor/github.com/vbauerster/mpb/v5/bar.go index 1a4c66fe1..13bda2247 100644 --- a/vendor/github.com/vbauerster/mpb/v5/bar.go +++ b/vendor/github.com/vbauerster/mpb/v5/bar.go @@ -69,6 +69,7 @@ type bState struct { trimSpace bool toComplete bool completeFlushed bool + ignoreComplete bool noPop bool aDecorators []decor.Decorator pDecorators []decor.Decorator @@ -170,17 +171,18 @@ func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) { } // SetTotal sets total dynamically. -// If total is less or equal to zero it takes progress' current value. -// If complete is true, complete event will be triggered. +// If total is less than or equal to zero it takes progress' current value. +// A complete flag enables or disables complete event on `current >= total`. func (b *Bar) SetTotal(total int64, complete bool) { select { case b.operateState <- func(s *bState) { + s.ignoreComplete = !complete if total <= 0 { s.total = s.current } else { s.total = total } - if complete && !s.toComplete { + if !s.ignoreComplete && !s.toComplete { s.current = s.total s.toComplete = true go b.refreshTillShutdown() @@ -197,7 +199,7 @@ func (b *Bar) SetCurrent(current int64) { s.iterated = true s.lastN = current - s.current s.current = current - if s.total > 0 && s.current >= s.total { + if !s.ignoreComplete && s.current >= s.total { s.current = s.total s.toComplete = true go b.refreshTillShutdown() @@ -224,7 +226,7 @@ func (b *Bar) IncrInt64(n int64) { s.iterated = true s.lastN = n s.current += n - if s.total > 0 && s.current >= s.total { + if !s.ignoreComplete && s.current >= s.total { s.current = s.total s.toComplete = true go b.refreshTillShutdown() diff --git a/vendor/github.com/vbauerster/mpb/v5/bar_filler.go b/vendor/github.com/vbauerster/mpb/v5/bar_filler.go index 00bf0a494..33dbf191d 100644 --- a/vendor/github.com/vbauerster/mpb/v5/bar_filler.go +++ b/vendor/github.com/vbauerster/mpb/v5/bar_filler.go @@ -76,7 +76,7 @@ func (s *barFiller) SetReverse(reverse bool) { s.flush = reverseFlush } else { s.tip = s.format[rTip] - s.flush = normalFlush + s.flush = regularFlush } s.reverse = reverse } @@ -125,7 +125,7 @@ func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) { s.flush(w, bb) } -func normalFlush(w io.Writer, bb [][]byte) { +func regularFlush(w io.Writer, bb [][]byte) { for i := 0; i < len(bb); i++ { w.Write(bb[i]) } diff --git a/vendor/github.com/vbauerster/mpb/v5/go.mod b/vendor/github.com/vbauerster/mpb/v5/go.mod index 672191fc8..1d8d52934 100644 --- a/vendor/github.com/vbauerster/mpb/v5/go.mod +++ b/vendor/github.com/vbauerster/mpb/v5/go.mod @@ -3,8 +3,8 @@ module github.com/vbauerster/mpb/v5 require ( github.com/VividCortex/ewma v1.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 - golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect + golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 + golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect ) go 1.14 diff --git a/vendor/github.com/vbauerster/mpb/v5/go.sum b/vendor/github.com/vbauerster/mpb/v5/go.sum index 9a411976a..99ca1bf67 100644 --- a/vendor/github.com/vbauerster/mpb/v5/go.sum +++ b/vendor/github.com/vbauerster/mpb/v5/go.sum @@ -3,11 +3,11 @@ github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmx github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA= -golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU= +golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/github.com/vbauerster/mpb/v5/internal/percentage.go b/vendor/github.com/vbauerster/mpb/v5/internal/percentage.go index 7e261cb22..e321e0a6b 100644 --- a/vendor/github.com/vbauerster/mpb/v5/internal/percentage.go +++ b/vendor/github.com/vbauerster/mpb/v5/internal/percentage.go @@ -7,6 +7,9 @@ func Percentage(total, current int64, width int) float64 { if total <= 0 { return 0 } + if current >= total { + return float64(width) + } return float64(int64(width)*current) / float64(total) } -- cgit v1.2.3-54-g00ecf