From 3d23f2229215b1178c4bfd560a700315718bed12 Mon Sep 17 00:00:00 2001 From: umohnani8 Date: Thu, 31 Aug 2017 15:57:03 -0400 Subject: [PATCH] Vendor in changes made to containers/image for OCI Signed-off-by: umohnani8 --- test/kpod.bats | 2 +- vendor.conf | 2 +- .../containers/image/docker/docker_client.go | 4 + .../containers/image/oci/archive/oci_dest.go | 135 +++++++++++ .../containers/image/oci/archive/oci_src.go | 88 +++++++ .../image/oci/archive/oci_transport.go | 226 ++++++++++++++++++ .../containers/image/oci/layout/oci_dest.go | 13 +- .../image/oci/layout/oci_transport.go | 113 +++++---- .../transports/alltransports/alltransports.go | 1 + .../github.com/containers/image/vendor.conf | 11 +- 10 files changed, 541 insertions(+), 54 deletions(-) create mode 100644 vendor/github.com/containers/image/oci/archive/oci_dest.go create mode 100644 vendor/github.com/containers/image/oci/archive/oci_src.go create mode 100644 vendor/github.com/containers/image/oci/archive/oci_transport.go diff --git a/test/kpod.bats b/test/kpod.bats index 6e5d4342..8ac5feda 100644 --- a/test/kpod.bats +++ b/test/kpod.bats @@ -169,7 +169,7 @@ function teardown() { run mkdir /tmp/oci-busybox echo "$output" [ "$status" -eq 0 ] - run ${KPOD_BINARY} $KPOD_OPTIONS push "$IMAGE" oci:/tmp/oci-busybox + run ${KPOD_BINARY} $KPOD_OPTIONS push "$IMAGE" oci:/tmp/oci-busybox:"$IMAGE" echo "$output" [ "$status" -eq 0 ] run rm -rf /tmp/oci-busybox diff --git a/vendor.conf b/vendor.conf index 5db5e7c8..f1663206 100644 --- a/vendor.conf +++ b/vendor.conf @@ -5,7 +5,7 @@ k8s.io/apimachinery release-1.7 https://github.com/kubernetes/apimachinery k8s.io/apiserver release-1.7 https://github.com/kubernetes/apiserver # github.com/sirupsen/logrus v1.0.0 -github.com/containers/image 74e359348c7ce9e0caf4fa75aa8de3809cf41c46 +github.com/containers/image 960e7256f8203360172ef97d5ace673dc085e30c github.com/ostreedev/ostree-go master github.com/containers/storage f8cff0727cf0802f0752ca58d2c05ec5270a47d5 github.com/containernetworking/cni v0.4.0 diff --git a/vendor/github.com/containers/image/docker/docker_client.go b/vendor/github.com/containers/image/docker/docker_client.go index 99102a6c..03737537 100644 --- a/vendor/github.com/containers/image/docker/docker_client.go +++ b/vendor/github.com/containers/image/docker/docker_client.go @@ -155,6 +155,10 @@ func setupCertificates(dir string, tlsc *tls.Config) error { if os.IsNotExist(err) { return nil } + if os.IsPermission(err) { + logrus.Debugf("Skipping scan of %s due to permission error: %v", dir, err) + return nil + } return err } diff --git a/vendor/github.com/containers/image/oci/archive/oci_dest.go b/vendor/github.com/containers/image/oci/archive/oci_dest.go new file mode 100644 index 00000000..b2c4bb63 --- /dev/null +++ b/vendor/github.com/containers/image/oci/archive/oci_dest.go @@ -0,0 +1,135 @@ +package archive + +import ( + "io" + "os" + + "github.com/containers/image/types" + "github.com/containers/storage/pkg/archive" + "github.com/pkg/errors" +) + +type ociArchiveImageDestination struct { + ref ociArchiveReference + unpackedDest types.ImageDestination + tempDirRef tempDirOCIRef +} + +// newImageDestination returns an ImageDestination for writing to an existing directory. +func newImageDestination(ctx *types.SystemContext, ref ociArchiveReference) (types.ImageDestination, error) { + tempDirRef, err := createOCIRef(ref.image) + if err != nil { + return nil, errors.Wrapf(err, "error creating oci reference") + } + unpackedDest, err := tempDirRef.ociRefExtracted.NewImageDestination(ctx) + if err != nil { + if err := tempDirRef.deleteTempDir(); err != nil { + return nil, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory) + } + return nil, err + } + return &ociArchiveImageDestination{ref: ref, + unpackedDest: unpackedDest, + tempDirRef: tempDirRef}, nil +} + +// Reference returns the reference used to set up this destination. +func (d *ociArchiveImageDestination) Reference() types.ImageReference { + return d.ref +} + +// Close removes resources associated with an initialized ImageDestination, if any +// Close deletes the temp directory of the oci-archive image +func (d *ociArchiveImageDestination) Close() error { + defer d.tempDirRef.deleteTempDir() + return d.unpackedDest.Close() +} + +func (d *ociArchiveImageDestination) SupportedManifestMIMETypes() []string { + return d.unpackedDest.SupportedManifestMIMETypes() +} + +// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures +func (d *ociArchiveImageDestination) SupportsSignatures() error { + return d.unpackedDest.SupportsSignatures() +} + +// ShouldCompressLayers returns true iff it is desirable to compress layer blobs written to this destination +func (d *ociArchiveImageDestination) ShouldCompressLayers() bool { + return d.unpackedDest.ShouldCompressLayers() +} + +// AcceptsForeignLayerURLs returns false iff foreign layers in manifest should be actually +// uploaded to the image destination, true otherwise. +func (d *ociArchiveImageDestination) AcceptsForeignLayerURLs() bool { + return d.unpackedDest.AcceptsForeignLayerURLs() +} + +// MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise +func (d *ociArchiveImageDestination) MustMatchRuntimeOS() bool { + return d.unpackedDest.MustMatchRuntimeOS() +} + +// PutBlob writes contents of stream and returns data representing the result (with all data filled in). +// inputInfo.Digest can be optionally provided if known; it is not mandatory for the implementation to verify it. +// inputInfo.Size is the expected length of stream, if known. +func (d *ociArchiveImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo) (types.BlobInfo, error) { + return d.unpackedDest.PutBlob(stream, inputInfo) +} + +// HasBlob returns true iff the image destination already contains a blob with the matching digest which can be reapplied using ReapplyBlob +func (d *ociArchiveImageDestination) HasBlob(info types.BlobInfo) (bool, int64, error) { + return d.unpackedDest.HasBlob(info) +} + +func (d *ociArchiveImageDestination) ReapplyBlob(info types.BlobInfo) (types.BlobInfo, error) { + return d.unpackedDest.ReapplyBlob(info) +} + +// PutManifest writes manifest to the destination +func (d *ociArchiveImageDestination) PutManifest(m []byte) error { + return d.unpackedDest.PutManifest(m) +} + +func (d *ociArchiveImageDestination) PutSignatures(signatures [][]byte) error { + return d.unpackedDest.PutSignatures(signatures) +} + +// Commit marks the process of storing the image as successful and asks for the image to be persisted +// after the directory is made, it is tarred up into a file and the directory is deleted +func (d *ociArchiveImageDestination) Commit() error { + if err := d.unpackedDest.Commit(); err != nil { + return errors.Wrapf(err, "error storing image %q", d.ref.image) + } + + // path of directory to tar up + src := d.tempDirRef.tempDirectory + // path to save tarred up file + dst := d.ref.resolvedFile + if err := tarDirectory(src, dst); err != nil { + return err + } + + return nil +} + +// tar converts the directory at src and saves it to dst +func tarDirectory(src, dst string) error { + // input is a stream of bytes from the archive of the directory at path + input, err := archive.Tar(src, archive.Uncompressed) + if err != nil { + return errors.Wrapf(err, "error retrieving stream of bytes from %q", src) + } + + // creates the tar file + outFile, err := os.Create(dst) + if err != nil { + return errors.Wrapf(err, "error creating tar file %q", dst) + } + defer outFile.Close() + + // copies the contents of the directory to the tar file + _, err = io.Copy(outFile, input) + + return err +} diff --git a/vendor/github.com/containers/image/oci/archive/oci_src.go b/vendor/github.com/containers/image/oci/archive/oci_src.go new file mode 100644 index 00000000..9aa16a3d --- /dev/null +++ b/vendor/github.com/containers/image/oci/archive/oci_src.go @@ -0,0 +1,88 @@ +package archive + +import ( + "context" + "io" + + ocilayout "github.com/containers/image/oci/layout" + "github.com/containers/image/types" + digest "github.com/opencontainers/go-digest" + imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +type ociArchiveImageSource struct { + ref ociArchiveReference + unpackedSrc types.ImageSource + tempDirRef tempDirOCIRef +} + +// newImageSource returns an ImageSource for reading from an existing directory. +// newImageSource untars the file and saves it in a temp directory +func newImageSource(ctx *types.SystemContext, ref ociArchiveReference, requestedManifestMIMETypes []string) (types.ImageSource, error) { + tempDirRef, err := createUntarTempDir(ref) + if err != nil { + return nil, errors.Wrap(err, "error creating temp directory") + } + + unpackedSrc, err := tempDirRef.ociRefExtracted.NewImageSource(ctx, requestedManifestMIMETypes) + if err != nil { + if err := tempDirRef.deleteTempDir(); err != nil { + return nil, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory) + } + return nil, err + } + return &ociArchiveImageSource{ref: ref, + unpackedSrc: unpackedSrc, + tempDirRef: tempDirRef}, nil +} + +// LoadManifestDescriptor loads the manifest +func LoadManifestDescriptor(imgRef types.ImageReference) (imgspecv1.Descriptor, error) { + ociArchRef, ok := imgRef.(ociArchiveReference) + if !ok { + return imgspecv1.Descriptor{}, errors.Errorf("error typecasting, need type ociArchiveReference") + } + tempDirRef, err := createUntarTempDir(ociArchRef) + if err != nil { + return imgspecv1.Descriptor{}, errors.Wrap(err, "error creating temp directory") + } + defer tempDirRef.deleteTempDir() + + descriptor, err := ocilayout.LoadManifestDescriptor(tempDirRef.ociRefExtracted) + if err != nil { + return imgspecv1.Descriptor{}, errors.Wrap(err, "error loading index") + } + return descriptor, nil +} + +// Reference returns the reference used to set up this source. +func (s *ociArchiveImageSource) Reference() types.ImageReference { + return s.ref +} + +// Close removes resources associated with an initialized ImageSource, if any. +// Close deletes the temporary directory at dst +func (s *ociArchiveImageSource) Close() error { + defer s.tempDirRef.deleteTempDir() + return s.unpackedSrc.Close() +} + +// GetManifest returns the image's manifest along with its MIME type +// (which may be empty when it can't be determined but the manifest is available). +func (s *ociArchiveImageSource) GetManifest() ([]byte, string, error) { + return s.unpackedSrc.GetManifest() +} + +func (s *ociArchiveImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) { + return s.unpackedSrc.GetTargetManifest(digest) +} + +// GetBlob returns a stream for the specified blob, and the blob's size. +func (s *ociArchiveImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) { + return s.unpackedSrc.GetBlob(info) +} + +func (s *ociArchiveImageSource) GetSignatures(c context.Context) ([][]byte, error) { + return s.unpackedSrc.GetSignatures(c) +} diff --git a/vendor/github.com/containers/image/oci/archive/oci_transport.go b/vendor/github.com/containers/image/oci/archive/oci_transport.go new file mode 100644 index 00000000..da68b031 --- /dev/null +++ b/vendor/github.com/containers/image/oci/archive/oci_transport.go @@ -0,0 +1,226 @@ +package archive + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/containers/image/directory/explicitfilepath" + "github.com/containers/image/docker/reference" + "github.com/containers/image/image" + ocilayout "github.com/containers/image/oci/layout" + "github.com/containers/image/transports" + "github.com/containers/image/types" + "github.com/containers/storage/pkg/archive" + "github.com/pkg/errors" +) + +func init() { + transports.Register(Transport) +} + +// Transport is an ImageTransport for OCI archive +// it creates an oci-archive tar file by calling into the OCI transport +// tarring the directory created by oci and deleting the directory +var Transport = ociArchiveTransport{} + +type ociArchiveTransport struct{} + +// ociArchiveReference is an ImageReference for OCI Archive paths +type ociArchiveReference struct { + file string + resolvedFile string + image string +} + +func (t ociArchiveTransport) Name() string { + return "oci-archive" +} + +// ParseReference converts a string, which should not start with the ImageTransport.Name prefix +// into an ImageReference. +func (t ociArchiveTransport) ParseReference(reference string) (types.ImageReference, error) { + return ParseReference(reference) +} + +// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys +func (t ociArchiveTransport) ValidatePolicyConfigurationScope(scope string) error { + var file string + sep := strings.SplitN(scope, ":", 2) + file = sep[0] + + if len(sep) == 2 { + image := sep[1] + if !refRegexp.MatchString(image) { + return errors.Errorf("Invalid image %s", image) + } + } + + if !strings.HasPrefix(file, "/") { + return errors.Errorf("Invalid scope %s: must be an absolute path", scope) + } + // Refuse also "/", otherwise "/" and "" would have the same semantics, + // and "" could be unexpectedly shadowed by the "/" entry. + // (Note: we do allow "/:someimage", a bit ridiculous but why refuse it?) + if scope == "/" { + return errors.New(`Invalid scope "/": Use the generic default scope ""`) + } + cleaned := filepath.Clean(file) + if cleaned != file { + return errors.Errorf(`Invalid scope %s: Uses non-canonical path format, perhaps try with path %s`, scope, cleaned) + } + return nil +} + +// annotation spex from https://github.com/opencontainers/image-spec/blob/master/annotations.md#pre-defined-annotation-keys +const ( + separator = `(?:[-._:@+]|--)` + alphanum = `(?:[A-Za-z0-9]+)` + component = `(?:` + alphanum + `(?:` + separator + alphanum + `)*)` +) + +var refRegexp = regexp.MustCompile(`^` + component + `(?:/` + component + `)*$`) + +// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OCI ImageReference. +func ParseReference(reference string) (types.ImageReference, error) { + var file, image string + sep := strings.SplitN(reference, ":", 2) + file = sep[0] + + if len(sep) == 2 { + image = sep[1] + } + return NewReference(file, image) +} + +// NewReference returns an OCI reference for a file and a image. +func NewReference(file, image string) (types.ImageReference, error) { + resolved, err := explicitfilepath.ResolvePathToFullyExplicit(file) + if err != nil { + return nil, err + } + // This is necessary to prevent directory paths returned by PolicyConfigurationNamespaces + // from being ambiguous with values of PolicyConfigurationIdentity. + if strings.Contains(resolved, ":") { + return nil, errors.Errorf("Invalid OCI reference %s:%s: path %s contains a colon", file, image, resolved) + } + if len(image) > 0 && !refRegexp.MatchString(image) { + return nil, errors.Errorf("Invalid image %s", image) + } + return ociArchiveReference{file: file, resolvedFile: resolved, image: image}, nil +} + +func (ref ociArchiveReference) Transport() types.ImageTransport { + return Transport +} + +// StringWithinTransport returns a string representation of the reference, which MUST be such that +// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference. +func (ref ociArchiveReference) StringWithinTransport() string { + return fmt.Sprintf("%s:%s", ref.file, ref.image) +} + +// DockerReference returns a Docker reference associated with this reference +func (ref ociArchiveReference) DockerReference() reference.Named { + return nil +} + +// PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup. +func (ref ociArchiveReference) PolicyConfigurationIdentity() string { + // NOTE: ref.image is not a part of the image identity, because "$dir:$someimage" and "$dir:" may mean the + // same image and the two can’t be statically disambiguated. Using at least the repository directory is + // less granular but hopefully still useful. + return fmt.Sprintf("%s", ref.resolvedFile) +} + +// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search +// for if explicit configuration for PolicyConfigurationIdentity() is not set +func (ref ociArchiveReference) PolicyConfigurationNamespaces() []string { + res := []string{} + path := ref.resolvedFile + for { + lastSlash := strings.LastIndex(path, "/") + // Note that we do not include "/"; it is redundant with the default "" global default, + // and rejected by ociTransport.ValidatePolicyConfigurationScope above. + if lastSlash == -1 || path == "/" { + break + } + res = append(res, path) + path = path[:lastSlash] + } + return res +} + +// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport. +// The caller must call .Close() on the returned Image. +func (ref ociArchiveReference) NewImage(ctx *types.SystemContext) (types.Image, error) { + src, err := newImageSource(ctx, ref, nil) + if err != nil { + return nil, err + } + return image.FromSource(src) +} + +// NewImageSource returns a types.ImageSource for this reference, +// asking the backend to use a manifest from requestedManifestMIMETypes if possible. +// The caller must call .Close() on the returned ImageSource. +func (ref ociArchiveReference) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) { + return newImageSource(ctx, ref, requestedManifestMIMETypes) +} + +// NewImageDestination returns a types.ImageDestination for this reference. +// The caller must call .Close() on the returned ImageDestination. +func (ref ociArchiveReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) { + return newImageDestination(ctx, ref) +} + +// DeleteImage deletes the named image from the registry, if supported. +func (ref ociArchiveReference) DeleteImage(ctx *types.SystemContext) error { + return errors.Errorf("Deleting images not implemented for oci: images") +} + +// struct to store the ociReference and temporary directory returned by createOCIRef +type tempDirOCIRef struct { + tempDirectory string + ociRefExtracted types.ImageReference +} + +// deletes the temporary directory created +func (t *tempDirOCIRef) deleteTempDir() error { + return os.RemoveAll(t.tempDirectory) +} + +// createOCIRef creates the oci reference of the image +func createOCIRef(image string) (tempDirOCIRef, error) { + dir, err := ioutil.TempDir("/var/tmp", "oci") + if err != nil { + return tempDirOCIRef{}, errors.Wrapf(err, "error creating temp directory") + } + ociRef, err := ocilayout.NewReference(dir, image) + if err != nil { + return tempDirOCIRef{}, err + } + + tempDirRef := tempDirOCIRef{tempDirectory: dir, ociRefExtracted: ociRef} + return tempDirRef, nil +} + +// creates the temporary directory and copies the tarred content to it +func createUntarTempDir(ref ociArchiveReference) (tempDirOCIRef, error) { + tempDirRef, err := createOCIRef(ref.image) + if err != nil { + return tempDirOCIRef{}, errors.Wrap(err, "error creating oci reference") + } + src := ref.resolvedFile + dst := tempDirRef.tempDirectory + if err := archive.UntarPath(src, dst); err != nil { + if err := tempDirRef.deleteTempDir(); err != nil { + return tempDirOCIRef{}, errors.Wrapf(err, "error deleting temp directory %q", tempDirRef.tempDirectory) + } + return tempDirOCIRef{}, errors.Wrapf(err, "error untarring file %q", tempDirRef.tempDirectory) + } + return tempDirRef, nil +} diff --git a/vendor/github.com/containers/image/oci/layout/oci_dest.go b/vendor/github.com/containers/image/oci/layout/oci_dest.go index 05367309..c4801e34 100644 --- a/vendor/github.com/containers/image/oci/layout/oci_dest.go +++ b/vendor/github.com/containers/image/oci/layout/oci_dest.go @@ -23,13 +23,16 @@ type ociImageDestination struct { } // newImageDestination returns an ImageDestination for writing to an existing directory. -func newImageDestination(ref ociReference) types.ImageDestination { +func newImageDestination(ref ociReference) (types.ImageDestination, error) { + if ref.image == "" { + return nil, errors.Errorf("cannot save image with empty image.ref.name") + } index := imgspecv1.Index{ Versioned: imgspec.Versioned{ SchemaVersion: 2, }, } - return &ociImageDestination{ref: ref, index: index} + return &ociImageDestination{ref: ref, index: index}, nil } // Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent, @@ -177,8 +180,12 @@ func (d *ociImageDestination) PutManifest(m []byte) error { return err } + if d.ref.image == "" { + return errors.Errorf("cannot save image with empyt image.ref.name") + } + annotations := make(map[string]string) - annotations["org.opencontainers.image.ref.name"] = d.ref.tag + annotations["org.opencontainers.image.ref.name"] = d.ref.image desc.Annotations = annotations desc.Platform = &imgspecv1.Platform{ Architecture: runtime.GOARCH, diff --git a/vendor/github.com/containers/image/oci/layout/oci_transport.go b/vendor/github.com/containers/image/oci/layout/oci_transport.go index f1fc1071..1406dd18 100644 --- a/vendor/github.com/containers/image/oci/layout/oci_transport.go +++ b/vendor/github.com/containers/image/oci/layout/oci_transport.go @@ -36,7 +36,14 @@ func (t ociTransport) ParseReference(reference string) (types.ImageReference, er return ParseReference(reference) } -var refRegexp = regexp.MustCompile(`^([A-Za-z0-9._-]+)+$`) +// annotation spex from https://github.com/opencontainers/image-spec/blob/master/annotations.md#pre-defined-annotation-keys +const ( + separator = `(?:[-._:@+]|--)` + alphanum = `(?:[A-Za-z0-9]+)` + component = `(?:` + alphanum + `(?:` + separator + alphanum + `)*)` +) + +var refRegexp = regexp.MustCompile(`^` + component + `(?:/` + component + `)*$`) // ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys // (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value). @@ -44,19 +51,14 @@ var refRegexp = regexp.MustCompile(`^([A-Za-z0-9._-]+)+$`) // scope passed to this function will not be "", that value is always allowed. func (t ociTransport) ValidatePolicyConfigurationScope(scope string) error { var dir string - sep := strings.LastIndex(scope, ":") - if sep == -1 { - dir = scope - } else { - dir = scope[:sep] - tag := scope[sep+1:] - if !refRegexp.MatchString(tag) { - return errors.Errorf("Invalid tag %s", tag) - } - } + sep := strings.SplitN(scope, ":", 2) + dir = sep[0] - if strings.Contains(dir, ":") { - return errors.Errorf("Invalid OCI reference %s: path contains a colon", scope) + if len(sep) == 2 { + image := sep[1] + if !refRegexp.MatchString(image) { + return errors.Errorf("Invalid image %s", image) + } } if !strings.HasPrefix(dir, "/") { @@ -64,7 +66,7 @@ func (t ociTransport) ValidatePolicyConfigurationScope(scope string) error { } // Refuse also "/", otherwise "/" and "" would have the same semantics, // and "" could be unexpectedly shadowed by the "/" entry. - // (Note: we do allow "/:sometag", a bit ridiculous but why refuse it?) + // (Note: we do allow "/:someimage", a bit ridiculous but why refuse it?) if scope == "/" { return errors.New(`Invalid scope "/": Use the generic default scope ""`) } @@ -85,28 +87,26 @@ type ociReference struct { // (But in general, we make no attempt to be completely safe against concurrent hostile filesystem modifications.) dir string // As specified by the user. May be relative, contain symlinks, etc. resolvedDir string // Absolute path with no symlinks, at least at the time of its creation. Primarily used for policy namespaces. - tag string + image string // If image=="", it means the only image in the index.json is used } // ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OCI ImageReference. func ParseReference(reference string) (types.ImageReference, error) { - var dir, tag string - sep := strings.LastIndex(reference, ":") - if sep == -1 { - dir = reference - tag = "latest" - } else { - dir = reference[:sep] - tag = reference[sep+1:] + var dir, image string + sep := strings.SplitN(reference, ":", 2) + dir = sep[0] + + if len(sep) == 2 { + image = sep[1] } - return NewReference(dir, tag) + return NewReference(dir, image) } -// NewReference returns an OCI reference for a directory and a tag. +// NewReference returns an OCI reference for a directory and a image. // // We do not expose an API supplying the resolvedDir; we could, but recomputing it // is generally cheap enough that we prefer being confident about the properties of resolvedDir. -func NewReference(dir, tag string) (types.ImageReference, error) { +func NewReference(dir, image string) (types.ImageReference, error) { resolved, err := explicitfilepath.ResolvePathToFullyExplicit(dir) if err != nil { return nil, err @@ -114,12 +114,12 @@ func NewReference(dir, tag string) (types.ImageReference, error) { // This is necessary to prevent directory paths returned by PolicyConfigurationNamespaces // from being ambiguous with values of PolicyConfigurationIdentity. if strings.Contains(resolved, ":") { - return nil, errors.Errorf("Invalid OCI reference %s:%s: path %s contains a colon", dir, tag, resolved) + return nil, errors.Errorf("Invalid OCI reference %s:%s: path %s contains a colon", dir, image, resolved) } - if !refRegexp.MatchString(tag) { - return nil, errors.Errorf("Invalid tag %s", tag) + if len(image) > 0 && !refRegexp.MatchString(image) { + return nil, errors.Errorf("Invalid image %s", image) } - return ociReference{dir: dir, resolvedDir: resolved, tag: tag}, nil + return ociReference{dir: dir, resolvedDir: resolved, image: image}, nil } func (ref ociReference) Transport() types.ImageTransport { @@ -132,7 +132,7 @@ func (ref ociReference) Transport() types.ImageTransport { // e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa. // WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix. func (ref ociReference) StringWithinTransport() string { - return fmt.Sprintf("%s:%s", ref.dir, ref.tag) + return fmt.Sprintf("%s:%s", ref.dir, ref.image) } // DockerReference returns a Docker reference associated with this reference @@ -150,7 +150,10 @@ func (ref ociReference) DockerReference() reference.Named { // not required/guaranteed that it will be a valid input to Transport().ParseReference(). // Returns "" if configuration identities for these references are not supported. func (ref ociReference) PolicyConfigurationIdentity() string { - return fmt.Sprintf("%s:%s", ref.resolvedDir, ref.tag) + // NOTE: ref.image is not a part of the image identity, because "$dir:$someimage" and "$dir:" may mean the + // same image and the two can’t be statically disambiguated. Using at least the repository directory is + // less granular but hopefully still useful. + return fmt.Sprintf("%s", ref.resolvedDir) } // PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search @@ -196,26 +199,48 @@ func (ref ociReference) getManifestDescriptor() (imgspecv1.Descriptor, error) { if err := json.NewDecoder(indexJSON).Decode(&index); err != nil { return imgspecv1.Descriptor{}, err } + var d *imgspecv1.Descriptor - for _, md := range index.Manifests { - if md.MediaType != imgspecv1.MediaTypeImageManifest { - continue + if ref.image == "" { + // return manifest if only one image is in the oci directory + if len(index.Manifests) == 1 { + d = &index.Manifests[0] + } else { + // ask user to choose image when more than one image in the oci directory + return imgspecv1.Descriptor{}, errors.Wrapf(err, "more than one image in oci, choose an image") } - refName, ok := md.Annotations["org.opencontainers.image.ref.name"] - if !ok { - continue - } - if refName == ref.tag { - d = &md - break + } else { + // if image specified, look through all manifests for a match + for _, md := range index.Manifests { + if md.MediaType != imgspecv1.MediaTypeImageManifest { + continue + } + refName, ok := md.Annotations["org.opencontainers.image.ref.name"] + if !ok { + continue + } + if refName == ref.image { + d = &md + break + } } } if d == nil { - return imgspecv1.Descriptor{}, fmt.Errorf("no descriptor found for reference %q", ref.tag) + return imgspecv1.Descriptor{}, fmt.Errorf("no descriptor found for reference %q", ref.image) } return *d, nil } +// LoadManifestDescriptor loads the manifest descriptor to be used to retrieve the image name +// when pulling an image +func LoadManifestDescriptor(imgRef types.ImageReference) (imgspecv1.Descriptor, error) { + ociRef, ok := imgRef.(ociReference) + if !ok { + return imgspecv1.Descriptor{}, errors.Errorf("error typecasting, need type ociRef") + } + return ociRef.getManifestDescriptor() +} + // NewImageSource returns a types.ImageSource for this reference, // asking the backend to use a manifest from requestedManifestMIMETypes if possible. // nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes. @@ -227,7 +252,7 @@ func (ref ociReference) NewImageSource(ctx *types.SystemContext, requestedManife // NewImageDestination returns a types.ImageDestination for this reference. // The caller must call .Close() on the returned ImageDestination. func (ref ociReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) { - return newImageDestination(ref), nil + return newImageDestination(ref) } // DeleteImage deletes the named image from the registry, if supported. diff --git a/vendor/github.com/containers/image/transports/alltransports/alltransports.go b/vendor/github.com/containers/image/transports/alltransports/alltransports.go index dd80b7f9..4279b9d2 100644 --- a/vendor/github.com/containers/image/transports/alltransports/alltransports.go +++ b/vendor/github.com/containers/image/transports/alltransports/alltransports.go @@ -10,6 +10,7 @@ import ( _ "github.com/containers/image/docker" _ "github.com/containers/image/docker/archive" _ "github.com/containers/image/docker/daemon" + _ "github.com/containers/image/oci/archive" _ "github.com/containers/image/oci/layout" _ "github.com/containers/image/openshift" // The ostree transport is registered by ostree*.go diff --git a/vendor/github.com/containers/image/vendor.conf b/vendor/github.com/containers/image/vendor.conf index d4725e12..669b5a76 100644 --- a/vendor/github.com/containers/image/vendor.conf +++ b/vendor/github.com/containers/image/vendor.conf @@ -1,9 +1,9 @@ github.com/sirupsen/logrus v1.0.0 -github.com/containers/storage 5d8c2f87387fa5be9fa526ae39fbd79b8bdf27be +github.com/containers/storage 47536c89fcc545a87745e1a1573addc439409165 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 -github.com/docker/distribution df5327f76fb6468b84a87771e361762b8be23fdb -github.com/docker/docker 75843d36aa5c3eaade50da005f9e0ff2602f3d5e -github.com/docker/go-connections 7da10c8c50cad14494ec818dcdfb6506265c0086 +github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716 +github.com/docker/docker 30eb4d8cdc422b023d5f11f29a82ecb73554183b +github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52 github.com/docker/libtrust aabc10ec26b754e797f9028f4589c5b7bd90dc20 github.com/ghodss/yaml 04f313413ffd65ce25f2541bfd2b2ceec5c0908c @@ -24,7 +24,7 @@ github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987 github.com/vbatts/tar-split bd4c5d64c3e9297f410025a3b1bd0c58f659e721 golang.org/x/crypto 453249f01cfeb54c3d549ddb75ff152ca243f9d8 golang.org/x/net 6b27048ae5e6ad1ef927e72e437531493de612fe -golang.org/x/sys 075e574b89e4c2d22f2286a7e2b919519c6f3547 +golang.org/x/sys 43e60d72a8e2bd92ee98319ba9a384a0e9837c08 gopkg.in/cheggaaa/pb.v1 d7e6ca3010b6f084d8056847f55d7f572f180678 gopkg.in/yaml.v2 a3f3340b5840cee44f372bddb5880fcbc419b46a k8s.io/client-go bcde30fb7eaed76fd98a36b4120321b94995ffb6 @@ -35,3 +35,4 @@ github.com/tchap/go-patricia v2.2.6 github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d github.com/BurntSushi/toml b26d9c308763d68093482582cea63d69be07a0f0 github.com/ostreedev/ostree-go aeb02c6b6aa2889db3ef62f7855650755befd460 +github.com/gogo/protobuf/proto fcdc5011193ff531a548e9b0301828d5a5b97fd8