diff options
-rw-r--r-- | cmd/podman/login.go | 1 | ||||
-rw-r--r-- | cmd/podman/logout.go | 1 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 4 | ||||
-rw-r--r-- | test/e2e/login_logout_test.go | 223 | ||||
-rw-r--r-- | vendor/github.com/containers/common/libimage/copier.go | 12 | ||||
-rw-r--r-- | vendor/github.com/containers/common/libimage/image.go | 8 | ||||
-rw-r--r-- | vendor/github.com/containers/common/libimage/import.go | 2 | ||||
-rw-r--r-- | vendor/github.com/containers/common/libimage/manifests/manifests.go | 18 | ||||
-rw-r--r-- | vendor/github.com/containers/common/libimage/pull.go | 80 | ||||
-rw-r--r-- | vendor/github.com/containers/common/libimage/search.go | 4 | ||||
-rw-r--r-- | vendor/github.com/containers/common/pkg/auth/auth.go | 153 | ||||
-rw-r--r-- | vendor/github.com/containers/common/pkg/auth/cli.go | 20 | ||||
-rw-r--r-- | vendor/github.com/containers/common/pkg/config/config.go | 11 | ||||
-rw-r--r-- | vendor/github.com/containers/common/pkg/config/pull_policy.go | 8 | ||||
-rw-r--r-- | vendor/github.com/containers/common/pkg/seccomp/types.go | 2 | ||||
-rw-r--r-- | vendor/modules.txt | 2 |
17 files changed, 462 insertions, 89 deletions
diff --git a/cmd/podman/login.go b/cmd/podman/login.go index a8dadf5cd..7e853b38d 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -52,6 +52,7 @@ func init() { loginOptions.Stdin = os.Stdin loginOptions.Stdout = os.Stdout loginOptions.AcceptUnspecifiedRegistry = true + loginOptions.AcceptRepositories = true } // Implementation of podman-login. diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go index 0ee134635..f44b13a1f 100644 --- a/cmd/podman/logout.go +++ b/cmd/podman/logout.go @@ -43,6 +43,7 @@ func init() { logoutOptions.Stdout = os.Stdout logoutOptions.AcceptUnspecifiedRegistry = true + logoutOptions.AcceptRepositories = true } // Implementation of podman-logout. @@ -12,7 +12,7 @@ require ( github.com/containernetworking/cni v0.8.1 github.com/containernetworking/plugins v0.9.1 github.com/containers/buildah v1.21.1-0.20210721171232-54cafea4c933 - github.com/containers/common v0.41.1-0.20210721172332-291287e9d060 + github.com/containers/common v0.41.1-0.20210730122913-cd6c45fd20e3 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.14.0 github.com/containers/ocicrypt v1.1.2 @@ -242,8 +242,8 @@ github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRD github.com/containers/buildah v1.21.1-0.20210721171232-54cafea4c933 h1:jqO3hDypBoKM5be+fVcqGHOpX2fOiQy2DFEeb/VKpsk= github.com/containers/buildah v1.21.1-0.20210721171232-54cafea4c933/go.mod h1:9gspFNeUJxIK72n1IMIKIHmtcePEZQsv0tjo+1LqkCo= github.com/containers/common v0.41.1-0.20210721112610-c95d2f794edf/go.mod h1:Ba5YVNCnyX6xDtg1JqEHa2EMVMW5UbHmIyEqsEwpeGE= -github.com/containers/common v0.41.1-0.20210721172332-291287e9d060 h1:HgGff2MeEKfYoKp2WQFl9xdsgP7KV8rr/1JZRIuPXmg= -github.com/containers/common v0.41.1-0.20210721172332-291287e9d060/go.mod h1:Ba5YVNCnyX6xDtg1JqEHa2EMVMW5UbHmIyEqsEwpeGE= +github.com/containers/common v0.41.1-0.20210730122913-cd6c45fd20e3 h1:lHOZ+G5B7aP2YPsbDo4DtALFAuFG5PWH3Pv5zL2bC08= +github.com/containers/common v0.41.1-0.20210730122913-cd6c45fd20e3/go.mod h1:UzAAjDsxwd4qkN1mgsk6aspduBY5bspxvKgwQElaBwk= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.13.2/go.mod h1:GkWursKDlDcUIT7L7vZf70tADvZCk/Ga0wgS0MuF0ag= diff --git a/test/e2e/login_logout_test.go b/test/e2e/login_logout_test.go index 6088d991f..7ad1fc1f2 100644 --- a/test/e2e/login_logout_test.go +++ b/test/e2e/login_logout_test.go @@ -97,6 +97,24 @@ var _ = Describe("Podman login and logout", func() { os.RemoveAll(certDirPath) }) + readAuthInfo := func(filePath string) map[string]interface{} { + authBytes, err := ioutil.ReadFile(filePath) + Expect(err).To(BeNil()) + + var authInfo map[string]interface{} + err = json.Unmarshal(authBytes, &authInfo) + Expect(err).To(BeNil()) + fmt.Println(authInfo) + + const authsKey = "auths" + Expect(authInfo).To(HaveKey(authsKey)) + + auths, ok := authInfo[authsKey].(map[string]interface{}) + Expect(ok).To(BeTrue()) + + return auths + } + It("podman login and logout", func() { session := podmanTest.Podman([]string{"login", "-u", "podmantest", "-p", "test", server}) session.WaitWithDefaultTimeout() @@ -151,10 +169,7 @@ var _ = Describe("Podman login and logout", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - authInfo, _ := ioutil.ReadFile(authFile) - var info map[string]interface{} - json.Unmarshal(authInfo, &info) - fmt.Println(info) + readAuthInfo(authFile) // push should fail with nonexistent authfile session = podmanTest.Podman([]string{"push", "--authfile", "/tmp/nonexistent", ALPINE, testImg}) @@ -284,4 +299,204 @@ var _ = Describe("Podman login and logout", func() { session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) }) + + It("podman login and logout with repository", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testRepository := server + "/podmantest" + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testRepository, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo := readAuthInfo(authFile) + Expect(authInfo).To(HaveKey(testRepository)) + + session = podmanTest.Podman([]string{ + "logout", + "--authfile", authFile, + testRepository, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo = readAuthInfo(authFile) + Expect(authInfo).NotTo(HaveKey(testRepository)) + }) + + It("podman login and logout with repository and specified image", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testTarget := server + "/podmantest/test-alpine" + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo := readAuthInfo(authFile) + Expect(authInfo).To(HaveKey(testTarget)) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + }) + + It("podman login and logout with repository with fallback", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testRepos := []string{ + server + "/podmantest", + server, + } + for _, testRepo := range testRepos { + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testRepo, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + } + + authInfo := readAuthInfo(authFile) + Expect(authInfo).To(HaveKey(testRepos[0])) + Expect(authInfo).To(HaveKey(testRepos[1])) + + session := podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testRepos[0] + "/test-image-alpine", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "logout", + "--authfile", authFile, + testRepos[0], + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testRepos[0] + "/test-image-alpine", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "logout", + "--authfile", authFile, + testRepos[1], + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo = readAuthInfo(authFile) + Expect(authInfo).NotTo(HaveKey(testRepos[0])) + Expect(authInfo).NotTo(HaveKey(testRepos[1])) + }) + + It("podman login with repository invalid arguments", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + for _, invalidArg := range []string{ + "https://" + server + "/podmantest", + server + "/podmantest/image:latest", + } { + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + invalidArg, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitWithError()) + } + }) + + It("podman login and logout with repository push with invalid auth.json credentials", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + // only `server` contains the correct login data + err := ioutil.WriteFile(authFile, []byte(fmt.Sprintf(`{"auths": { + "%s/podmantest": { "auth": "cG9kbWFudGVzdDp3cm9uZw==" }, + "%s": { "auth": "cG9kbWFudGVzdDp0ZXN0" } + }}`, server, server)), 0644) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, server + "/podmantest/test-image", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, server + "/test-image", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + }) + + It("podman login and logout with repository pull with wrong auth.json credentials", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testTarget := server + "/podmantest/test-alpine" + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // only `server + /podmantest` and `server` have the correct login data + err := ioutil.WriteFile(authFile, []byte(fmt.Sprintf(`{"auths": { + "%s/podmantest/test-alpine": { "auth": "cG9kbWFudGVzdDp3cm9uZw==" }, + "%s/podmantest": { "auth": "cG9kbWFudGVzdDp0ZXN0" }, + "%s": { "auth": "cG9kbWFudGVzdDp0ZXN0" } + }}`, server, server, server)), 0644) + Expect(err).To(BeNil()) + + session = podmanTest.Podman([]string{ + "pull", + "--authfile", authFile, + server + "/podmantest/test-alpine", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + }) }) diff --git a/vendor/github.com/containers/common/libimage/copier.go b/vendor/github.com/containers/common/libimage/copier.go index 34cc0d45d..a44f098ad 100644 --- a/vendor/github.com/containers/common/libimage/copier.go +++ b/vendor/github.com/containers/common/libimage/copier.go @@ -342,7 +342,7 @@ func (c *copier) copy(ctx context.Context, source, destination types.ImageRefere } } - var copiedManifest []byte + var returnManifest []byte f := func() error { opts := c.imageCopyOptions if sourceInsecure != nil { @@ -354,11 +354,13 @@ func (c *copier) copy(ctx context.Context, source, destination types.ImageRefere opts.DestinationCtx.DockerInsecureSkipTLSVerify = value } - var err error - copiedManifest, err = copy.Image(ctx, c.policyContext, destination, source, &opts) + copiedManifest, err := copy.Image(ctx, c.policyContext, destination, source, &opts) + if err == nil { + returnManifest = copiedManifest + } return err } - return copiedManifest, retry.RetryIfNecessary(ctx, f, &c.retryOptions) + return returnManifest, retry.RetryIfNecessary(ctx, f, &c.retryOptions) } // checkRegistrySourcesAllows checks the $BUILD_REGISTRY_SOURCES environment @@ -369,7 +371,7 @@ func (c *copier) copy(ctx context.Context, source, destination types.ImageRefere // If set, the insecure return value indicates whether the registry is set to // be insecure. // -// NOTE: this functionality is required by Buildah. +// NOTE: this functionality is required by Buildah for OpenShift. func checkRegistrySourcesAllows(dest types.ImageReference) (insecure *bool, err error) { registrySources, ok := os.LookupEnv("BUILD_REGISTRY_SOURCES") if !ok || registrySources == "" { diff --git a/vendor/github.com/containers/common/libimage/image.go b/vendor/github.com/containers/common/libimage/image.go index 5b060a185..c47e63339 100644 --- a/vendor/github.com/containers/common/libimage/image.go +++ b/vendor/github.com/containers/common/libimage/image.go @@ -836,9 +836,9 @@ func (i *Image) Manifest(ctx context.Context) (rawManifest []byte, mimeType stri return src.GetManifest(ctx, nil) } -// getImageDigest creates an image object and uses the hex value of the digest as the image ID -// for parsing the store reference -func getImageDigest(ctx context.Context, src types.ImageReference, sys *types.SystemContext) (string, error) { +// getImageID creates an image object and uses the hex value of the config +// blob's digest (if it has one) as the image ID for parsing the store reference +func getImageID(ctx context.Context, src types.ImageReference, sys *types.SystemContext) (string, error) { newImg, err := src.NewImage(ctx, sys) if err != nil { return "", err @@ -852,5 +852,5 @@ func getImageDigest(ctx context.Context, src types.ImageReference, sys *types.Sy if err = imageDigest.Validate(); err != nil { return "", errors.Wrapf(err, "error getting config info") } - return "@" + imageDigest.Hex(), nil + return "@" + imageDigest.Encoded(), nil } diff --git a/vendor/github.com/containers/common/libimage/import.go b/vendor/github.com/containers/common/libimage/import.go index 9926aaec7..bcfb4e129 100644 --- a/vendor/github.com/containers/common/libimage/import.go +++ b/vendor/github.com/containers/common/libimage/import.go @@ -86,7 +86,7 @@ func (r *Runtime) Import(ctx context.Context, path string, options *ImportOption return "", err } - id, err := getImageDigest(ctx, srcRef, r.systemContextCopy()) + id, err := getImageID(ctx, srcRef, r.systemContextCopy()) if err != nil { return "", err } diff --git a/vendor/github.com/containers/common/libimage/manifests/manifests.go b/vendor/github.com/containers/common/libimage/manifests/manifests.go index 875c2948d..81b5343c0 100644 --- a/vendor/github.com/containers/common/libimage/manifests/manifests.go +++ b/vendor/github.com/containers/common/libimage/manifests/manifests.go @@ -18,6 +18,7 @@ import ( "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" "github.com/containers/storage" + "github.com/containers/storage/pkg/lockfile" digest "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -395,3 +396,20 @@ func (l *list) Remove(instanceDigest digest.Digest) error { } return err } + +// LockerForImage returns a Locker for a given image record. It's recommended +// that processes which use LoadFromImage() to load a list from an image and +// then use that list's SaveToImage() method to save a modified version of the +// list to that image record use this lock to avoid accidentally wiping out +// changes that another process is also attempting to make. +func LockerForImage(store storage.Store, image string) (lockfile.Locker, error) { + img, err := store.Image(image) + if err != nil { + return nil, errors.Wrapf(err, "locating image %q for locating lock", image) + } + d := digest.NewDigestFromEncoded(digest.Canonical, img.ID) + if err := d.Validate(); err != nil { + return nil, errors.Wrapf(err, "coercing image ID for %q into a digest", image) + } + return store.GetDigestLock(d) +} diff --git a/vendor/github.com/containers/common/libimage/pull.go b/vendor/github.com/containers/common/libimage/pull.go index 1a6ad1ce2..97347178a 100644 --- a/vendor/github.com/containers/common/libimage/pull.go +++ b/vendor/github.com/containers/common/libimage/pull.go @@ -12,6 +12,7 @@ import ( dockerArchiveTransport "github.com/containers/image/v5/docker/archive" dockerDaemonTransport "github.com/containers/image/v5/docker/daemon" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/manifest" ociArchiveTransport "github.com/containers/image/v5/oci/archive" ociTransport "github.com/containers/image/v5/oci/layout" "github.com/containers/image/v5/pkg/shortnames" @@ -19,6 +20,7 @@ import ( "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" "github.com/containers/storage" + digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -192,19 +194,19 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference, imageName = storageName case ociArchiveTransport.Transport.Name(): - manifest, err := ociArchiveTransport.LoadManifestDescriptor(ref) + manifestDescriptor, err := ociArchiveTransport.LoadManifestDescriptor(ref) if err != nil { return nil, err } - // if index.json has no reference name, compute the image digest instead - if manifest.Annotations == nil || manifest.Annotations["org.opencontainers.image.ref.name"] == "" { - storageName, err = getImageDigest(ctx, ref, nil) + // if index.json has no reference name, compute the image ID instead + if manifestDescriptor.Annotations == nil || manifestDescriptor.Annotations["org.opencontainers.image.ref.name"] == "" { + storageName, err = getImageID(ctx, ref, nil) if err != nil { return nil, err } imageName = "sha256:" + storageName[1:] } else { - storageName = manifest.Annotations["org.opencontainers.image.ref.name"] + storageName = manifestDescriptor.Annotations["org.opencontainers.image.ref.name"] named, err := NormalizeName(storageName) if err != nil { return nil, err @@ -248,7 +250,7 @@ func (r *Runtime) storageReferencesReferencesFromArchiveReader(ctx context.Conte var imageNames []string if len(destNames) == 0 { - destName, err := getImageDigest(ctx, readerRef, &r.systemContext) + destName, err := getImageID(ctx, readerRef, &r.systemContext) if err != nil { return nil, nil, err } @@ -316,8 +318,8 @@ func (r *Runtime) copyFromDockerArchiveReaderReference(ctx context.Context, read } // copyFromRegistry pulls the specified, possibly unqualified, name from a -// registry. On successful pull it returns the used fully-qualified name that -// can later be used to look up the image in the local containers storage. +// registry. On successful pull it returns the ID of the image in local +// storage. // // If options.All is set, all tags from the specified registry will be pulled. func (r *Runtime) copyFromRegistry(ctx context.Context, ref types.ImageReference, inputName string, pullPolicy config.PullPolicy, options *PullOptions) ([]string, error) { @@ -337,7 +339,7 @@ func (r *Runtime) copyFromRegistry(ctx context.Context, ref types.ImageReference return nil, err } - pulledTags := []string{} + pulledIDs := []string{} for _, tag := range tags { select { // Let's be gentle with Podman remote. case <-ctx.Done(): @@ -353,15 +355,54 @@ func (r *Runtime) copyFromRegistry(ctx context.Context, ref types.ImageReference if err != nil { return nil, err } - pulledTags = append(pulledTags, pulled...) + pulledIDs = append(pulledIDs, pulled...) } - return pulledTags, nil + return pulledIDs, nil +} + +// imageIDsForManifest() parses the manifest of the copied image and then looks +// up the IDs of the matching image. There's a small slice of time, between +// when we copy the image into local storage and when we go to look for it +// using the name that we gave it when we copied it, when the name we wanted to +// assign to the image could have been moved, but the image's ID will remain +// the same until it is deleted. +func (r *Runtime) imagesIDsForManifest(manifestBytes []byte, sys *types.SystemContext) ([]string, error) { + var imageDigest digest.Digest + manifestType := manifest.GuessMIMEType(manifestBytes) + if manifest.MIMETypeIsMultiImage(manifestType) { + list, err := manifest.ListFromBlob(manifestBytes, manifestType) + if err != nil { + return nil, errors.Wrapf(err, "parsing manifest list") + } + d, err := list.ChooseInstance(sys) + if err != nil { + return nil, errors.Wrapf(err, "choosing instance from manifest list") + } + imageDigest = d + } else { + d, err := manifest.Digest(manifestBytes) + if err != nil { + return nil, errors.Wrapf(err, "digesting manifest") + } + imageDigest = d + } + var results []string + images, err := r.store.ImagesByDigest(imageDigest) + if err != nil { + return nil, errors.Wrapf(err, "listing images by manifest digest") + } + for _, image := range images { + results = append(results, image.ID) + } + if len(results) == 0 { + return nil, errors.Wrapf(storage.ErrImageUnknown, "identifying new image by manifest digest") + } + return results, nil } // copySingleImageFromRegistry pulls the specified, possibly unqualified, name -// from a registry. On successful pull it returns the used fully-qualified -// name that can later be used to look up the image in the local containers +// from a registry. On successful pull it returns the ID of the image in local // storage. func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName string, pullPolicy config.PullPolicy, options *PullOptions) ([]string, error) { //nolint:gocyclo // Sanity check. @@ -375,7 +416,7 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str err error ) - // Always check if there's a local image. If, we should use it's + // Always check if there's a local image. If so, we should use its // resolved name for pulling. Assume we're doing a `pull foo`. // If there's already a local image "localhost/foo", then we should // attempt pulling that instead of doing the full short-name dance. @@ -454,7 +495,7 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str } } - // If we found a local image, we should use it's locally resolved name + // If we found a local image, we should use its locally resolved name // (see containers/buildah/issues/2904). An exception is if a custom // platform is specified (e.g., `--arch=arm64`). In that case, we need // to pessimistically pull the image since some images declare wrong @@ -462,7 +503,8 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str // containers/podman/issues/10682). // // In other words: multi-arch support can only be as good as the images - // in the wild. + // in the wild, so we shouldn't break things for our users by trying to + // insist that they make sense. if localImage != nil && !customPlatform { if imageName != resolvedImageName { logrus.Debugf("Image %s resolved to local image %s which will be used for pulling", imageName, resolvedImageName) @@ -541,7 +583,8 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str return nil, err } } - if _, err := c.copy(ctx, srcRef, destRef); err != nil { + var manifestBytes []byte + if manifestBytes, err = c.copy(ctx, srcRef, destRef); err != nil { logrus.Debugf("Error pulling candidate %s: %v", candidateString, err) pullErrors = append(pullErrors, err) continue @@ -554,6 +597,9 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str } logrus.Debugf("Pulled candidate %s successfully", candidateString) + if ids, err := r.imagesIDsForManifest(manifestBytes, sys); err == nil { + return ids, nil + } return []string{candidate.Value.String()}, nil } diff --git a/vendor/github.com/containers/common/libimage/search.go b/vendor/github.com/containers/common/libimage/search.go index 4d1b842e7..df29bc7da 100644 --- a/vendor/github.com/containers/common/libimage/search.go +++ b/vendor/github.com/containers/common/libimage/search.go @@ -185,6 +185,10 @@ func (r *Runtime) searchImageInRegistry(ctx context.Context, term, registry stri sys.DockerInsecureSkipTLSVerify = options.InsecureSkipTLSVerify } + if options.Authfile != "" { + sys.AuthFilePath = options.Authfile + } + if options.ListTags { results, err := searchRepositoryTags(ctx, sys, registry, term, options) if err != nil { diff --git a/vendor/github.com/containers/common/pkg/auth/auth.go b/vendor/github.com/containers/common/pkg/auth/auth.go index d2e75c1f7..093da0299 100644 --- a/vendor/github.com/containers/common/pkg/auth/auth.go +++ b/vendor/github.com/containers/common/pkg/auth/auth.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/pkg/docker/config" "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/image/v5/types" @@ -69,30 +70,50 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO systemContext = systemContextWithOptions(systemContext, opts.AuthFile, opts.CertDir) var ( - server string - err error + authConfig types.DockerAuthConfig + key, registry string + ref reference.Named + err error ) - if len(args) > 1 { - return errors.New("login accepts only one registry to login to") - } - if len(args) == 0 { + l := len(args) + switch l { + case 0: if !opts.AcceptUnspecifiedRegistry { return errors.New("please provide a registry to login to") } - if server, err = defaultRegistryWhenUnspecified(systemContext); err != nil { + if key, err = defaultRegistryWhenUnspecified(systemContext); err != nil { return err } - logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", server) - } else { - server = getRegistryName(args[0]) + registry = key + logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", key) + + case 1: + key, registry, ref, err = parseRegistryArgument(args[0], opts.AcceptRepositories) + if err != nil { + return err + } + + default: + return errors.New("login accepts only one registry to login to") + } - authConfig, err := config.GetCredentials(systemContext, server) - if err != nil { - return errors.Wrap(err, "reading auth file") + + if ref != nil { + authConfig, err = config.GetCredentialsForRef(systemContext, ref) + if err != nil { + return errors.Wrap(err, "get credentials for repository") + } + } else { + // nolint: staticcheck + authConfig, err = config.GetCredentials(systemContext, registry) + if err != nil { + return errors.Wrap(err, "get credentials") + } } + if opts.GetLoginSet { if authConfig.Username == "" { - return errors.Errorf("not logged into %s", server) + return errors.Errorf("not logged into %s", key) } fmt.Fprintf(opts.Stdout, "%s\n", authConfig.Username) return nil @@ -119,9 +140,9 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO // If no username and no password is specified, try to use existing ones. if opts.Username == "" && password == "" && authConfig.Username != "" && authConfig.Password != "" { - fmt.Println("Authenticating with existing credentials...") - if err := docker.CheckAuth(ctx, systemContext, authConfig.Username, authConfig.Password, server); err == nil { - fmt.Fprintln(opts.Stdout, "Existing credentials are valid. Already logged in to", server) + fmt.Fprintf(opts.Stdout, "Authenticating with existing credentials for %s\n", key) + if err := docker.CheckAuth(ctx, systemContext, authConfig.Username, authConfig.Password, registry); err == nil { + fmt.Fprintf(opts.Stdout, "Existing credentials are valid. Already logged in to %s\n", registry) return nil } fmt.Fprintln(opts.Stdout, "Existing credentials are invalid, please enter valid username and password") @@ -132,9 +153,9 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO return errors.Wrap(err, "getting username and password") } - if err = docker.CheckAuth(ctx, systemContext, username, password, server); err == nil { + if err = docker.CheckAuth(ctx, systemContext, username, password, registry); err == nil { // Write the new credentials to the authfile - desc, err := config.SetCredentials(systemContext, server, username, password) + desc, err := config.SetCredentials(systemContext, key, username, password) if err != nil { return err } @@ -147,10 +168,45 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO return nil } if unauthorized, ok := err.(docker.ErrUnauthorizedForCredentials); ok { - logrus.Debugf("error logging into %q: %v", server, unauthorized) - return errors.Errorf("error logging into %q: invalid username/password", server) + logrus.Debugf("error logging into %q: %v", key, unauthorized) + return errors.Errorf("error logging into %q: invalid username/password", key) + } + return errors.Wrapf(err, "authenticating creds for %q", key) +} + +// parseRegistryArgument verifies the provided arg depending if we accept +// repositories or not. +func parseRegistryArgument(arg string, acceptRepositories bool) (key, registry string, maybeRef reference.Named, err error) { + if !acceptRepositories { + registry = getRegistryName(arg) + key = registry + return key, registry, maybeRef, nil + } + + key = trimScheme(arg) + if key != arg { + return key, registry, nil, errors.New("credentials key has https[s]:// prefix") } - return errors.Wrapf(err, "authenticating creds for %q", server) + + registry = getRegistryName(key) + if registry == key { + // We cannot parse a reference from a registry, so we stop here + return key, registry, nil, nil + } + + ref, parseErr := reference.ParseNamed(key) + if parseErr != nil { + return key, registry, nil, errors.Wrapf(parseErr, "parse reference from %q", key) + } + + if !reference.IsNameOnly(ref) { + return key, registry, nil, errors.Errorf("reference %q contains tag or digest", ref.String()) + } + + maybeRef = ref + registry = reference.Domain(ref) + + return key, registry, maybeRef, nil } // getRegistryName scrubs and parses the input to get the server name @@ -158,13 +214,21 @@ func getRegistryName(server string) string { // removes 'http://' or 'https://' from the front of the // server/registry string if either is there. This will be mostly used // for user input from 'Buildah login' and 'Buildah logout'. - server = strings.TrimPrefix(strings.TrimPrefix(server, "https://"), "http://") + server = trimScheme(server) // gets the registry from the input. If the input is of the form // quay.io/myuser/myimage, it will parse it and just return quay.io split := strings.Split(server, "/") return split[0] } +// trimScheme removes the HTTP(s) scheme from the provided repository. +func trimScheme(repository string) string { + // removes 'http://' or 'https://' from the front of the + // server/registry string if either is there. This will be mostly used + // for user input from 'Buildah login' and 'Buildah logout'. + return strings.TrimPrefix(strings.TrimPrefix(repository, "https://"), "http://") +} + // getUserAndPass gets the username and password from STDIN if not given // using the -u and -p flags. If the username prompt is left empty, the // displayed userFromAuthFile will be used instead. @@ -209,8 +273,9 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri systemContext = systemContextWithOptions(systemContext, opts.AuthFile, "") var ( - server string - err error + key, registry string + ref reference.Named + err error ) if len(args) > 1 { return errors.New("logout accepts only one registry to logout from") @@ -219,16 +284,20 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri if !opts.AcceptUnspecifiedRegistry { return errors.New("please provide a registry to logout from") } - if server, err = defaultRegistryWhenUnspecified(systemContext); err != nil { + if key, err = defaultRegistryWhenUnspecified(systemContext); err != nil { return err } - logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", server) + registry = key + logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", key) } if len(args) != 0 { if opts.All { return errors.New("--all takes no arguments") } - server = getRegistryName(args[0]) + key, registry, ref, err = parseRegistryArgument(args[0], opts.AcceptRepositories) + if err != nil { + return err + } } if opts.All { @@ -239,24 +308,34 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri return nil } - err = config.RemoveAuthentication(systemContext, server) + err = config.RemoveAuthentication(systemContext, key) switch errors.Cause(err) { case nil: - fmt.Fprintf(opts.Stdout, "Removed login credentials for %s\n", server) + fmt.Fprintf(opts.Stdout, "Removed login credentials for %s\n", key) return nil case config.ErrNotLoggedIn: - authConfig, err := config.GetCredentials(systemContext, server) - if err != nil { - return errors.Wrap(err, "reading auth file") + var authConfig types.DockerAuthConfig + if ref != nil { + authConfig, err = config.GetCredentialsForRef(systemContext, ref) + if err != nil { + return errors.Wrap(err, "get credentials for repository") + } + } else { + // nolint: staticcheck + authConfig, err = config.GetCredentials(systemContext, registry) + if err != nil { + return errors.Wrap(err, "get credentials") + } } - authInvalid := docker.CheckAuth(context.Background(), systemContext, authConfig.Username, authConfig.Password, server) + + authInvalid := docker.CheckAuth(context.Background(), systemContext, authConfig.Username, authConfig.Password, registry) if authConfig.Username != "" && authConfig.Password != "" && authInvalid == nil { - fmt.Printf("Not logged into %s with current tool. Existing credentials were established via docker login. Please use docker logout instead.\n", server) + fmt.Printf("Not logged into %s with current tool. Existing credentials were established via docker login. Please use docker logout instead.\n", key) return nil } - return errors.Errorf("Not logged into %s\n", server) + return errors.Errorf("Not logged into %s\n", key) default: - return errors.Wrapf(err, "logging out of %q", server) + return errors.Wrapf(err, "logging out of %q", key) } } diff --git a/vendor/github.com/containers/common/pkg/auth/cli.go b/vendor/github.com/containers/common/pkg/auth/cli.go index 5a7c1137c..7266bf48b 100644 --- a/vendor/github.com/containers/common/pkg/auth/cli.go +++ b/vendor/github.com/containers/common/pkg/auth/cli.go @@ -14,13 +14,14 @@ type LoginOptions struct { // CLI flags managed by the FlagSet returned by GetLoginFlags // Callers that use GetLoginFlags should not need to touch these values at all; callers that use // other CLI frameworks should set them based on user input. - AuthFile string - CertDir string - Password string - Username string - StdinPassword bool - GetLoginSet bool - Verbose bool // set to true for verbose output + AuthFile string + CertDir string + Password string + Username string + StdinPassword bool + GetLoginSet bool + Verbose bool // set to true for verbose output + AcceptRepositories bool // set to true to allow namespaces or repositories rather than just registries // Options caller can set Stdin io.Reader // set to os.Stdin Stdout io.Writer // set to os.Stdout @@ -32,8 +33,9 @@ type LogoutOptions struct { // CLI flags managed by the FlagSet returned by GetLogoutFlags // Callers that use GetLogoutFlags should not need to touch these values at all; callers that use // other CLI frameworks should set them based on user input. - AuthFile string - All bool + AuthFile string + All bool + AcceptRepositories bool // set to true to allow namespaces or repositories rather than just registries // Options caller can set Stdout io.Writer // set to os.Stdout AcceptUnspecifiedRegistry bool // set to true if allows logout with unspecified registry diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index 84876026d..008cfb642 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -637,9 +637,14 @@ func (c *Config) CheckCgroupsAndAdjustConfig() { session := os.Getenv("DBUS_SESSION_BUS_ADDRESS") hasSession := session != "" - if hasSession && strings.HasPrefix(session, "unix:path=") { - _, err := os.Stat(strings.TrimPrefix(session, "unix:path=")) - hasSession = err == nil + if hasSession { + for _, part := range strings.Split(session, ",") { + if strings.HasPrefix(part, "unix:path=") { + _, err := os.Stat(strings.TrimPrefix(part, "unix:path=")) + hasSession = err == nil + break + } + } } if !hasSession && unshare.GetRootlessUID() != 0 { diff --git a/vendor/github.com/containers/common/pkg/config/pull_policy.go b/vendor/github.com/containers/common/pkg/config/pull_policy.go index 7c32dd660..8c1f0ec29 100644 --- a/vendor/github.com/containers/common/pkg/config/pull_policy.go +++ b/vendor/github.com/containers/common/pkg/config/pull_policy.go @@ -76,13 +76,13 @@ func (p PullPolicy) Validate() error { // * "never" <-> PullPolicyNever func ParsePullPolicy(s string) (PullPolicy, error) { switch s { - case "always": + case "always", "Always": return PullPolicyAlways, nil - case "missing", "ifnotpresent", "": + case "missing", "Missing", "ifnotpresent", "IfNotPresent", "": return PullPolicyMissing, nil - case "newer", "ifnewer": + case "newer", "Newer", "ifnewer", "IfNewer": return PullPolicyNewer, nil - case "never": + case "never", "Never": return PullPolicyNever, nil default: return PullPolicyUnsupported, errors.Errorf("unsupported pull policy %q", s) diff --git a/vendor/github.com/containers/common/pkg/seccomp/types.go b/vendor/github.com/containers/common/pkg/seccomp/types.go index 36712458a..07751f729 100644 --- a/vendor/github.com/containers/common/pkg/seccomp/types.go +++ b/vendor/github.com/containers/common/pkg/seccomp/types.go @@ -7,7 +7,7 @@ package seccomp // Seccomp represents the config for a seccomp profile for syscall restriction. type Seccomp struct { DefaultAction Action `json:"defaultAction"` - DefaultErrnoRet *uint `json:"defaultErrnoRet"` + DefaultErrnoRet *uint `json:"defaultErrnoRet,omitempty"` // Architectures is kept to maintain backward compatibility with the old // seccomp profile. Architectures []Arch `json:"architectures,omitempty"` diff --git a/vendor/modules.txt b/vendor/modules.txt index 6b0eb5244..dc1a54b63 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -93,7 +93,7 @@ github.com/containers/buildah/pkg/overlay github.com/containers/buildah/pkg/parse github.com/containers/buildah/pkg/rusage github.com/containers/buildah/util -# github.com/containers/common v0.41.1-0.20210721172332-291287e9d060 +# github.com/containers/common v0.41.1-0.20210730122913-cd6c45fd20e3 github.com/containers/common/libimage github.com/containers/common/libimage/manifests github.com/containers/common/pkg/apparmor |