From a6707e4348b3ae4db60eadbb77bb1af3b9e8bf94 Mon Sep 17 00:00:00 2001 From: Miloslav Trmač Date: Sat, 9 Feb 2019 00:13:48 +0100 Subject: Vendor in latest c/storage and c/image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miloslav Trmač --- vendor.conf | 4 +- vendor/github.com/containers/image/copy/copy.go | 87 +++- .../containers/image/docker/docker_client.go | 33 +- .../containers/image/docker/docker_image.go | 2 +- .../containers/image/docker/docker_image_dest.go | 62 +-- .../containers/image/docker/docker_image_src.go | 16 +- .../containers/image/docker/tarfile/src.go | 76 ++-- .../containers/image/ostree/ostree_src.go | 13 +- .../pkg/sysregistriesv2/system_registries_v2.go | 23 +- .../containers/image/storage/storage_image.go | 73 +++- .../containers/image/storage/storage_reference.go | 22 +- .../containers/image/storage/storage_transport.go | 5 - .../github.com/containers/image/version/version.go | 2 +- vendor/github.com/containers/storage/images.go | 193 ++++++--- .../github.com/containers/storage/images_ffjson.go | 2 +- .../containers/storage/pkg/config/config.go | 96 +++++ vendor/github.com/containers/storage/store.go | 441 ++++++++++++--------- vendor/github.com/containers/storage/vendor.conf | 9 +- 18 files changed, 763 insertions(+), 396 deletions(-) create mode 100644 vendor/github.com/containers/storage/pkg/config/config.go diff --git a/vendor.conf b/vendor.conf index 911b83325..36452038f 100644 --- a/vendor.conf +++ b/vendor.conf @@ -15,8 +15,8 @@ github.com/containerd/cgroups 39b18af02c4120960f517a3a4c2588fabb61d02c github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d github.com/containernetworking/cni v0.7.0-alpha1 github.com/containernetworking/plugins v0.7.4 -github.com/containers/image v1.3 -github.com/containers/storage v1.9 +github.com/containers/image 93bced01015eb94bec4821df1876314be8197680 +github.com/containers/storage 06b6c2e4cf254f5922a79da058c94ac2a65bb92f github.com/containers/psgo v1.1 github.com/coreos/go-systemd v14 github.com/cri-o/ocicni 2d2983e40c242322a56c22a903785e7f83eb378c diff --git a/vendor/github.com/containers/image/copy/copy.go b/vendor/github.com/containers/image/copy/copy.go index 89c7e580f..2d3a2a1a8 100644 --- a/vendor/github.com/containers/image/copy/copy.go +++ b/vendor/github.com/containers/image/copy/copy.go @@ -6,13 +6,16 @@ import ( "fmt" "io" "io/ioutil" + "os" "reflect" "runtime" "strings" "sync" "time" + "github.com/containers/image/docker/reference" "github.com/containers/image/image" + "github.com/containers/image/manifest" "github.com/containers/image/pkg/blobinfocache" "github.com/containers/image/pkg/compression" "github.com/containers/image/signature" @@ -22,6 +25,7 @@ import ( "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh/terminal" "golang.org/x/sync/semaphore" pb "gopkg.in/cheggaaa/pb.v1" ) @@ -84,6 +88,7 @@ type copier struct { dest types.ImageDestination rawSource types.ImageSource reportWriter io.Writer + progressOutput io.Writer progressInterval time.Duration progress chan types.ProgressProperties blobInfoCache types.BlobInfoCache @@ -152,11 +157,19 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, } }() + // If reportWriter is not a TTY (e.g., when piping to a file), do not + // print the progress bars to avoid long and hard to parse output. + // createProgressBar() will print a single line instead. + progressOutput := reportWriter + if !isTTY(reportWriter) { + progressOutput = ioutil.Discard + } copyInParallel := dest.HasThreadSafePutBlob() && rawSource.HasThreadSafeGetBlob() c := &copier{ dest: dest, rawSource: rawSource, reportWriter: reportWriter, + progressOutput: progressOutput, progressInterval: options.ProgressInterval, progress: options.Progress, copyInParallel: copyInParallel, @@ -201,7 +214,7 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, // Image copies a single (on-manifest-list) image unparsedImage, using policyContext to validate // source image admissibility. -func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.PolicyContext, options *Options, unparsedImage *image.UnparsedImage) (manifest []byte, retErr error) { +func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.PolicyContext, options *Options, unparsedImage *image.UnparsedImage) (manifestBytes []byte, retErr error) { // The caller is handling manifest lists; this could happen only if a manifest list contains a manifest list. // Make sure we fail cleanly in such cases. multiImage, err := isMultiImage(ctx, unparsedImage) @@ -224,6 +237,26 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli return nil, errors.Wrapf(err, "Error initializing image from source %s", transports.ImageName(c.rawSource.Reference())) } + // If the destination is a digested reference, make a note of that, determine what digest value we're + // expecting, and check that the source manifest matches it. + destIsDigestedReference := false + if named := c.dest.Reference().DockerReference(); named != nil { + if digested, ok := named.(reference.Digested); ok { + destIsDigestedReference = true + sourceManifest, _, err := src.Manifest(ctx) + if err != nil { + return nil, errors.Wrapf(err, "Error reading manifest from source image") + } + matches, err := manifest.MatchesDigest(sourceManifest, digested.Digest()) + if err != nil { + return nil, errors.Wrapf(err, "Error computing digest of source image's manifest") + } + if !matches { + return nil, errors.New("Digest of source image's manifest would not match destination reference") + } + } + } + if err := checkImageDestinationForCurrentRuntimeOS(ctx, options.DestinationCtx, src, c.dest); err != nil { return nil, err } @@ -251,15 +284,15 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli manifestUpdates: &types.ManifestUpdateOptions{InformationOnly: types.ManifestUpdateInformation{Destination: c.dest}}, src: src, // diffIDsAreNeeded is computed later - canModifyManifest: len(sigs) == 0, - // Ensure _this_ copy sees exactly the intended data when either processing a signed image or signing it. - // This may be too conservative, but for now, better safe than sorry, _especially_ on the SignBy path: - // The signature makes the content non-repudiable, so it very much matters that the signature is made over exactly what the user intended. - // We do intend the RecordDigestUncompressedPair calls to only work with reliable data, but at least there’s a risk - // that the compressed version coming from a third party may be designed to attack some other decompressor implementation, - // and we would reuse and sign it. - canSubstituteBlobs: len(sigs) == 0 && options.SignBy == "", + canModifyManifest: len(sigs) == 0 && !destIsDigestedReference, } + // Ensure _this_ copy sees exactly the intended data when either processing a signed image or signing it. + // This may be too conservative, but for now, better safe than sorry, _especially_ on the SignBy path: + // The signature makes the content non-repudiable, so it very much matters that the signature is made over exactly what the user intended. + // We do intend the RecordDigestUncompressedPair calls to only work with reliable data, but at least there’s a risk + // that the compressed version coming from a third party may be designed to attack some other decompressor implementation, + // and we would reuse and sign it. + ic.canSubstituteBlobs = ic.canModifyManifest && options.SignBy == "" if err := ic.updateEmbeddedDockerReference(); err != nil { return nil, err @@ -283,7 +316,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli // and at least with the OpenShift registry "acceptschema2" option, there is no way to detect the support // without actually trying to upload something and getting a types.ManifestTypeRejectedError. // So, try the preferred manifest MIME type. If the process succeeds, fine… - manifest, err = ic.copyUpdatedConfigAndManifest(ctx) + manifestBytes, err = ic.copyUpdatedConfigAndManifest(ctx) if err != nil { logrus.Debugf("Writing manifest using preferred type %s failed: %v", preferredManifestMIMEType, err) // … if it fails, _and_ the failure is because the manifest is rejected, we may have other options. @@ -314,7 +347,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli } // We have successfully uploaded a manifest. - manifest = attemptedManifest + manifestBytes = attemptedManifest errs = nil // Mark this as a success so that we don't abort below. break } @@ -324,7 +357,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli } if options.SignBy != "" { - newSig, err := c.createSignature(manifest, options.SignBy) + newSig, err := c.createSignature(manifestBytes, options.SignBy) if err != nil { return nil, err } @@ -336,7 +369,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli return nil, errors.Wrap(err, "Error writing signatures") } - return manifest, nil + return manifestBytes, nil } // Printf writes a formatted string to c.reportWriter. @@ -394,17 +427,30 @@ func shortDigest(d digest.Digest) string { return d.Encoded()[:12] } -// createProgressBar creates a pb.ProgressBar. -func createProgressBar(srcInfo types.BlobInfo, kind string, writer io.Writer) *pb.ProgressBar { +// createProgressBar creates a pb.ProgressBar. Note that if the copier's +// reportWriter is ioutil.Discard, the progress bar's output will be discarded +// and a single line will be printed instead. +func (c *copier) createProgressBar(srcInfo types.BlobInfo, kind string) *pb.ProgressBar { bar := pb.New(int(srcInfo.Size)).SetUnits(pb.U_BYTES) bar.SetMaxWidth(80) bar.ShowTimeLeft = false bar.ShowPercent = false bar.Prefix(fmt.Sprintf("Copying %s %s:", kind, shortDigest(srcInfo.Digest))) - bar.Output = writer + bar.Output = c.progressOutput + if bar.Output == ioutil.Discard { + c.Printf("Copying %s %s\n", kind, srcInfo.Digest) + } return bar } +// isTTY returns true if the io.Writer is a file and a tty. +func isTTY(w io.Writer) bool { + if f, ok := w.(*os.File); ok { + return terminal.IsTerminal(int(f.Fd())) + } + return false +} + // copyLayers copies layers from ic.src/ic.c.rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest. func (ic *imageCopier) copyLayers(ctx context.Context) error { srcInfos := ic.src.LayerInfos() @@ -456,7 +502,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error { bar.Finish() } else { cld.destInfo = srcLayer - logrus.Debugf("Skipping foreign layer %q copy to %s\n", cld.destInfo.Digest, ic.c.dest.Reference().Transport().Name()) + logrus.Debugf("Skipping foreign layer %q copy to %s", cld.destInfo.Digest, ic.c.dest.Reference().Transport().Name()) bar.Prefix(fmt.Sprintf("Skipping blob %s (foreign layer):", shortDigest(srcLayer.Digest))) bar.Add64(bar.Total) bar.Finish() @@ -469,12 +515,13 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error { progressBars := make([]*pb.ProgressBar, numLayers) for i, srcInfo := range srcInfos { - bar := createProgressBar(srcInfo, "blob", nil) + bar := ic.c.createProgressBar(srcInfo, "blob") progressBars[i] = bar } progressPool := pb.NewPool(progressBars...) - progressPool.Output = ic.c.reportWriter + progressPool.Output = ic.c.progressOutput + if err := progressPool.Start(); err != nil { return errors.Wrapf(err, "error creating progress-bar pool") } @@ -568,7 +615,7 @@ func (c *copier) copyConfig(ctx context.Context, src types.Image) error { if err != nil { return errors.Wrapf(err, "Error reading config blob %s", srcInfo.Digest) } - bar := createProgressBar(srcInfo, "config", c.reportWriter) + bar := c.createProgressBar(srcInfo, "config") defer bar.Finish() bar.Start() destInfo, err := c.copyBlobFromStream(ctx, bytes.NewReader(configBlob), srcInfo, nil, false, true, bar) diff --git a/vendor/github.com/containers/image/docker/docker_client.go b/vendor/github.com/containers/image/docker/docker_client.go index 23d2ac70f..43eb22ba2 100644 --- a/vendor/github.com/containers/image/docker/docker_client.go +++ b/vendor/github.com/containers/image/docker/docker_client.go @@ -91,7 +91,6 @@ type dockerClient struct { password string signatureBase signatureStorageBase scope authScope - extraScope *authScope // If non-nil, a temporary extra token scope (necessary for mounting from another repo) // The following members are detected registry properties: // They are set after a successful detectProperties(), and never change afterwards. scheme string // Empty value also used to indicate detectProperties() has not yet succeeded. @@ -282,7 +281,7 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password client.username = username client.password = password - resp, err := client.makeRequest(ctx, "GET", "/v2/", nil, nil, v2Auth) + resp, err := client.makeRequest(ctx, "GET", "/v2/", nil, nil, v2Auth, nil) if err != nil { return err } @@ -362,8 +361,8 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima q.Set("n", strconv.Itoa(limit)) u.RawQuery = q.Encode() - logrus.Debugf("trying to talk to v1 search endpoint\n") - resp, err := client.makeRequest(ctx, "GET", u.String(), nil, nil, noAuth) + logrus.Debugf("trying to talk to v1 search endpoint") + resp, err := client.makeRequest(ctx, "GET", u.String(), nil, nil, noAuth, nil) if err != nil { logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, err) } else { @@ -379,8 +378,8 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima } } - logrus.Debugf("trying to talk to v2 search endpoint\n") - resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil, v2Auth) + logrus.Debugf("trying to talk to v2 search endpoint") + resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil, v2Auth, nil) if err != nil { logrus.Debugf("error getting search results from v2 endpoint %q: %v", registry, err) } else { @@ -409,20 +408,20 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima // makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. // The host name and schema is taken from the client or autodetected, and the path is relative to it, i.e. the path usually starts with /v2/. -func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader, auth sendAuth) (*http.Response, error) { +func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader, auth sendAuth, extraScope *authScope) (*http.Response, error) { if err := c.detectProperties(ctx); err != nil { return nil, err } url := fmt.Sprintf("%s://%s%s", c.scheme, c.registry, path) - return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth) + return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth, extraScope) } // makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. // streamLen, if not -1, specifies the length of the data expected on stream. // makeRequest should generally be preferred. // TODO(runcom): too many arguments here, use a struct -func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth) (*http.Response, error) { +func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) { req, err := http.NewRequest(method, url, stream) if err != nil { return nil, err @@ -441,7 +440,7 @@ func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url req.Header.Add("User-Agent", c.sys.DockerRegistryUserAgent) } if auth == v2Auth { - if err := c.setupRequestAuth(req); err != nil { + if err := c.setupRequestAuth(req, extraScope); err != nil { return nil, err } } @@ -460,7 +459,7 @@ func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url // 2) gcr.io is sending 401 without a WWW-Authenticate header in the real request // // debugging: https://github.com/containers/image/pull/211#issuecomment-273426236 and follows up -func (c *dockerClient) setupRequestAuth(req *http.Request) error { +func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope) error { if len(c.challenges) == 0 { return nil } @@ -474,10 +473,10 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error { case "bearer": cacheKey := "" scopes := []authScope{c.scope} - if c.extraScope != nil { + if extraScope != nil { // Using ':' as a separator here is unambiguous because getBearerToken below uses the same separator when formatting a remote request (and because repository names can't contain colons). - cacheKey = fmt.Sprintf("%s:%s", c.extraScope.remoteName, c.extraScope.actions) - scopes = append(scopes, *c.extraScope) + cacheKey = fmt.Sprintf("%s:%s", extraScope.remoteName, extraScope.actions) + scopes = append(scopes, *extraScope) } var token bearerToken t, inCache := c.tokenCache.Load(cacheKey) @@ -564,7 +563,7 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error { ping := func(scheme string) error { url := fmt.Sprintf(resolvedPingV2URL, scheme, c.registry) - resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth) + resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth, nil) if err != nil { logrus.Debugf("Ping %s err %s (%#v)", url, err.Error(), err) return err @@ -591,7 +590,7 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error { // best effort to understand if we're talking to a V1 registry pingV1 := func(scheme string) bool { url := fmt.Sprintf(resolvedPingV1URL, scheme, c.registry) - resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth) + resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth, nil) if err != nil { logrus.Debugf("Ping %s err %s (%#v)", url, err.Error(), err) return false @@ -625,7 +624,7 @@ func (c *dockerClient) detectProperties(ctx context.Context) error { // using the original data structures. func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerReference, manifestDigest digest.Digest) (*extensionSignatureList, error) { path := fmt.Sprintf(extensionsSignaturePath, reference.Path(ref.ref), manifestDigest) - res, err := c.makeRequest(ctx, "GET", path, nil, nil, v2Auth) + res, err := c.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/docker/docker_image.go b/vendor/github.com/containers/image/docker/docker_image.go index 2ab95f329..530c7513e 100644 --- a/vendor/github.com/containers/image/docker/docker_image.go +++ b/vendor/github.com/containers/image/docker/docker_image.go @@ -66,7 +66,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types. tags := make([]string, 0) for { - res, err := client.makeRequest(ctx, "GET", path, nil, nil, v2Auth) + res, err := client.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/docker/docker_image_dest.go b/vendor/github.com/containers/image/docker/docker_image_dest.go index 973d160d0..38500dd0e 100644 --- a/vendor/github.com/containers/image/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/docker/docker_image_dest.go @@ -12,6 +12,7 @@ import ( "net/url" "os" "path/filepath" + "strings" "github.com/containers/image/docker/reference" "github.com/containers/image/manifest" @@ -113,7 +114,7 @@ func (c *sizeCounter) Write(p []byte) (n int, err error) { // HasThreadSafePutBlob indicates whether PutBlob can be executed concurrently. func (d *dockerImageDestination) HasThreadSafePutBlob() bool { - return false + return true } // PutBlob writes contents of stream and returns data representing the result (with all data filled in). @@ -140,7 +141,7 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, // FIXME? Chunked upload, progress reporting, etc. uploadPath := fmt.Sprintf(blobUploadPath, reference.Path(d.ref.ref)) logrus.Debugf("Uploading %s", uploadPath) - res, err := d.c.makeRequest(ctx, "POST", uploadPath, nil, nil, v2Auth) + res, err := d.c.makeRequest(ctx, "POST", uploadPath, nil, nil, v2Auth, nil) if err != nil { return types.BlobInfo{}, err } @@ -157,7 +158,7 @@ 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) + res, err = d.c.makeRequestToResolvedURL(ctx, "PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, inputInfo.Size, v2Auth, nil) if err != nil { logrus.Debugf("Error uploading layer chunked, response %#v", res) return types.BlobInfo{}, err @@ -176,7 +177,7 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, // TODO: check inputInfo.Digest == computedDigest https://github.com/containers/image/pull/70#discussion_r77646717 locationQuery.Set("digest", computedDigest.String()) uploadLocation.RawQuery = locationQuery.Encode() - res, err = d.c.makeRequestToResolvedURL(ctx, "PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1, v2Auth) + res, err = d.c.makeRequestToResolvedURL(ctx, "PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1, v2Auth, nil) if err != nil { return types.BlobInfo{}, err } @@ -194,10 +195,10 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, // blobExists returns true iff repo contains a blob with digest, and if so, also its size. // If the destination does not contain the blob, or it is unknown, blobExists ordinarily returns (false, -1, nil); // it returns a non-nil error only on an unexpected failure. -func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference.Named, digest digest.Digest) (bool, int64, error) { +func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference.Named, digest digest.Digest, extraScope *authScope) (bool, int64, error) { checkPath := fmt.Sprintf(blobsPath, reference.Path(repo), digest.String()) logrus.Debugf("Checking %s", checkPath) - res, err := d.c.makeRequest(ctx, "HEAD", checkPath, nil, nil, v2Auth) + res, err := d.c.makeRequest(ctx, "HEAD", checkPath, nil, nil, v2Auth, extraScope) if err != nil { return false, -1, err } @@ -218,7 +219,7 @@ func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference. } // mountBlob tries to mount blob srcDigest from srcRepo to the current destination. -func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo reference.Named, srcDigest digest.Digest) error { +func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo reference.Named, srcDigest digest.Digest, extraScope *authScope) error { u := url.URL{ Path: fmt.Sprintf(blobUploadPath, reference.Path(d.ref.ref)), RawQuery: url.Values{ @@ -228,7 +229,7 @@ func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo referenc } mountPath := u.String() logrus.Debugf("Trying to mount %s", mountPath) - res, err := d.c.makeRequest(ctx, "POST", mountPath, nil, nil, v2Auth) + res, err := d.c.makeRequest(ctx, "POST", mountPath, nil, nil, v2Auth, extraScope) if err != nil { return err } @@ -246,7 +247,7 @@ func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo referenc return errors.Wrap(err, "Error determining upload URL after a mount attempt") } logrus.Debugf("... started an upload instead of mounting, trying to cancel at %s", uploadLocation.String()) - res2, err := d.c.makeRequestToResolvedURL(ctx, "DELETE", uploadLocation.String(), nil, nil, -1, v2Auth) + res2, err := d.c.makeRequestToResolvedURL(ctx, "DELETE", uploadLocation.String(), nil, nil, -1, v2Auth, extraScope) if err != nil { logrus.Debugf("Error trying to cancel an inadvertent upload: %s", err) } else { @@ -276,7 +277,7 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. } // First, check whether the blob happens to already exist at the destination. - exists, size, err := d.blobExists(ctx, d.ref.ref, info.Digest) + exists, size, err := d.blobExists(ctx, d.ref.ref, info.Digest, nil) if err != nil { return false, types.BlobInfo{}, err } @@ -286,15 +287,6 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. } // Then try reusing blobs from other locations. - - // Checking candidateRepo, and mounting from it, requires an expanded token scope. - // We still want to reuse the ping information and other aspects of the client, so rather than make a fresh copy, there is this a bit ugly extraScope hack. - if d.c.extraScope != nil { - return false, types.BlobInfo{}, errors.New("Internal error: dockerClient.extraScope was set before TryReusingBlob") - } - defer func() { - d.c.extraScope = nil - }() for _, candidate := range cache.CandidateLocations(d.ref.Transport(), bicTransportScope(d.ref), info.Digest, canSubstitute) { candidateRepo, err := parseBICLocationReference(candidate.Location) if err != nil { @@ -314,7 +306,10 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. } // Whatever happens here, don't abort the entire operation. It's likely we just don't have permissions, and if it is a critical network error, we will find out soon enough anyway. - d.c.extraScope = &authScope{ + + // Checking candidateRepo, and mounting from it, requires an + // expanded token scope. + extraScope := &authScope{ remoteName: reference.Path(candidateRepo), actions: "pull", } @@ -325,7 +320,7 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. // Even worse, docker/distribution does not actually reasonably implement canceling uploads // (it would require a "delete" action in the token, and Quay does not give that to anyone, so we can't ask); // so, be a nice client and don't create unnecesary upload sessions on the server. - exists, size, err := d.blobExists(ctx, candidateRepo, candidate.Digest) + exists, size, err := d.blobExists(ctx, candidateRepo, candidate.Digest, extraScope) if err != nil { logrus.Debugf("... Failed: %v", err) continue @@ -335,7 +330,7 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. continue // logrus.Debug() already happened in blobExists } if candidateRepo.Name() != d.ref.ref.Name() { - if err := d.mountBlob(ctx, candidateRepo, candidate.Digest); err != nil { + if err := d.mountBlob(ctx, candidateRepo, candidate.Digest, extraScope); err != nil { logrus.Debugf("... Mount failed: %v", err) continue } @@ -369,7 +364,7 @@ func (d *dockerImageDestination) PutManifest(ctx context.Context, m []byte) erro if mimeType != "" { headers["Content-Type"] = []string{mimeType} } - res, err := d.c.makeRequest(ctx, "PUT", path, headers, bytes.NewReader(m), v2Auth) + res, err := d.c.makeRequest(ctx, "PUT", path, headers, bytes.NewReader(m), v2Auth, nil) if err != nil { return err } @@ -396,14 +391,29 @@ func isManifestInvalidError(err error) bool { if !ok || len(errors) == 0 { return false } - ec, ok := errors[0].(errcode.ErrorCoder) + err = errors[0] + ec, ok := err.(errcode.ErrorCoder) if !ok { return false } + + switch ec.ErrorCode() { // ErrorCodeManifestInvalid is returned by OpenShift with acceptschema2=false. + case v2.ErrorCodeManifestInvalid: + return true // ErrorCodeTagInvalid is returned by docker/distribution (at least as of commit ec87e9b6971d831f0eff752ddb54fb64693e51cd) // when uploading to a tag (because it can’t find a matching tag inside the manifest) - return ec.ErrorCode() == v2.ErrorCodeManifestInvalid || ec.ErrorCode() == v2.ErrorCodeTagInvalid + case v2.ErrorCodeTagInvalid: + return true + // ErrorCodeUnsupported with 'Invalid JSON syntax' is returned by AWS ECR when + // uploading an OCI manifest that is (correctly, according to the spec) missing + // a top-level media type. See libpod issue #1719 + // FIXME: remove this case when ECR behavior is fixed + case errcode.ErrorCodeUnsupported: + return strings.Contains(err.Error(), "Invalid JSON syntax") + default: + return false + } } func (d *dockerImageDestination) PutSignatures(ctx context.Context, signatures [][]byte) error { @@ -574,7 +584,7 @@ sigExists: } path := fmt.Sprintf(extensionsSignaturePath, reference.Path(d.ref.ref), d.manifestDigest.String()) - res, err := d.c.makeRequest(ctx, "PUT", path, nil, bytes.NewReader(body), v2Auth) + 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/docker/docker_image_src.go b/vendor/github.com/containers/image/docker/docker_image_src.go index c88ff2f34..8367792bf 100644 --- a/vendor/github.com/containers/image/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/docker/docker_image_src.go @@ -89,7 +89,7 @@ func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest strin path := fmt.Sprintf(manifestPath, reference.Path(s.ref.ref), tagOrDigest) headers := make(map[string][]string) headers["Accept"] = manifest.DefaultRequestedManifestMIMETypes - res, err := s.c.makeRequest(ctx, "GET", path, headers, nil, v2Auth) + res, err := s.c.makeRequest(ctx, "GET", path, headers, nil, v2Auth, nil) if err != nil { return nil, "", err } @@ -137,7 +137,7 @@ func (s *dockerImageSource) getExternalBlob(ctx context.Context, urls []string) err error ) for _, url := range urls { - resp, err = s.c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth) + resp, err = s.c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth, nil) if err == nil { if resp.StatusCode != http.StatusOK { err = errors.Errorf("error fetching external blob from %q: %d (%s)", url, resp.StatusCode, http.StatusText(resp.StatusCode)) @@ -147,10 +147,10 @@ func (s *dockerImageSource) getExternalBlob(ctx context.Context, urls []string) break } } - if resp.Body != nil && err == nil { - return resp.Body, getBlobSize(resp), nil + if err != nil { + return nil, 0, err } - return nil, 0, err + return resp.Body, getBlobSize(resp), nil } func getBlobSize(resp *http.Response) int64 { @@ -176,7 +176,7 @@ func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca path := fmt.Sprintf(blobsPath, reference.Path(s.ref.ref), info.Digest.String()) logrus.Debugf("Downloading %s", path) - res, err := s.c.makeRequest(ctx, "GET", path, nil, nil, v2Auth) + res, err := s.c.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil) if err != nil { return nil, 0, err } @@ -340,7 +340,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere return err } getPath := fmt.Sprintf(manifestPath, reference.Path(ref.ref), refTail) - get, err := c.makeRequest(ctx, "GET", getPath, headers, nil, v2Auth) + get, err := c.makeRequest(ctx, "GET", getPath, headers, nil, v2Auth, nil) if err != nil { return err } @@ -362,7 +362,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere // When retrieving the digest from a registry >= 2.3 use the following header: // "Accept": "application/vnd.docker.distribution.manifest.v2+json" - delete, err := c.makeRequest(ctx, "DELETE", deletePath, headers, nil, v2Auth) + delete, err := c.makeRequest(ctx, "DELETE", deletePath, headers, nil, v2Auth, nil) if err != nil { return err } diff --git a/vendor/github.com/containers/image/docker/tarfile/src.go b/vendor/github.com/containers/image/docker/tarfile/src.go index 889e5f8e8..03735f8a4 100644 --- a/vendor/github.com/containers/image/docker/tarfile/src.go +++ b/vendor/github.com/containers/image/docker/tarfile/src.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "os" "path" + "sync" "github.com/containers/image/internal/tmpdir" "github.com/containers/image/manifest" @@ -21,8 +22,10 @@ import ( // Source is a partial implementation of types.ImageSource for reading from tarPath. type Source struct { tarPath string - removeTarPathOnClose bool // Remove temp file on close if true + removeTarPathOnClose bool // Remove temp file on close if true + cacheDataLock sync.Once // Atomic way to ensure that ensureCachedDataIsPresent is only invoked once // The following data is only available after ensureCachedDataIsPresent() succeeds + cacheDataResult error // The return value of ensureCachedDataIsPresent, since it should be as safe to cache as the side effects tarManifest *ManifestItem // nil if not available yet. configBytes []byte configDigest digest.Digest @@ -199,43 +202,46 @@ func (s *Source) readTarComponent(path string) ([]byte, error) { // ensureCachedDataIsPresent loads data necessary for any of the public accessors. func (s *Source) ensureCachedDataIsPresent() error { - if s.tarManifest != nil { - return nil - } - - // Read and parse manifest.json - tarManifest, err := s.loadTarManifest() - if err != nil { - return err - } + s.cacheDataLock.Do(func() { + // Read and parse manifest.json + tarManifest, err := s.loadTarManifest() + if err != nil { + s.cacheDataResult = err + return + } - // Check to make sure length is 1 - if len(tarManifest) != 1 { - return errors.Errorf("Unexpected tar manifest.json: expected 1 item, got %d", len(tarManifest)) - } + // Check to make sure length is 1 + if len(tarManifest) != 1 { + s.cacheDataResult = errors.Errorf("Unexpected tar manifest.json: expected 1 item, got %d", len(tarManifest)) + return + } - // Read and parse config. - configBytes, err := s.readTarComponent(tarManifest[0].Config) - if err != nil { - return err - } - var parsedConfig manifest.Schema2Image // There's a lot of info there, but we only really care about layer DiffIDs. - if err := json.Unmarshal(configBytes, &parsedConfig); err != nil { - return errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config) - } + // Read and parse config. + configBytes, err := s.readTarComponent(tarManifest[0].Config) + if err != nil { + s.cacheDataResult = err + return + } + var parsedConfig manifest.Schema2Image // There's a lot of info there, but we only really care about layer DiffIDs. + if err := json.Unmarshal(configBytes, &parsedConfig); err != nil { + s.cacheDataResult = errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config) + return + } - knownLayers, err := s.prepareLayerData(&tarManifest[0], &parsedConfig) - if err != nil { - return err - } + knownLayers, err := s.prepareLayerData(&tarManifest[0], &parsedConfig) + if err != nil { + s.cacheDataResult = err + return + } - // Success; commit. - s.tarManifest = &tarManifest[0] - s.configBytes = configBytes - s.configDigest = digest.FromBytes(configBytes) - s.orderedDiffIDList = parsedConfig.RootFS.DiffIDs - s.knownLayers = knownLayers - return nil + // Success; commit. + s.tarManifest = &tarManifest[0] + s.configBytes = configBytes + s.configDigest = digest.FromBytes(configBytes) + s.orderedDiffIDList = parsedConfig.RootFS.DiffIDs + s.knownLayers = knownLayers + }) + return s.cacheDataResult } // loadTarManifest loads and decodes the manifest.json. @@ -399,7 +405,7 @@ func (r uncompressedReadCloser) Close() error { // HasThreadSafeGetBlob indicates whether GetBlob can be executed concurrently. func (s *Source) HasThreadSafeGetBlob() bool { - return false + return true } // GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown). diff --git a/vendor/github.com/containers/image/ostree/ostree_src.go b/vendor/github.com/containers/image/ostree/ostree_src.go index df432c9f3..35d852139 100644 --- a/vendor/github.com/containers/image/ostree/ostree_src.go +++ b/vendor/github.com/containers/image/ostree/ostree_src.go @@ -17,7 +17,7 @@ import ( "github.com/containers/image/types" "github.com/containers/storage/pkg/ioutils" "github.com/klauspost/pgzip" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" glib "github.com/ostreedev/ostree-go/pkg/glibobject" "github.com/pkg/errors" "github.com/vbatts/tar-split/tar/asm" @@ -313,24 +313,19 @@ func (s *ostreeImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca if err != nil { return nil, 0, err } - defer mfz.Close() metaUnpacker := storage.NewJSONUnpacker(mfz) getter, err := newOSTreePathFileGetter(s.repo, branch) if err != nil { + mfz.Close() return nil, 0, err } ots := asm.NewOutputTarStream(getter, metaUnpacker) - pipeReader, pipeWriter := io.Pipe() - go func() { - io.Copy(pipeWriter, ots) - pipeWriter.Close() - }() - - rc := ioutils.NewReadCloserWrapper(pipeReader, func() error { + rc := ioutils.NewReadCloserWrapper(ots, func() error { getter.Close() + mfz.Close() return ots.Close() }) return rc, layerSize, nil diff --git a/vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go b/vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go index 9e3e9cfe1..3d0bb0df2 100644 --- a/vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go +++ b/vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go @@ -53,20 +53,23 @@ type Registry struct { Prefix string `toml:"prefix"` } -// backwards compatability to sysregistries v1 -type v1TOMLregistries struct { +// V1TOMLregistries is for backwards compatibility to sysregistries v1 +type V1TOMLregistries struct { Registries []string `toml:"registries"` } +// V1TOMLConfig is for backwards compatibility to sysregistries v1 +type V1TOMLConfig struct { + Search V1TOMLregistries `toml:"search"` + Insecure V1TOMLregistries `toml:"insecure"` + Block V1TOMLregistries `toml:"block"` +} + // tomlConfig is the data type used to unmarshal the toml config. type tomlConfig struct { Registries []Registry `toml:"registry"` // backwards compatability to sysregistries v1 - V1Registries struct { - Search v1TOMLregistries `toml:"search"` - Insecure v1TOMLregistries `toml:"insecure"` - Block v1TOMLregistries `toml:"block"` - } `toml:"registries"` + V1TOMLConfig `toml:"registries"` } // InvalidRegistries represents an invalid registry configurations. An example @@ -129,21 +132,21 @@ func getV1Registries(config *tomlConfig) ([]Registry, error) { // Note: config.V1Registries.Search needs to be processed first to ensure registryOrder is populated in the right order // if one of the search registries is also in one of the other lists. - for _, search := range config.V1Registries.Search.Registries { + for _, search := range config.V1TOMLConfig.Search.Registries { reg, err := getRegistry(search) if err != nil { return nil, err } reg.Search = true } - for _, blocked := range config.V1Registries.Block.Registries { + for _, blocked := range config.V1TOMLConfig.Block.Registries { reg, err := getRegistry(blocked) if err != nil { return nil, err } reg.Blocked = true } - for _, insecure := range config.V1Registries.Insecure.Registries { + for _, insecure := range config.V1TOMLConfig.Insecure.Registries { reg, err := getRegistry(insecure) if err != nil { return nil, err diff --git a/vendor/github.com/containers/image/storage/storage_image.go b/vendor/github.com/containers/image/storage/storage_image.go index b53fbdf6e..67dc6142b 100644 --- a/vendor/github.com/containers/image/storage/storage_image.go +++ b/vendor/github.com/containers/image/storage/storage_image.go @@ -14,6 +14,7 @@ import ( "sync" "sync/atomic" + "github.com/containers/image/docker/reference" "github.com/containers/image/image" "github.com/containers/image/internal/tmpdir" "github.com/containers/image/manifest" @@ -70,6 +71,13 @@ type storageImageCloser struct { size int64 } +// manifestBigDataKey returns a key suitable for recording a manifest with the specified digest using storage.Store.ImageBigData and related functions. +// If a specific manifest digest is explicitly requested by the user, the key retruned function should be used preferably; +// for compatibility, if a manifest is not available under this key, check also storage.ImageDigestBigDataKey +func manifestBigDataKey(digest digest.Digest) string { + return storage.ImageDigestManifestBigDataNamePrefix + "-" + digest.String() +} + // newImageSource sets up an image for reading. func newImageSource(imageRef storageReference) (*storageImageSource, error) { // First, locate the image. @@ -177,12 +185,29 @@ func (s *storageImageSource) GetManifest(ctx context.Context, instanceDigest *di return nil, "", ErrNoManifestLists } if len(s.cachedManifest) == 0 { - // We stored the manifest as an item named after storage.ImageDigestBigDataKey. - cachedBlob, err := s.imageRef.transport.store.ImageBigData(s.image.ID, storage.ImageDigestBigDataKey) - if err != nil { - return nil, "", err + // The manifest is stored as a big data item. + // Prefer the manifest corresponding to the user-specified digest, if available. + if s.imageRef.named != nil { + if digested, ok := s.imageRef.named.(reference.Digested); ok { + key := manifestBigDataKey(digested.Digest()) + blob, err := s.imageRef.transport.store.ImageBigData(s.image.ID, key) + if err != nil && !os.IsNotExist(err) { // os.IsNotExist is true if the image exists but there is no data corresponding to key + return nil, "", err + } + if err == nil { + s.cachedManifest = blob + } + } + } + // If the user did not specify a digest, or this is an old image stored before manifestBigDataKey was introduced, use the default manifest. + // Note that the manifest may not match the expected digest, and that is likely to fail eventually, e.g. in c/image/image/UnparsedImage.Manifest(). + if len(s.cachedManifest) == 0 { + cachedBlob, err := s.imageRef.transport.store.ImageBigData(s.image.ID, storage.ImageDigestBigDataKey) + if err != nil { + return nil, "", err + } + s.cachedManifest = cachedBlob } - s.cachedManifest = cachedBlob } return s.cachedManifest, manifest.GuessMIMEType(s.cachedManifest), err } @@ -660,6 +685,7 @@ func (s *storageImageDestination) Commit(ctx context.Context) error { } lastLayer = layer.ID } + // If one of those blobs was a configuration blob, then we can try to dig out the date when the image // was originally created, in case we're just copying it. If not, no harm done. options := &storage.ImageOptions{} @@ -667,9 +693,6 @@ func (s *storageImageDestination) Commit(ctx context.Context) error { logrus.Debugf("setting image creation date to %s", inspect.Created) options.CreationDate = *inspect.Created } - if manifestDigest, err := manifest.Digest(s.manifest); err == nil { - options.Digest = manifestDigest - } // Create the image record, pointing to the most-recently added layer. intendedID := s.imageRef.id if intendedID == "" { @@ -735,8 +758,20 @@ func (s *storageImageDestination) Commit(ctx context.Context) error { } logrus.Debugf("set names of image %q to %v", img.ID, names) } - // Save the manifest. Use storage.ImageDigestBigDataKey as the item's - // name, so that its digest can be used to locate the image in the Store. + // Save the manifest. Allow looking it up by digest by using the key convention defined by the Store. + // Record the manifest twice: using a digest-specific key to allow references to that specific digest instance, + // and using storage.ImageDigestBigDataKey for future users that don’t specify any digest and for compatibility with older readers. + manifestDigest, err := manifest.Digest(s.manifest) + if err != nil { + return errors.Wrapf(err, "error computing manifest digest") + } + if err := s.imageRef.transport.store.SetImageBigData(img.ID, manifestBigDataKey(manifestDigest), s.manifest); err != nil { + if _, err2 := s.imageRef.transport.store.DeleteImage(img.ID, true); err2 != nil { + logrus.Debugf("error deleting incomplete image %q: %v", img.ID, err2) + } + logrus.Debugf("error saving manifest for image %q: %v", img.ID, err) + return err + } if err := s.imageRef.transport.store.SetImageBigData(img.ID, storage.ImageDigestBigDataKey, s.manifest); err != nil { if _, err2 := s.imageRef.transport.store.DeleteImage(img.ID, true); err2 != nil { logrus.Debugf("error deleting incomplete image %q: %v", img.ID, err2) @@ -788,9 +823,21 @@ func (s *storageImageDestination) SupportedManifestMIMETypes() []string { } // PutManifest writes the manifest to the destination. -func (s *storageImageDestination) PutManifest(ctx context.Context, manifest []byte) error { - s.manifest = make([]byte, len(manifest)) - copy(s.manifest, manifest) +func (s *storageImageDestination) PutManifest(ctx context.Context, manifestBlob []byte) error { + if s.imageRef.named != nil { + if digested, ok := s.imageRef.named.(reference.Digested); ok { + matches, err := manifest.MatchesDigest(manifestBlob, digested.Digest()) + if err != nil { + return err + } + if !matches { + return fmt.Errorf("Manifest does not match expected digest %s", digested.Digest()) + } + } + } + + s.manifest = make([]byte, len(manifestBlob)) + copy(s.manifest, manifestBlob) return nil } diff --git a/vendor/github.com/containers/image/storage/storage_reference.go b/vendor/github.com/containers/image/storage/storage_reference.go index 73306b972..c046d9f22 100644 --- a/vendor/github.com/containers/image/storage/storage_reference.go +++ b/vendor/github.com/containers/image/storage/storage_reference.go @@ -55,7 +55,7 @@ func imageMatchesRepo(image *storage.Image, ref reference.Named) bool { // one present with the same name or ID, and return the image. func (s *storageReference) resolveImage() (*storage.Image, error) { var loadedImage *storage.Image - if s.id == "" { + if s.id == "" && s.named != nil { // Look for an image that has the expanded reference name as an explicit Name value. image, err := s.transport.store.Image(s.named.String()) if image != nil && err == nil { @@ -69,7 +69,7 @@ func (s *storageReference) resolveImage() (*storage.Image, error) { // though possibly with a different tag or digest, as a Name value, so // that the canonical reference can be implicitly resolved to the image. images, err := s.transport.store.ImagesByDigest(digested.Digest()) - if images != nil && err == nil { + if err == nil && len(images) > 0 { for _, image := range images { if imageMatchesRepo(image, s.named) { loadedImage = image @@ -97,6 +97,24 @@ func (s *storageReference) resolveImage() (*storage.Image, error) { return nil, ErrNoSuchImage } } + // Default to having the image digest that we hand back match the most recently + // added manifest... + if digest, ok := loadedImage.BigDataDigests[storage.ImageDigestBigDataKey]; ok { + loadedImage.Digest = digest + } + // ... unless the named reference says otherwise, and it matches one of the digests + // in the image. For those cases, set the Digest field to that value, for the + // sake of older consumers that don't know there's a whole list in there now. + if s.named != nil { + if digested, ok := s.named.(reference.Digested); ok { + for _, digest := range loadedImage.Digests { + if digest == digested.Digest() { + loadedImage.Digest = digest + break + } + } + } + } return loadedImage, nil } diff --git a/vendor/github.com/containers/image/storage/storage_transport.go b/vendor/github.com/containers/image/storage/storage_transport.go index b53c389bd..02d2f5c08 100644 --- a/vendor/github.com/containers/image/storage/storage_transport.go +++ b/vendor/github.com/containers/image/storage/storage_transport.go @@ -284,11 +284,6 @@ func (s storageTransport) GetStoreImage(store storage.Store, ref types.ImageRefe } } if sref, ok := ref.(*storageReference); ok { - if sref.id != "" { - if img, err := store.Image(sref.id); err == nil { - return img, nil - } - } tmpRef := *sref if img, err := tmpRef.resolveImage(); err == nil { return img, nil diff --git a/vendor/github.com/containers/image/version/version.go b/vendor/github.com/containers/image/version/version.go index 6644bcff3..10075992d 100644 --- a/vendor/github.com/containers/image/version/version.go +++ b/vendor/github.com/containers/image/version/version.go @@ -8,7 +8,7 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 1 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 0 + VersionPatch = 5 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "-dev" diff --git a/vendor/github.com/containers/storage/images.go b/vendor/github.com/containers/storage/images.go index d99842534..fa4a7c43b 100644 --- a/vendor/github.com/containers/storage/images.go +++ b/vendor/github.com/containers/storage/images.go @@ -5,8 +5,10 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "time" + "github.com/containers/image/manifest" "github.com/containers/storage/pkg/ioutils" "github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/truncindex" @@ -15,9 +17,13 @@ import ( ) const ( - // ImageDigestBigDataKey is the name of the big data item whose - // contents we consider useful for computing a "digest" of the - // image, by which we can locate the image later. + // ImageDigestManifestBigDataNamePrefix is a prefix of big data item + // names which we consider to be manifests, used for computing a + // "digest" value for the image as a whole, by which we can locate the + // image later. + ImageDigestManifestBigDataNamePrefix = "manifest" + // ImageDigestBigDataKey is provided for compatibility with older + // versions of the image library. It will be removed in the future. ImageDigestBigDataKey = "manifest" ) @@ -27,12 +33,19 @@ type Image struct { // value which was generated by the library. ID string `json:"id"` - // Digest is a digest value that we can use to locate the image. + // Digest is a digest value that we can use to locate the image, if one + // was specified at creation-time. Digest digest.Digest `json:"digest,omitempty"` + // Digests is a list of digest values of the image's manifests, and + // possibly a manually-specified value, that we can use to locate the + // image. If Digest is set, its value is also in this list. + Digests []digest.Digest `json:"-"` + // Names is an optional set of user-defined convenience values. The // image can be referred to by its ID or any of its names. Names are - // unique among images. + // unique among images, and are often the text representation of tagged + // or canonical references. Names []string `json:"names,omitempty"` // TopLayer is the ID of the topmost layer of the image itself, if the @@ -92,8 +105,10 @@ type ROImageStore interface { // Images returns a slice enumerating the known images. Images() ([]Image, error) - // Images returns a slice enumerating the images which have a big data - // item with the name ImageDigestBigDataKey and the specified digest. + // ByDigest returns a slice enumerating the images which have either an + // explicitly-set digest, or a big data item with a name that starts + // with ImageDigestManifestBigDataNamePrefix, which matches the + // specified digest. ByDigest(d digest.Digest) ([]*Image, error) } @@ -111,7 +126,8 @@ type ImageStore interface { Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (*Image, error) // SetNames replaces the list of names associated with an image with the - // supplied values. + // supplied values. The values are expected to be valid normalized + // named image references. SetNames(id string, names []string) error // Delete removes the record of the image. @@ -135,6 +151,7 @@ func copyImage(i *Image) *Image { return &Image{ ID: i.ID, Digest: i.Digest, + Digests: copyDigestSlice(i.Digests), Names: copyStringSlice(i.Names), TopLayer: i.TopLayer, MappedTopLayers: copyStringSlice(i.MappedTopLayers), @@ -147,6 +164,17 @@ func copyImage(i *Image) *Image { } } +func copyImageSlice(slice []*Image) []*Image { + if len(slice) > 0 { + cp := make([]*Image, len(slice)) + for i := range slice { + cp[i] = copyImage(slice[i]) + } + return cp + } + return nil +} + func (r *imageStore) Images() ([]Image, error) { images := make([]Image, len(r.images)) for i := range r.images { @@ -167,6 +195,46 @@ func (r *imageStore) datapath(id, key string) string { return filepath.Join(r.datadir(id), makeBigDataBaseName(key)) } +// bigDataNameIsManifest determines if a big data item with the specified name +// is considered to be representative of the image, in that its digest can be +// said to also be the image's digest. Currently, if its name is, or begins +// with, "manifest", we say that it is. +func bigDataNameIsManifest(name string) bool { + return strings.HasPrefix(name, ImageDigestManifestBigDataNamePrefix) +} + +// recomputeDigests takes a fixed digest and a name-to-digest map and builds a +// list of the unique values that would identify the image. +func (image *Image) recomputeDigests() error { + validDigests := make([]digest.Digest, 0, len(image.BigDataDigests)+1) + digests := make(map[digest.Digest]struct{}) + if image.Digest != "" { + if err := image.Digest.Validate(); err != nil { + return errors.Wrapf(err, "error validating image digest %q", string(image.Digest)) + } + digests[image.Digest] = struct{}{} + validDigests = append(validDigests, image.Digest) + } + for name, digest := range image.BigDataDigests { + if !bigDataNameIsManifest(name) { + continue + } + if digest.Validate() != nil { + return errors.Wrapf(digest.Validate(), "error validating digest %q for big data item %q", string(digest), name) + } + // Deduplicate the digest values. + if _, known := digests[digest]; !known { + digests[digest] = struct{}{} + validDigests = append(validDigests, digest) + } + } + if image.Digest == "" && len(validDigests) > 0 { + image.Digest = validDigests[0] + } + image.Digests = validDigests + return nil +} + func (r *imageStore) Load() error { shouldSave := false rpath := r.imagespath() @@ -189,17 +257,18 @@ func (r *imageStore) Load() error { r.removeName(conflict, name) shouldSave = true } - names[name] = images[n] } - // Implicit digest - if digest, ok := image.BigDataDigests[ImageDigestBigDataKey]; ok { - digests[digest] = append(digests[digest], images[n]) + // Compute the digest list. + err = image.recomputeDigests() + if err != nil { + return errors.Wrapf(err, "error computing digests for image with ID %q (%v)", image.ID, image.Names) } - // Explicit digest - if image.Digest == "" { - image.Digest = image.BigDataDigests[ImageDigestBigDataKey] - } else if image.Digest != image.BigDataDigests[ImageDigestBigDataKey] { - digests[image.Digest] = append(digests[image.Digest], images[n]) + for _, name := range image.Names { + names[name] = image + } + for _, digest := range image.Digests { + list := digests[digest] + digests[digest] = append(list, image) } } } @@ -333,12 +402,12 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c } } if _, idInUse := r.byid[id]; idInUse { - return nil, ErrDuplicateID + return nil, errors.Wrapf(ErrDuplicateID, "an image with ID %q already exists", id) } names = dedupeNames(names) for _, name := range names { - if _, nameInUse := r.byname[name]; nameInUse { - return nil, ErrDuplicateName + if image, nameInUse := r.byname[name]; nameInUse { + return nil, errors.Wrapf(ErrDuplicateName, "image name %q is already associated with image %q", name, image.ID) } } if created.IsZero() { @@ -348,6 +417,7 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c image = &Image{ ID: id, Digest: searchableDigest, + Digests: nil, Names: names, TopLayer: layer, Metadata: metadata, @@ -357,16 +427,20 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c Created: created, Flags: make(map[string]interface{}), } + err := image.recomputeDigests() + if err != nil { + return nil, errors.Wrapf(err, "error validating digests for new image") + } r.images = append(r.images, image) r.idindex.Add(id) r.byid[id] = image - if searchableDigest != "" { - list := r.bydigest[searchableDigest] - r.bydigest[searchableDigest] = append(list, image) - } for _, name := range names { r.byname[name] = image } + for _, digest := range image.Digests { + list := r.bydigest[digest] + r.bydigest[digest] = append(list, image) + } err = r.Save() image = copyImage(image) } @@ -444,6 +518,14 @@ func (r *imageStore) Delete(id string) error { for _, name := range image.Names { delete(r.byname, name) } + for _, digest := range image.Digests { + prunedList := imageSliceWithoutValue(r.bydigest[digest], image) + if len(prunedList) == 0 { + delete(r.bydigest, digest) + } else { + r.bydigest[digest] = prunedList + } + } if toDeleteIndex != -1 { // delete the image at toDeleteIndex if toDeleteIndex == len(r.images)-1 { @@ -452,28 +534,6 @@ func (r *imageStore) Delete(id string) error { r.images = append(r.images[:toDeleteIndex], r.images[toDeleteIndex+1:]...) } } - if digest, ok := image.BigDataDigests[ImageDigestBigDataKey]; ok { - // remove the image from the digest-based index - if list, ok := r.bydigest[digest]; ok { - prunedList := imageSliceWithoutValue(list, image) - if len(prunedList) == 0 { - delete(r.bydigest, digest) - } else { - r.bydigest[digest] = prunedList - } - } - } - if image.Digest != "" { - // remove the image's hard-coded digest from the digest-based index - if list, ok := r.bydigest[image.Digest]; ok { - prunedList := imageSliceWithoutValue(list, image) - if len(prunedList) == 0 { - delete(r.bydigest, image.Digest) - } else { - r.bydigest[image.Digest] = prunedList - } - } - } if err := r.Save(); err != nil { return err } @@ -504,7 +564,7 @@ func (r *imageStore) Exists(id string) bool { func (r *imageStore) ByDigest(d digest.Digest) ([]*Image, error) { if images, ok := r.bydigest[d]; ok { - return images, nil + return copyImageSlice(images), nil } return nil, ErrImageUnknown } @@ -606,10 +666,19 @@ func (r *imageStore) SetBigData(id, key string, data []byte) error { if !ok { return ErrImageUnknown } - if err := os.MkdirAll(r.datadir(image.ID), 0700); err != nil { + err := os.MkdirAll(r.datadir(image.ID), 0700) + if err != nil { return err } - err := ioutils.AtomicWriteFile(r.datapath(image.ID, key), data, 0600) + var newDigest digest.Digest + if bigDataNameIsManifest(key) { + if newDigest, err = manifest.Digest(data); err != nil { + return errors.Wrapf(err, "error digesting manifest") + } + } else { + newDigest = digest.Canonical.FromBytes(data) + } + err = ioutils.AtomicWriteFile(r.datapath(image.ID, key), data, 0600) if err == nil { save := false if image.BigDataSizes == nil { @@ -621,7 +690,6 @@ func (r *imageStore) SetBigData(id, key string, data []byte) error { image.BigDataDigests = make(map[string]digest.Digest) } oldDigest, digestOk := image.BigDataDigests[key] - newDigest := digest.Canonical.FromBytes(data) image.BigDataDigests[key] = newDigest if !sizeOk || oldSize != image.BigDataSizes[key] || !digestOk || oldDigest != newDigest { save = true @@ -637,20 +705,21 @@ func (r *imageStore) SetBigData(id, key string, data []byte) error { image.BigDataNames = append(image.BigDataNames, key) save = true } - if key == ImageDigestBigDataKey { - if oldDigest != "" && oldDigest != newDigest && oldDigest != image.Digest { - // remove the image from the list of images in the digest-based - // index which corresponds to the old digest for this item, unless - // it's also the hard-coded digest - if list, ok := r.bydigest[oldDigest]; ok { - prunedList := imageSliceWithoutValue(list, image) - if len(prunedList) == 0 { - delete(r.bydigest, oldDigest) - } else { - r.bydigest[oldDigest] = prunedList - } + for _, oldDigest := range image.Digests { + // remove the image from the list of images in the digest-based index + if list, ok := r.bydigest[oldDigest]; ok { + prunedList := imageSliceWithoutValue(list, image) + if len(prunedList) == 0 { + delete(r.bydigest, oldDigest) + } else { + r.bydigest[oldDigest] = prunedList } } + } + if err = image.recomputeDigests(); err != nil { + return errors.Wrapf(err, "error loading recomputing image digest information for %s", image.ID) + } + for _, newDigest := range image.Digests { // add the image to the list of images in the digest-based index which // corresponds to the new digest for this item, unless it's already there list := r.bydigest[newDigest] diff --git a/vendor/github.com/containers/storage/images_ffjson.go b/vendor/github.com/containers/storage/images_ffjson.go index 539acfe93..6b40ebd59 100644 --- a/vendor/github.com/containers/storage/images_ffjson.go +++ b/vendor/github.com/containers/storage/images_ffjson.go @@ -1,5 +1,5 @@ // Code generated by ffjson . DO NOT EDIT. -// source: ./images.go +// source: images.go package storage diff --git a/vendor/github.com/containers/storage/pkg/config/config.go b/vendor/github.com/containers/storage/pkg/config/config.go new file mode 100644 index 000000000..bdb5fbcb8 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/config/config.go @@ -0,0 +1,96 @@ +package config + +// ThinpoolOptionsConfig represents the "storage.options.thinpool" +// TOML config table. +type ThinpoolOptionsConfig struct { + // AutoExtendPercent determines the amount by which pool needs to be + // grown. This is specified in terms of % of pool size. So a value of + // 20 means that when threshold is hit, pool will be grown by 20% of + // existing pool size. + AutoExtendPercent string `toml:"autoextend_percent"` + + // AutoExtendThreshold determines the pool extension threshold in terms + // of percentage of pool size. For example, if threshold is 60, that + // means when pool is 60% full, threshold has been hit. + AutoExtendThreshold string `toml:"autoextend_threshold"` + + // BaseSize specifies the size to use when creating the base device, + // which limits the size of images and containers. + BaseSize string `toml:"basesize"` + + // BlockSize specifies a custom blocksize to use for the thin pool. + BlockSize string `toml:"blocksize"` + + // DirectLvmDevice specifies a custom block storage device to use for + // the thin pool. + DirectLvmDevice string `toml:"directlvm_device"` + + // DirectLvmDeviceForcewipes device even if device already has a + // filesystem + DirectLvmDeviceForce string `toml:"directlvm_device_force"` + + // Fs specifies the filesystem type to use for the base device. + Fs string `toml:"fs"` + + // log_level sets the log level of devicemapper. + LogLevel string `toml:"log_level"` + + // MinFreeSpace specifies the min free space percent in a thin pool + // require for new device creation to + MinFreeSpace string `toml:"min_free_space"` + + // MkfsArg specifies extra mkfs arguments to be used when creating the + // basedevice. + MkfsArg string `toml:"mkfsarg"` + + // MountOpt specifies extra mount options used when mounting the thin + // devices. + MountOpt string `toml:"mountopt"` + + // UseDeferredDeletion marks device for deferred deletion + UseDeferredDeletion string `toml:"use_deferred_deletion"` + + // UseDeferredRemoval marks device for deferred removal + UseDeferredRemoval string `toml:"use_deferred_removal"` + + // XfsNoSpaceMaxRetriesFreeSpace specifies the maximum number of + // retries XFS should attempt to complete IO when ENOSPC (no space) + // error is returned by underlying storage device. + XfsNoSpaceMaxRetries string `toml:"xfs_nospace_max_retries"` +} + +// OptionsConfig represents the "storage.options" TOML config table. +type OptionsConfig struct { + // AdditionalImagesStores is the location of additional read/only + // Image stores. Usually used to access Networked File System + // for shared image content + AdditionalImageStores []string `toml:"additionalimagestores"` + + // Size + Size string `toml:"size"` + + // RemapUIDs is a list of default UID mappings to use for layers. + RemapUIDs string `toml:"remap-uids"` + // RemapGIDs is a list of default GID mappings to use for layers. + RemapGIDs string `toml:"remap-gids"` + + // RemapUser is the name of one or more entries in /etc/subuid which + // should be used to set up default UID mappings. + RemapUser string `toml:"remap-user"` + // RemapGroup is the name of one or more entries in /etc/subgid which + // should be used to set up default GID mappings. + RemapGroup string `toml:"remap-group"` + // Thinpool container options to be handed to thinpool drivers + Thinpool struct{ ThinpoolOptionsConfig } `toml:"thinpool"` + // OSTree repository + OstreeRepo string `toml:"ostree_repo"` + + // Do not create a bind mount on the storage home + SkipMountHome string `toml:"skip_mount_home"` + + // Alternative program to use for the mount of the file system + MountProgram string `toml:"mount_program"` + + // MountOpt specifies extra mount options used when mounting + MountOpt string `toml:"mountopt"` +} diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index 3fe305cc1..856c73e51 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -18,6 +18,7 @@ import ( "github.com/BurntSushi/toml" drivers "github.com/containers/storage/drivers" "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/config" "github.com/containers/storage/pkg/directory" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/ioutils" @@ -842,12 +843,16 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return nil, -1, err + } } rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return nil, -1, err + } } if id == "" { id = stringid.GenerateRandomID() @@ -870,7 +875,9 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w lstore.Lock() defer lstore.Unlock() if modified, err := lstore.Modified(); modified || err != nil { - lstore.Load() + if err = lstore.Load(); err != nil { + return nil, -1, err + } } } if l, err := lstore.Get(parent); err == nil && l != nil { @@ -946,7 +953,9 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, o store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } ilayer, err = store.Get(layer) if err == nil { @@ -966,7 +975,9 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, o ristore.Lock() defer ristore.Unlock() if modified, err := ristore.Modified(); modified || err != nil { - ristore.Load() + if err = ristore.Load(); err != nil { + return nil, err + } } creationDate := time.Now().UTC() @@ -1004,7 +1015,9 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, crea store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } } // Walk the top layer list. @@ -1125,14 +1138,18 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return nil, err + } } var cimage *Image for _, store := range append([]ROImageStore{istore}, istores...) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } cimage, err = store.Get(image) if err == nil { @@ -1162,7 +1179,9 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return nil, err + } } if !options.HostUIDMapping && len(options.UIDMap) == 0 { uidMap = s.uidMap @@ -1222,7 +1241,9 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return nil, err + } } options.IDMappingOptions = IDMappingOptions{ HostUIDMapping: len(options.UIDMap) == 0, @@ -1254,17 +1275,23 @@ func (s *store) SetMetadata(id, metadata string) error { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return err + } } ristore.Lock() defer ristore.Unlock() if modified, err := ristore.Modified(); modified || err != nil { - ristore.Load() + if err := ristore.Load(); err != nil { + return err + } } rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return err + } } if rlstore.Exists(id) { @@ -1292,7 +1319,9 @@ func (s *store) Metadata(id string) (string, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return "", err + } } if store.Exists(id) { return store.Metadata(id) @@ -1311,7 +1340,9 @@ func (s *store) Metadata(id string) (string, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return "", err + } } if store.Exists(id) { return store.Metadata(id) @@ -1325,7 +1356,9 @@ func (s *store) Metadata(id string) (string, error) { cstore.Lock() defer cstore.Unlock() if modified, err := cstore.Modified(); modified || err != nil { - cstore.Load() + if err = cstore.Load(); err != nil { + return "", err + } } if cstore.Exists(id) { return cstore.Metadata(id) @@ -1346,7 +1379,9 @@ func (s *store) ListImageBigData(id string) ([]string, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } bigDataNames, err := store.BigDataNames(id) if err == nil { @@ -1369,7 +1404,9 @@ func (s *store) ImageBigDataSize(id, key string) (int64, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return -1, err + } } size, err := store.BigDataSize(id, key) if err == nil { @@ -1393,7 +1430,9 @@ func (s *store) ImageBigDataDigest(id, key string) (digest.Digest, error) { ristore.Lock() defer ristore.Unlock() if modified, err := ristore.Modified(); modified || err != nil { - ristore.Load() + if err = ristore.Load(); err != nil { + return "", nil + } } d, err := ristore.BigDataDigest(id, key) if err == nil && d.Validate() == nil { @@ -1416,7 +1455,9 @@ func (s *store) ImageBigData(id, key string) ([]byte, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } data, err := store.BigData(id, key) if err == nil { @@ -1435,7 +1476,9 @@ func (s *store) SetImageBigData(id, key string, data []byte) error { ristore.Lock() defer ristore.Unlock() if modified, err := ristore.Modified(); modified || err != nil { - ristore.Load() + if err = ristore.Load(); err != nil { + return nil + } } return ristore.SetBigData(id, key, data) @@ -1456,7 +1499,9 @@ func (s *store) ImageSize(id string) (int64, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return -1, err + } } } @@ -1475,7 +1520,9 @@ func (s *store) ImageSize(id string) (int64, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return -1, err + } } if image, err = store.Get(id); err == nil { imageStore = store @@ -1560,7 +1607,9 @@ func (s *store) ContainerSize(id string) (int64, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return -1, err + } } } @@ -1582,7 +1631,9 @@ func (s *store) ContainerSize(id string) (int64, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return -1, err + } } // Read the container record. @@ -1644,7 +1695,9 @@ func (s *store) ListContainerBigData(id string) ([]string, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return nil, err + } } return rcstore.BigDataNames(id) @@ -1658,7 +1711,9 @@ func (s *store) ContainerBigDataSize(id, key string) (int64, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return -1, err + } } return rcstore.BigDataSize(id, key) } @@ -1671,7 +1726,9 @@ func (s *store) ContainerBigDataDigest(id, key string) (digest.Digest, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return "", err + } } return rcstore.BigDataDigest(id, key) } @@ -1684,7 +1741,9 @@ func (s *store) ContainerBigData(id, key string) ([]byte, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return nil, err + } } return rcstore.BigData(id, key) } @@ -1697,7 +1756,9 @@ func (s *store) SetContainerBigData(id, key string, data []byte) error { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return err + } } return rcstore.SetBigData(id, key, data) } @@ -1715,7 +1776,9 @@ func (s *store) Exists(id string) bool { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return false + } } if store.Exists(id) { return true @@ -1734,7 +1797,9 @@ func (s *store) Exists(id string) bool { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return false + } } if store.Exists(id) { return true @@ -1748,7 +1813,9 @@ func (s *store) Exists(id string) bool { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return false + } } if rcstore.Exists(id) { return true @@ -1779,7 +1846,9 @@ func (s *store) SetNames(id string, names []string) error { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return err + } } if rlstore.Exists(id) { return rlstore.SetNames(id, deduped) @@ -1792,7 +1861,9 @@ func (s *store) SetNames(id string, names []string) error { ristore.Lock() defer ristore.Unlock() if modified, err := ristore.Modified(); modified || err != nil { - ristore.Load() + if err = ristore.Load(); err != nil { + return err + } } if ristore.Exists(id) { return ristore.SetNames(id, deduped) @@ -1805,7 +1876,9 @@ func (s *store) SetNames(id string, names []string) error { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return err + } } if rcstore.Exists(id) { return rcstore.SetNames(id, deduped) @@ -1826,7 +1899,9 @@ func (s *store) Names(id string) ([]string, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } if l, err := store.Get(id); l != nil && err == nil { return l.Names, nil @@ -1845,7 +1920,9 @@ func (s *store) Names(id string) ([]string, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } if i, err := store.Get(id); i != nil && err == nil { return i.Names, nil @@ -1859,7 +1936,9 @@ func (s *store) Names(id string) ([]string, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return nil, err + } } if c, err := rcstore.Get(id); c != nil && err == nil { return c.Names, nil @@ -1880,7 +1959,9 @@ func (s *store) Lookup(name string) (string, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return "", err + } } if l, err := store.Get(name); l != nil && err == nil { return l.ID, nil @@ -1899,7 +1980,9 @@ func (s *store) Lookup(name string) (string, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return "", err + } } if i, err := store.Get(name); i != nil && err == nil { return i.ID, nil @@ -1913,7 +1996,9 @@ func (s *store) Lookup(name string) (string, error) { cstore.Lock() defer cstore.Unlock() if modified, err := cstore.Modified(); modified || err != nil { - cstore.Load() + if err = cstore.Load(); err != nil { + return "", err + } } if c, err := cstore.Get(name); c != nil && err == nil { return c.ID, nil @@ -1939,17 +2024,23 @@ func (s *store) DeleteLayer(id string) error { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return err + } } ristore.Lock() defer ristore.Unlock() if modified, err := ristore.Modified(); modified || err != nil { - ristore.Load() + if err = ristore.Load(); err != nil { + return err + } } rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return err + } } if rlstore.Exists(id) { @@ -2005,17 +2096,23 @@ func (s *store) DeleteImage(id string, commit bool) (layers []string, err error) rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return nil, err + } } ristore.Lock() defer ristore.Unlock() if modified, err := ristore.Modified(); modified || err != nil { - ristore.Load() + if err = ristore.Load(); err != nil { + return nil, err + } } rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return nil, err + } } layersToRemove := []string{} if ristore.Exists(id) { @@ -2137,17 +2234,23 @@ func (s *store) DeleteContainer(id string) error { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return err + } } ristore.Lock() defer ristore.Unlock() if modified, err := ristore.Modified(); modified || err != nil { - ristore.Load() + if err = ristore.Load(); err != nil { + return err + } } rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return err + } } if rcstore.Exists(id) { @@ -2192,17 +2295,23 @@ func (s *store) Delete(id string) error { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return err + } } ristore.Lock() defer ristore.Unlock() if modified, err := ristore.Modified(); modified || err != nil { - ristore.Load() + if err := ristore.Load(); err != nil { + return err + } } rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return err + } } if rcstore.Exists(id) { @@ -2254,17 +2363,23 @@ func (s *store) Wipe() error { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return err + } } ristore.Lock() defer ristore.Unlock() if modified, err := ristore.Modified(); modified || err != nil { - ristore.Load() + if err = ristore.Load(); err != nil { + return err + } } rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return err + } } if err = rcstore.Wipe(); err != nil { @@ -2306,7 +2421,9 @@ func (s *store) Mount(id, mountLabel string) (string, error) { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return "", err + } } if rlstore.Exists(id) { options := drivers.MountOpts{ @@ -2331,7 +2448,9 @@ func (s *store) Mounted(id string) (int, error) { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return 0, err + } } return rlstore.Mounted(id) @@ -2348,7 +2467,9 @@ func (s *store) Unmount(id string, force bool) (bool, error) { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return false, err + } } if rlstore.Exists(id) { return rlstore.Unmount(id, force) @@ -2369,7 +2490,9 @@ func (s *store) Changes(from, to string) ([]archive.Change, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } if store.Exists(to) { return store.Changes(from, to) @@ -2391,7 +2514,9 @@ func (s *store) DiffSize(from, to string) (int64, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return -1, err + } } if store.Exists(to) { return store.DiffSize(from, to) @@ -2412,7 +2537,9 @@ func (s *store) Diff(from, to string, options *DiffOptions) (io.ReadCloser, erro for _, store := range append([]ROLayerStore{lstore}, lstores...) { store.Lock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } if store.Exists(to) { rc, err := store.Diff(from, to, options) @@ -2440,7 +2567,9 @@ func (s *store) ApplyDiff(to string, diff io.Reader) (int64, error) { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return -1, err + } } if rlstore.Exists(to) { return rlstore.ApplyDiff(to, diff) @@ -2463,7 +2592,9 @@ func (s *store) layersByMappedDigest(m func(ROLayerStore, digest.Digest) ([]Laye store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } storeLayers, err := m(store, d) if err != nil { @@ -2507,7 +2638,9 @@ func (s *store) LayerSize(id string) (int64, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return -1, err + } } if store.Exists(id) { return store.Size(id) @@ -2524,7 +2657,9 @@ func (s *store) LayerParentOwners(id string) ([]int, []int, error) { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return nil, nil, err + } } if rlstore.Exists(id) { return rlstore.ParentOwners(id) @@ -2544,12 +2679,16 @@ func (s *store) ContainerParentOwners(id string) ([]int, []int, error) { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return nil, nil, err + } } rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return nil, nil, err + } } container, err := rcstore.Get(id) if err != nil { @@ -2577,7 +2716,9 @@ func (s *store) Layers() ([]Layer, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } storeLayers, err := store.Layers() if err != nil { @@ -2603,7 +2744,9 @@ func (s *store) Images() ([]Image, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } storeImages, err := store.Images() if err != nil { @@ -2623,7 +2766,9 @@ func (s *store) Containers() ([]Container, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return nil, err + } } return rcstore.Containers() @@ -2642,7 +2787,9 @@ func (s *store) Layer(id string) (*Layer, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } layer, err := store.Get(id) if err == nil { @@ -2665,7 +2812,9 @@ func (s *store) Image(id string) (*Image, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } image, err := store.Get(id) if err == nil { @@ -2695,7 +2844,9 @@ func (s *store) ImagesByTopLayer(id string) ([]*Image, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } imageList, err := store.Images() if err != nil { @@ -2726,7 +2877,9 @@ func (s *store) ImagesByDigest(d digest.Digest) ([]*Image, error) { store.Lock() defer store.Unlock() if modified, err := store.Modified(); modified || err != nil { - store.Load() + if err = store.Load(); err != nil { + return nil, err + } } imageList, err := store.ByDigest(d) if err != nil && err != ErrImageUnknown { @@ -2745,7 +2898,9 @@ func (s *store) Container(id string) (*Container, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return nil, err + } } return rcstore.Get(id) @@ -2759,7 +2914,9 @@ func (s *store) ContainerLayerID(id string) (string, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return "", err + } } container, err := rcstore.Get(id) if err != nil { @@ -2780,7 +2937,9 @@ func (s *store) ContainerByLayer(id string) (*Container, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return nil, err + } } containerList, err := rcstore.Containers() if err != nil { @@ -2803,7 +2962,9 @@ func (s *store) ContainerDirectory(id string) (string, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return "", err + } } id, err = rcstore.Lookup(id) @@ -2828,7 +2989,9 @@ func (s *store) ContainerRunDirectory(id string) (string, error) { rcstore.Lock() defer rcstore.Unlock() if modified, err := rcstore.Modified(); modified || err != nil { - rcstore.Load() + if err = rcstore.Load(); err != nil { + return "", err + } } id, err = rcstore.Lookup(id) @@ -2899,7 +3062,9 @@ func (s *store) Shutdown(force bool) ([]string, error) { rlstore.Lock() defer rlstore.Unlock() if modified, err := rlstore.Modified(); modified || err != nil { - rlstore.Load() + if err = rlstore.Load(); err != nil { + return nil, err + } } layers, err := rlstore.Layers() @@ -2992,6 +3157,15 @@ func copyStringDigestMap(m map[string]digest.Digest) map[string]digest.Digest { return ret } +func copyDigestSlice(slice []digest.Digest) []digest.Digest { + if len(slice) == 0 { + return nil + } + ret := make([]digest.Digest, len(slice)) + copy(ret, slice) + return ret +} + // copyStringInterfaceMap still forces us to assume that the interface{} is // a non-pointer scalar value func copyStringInterfaceMap(m map[string]interface{}) map[string]interface{} { @@ -3005,108 +3179,13 @@ func copyStringInterfaceMap(m map[string]interface{}) map[string]interface{} { // DefaultConfigFile path to the system wide storage.conf file const DefaultConfigFile = "/etc/containers/storage.conf" -// ThinpoolOptionsConfig represents the "storage.options.thinpool" -// TOML config table. -type ThinpoolOptionsConfig struct { - // AutoExtendPercent determines the amount by which pool needs to be - // grown. This is specified in terms of % of pool size. So a value of - // 20 means that when threshold is hit, pool will be grown by 20% of - // existing pool size. - AutoExtendPercent string `toml:"autoextend_percent"` - - // AutoExtendThreshold determines the pool extension threshold in terms - // of percentage of pool size. For example, if threshold is 60, that - // means when pool is 60% full, threshold has been hit. - AutoExtendThreshold string `toml:"autoextend_threshold"` - - // BaseSize specifies the size to use when creating the base device, - // which limits the size of images and containers. - BaseSize string `toml:"basesize"` - - // BlockSize specifies a custom blocksize to use for the thin pool. - BlockSize string `toml:"blocksize"` - - // DirectLvmDevice specifies a custom block storage device to use for - // the thin pool. - DirectLvmDevice string `toml:"directlvm_device"` - - // DirectLvmDeviceForcewipes device even if device already has a - // filesystem - DirectLvmDeviceForce string `toml:"directlvm_device_force"` - - // Fs specifies the filesystem type to use for the base device. - Fs string `toml:"fs"` - - // log_level sets the log level of devicemapper. - LogLevel string `toml:"log_level"` - - // MinFreeSpace specifies the min free space percent in a thin pool - // require for new device creation to - MinFreeSpace string `toml:"min_free_space"` - - // MkfsArg specifies extra mkfs arguments to be used when creating the - // basedevice. - MkfsArg string `toml:"mkfsarg"` - - // MountOpt specifies extra mount options used when mounting the thin - // devices. - MountOpt string `toml:"mountopt"` - - // UseDeferredDeletion marks device for deferred deletion - UseDeferredDeletion string `toml:"use_deferred_deletion"` - - // UseDeferredRemoval marks device for deferred removal - UseDeferredRemoval string `toml:"use_deferred_removal"` - - // XfsNoSpaceMaxRetriesFreeSpace specifies the maximum number of - // retries XFS should attempt to complete IO when ENOSPC (no space) - // error is returned by underlying storage device. - XfsNoSpaceMaxRetries string `toml:"xfs_nospace_max_retries"` -} - -// OptionsConfig represents the "storage.options" TOML config table. -type OptionsConfig struct { - // AdditionalImagesStores is the location of additional read/only - // Image stores. Usually used to access Networked File System - // for shared image content - AdditionalImageStores []string `toml:"additionalimagestores"` - - // Size - Size string `toml:"size"` - - // RemapUIDs is a list of default UID mappings to use for layers. - RemapUIDs string `toml:"remap-uids"` - // RemapGIDs is a list of default GID mappings to use for layers. - RemapGIDs string `toml:"remap-gids"` - - // RemapUser is the name of one or more entries in /etc/subuid which - // should be used to set up default UID mappings. - RemapUser string `toml:"remap-user"` - // RemapGroup is the name of one or more entries in /etc/subgid which - // should be used to set up default GID mappings. - RemapGroup string `toml:"remap-group"` - // Thinpool container options to be handed to thinpool drivers - Thinpool struct{ ThinpoolOptionsConfig } `toml:"thinpool"` - // OSTree repository - OstreeRepo string `toml:"ostree_repo"` - - // Do not create a bind mount on the storage home - SkipMountHome string `toml:"skip_mount_home"` - - // Alternative program to use for the mount of the file system - MountProgram string `toml:"mount_program"` - - // MountOpt specifies extra mount options used when mounting - MountOpt string `toml:"mountopt"` -} - // TOML-friendly explicit tables used for conversions. type tomlConfig struct { Storage struct { - Driver string `toml:"driver"` - RunRoot string `toml:"runroot"` - GraphRoot string `toml:"graphroot"` - Options struct{ OptionsConfig } `toml:"options"` + Driver string `toml:"driver"` + RunRoot string `toml:"runroot"` + GraphRoot string `toml:"graphroot"` + Options struct{ config.OptionsConfig } `toml:"options"` } `toml:"storage"` } diff --git a/vendor/github.com/containers/storage/vendor.conf b/vendor/github.com/containers/storage/vendor.conf index 04af9010b..c143b049d 100644 --- a/vendor/github.com/containers/storage/vendor.conf +++ b/vendor/github.com/containers/storage/vendor.conf @@ -1,12 +1,18 @@ github.com/BurntSushi/toml master github.com/Microsoft/go-winio 307e919c663683a9000576fdc855acaf9534c165 github.com/Microsoft/hcsshim a8d9cc56cbce765a7eebdf4792e6ceceeff3edb8 +github.com/containers/image master github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00 github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52 +github.com/docker/libtrust master +github.com/klauspost/compress v1.4.1 +github.com/klauspost/cpuid v1.2.0 +github.com/klauspost/pgzip v1.2.1 github.com/mattn/go-shellwords 753a2322a99f87c0eff284980e77f53041555bc6 github.com/mistifyio/go-zfs c0224de804d438efd11ea6e52ada8014537d6062 github.com/opencontainers/go-digest master +github.com/opencontainers/image-spec master github.com/opencontainers/runc 6c22e77604689db8725fa866f0f2ec0b3e8c3a07 github.com/opencontainers/selinux v1.1 github.com/ostreedev/ostree-go master @@ -23,6 +29,3 @@ golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5 gotest.tools master github.com/google/go-cmp master -github.com/klauspost/pgzip v1.2.1 -github.com/klauspost/compress v1.4.1 -github.com/klauspost/cpuid v1.2.0 -- cgit v1.2.3-54-g00ecf