From a4a70b4506ec4abb8b3bbc3873ee5ca015a8ed08 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Thu, 24 Oct 2019 10:37:22 -0400 Subject: bump containers/image to v5.0.0, buildah to v1.11.4 Move to containers/image v5 and containers/buildah to v1.11.4. Replace an equality check with a type assertion when checking for a docker.ErrUnauthorizedForCredentials in `podman login`. Signed-off-by: Nalin Dahyabhai --- libpod/image/docker_registry_options.go | 4 ++-- libpod/image/image.go | 22 +++++++++++----------- libpod/image/parts.go | 2 +- libpod/image/pull.go | 22 +++++++++++----------- libpod/image/pull_test.go | 6 +++--- libpod/image/search.go | 4 ++-- libpod/image/utils.go | 8 ++++---- 7 files changed, 34 insertions(+), 34 deletions(-) (limited to 'libpod/image') diff --git a/libpod/image/docker_registry_options.go b/libpod/image/docker_registry_options.go index d205fe4ac..75417fe8b 100644 --- a/libpod/image/docker_registry_options.go +++ b/libpod/image/docker_registry_options.go @@ -3,8 +3,8 @@ package image import ( "fmt" - "github.com/containers/image/v4/docker/reference" - "github.com/containers/image/v4/types" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/types" podmanVersion "github.com/containers/libpod/version" ) diff --git a/libpod/image/image.go b/libpod/image/image.go index 9adefb5c5..5c530858a 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -12,17 +12,17 @@ import ( "syscall" "time" - cp "github.com/containers/image/v4/copy" - "github.com/containers/image/v4/directory" - dockerarchive "github.com/containers/image/v4/docker/archive" - "github.com/containers/image/v4/docker/reference" - "github.com/containers/image/v4/manifest" - ociarchive "github.com/containers/image/v4/oci/archive" - is "github.com/containers/image/v4/storage" - "github.com/containers/image/v4/tarball" - "github.com/containers/image/v4/transports" - "github.com/containers/image/v4/transports/alltransports" - "github.com/containers/image/v4/types" + cp "github.com/containers/image/v5/copy" + "github.com/containers/image/v5/directory" + dockerarchive "github.com/containers/image/v5/docker/archive" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/manifest" + ociarchive "github.com/containers/image/v5/oci/archive" + is "github.com/containers/image/v5/storage" + "github.com/containers/image/v5/tarball" + "github.com/containers/image/v5/transports" + "github.com/containers/image/v5/transports/alltransports" + "github.com/containers/image/v5/types" "github.com/containers/libpod/libpod/driver" "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/pkg/inspect" diff --git a/libpod/image/parts.go b/libpod/image/parts.go index 69bc44cdd..d4677f935 100644 --- a/libpod/image/parts.go +++ b/libpod/image/parts.go @@ -3,7 +3,7 @@ package image import ( "strings" - "github.com/containers/image/v4/docker/reference" + "github.com/containers/image/v5/docker/reference" "github.com/pkg/errors" ) diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 36950b6f3..7584ed9d8 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -7,17 +7,17 @@ import ( "path/filepath" "strings" - cp "github.com/containers/image/v4/copy" - "github.com/containers/image/v4/directory" - "github.com/containers/image/v4/docker" - dockerarchive "github.com/containers/image/v4/docker/archive" - "github.com/containers/image/v4/docker/tarfile" - ociarchive "github.com/containers/image/v4/oci/archive" - oci "github.com/containers/image/v4/oci/layout" - is "github.com/containers/image/v4/storage" - "github.com/containers/image/v4/transports" - "github.com/containers/image/v4/transports/alltransports" - "github.com/containers/image/v4/types" + cp "github.com/containers/image/v5/copy" + "github.com/containers/image/v5/directory" + "github.com/containers/image/v5/docker" + dockerarchive "github.com/containers/image/v5/docker/archive" + "github.com/containers/image/v5/docker/tarfile" + ociarchive "github.com/containers/image/v5/oci/archive" + oci "github.com/containers/image/v5/oci/layout" + is "github.com/containers/image/v5/storage" + "github.com/containers/image/v5/transports" + "github.com/containers/image/v5/transports/alltransports" + "github.com/containers/image/v5/types" "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/pkg/registries" "github.com/hashicorp/go-multierror" diff --git a/libpod/image/pull_test.go b/libpod/image/pull_test.go index 131b8b1f6..0046cdfef 100644 --- a/libpod/image/pull_test.go +++ b/libpod/image/pull_test.go @@ -9,9 +9,9 @@ import ( "strings" "testing" - "github.com/containers/image/v4/transports" - "github.com/containers/image/v4/transports/alltransports" - "github.com/containers/image/v4/types" + "github.com/containers/image/v5/transports" + "github.com/containers/image/v5/transports/alltransports" + "github.com/containers/image/v5/types" "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" "github.com/stretchr/testify/assert" diff --git a/libpod/image/search.go b/libpod/image/search.go index 0313c2d6e..fd29dac45 100644 --- a/libpod/image/search.go +++ b/libpod/image/search.go @@ -6,8 +6,8 @@ import ( "strings" "sync" - "github.com/containers/image/v4/docker" - "github.com/containers/image/v4/types" + "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/types" sysreg "github.com/containers/libpod/pkg/registries" "github.com/pkg/errors" "github.com/sirupsen/logrus" diff --git a/libpod/image/utils.go b/libpod/image/utils.go index e4ff1cfc4..5c971cb2b 100644 --- a/libpod/image/utils.go +++ b/libpod/image/utils.go @@ -7,10 +7,10 @@ import ( "regexp" "strings" - cp "github.com/containers/image/v4/copy" - "github.com/containers/image/v4/docker/reference" - "github.com/containers/image/v4/signature" - "github.com/containers/image/v4/types" + cp "github.com/containers/image/v5/copy" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/signature" + "github.com/containers/image/v5/types" "github.com/containers/storage" "github.com/pkg/errors" ) -- cgit v1.2.3-54-g00ecf From 803357334ce72e14f263a0951d83cdab3fff2bde Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Wed, 25 Sep 2019 16:43:50 -0400 Subject: image: don't get confused by lists When an image can be opened as an ImageSource but not an Image, handle the case where it's an image list all by itself, the case where it's an image for a different architecture/OS combination, or the case where it's both. Signed-off-by: Nalin Dahyabhai --- libpod/image/image.go | 206 ++++++++++++++++++++++++++++++-------------------- vendor/modules.txt | 2 +- 2 files changed, 123 insertions(+), 85 deletions(-) (limited to 'libpod/image') diff --git a/libpod/image/image.go b/libpod/image/image.go index 5c530858a..2ab88f2e9 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -3,6 +3,7 @@ package image import ( "context" "encoding/json" + stderrors "errors" "fmt" "io" "io/ioutil" @@ -16,6 +17,7 @@ import ( "github.com/containers/image/v5/directory" dockerarchive "github.com/containers/image/v5/docker/archive" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/image" "github.com/containers/image/v5/manifest" ociarchive "github.com/containers/image/v5/oci/archive" is "github.com/containers/image/v5/storage" @@ -42,8 +44,8 @@ import ( type Image struct { // Adding these two structs for now but will cull when we near // completion of this library. - imgRef types.Image - storeRef types.ImageReference + imgRef types.Image + imgSrcRef types.ImageSource inspect.ImageData inspect.ImageResult inspectInfo *types.ImageInspectInfo @@ -72,7 +74,10 @@ type InfoImage struct { } // ErrRepoTagNotFound is the error returned when the image id given doesn't match a rep tag in store -var ErrRepoTagNotFound = errors.New("unable to match user input to any specific repotag") +var ErrRepoTagNotFound = stderrors.New("unable to match user input to any specific repotag") + +// ErrImageIsBareList is the error returned when the image is just a list or index +var ErrImageIsBareList = stderrors.New("image contains a manifest list or image index, but no runnable image") // NewImageRuntimeFromStore creates an ImageRuntime based on a provided store func NewImageRuntimeFromStore(store storage.Store) *Runtime { @@ -294,9 +299,18 @@ func (i *Image) Digest() digest.Digest { return i.image.Digest } +// GetManifest returns the image's manifest as a byte array +// and manifest type as a string. +func (i *Image) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) { + imgSrcRef, err := i.toImageSourceRef(ctx) + if err != nil { + return nil, "", err + } + return imgSrcRef.GetManifest(ctx, instanceDigest) +} + // Manifest returns the image's manifest as a byte array -// and manifest type as a string. The manifest type is -// MediaTypeImageManifest from ociv1. +// and manifest type as a string. func (i *Image) Manifest(ctx context.Context) ([]byte, string, error) { imgRef, err := i.toImageRef(ctx) if err != nil { @@ -379,26 +393,6 @@ func (i *Image) Remove(ctx context.Context, force bool) error { return nil } -// TODO: Rework this method to not require an assembly of the fq name with transport -/* -// GetManifest tries to GET an images manifest, returns nil on success and err on failure -func (i *Image) GetManifest() error { - pullRef, err := alltransports.ParseImageName(i.assembleFqNameTransport()) - if err != nil { - return errors.Errorf("unable to parse '%s'", i.Names()[0]) - } - imageSource, err := pullRef.NewImageSource(nil) - if err != nil { - return errors.Wrapf(err, "unable to create new image source") - } - _, _, err = imageSource.GetManifest(nil) - if err == nil { - return nil - } - return err -} -*/ - // getImage retrieves an image matching the given name or hash from system // storage // If no matching image can be found, an error is returned @@ -612,76 +606,98 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere // MatchesID returns a bool based on if the input id // matches the image's id +// TODO: This isn't used anywhere, so remove it func (i *Image) MatchesID(id string) bool { return strings.HasPrefix(i.ID(), id) } -// toStorageReference returns a *storageReference from an Image -func (i *Image) toStorageReference() (types.ImageReference, error) { - var lookupName string - if i.storeRef == nil { - if i.image != nil { - lookupName = i.ID() - } else { - lookupName = i.InputName - } - storeRef, err := is.Transport.ParseStoreReference(i.imageruntime.store, lookupName) - if err != nil { - return nil, err - } - i.storeRef = storeRef - } - return i.storeRef, nil -} - // ToImageRef returns an image reference type from an image // TODO: Hopefully we can remove this exported function for mheon func (i *Image) ToImageRef(ctx context.Context) (types.Image, error) { return i.toImageRef(ctx) } -// toImageRef returns an Image Reference type from an image -func (i *Image) toImageRef(ctx context.Context) (types.Image, error) { +// toImageSourceRef returns an ImageSource Reference type from an image +func (i *Image) toImageSourceRef(ctx context.Context) (types.ImageSource, error) { if i == nil { - return nil, errors.Errorf("cannot convert nil image to image reference") + return nil, errors.Errorf("cannot convert nil image to image source reference") } - if i.imgRef == nil { + if i.imgSrcRef == nil { ref, err := is.Transport.ParseStoreReference(i.imageruntime.store, "@"+i.ID()) if err != nil { return nil, errors.Wrapf(err, "error parsing reference to image %q", i.ID()) } - imgRef, err := ref.NewImage(ctx, nil) + imgSrcRef, err := ref.NewImageSource(ctx, nil) if err != nil { - return nil, errors.Wrapf(err, "error reading image %q", i.ID()) + return nil, errors.Wrapf(err, "error reading image %q as image source", i.ID()) } - i.imgRef = imgRef + i.imgSrcRef = imgSrcRef } - return i.imgRef, nil -} - -// sizer knows its size. -type sizer interface { - Size() (int64, error) + return i.imgSrcRef, nil } //Size returns the size of the image func (i *Image) Size(ctx context.Context) (*uint64, error) { - storeRef, err := is.Transport.ParseStoreReference(i.imageruntime.store, i.ID()) - if err != nil { - return nil, err + if i.image == nil { + localImage, err := i.getLocalImage() + if err != nil { + return nil, err + } + i.image = localImage + } + if sum, err := i.imageruntime.store.ImageSize(i.ID()); err == nil && sum >= 0 { + usum := uint64(sum) + return &usum, nil + } + return nil, errors.Errorf("unable to determine size") +} + +// toImageRef returns an Image Reference type from an image +func (i *Image) toImageRef(ctx context.Context) (types.Image, error) { + if i == nil { + return nil, errors.Errorf("cannot convert nil image to image reference") } - systemContext := &types.SystemContext{} - img, err := storeRef.NewImageSource(ctx, systemContext) + imgSrcRef, err := i.toImageSourceRef(ctx) if err != nil { return nil, err } - if s, ok := img.(sizer); ok { - if sum, err := s.Size(); err == nil { - usum := uint64(sum) - return &usum, nil + if i.imgRef == nil { + systemContext := &types.SystemContext{} + unparsedDefaultInstance := image.UnparsedInstance(imgSrcRef, nil) + imgRef, err := image.FromUnparsedImage(ctx, systemContext, unparsedDefaultInstance) + if err != nil { + // check for a "tried-to-treat-a-bare-list-like-a-runnable-image" problem, else + // return info about the not-a-bare-list runnable image part of this storage.Image + if manifestBytes, manifestType, err2 := imgSrcRef.GetManifest(ctx, nil); err2 == nil { + if manifest.MIMETypeIsMultiImage(manifestType) { + if list, err3 := manifest.ListFromBlob(manifestBytes, manifestType); err3 == nil { + switch manifestType { + case ociv1.MediaTypeImageIndex: + err = errors.Wrapf(ErrImageIsBareList, "%q is an image index", i.InputName) + case manifest.DockerV2ListMediaType: + err = errors.Wrapf(ErrImageIsBareList, "%q is a manifest list", i.InputName) + default: + err = errors.Wrapf(ErrImageIsBareList, "%q", i.InputName) + } + for _, instanceDigest := range list.Instances() { + instance := instanceDigest + unparsedInstance := image.UnparsedInstance(imgSrcRef, &instance) + if imgRef2, err4 := image.FromUnparsedImage(ctx, systemContext, unparsedInstance); err4 == nil { + imgRef = imgRef2 + err = nil + break + } + } + } + } + } + if err != nil { + return nil, errors.Wrapf(err, "error reading image %q as image", i.ID()) + } } + i.imgRef = imgRef } - return nil, errors.Errorf("unable to determine size") + return i.imgRef, nil } // DriverData gets the driver data from the store on a layer @@ -708,6 +724,9 @@ type History struct { func (i *Image) History(ctx context.Context) ([]*History, error) { img, err := i.toImageRef(ctx) if err != nil { + if errors.Cause(err) == ErrImageIsBareList { + return nil, nil + } return nil, err } oci, err := img.OCIConfig(ctx) @@ -853,7 +872,10 @@ func (i *Image) GetLabel(ctx context.Context, label string) (string, error) { func (i *Image) Annotations(ctx context.Context) (map[string]string, error) { imageManifest, manifestType, err := i.Manifest(ctx) if err != nil { - return nil, err + imageManifest, manifestType, err = i.GetManifest(ctx, nil) + if err != nil { + return nil, err + } } annotations := make(map[string]string) switch manifestType { @@ -868,24 +890,19 @@ func (i *Image) Annotations(ctx context.Context) (map[string]string, error) { return annotations, nil } -// ociv1Image converts and image to an imgref and then an -// ociv1 image type +// ociv1Image converts an image to an imgref and then returns its config blob +// converted to an ociv1 image type func (i *Image) ociv1Image(ctx context.Context) (*ociv1.Image, error) { imgRef, err := i.toImageRef(ctx) if err != nil { return nil, err } - return imgRef.OCIConfig(ctx) } func (i *Image) imageInspectInfo(ctx context.Context) (*types.ImageInspectInfo, error) { if i.inspectInfo == nil { - sr, err := i.toStorageReference() - if err != nil { - return nil, err - } - ic, err := sr.NewImage(ctx, &types.SystemContext{}) + ic, err := i.toImageRef(ctx) if err != nil { return nil, err } @@ -906,20 +923,20 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { ociv1Img, err := i.ociv1Image(ctx) if err != nil { - return nil, err + ociv1Img = &ociv1.Image{} } info, err := i.imageInspectInfo(ctx) if err != nil { - return nil, err + info = &types.ImageInspectInfo{} } annotations, err := i.Annotations(ctx) if err != nil { return nil, err } - size, err := i.Size(ctx) - if err != nil { - return nil, err + size := int64(-1) + if usize, err := i.Size(ctx); err == nil { + size = int64(*usize) } repoDigests, err := i.RepoDigests() @@ -932,7 +949,7 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { return nil, err } - _, manifestType, err := i.Manifest(ctx) + _, manifestType, err := i.GetManifest(ctx, nil) if err != nil { return nil, errors.Wrapf(err, "unable to determine manifest type") } @@ -952,8 +969,8 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { Os: ociv1Img.OS, Config: &ociv1Img.Config, Version: info.DockerVersion, - Size: int64(*size), - VirtualSize: int64(*size), + Size: size, + VirtualSize: size, Annotations: annotations, Digest: i.Digest(), Labels: info.Labels, @@ -1082,6 +1099,9 @@ func splitString(input string) string { func (i *Image) IsParent(ctx context.Context) (bool, error) { children, err := i.getChildren(ctx, 1) if err != nil { + if errors.Cause(err) == ErrImageIsBareList { + return false, nil + } return false, err } return len(children) > 0, nil @@ -1165,6 +1185,9 @@ func (i *Image) GetParent(ctx context.Context) (*Image, error) { // fetch the configuration for the child image child, err := i.ociv1Image(ctx) if err != nil { + if errors.Cause(err) == ErrImageIsBareList { + return nil, nil + } return nil, err } for _, img := range images { @@ -1205,12 +1228,24 @@ func (i *Image) GetParent(ctx context.Context) (*Image, error) { // GetChildren returns a list of the imageIDs that depend on the image func (i *Image) GetChildren(ctx context.Context) ([]string, error) { - return i.getChildren(ctx, 0) + children, err := i.getChildren(ctx, 0) + if err != nil { + if errors.Cause(err) == ErrImageIsBareList { + return nil, nil + } + return nil, err + } + return children, nil } // getChildren returns a list of at most "max" imageIDs that depend on the image func (i *Image) getChildren(ctx context.Context, max int) ([]string, error) { var children []string + + if _, err := i.toImageRef(ctx); err != nil { + return nil, nil + } + images, err := i.imageruntime.GetImages() if err != nil { return nil, err @@ -1301,6 +1336,9 @@ func (i *Image) Comment(ctx context.Context, manifestType string) (string, error } ociv1Img, err := i.ociv1Image(ctx) if err != nil { + if errors.Cause(err) == ErrImageIsBareList { + return "", nil + } return "", err } if len(ociv1Img.History) > 0 { diff --git a/vendor/modules.txt b/vendor/modules.txt index 300561370..ae456b4a5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -87,10 +87,10 @@ github.com/containers/image/v5/storage github.com/containers/image/v5/copy github.com/containers/image/v5/docker/reference github.com/containers/image/v5/docker/tarfile +github.com/containers/image/v5/image github.com/containers/image/v5/oci/layout github.com/containers/image/v5/tarball github.com/containers/image/v5/pkg/sysregistriesv2 -github.com/containers/image/v5/image github.com/containers/image/v5/directory/explicitfilepath github.com/containers/image/v5/docker/policyconfiguration github.com/containers/image/v5/pkg/blobinfocache/none -- cgit v1.2.3-54-g00ecf From b9313d355e8cd6307d8772ad9c21958ffe981e5b Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Wed, 16 Oct 2019 11:57:45 -0400 Subject: pull/create: add --override-arch/--override-os flags Add --override-arch and --override-os as hidden flags, in line with the global flag names that skopeo uses, so that we can test behavior around manifest lists without having to conditionalize more of it by arch. Signed-off-by: Nalin Dahyabhai --- API.md | 4 ++++ cmd/podman/build.go | 7 ++++++- cmd/podman/cliconfig/config.go | 2 ++ cmd/podman/common.go | 10 ++++++++++ cmd/podman/pull.go | 6 ++++++ cmd/podman/shared/create.go | 7 ++++++- cmd/podman/shared/intermediate.go | 2 ++ cmd/podman/shared/intermediate_varlink.go | 4 ++++ cmd/podman/varlink/io.podman.varlink | 2 ++ libpod/image/docker_registry_options.go | 8 ++++++++ libpod/image/pull.go | 8 ++++++++ 11 files changed, 58 insertions(+), 2 deletions(-) (limited to 'libpod/image') diff --git a/API.md b/API.md index 1cbdacb12..e79f6ee5e 100755 --- a/API.md +++ b/API.md @@ -1557,6 +1557,10 @@ oomKillDisable [?bool](#?bool) oomScoreAdj [?int](#?int) +overrideArch [?string](#?string) + +overrideOS [?string](#?string) + pid [?string](#?string) pidsLimit [?int](#?int) diff --git a/cmd/podman/build.go b/cmd/podman/build.go index f4efea544..e9ebc50aa 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -9,6 +9,7 @@ import ( "github.com/containers/buildah" "github.com/containers/buildah/imagebuildah" buildahcli "github.com/containers/buildah/pkg/cli" + "github.com/containers/image/v5/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/adapter" @@ -360,7 +361,11 @@ func buildCmd(c *cliconfig.BuildValues) error { RuntimeArgs: runtimeFlags, SignaturePolicyPath: c.SignaturePolicy, Squash: c.Squash, - Target: c.Target, + SystemContext: &types.SystemContext{ + OSChoice: c.OverrideOS, + ArchitectureChoice: c.OverrideArch, + }, + Target: c.Target, } return runtime.Build(getContext(), c, options, containerfiles) } diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index 86258a543..1bb5fa30c 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -431,6 +431,8 @@ type PullValues struct { Authfile string CertDir string Creds string + OverrideArch string + OverrideOS string Quiet bool SignaturePolicy string TlsVerify bool diff --git a/cmd/podman/common.go b/cmd/podman/common.go index e93586b62..33a848553 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -370,6 +370,16 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "oom-score-adj", 0, "Tune the host's OOM preferences (-1000 to 1000)", ) + createFlags.String( + "override-arch", "", + "use `ARCH` instead of the architecture of the machine for choosing images", + ) + markFlagHidden(createFlags, "override-arch") + createFlags.String( + "override-os", "", + "use `OS` instead of the running OS for choosing images", + ) + markFlagHidden(createFlags, "override-os") createFlags.String( "pid", "", "PID namespace to use", diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index 905b1987d..d64793147 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -54,6 +54,10 @@ func init() { flags.BoolVar(&pullCommand.AllTags, "all-tags", false, "All tagged images in the repository will be pulled") flags.StringVar(&pullCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.BoolVarP(&pullCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") + flags.StringVar(&pullCommand.OverrideArch, "override-arch", "", "use `ARCH` instead of the architecture of the machine for choosing images") + markFlagHidden(flags, "override-arch") + flags.StringVar(&pullCommand.OverrideOS, "override-os", "", "use `OS` instead of the running OS for choosing images") + markFlagHidden(flags, "override-os") // Disabled flags for the remote client if !remote { flags.StringVar(&pullCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") @@ -122,6 +126,8 @@ func pullCmd(c *cliconfig.PullValues) (retError error) { dockerRegistryOptions := image.DockerRegistryOptions{ DockerRegistryCreds: registryCreds, DockerCertPath: c.CertDir, + OSChoice: c.OverrideOS, + ArchitectureChoice: c.OverrideArch, } if c.IsSet("tls-verify") { dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index f37f5fa62..759903c19 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -89,7 +89,12 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. return nil, nil, err } - newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, GetAuthFile(c.String("authfile")), writer, nil, image.SigningOptions{}, nil, pullType) + dockerRegistryOptions := image.DockerRegistryOptions{ + OSChoice: c.String("override-os"), + ArchitectureChoice: c.String("override-arch"), + } + + newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, GetAuthFile(c.String("authfile")), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType) if err != nil { return nil, nil, err } diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index 0f71dc087..bc12bd2a5 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -428,6 +428,8 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["no-hosts"] = newCRBool(c, "no-hosts") m["oom-kill-disable"] = newCRBool(c, "oom-kill-disable") m["oom-score-adj"] = newCRInt(c, "oom-score-adj") + m["override-arch"] = newCRString(c, "override-arch") + m["override-os"] = newCRString(c, "override-os") m["pid"] = newCRString(c, "pid") m["pids-limit"] = newCRInt64(c, "pids-limit") m["pod"] = newCRString(c, "pod") diff --git a/cmd/podman/shared/intermediate_varlink.go b/cmd/podman/shared/intermediate_varlink.go index c95470a72..89bd52324 100644 --- a/cmd/podman/shared/intermediate_varlink.go +++ b/cmd/podman/shared/intermediate_varlink.go @@ -131,6 +131,8 @@ func (g GenericCLIResults) MakeVarlink() iopodman.Create { Network: StringToPtr(g.Find("network")), OomKillDisable: BoolToPtr(g.Find("oom-kill-disable")), OomScoreAdj: AnyIntToInt64Ptr(g.Find("oom-score-adj")), + OverrideOS: StringToPtr(g.Find("override-os")), + OverrideArch: StringToPtr(g.Find("override-arch")), Pid: StringToPtr(g.Find("pid")), PidsLimit: AnyIntToInt64Ptr(g.Find("pids-limit")), Pod: StringToPtr(g.Find("pod")), @@ -389,6 +391,8 @@ func VarlinkCreateToGeneric(opts iopodman.Create) GenericCLIResults { m["no-hosts"] = boolFromVarlink(opts.NoHosts, "no-hosts", false) m["oom-kill-disable"] = boolFromVarlink(opts.OomKillDisable, "oon-kill-disable", false) m["oom-score-adj"] = intFromVarlink(opts.OomScoreAdj, "oom-score-adj", nil) + m["override-os"] = stringFromVarlink(opts.OverrideOS, "override-os", nil) + m["override-arch"] = stringFromVarlink(opts.OverrideArch, "override-arch", nil) m["pid"] = stringFromVarlink(opts.Pid, "pid", nil) m["pids-limit"] = int64FromVarlink(opts.PidsLimit, "pids-limit", nil) m["pod"] = stringFromVarlink(opts.Pod, "pod", nil) diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index dca366bc5..9ec7d1172 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -342,6 +342,8 @@ type Create ( noHosts: ?bool, oomKillDisable: ?bool, oomScoreAdj: ?int, + overrideArch: ?string, + overrideOS: ?string, pid: ?string, pidsLimit: ?int, pod: ?string, diff --git a/libpod/image/docker_registry_options.go b/libpod/image/docker_registry_options.go index 75417fe8b..62a4af465 100644 --- a/libpod/image/docker_registry_options.go +++ b/libpod/image/docker_registry_options.go @@ -26,6 +26,10 @@ type DockerRegistryOptions struct { // certificates and allows connecting to registries without encryption // - or forces it on even if registries.conf has the registry configured as insecure. DockerInsecureSkipTLSVerify types.OptionalBool + // If not "", overrides the use of platform.GOOS when choosing an image or verifying OS match. + OSChoice string + // If not "", overrides the use of platform.GOARCH when choosing an image or verifying architecture match. + ArchitectureChoice string } // GetSystemContext constructs a new system context from a parent context. the values in the DockerRegistryOptions, and other parameters. @@ -35,12 +39,16 @@ func (o DockerRegistryOptions) GetSystemContext(parent *types.SystemContext, add DockerCertPath: o.DockerCertPath, DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify, DockerArchiveAdditionalTags: additionalDockerArchiveTags, + OSChoice: o.OSChoice, + ArchitectureChoice: o.ArchitectureChoice, } if parent != nil { sc.SignaturePolicyPath = parent.SignaturePolicyPath sc.AuthFilePath = parent.AuthFilePath sc.DirForceCompress = parent.DirForceCompress sc.DockerRegistryUserAgent = parent.DockerRegistryUserAgent + sc.OSChoice = parent.OSChoice + sc.ArchitectureChoice = parent.ArchitectureChoice } return sc } diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 7584ed9d8..7f5dc33b9 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -223,6 +223,10 @@ func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName s var goal *pullGoal sc := GetSystemContext(signaturePolicyPath, authfile, false) + if dockerOptions != nil { + sc.OSChoice = dockerOptions.OSChoice + sc.ArchitectureChoice = dockerOptions.ArchitectureChoice + } sc.BlobInfoCacheDir = filepath.Join(ir.store.GraphRoot(), "cache") srcRef, err := alltransports.ParseImageName(inputName) if err != nil { @@ -246,6 +250,10 @@ func (ir *Runtime) pullImageFromReference(ctx context.Context, srcRef types.Imag defer span.Finish() sc := GetSystemContext(signaturePolicyPath, authfile, false) + if dockerOptions != nil { + sc.OSChoice = dockerOptions.OSChoice + sc.ArchitectureChoice = dockerOptions.ArchitectureChoice + } goal, err := ir.pullGoalFromImageReference(ctx, srcRef, transports.ImageName(srcRef), sc) if err != nil { return nil, errors.Wrapf(err, "error determining pull goal for image %q", transports.ImageName(srcRef)) -- cgit v1.2.3-54-g00ecf From 07195ff09fdcb0d2d3a044c92665b082d6e742b1 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Wed, 16 Oct 2019 12:00:12 -0400 Subject: API: report multiple digests for images Be prepared to report multiple image digests for images which contain multiple manifests but, because they continue to have the same set of layers and the same configuration, are considered to be the same image. Signed-off-by: Nalin Dahyabhai --- API.md | 2 ++ cmd/podman/images.go | 16 ++++++++++------ cmd/podman/varlink/io.podman.varlink | 3 ++- libpod/image/image.go | 5 +++++ pkg/adapter/runtime_remote.go | 11 +++++++++++ 5 files changed, 30 insertions(+), 7 deletions(-) (limited to 'libpod/image') diff --git a/API.md b/API.md index e79f6ee5e..d96ea6cd0 100755 --- a/API.md +++ b/API.md @@ -1675,6 +1675,8 @@ id [string](https://godoc.org/builtin#string) digest [string](https://godoc.org/builtin#string) +digests [[]string](#[]string) + parentId [string](https://godoc.org/builtin#string) repoTags [[]string](#[]string) diff --git a/cmd/podman/images.go b/cmd/podman/images.go index e363fa3bb..6bb08e195 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -27,6 +27,7 @@ type imagesTemplateParams struct { Tag string ID string Digest digest.Digest + Digests []digest.Digest Created string CreatedTime time.Time Size string @@ -34,12 +35,13 @@ type imagesTemplateParams struct { } type imagesJSONParams struct { - ID string `json:"id"` - Name []string `json:"names"` - Digest digest.Digest `json:"digest"` - Created time.Time `json:"created"` - Size *uint64 `json:"size"` - ReadOnly bool `json:"readonly"` + ID string `json:"id"` + Name []string `json:"names"` + Digest digest.Digest `json:"digest"` + Digests []digest.Digest `json:"digests"` + Created time.Time `json:"created"` + Size *uint64 `json:"size"` + ReadOnly bool `json:"readonly"` } type imagesOptions struct { @@ -290,6 +292,7 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma Tag: tag, ID: imageID, Digest: img.Digest(), + Digests: img.Digests(), CreatedTime: createdTime, Created: units.HumanDuration(time.Since(createdTime)) + " ago", Size: sizeStr, @@ -321,6 +324,7 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) ID: img.ID(), Name: img.Names(), Digest: img.Digest(), + Digests: img.Digests(), Created: img.Created(), Size: size, ReadOnly: img.IsReadOnly(), diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 9ec7d1172..f9339fccb 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -58,7 +58,8 @@ type VolumeRemoveOpts ( type Image ( id: string, - digest: string, + digest: string, + digests: []string, parentId: string, repoTags: []string, repoDigests: []string, diff --git a/libpod/image/image.go b/libpod/image/image.go index 2ab88f2e9..faa3b648a 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -299,6 +299,11 @@ func (i *Image) Digest() digest.Digest { return i.image.Digest } +// Digests returns the image's digests +func (i *Image) Digests() []digest.Digest { + return i.image.Digests +} + // GetManifest returns the image's manifest as a byte array // and manifest type as a string. func (i *Image) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) { diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index fef3986f1..12bf550f2 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -146,6 +146,7 @@ type remoteImage struct { InputName string Names []string Digest digest.Digest + Digests []digest.Digest isParent bool Runtime *LocalRuntime TopLayer string @@ -226,10 +227,15 @@ func imageInListToContainerImage(i iopodman.Image, name string, runtime *LocalRu if err != nil { return nil, err } + var digests []digest.Digest + for _, d := range i.Digests { + digests = append(digests, digest.Digest(d)) + } ri := remoteImage{ InputName: name, ID: i.Id, Digest: digest.Digest(i.Digest), + Digests: digests, Labels: i.Labels, RepoTags: i.RepoTags, RepoDigests: i.RepoTags, @@ -352,6 +358,11 @@ func (ci *ContainerImage) Digest() digest.Digest { return ci.remoteImage.Digest } +// Digests returns the image's digests +func (ci *ContainerImage) Digests() []digest.Digest { + return append([]digest.Digest{}, ci.remoteImage.Digests...) +} + // Labels returns a map of the image's labels func (ci *ContainerImage) Labels(ctx context.Context) (map[string]string, error) { return ci.remoteImage.Labels, nil -- cgit v1.2.3-54-g00ecf From 248bb61b14a3f0d4e1d244eff85b30f48554a6a8 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Wed, 16 Oct 2019 12:01:30 -0400 Subject: images: distinguish between tags and digests Generate an image's RepoDigests list using all applicable digests, and refrain from outputting a digest in the tag column of the "images" output. Signed-off-by: Nalin Dahyabhai --- cmd/podman/images.go | 14 +++++++----- libpod/image/image.go | 55 ++++++++++++++++++++++++++++++++++++---------- libpod/image/image_test.go | 13 +++++++++++ libpod/image/utils.go | 14 ++++++------ 4 files changed, 72 insertions(+), 24 deletions(-) (limited to 'libpod/image') diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 6bb08e195..6157fda2a 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -206,9 +206,9 @@ func (i imagesOptions) setOutputFormat() string { if i.quiet { return formats.IDString } - format := "table {{.Repository}}\t{{.Tag}}\t" + format := "table {{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}{{end}}\t" if i.noHeading { - format = "{{.Repository}}\t{{.Tag}}\t" + format = "{{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}{{end}}\t" } if i.digests { format += "{{.Digest}}\t" @@ -270,7 +270,7 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma imageID = shortID(img.ID()) } - // get all specified repo:tag pairs and print them separately + // get all specified repo:tag and repo@digest pairs and print them separately repopairs, err := image.ReposToMap(img.Names()) if err != nil { logrus.Errorf("error finding tag/digest for %s", img.ID()) @@ -287,11 +287,16 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma lastNumIdx := strings.LastIndexFunc(sizeStr, unicode.IsNumber) sizeStr = sizeStr[:lastNumIdx+1] + " " + sizeStr[lastNumIdx+1:] } + var imageDigest digest.Digest + if len(tag) == 71 && strings.HasPrefix(tag, "sha256:") { + imageDigest = digest.Digest(tag) + tag = "" + } params := imagesTemplateParams{ Repository: repo, Tag: tag, ID: imageID, - Digest: img.Digest(), + Digest: imageDigest, Digests: img.Digests(), CreatedTime: createdTime, Created: units.HumanDuration(time.Since(createdTime)) + " ago", @@ -302,7 +307,6 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma if opts.quiet { // Show only one image ID when quiet break outer } - } } } diff --git a/libpod/image/image.go b/libpod/image/image.go index faa3b648a..c912ac2ca 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "os" "path/filepath" + "sort" "strings" "syscall" "time" @@ -324,29 +325,54 @@ func (i *Image) Manifest(ctx context.Context) ([]byte, string, error) { return imgRef.Manifest(ctx) } -// Names returns a string array of names associated with the image +// Names returns a string array of names associated with the image, which may be a mixture of tags and digests func (i *Image) Names() []string { return i.image.Names } -// RepoDigests returns a string array of repodigests associated with the image -func (i *Image) RepoDigests() ([]string, error) { - var repoDigests []string - imageDigest := i.Digest() - +// RepoTags returns a string array of repotags associated with the image +func (i *Image) RepoTags() ([]string, error) { + var repoTags []string for _, name := range i.Names() { named, err := reference.ParseNormalizedNamed(name) if err != nil { return nil, err } - - canonical, err := reference.WithDigest(reference.TrimNamed(named), imageDigest) - if err != nil { - return nil, err + if tagged, isTagged := named.(reference.NamedTagged); isTagged { + repoTags = append(repoTags, tagged.String()) } + } + return repoTags, nil +} + +// RepoDigests returns a string array of repodigests associated with the image +func (i *Image) RepoDigests() ([]string, error) { + var repoDigests []string + added := make(map[string]struct{}) - repoDigests = append(repoDigests, canonical.String()) + for _, name := range i.Names() { + for _, imageDigest := range append(i.Digests(), i.Digest()) { + if imageDigest == "" { + continue + } + + named, err := reference.ParseNormalizedNamed(name) + if err != nil { + return nil, err + } + + canonical, err := reference.WithDigest(reference.TrimNamed(named), imageDigest) + if err != nil { + return nil, err + } + + if _, alreadyInList := added[canonical.String()]; !alreadyInList { + repoDigests = append(repoDigests, canonical.String()) + added[canonical.String()] = struct{}{} + } + } } + sort.Strings(repoDigests) return repoDigests, nil } @@ -944,6 +970,11 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { size = int64(*usize) } + repoTags, err := i.RepoTags() + if err != nil { + return nil, err + } + repoDigests, err := i.RepoDigests() if err != nil { return nil, err @@ -965,7 +996,7 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { data := &inspect.ImageData{ ID: i.ID(), - RepoTags: i.Names(), + RepoTags: repoTags, RepoDigests: repoDigests, Comment: comment, Created: ociv1Img.Created, diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go index ef39d09c3..5aff7d860 100644 --- a/libpod/image/image_test.go +++ b/libpod/image/image_test.go @@ -247,6 +247,19 @@ func TestImage_RepoDigests(t *testing.T) { } assert.Equal(t, test.expected, actual) + + image = &Image{ + image: &storage.Image{ + Names: test.names, + Digests: []digest.Digest{dgst}, + }, + } + actual, err = image.RepoDigests() + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, test.expected, actual) }) } } diff --git a/libpod/image/utils.go b/libpod/image/utils.go index 5c971cb2b..b7ea63c66 100644 --- a/libpod/image/utils.go +++ b/libpod/image/utils.go @@ -87,18 +87,18 @@ func hasTransport(image string) bool { } // ReposToMap parses the specified repotags and returns a map with repositories -// as keys and the corresponding arrays of tags as values. -func ReposToMap(repotags []string) (map[string][]string, error) { - // map format is repo -> tag +// as keys and the corresponding arrays of tags or digests-as-strings as values. +func ReposToMap(names []string) (map[string][]string, error) { + // map format is repo -> []tag-or-digest repos := make(map[string][]string) - for _, repo := range repotags { + for _, name := range names { var repository, tag string - if len(repo) > 0 { - named, err := reference.ParseNormalizedNamed(repo) - repository = named.Name() + if len(name) > 0 { + named, err := reference.ParseNormalizedNamed(name) if err != nil { return nil, err } + repository = named.Name() if ref, ok := named.(reference.NamedTagged); ok { tag = ref.Tag() } else if ref, ok := named.(reference.Canonical); ok { -- cgit v1.2.3-54-g00ecf