Add --accept-repositories integration tests

This adds the integration tests for the repository or namespaced
registry feature introduced in c/common.

Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
This commit is contained in:
Sascha Grunert 2021-07-20 12:20:43 +02:00
parent a5de831418
commit 732ece6ae2
No known key found for this signature in database
GPG key ID: 09D97D153EF94D93
17 changed files with 462 additions and 89 deletions

View file

@ -52,6 +52,7 @@ func init() {
loginOptions.Stdin = os.Stdin
loginOptions.Stdout = os.Stdout
loginOptions.AcceptUnspecifiedRegistry = true
loginOptions.AcceptRepositories = true
}
// Implementation of podman-login.

View file

@ -43,6 +43,7 @@ func init() {
logoutOptions.Stdout = os.Stdout
logoutOptions.AcceptUnspecifiedRegistry = true
logoutOptions.AcceptRepositories = true
}
// Implementation of podman-logout.

2
go.mod
View file

@ -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

4
go.sum
View file

@ -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=

View file

@ -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())
})
})

View file

@ -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 == "" {

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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 {

View file

@ -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)
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")
}
if ref != nil {
authConfig, err = config.GetCredentialsForRef(systemContext, ref)
if err != nil {
return errors.Wrap(err, "get credentials for repository")
}
} else {
server = getRegistryName(args[0])
}
authConfig, err := config.GetCredentials(systemContext, server)
if err != nil {
return errors.Wrap(err, "reading auth file")
// 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", server)
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")
}
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)
}
}

View file

@ -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

View file

@ -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 {

View file

@ -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)

View file

@ -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"`

2
vendor/modules.txt vendored
View file

@ -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