diff --git a/cmd/skopeo/layers.go b/cmd/skopeo/layers.go index 1c1f6b5d..c5c686ca 100644 --- a/cmd/skopeo/layers.go +++ b/cmd/skopeo/layers.go @@ -26,15 +26,16 @@ var layersCmd = cli.Command{ if err != nil { return err } - src := image.FromSource(rawSource) + src, err := image.FromSource(rawSource) + if err != nil { + rawSource.Close() + return err + } defer src.Close() blobDigests := c.Args().Tail() if len(blobDigests) == 0 { - layers, err := src.LayerInfos() - if err != nil { - return err - } + layers := src.LayerInfos() seenLayers := map[string]struct{}{} for _, info := range layers { if _, ok := seenLayers[info.Digest]; !ok { @@ -42,10 +43,7 @@ var layersCmd = cli.Command{ seenLayers[info.Digest] = struct{}{} } } - configInfo, err := src.ConfigInfo() - if err != nil { - return err - } + configInfo := src.ConfigInfo() if configInfo.Digest != "" { blobDigests = append(blobDigests, configInfo.Digest) } diff --git a/vendor/github.com/containers/image/copy/copy.go b/vendor/github.com/containers/image/copy/copy.go index 450827cc..647e97b4 100644 --- a/vendor/github.com/containers/image/copy/copy.go +++ b/vendor/github.com/containers/image/copy/copy.go @@ -109,26 +109,26 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des if err != nil { return fmt.Errorf("Error initializing source %s: %v", transports.ImageName(srcRef), err) } - src := image.FromSource(rawSource) - defer src.Close() - - multiImage, err := src.IsMultiImage() - if err != nil { - return err - } - if multiImage { - return fmt.Errorf("can not copy %s: manifest contains multiple images", transports.ImageName(srcRef)) - } + unparsedImage := image.UnparsedFromSource(rawSource) + defer func() { + if unparsedImage != nil { + unparsedImage.Close() + } + }() // Please keep this policy check BEFORE reading any other information about the image. - if allowed, err := policyContext.IsRunningImageAllowed(src); !allowed || err != nil { // Be paranoid and fail if either return value indicates so. + if allowed, err := policyContext.IsRunningImageAllowed(unparsedImage); !allowed || err != nil { // Be paranoid and fail if either return value indicates so. return fmt.Errorf("Source image rejected: %v", err) } - - writeReport("Getting image source manifest\n") - manifest, _, err := src.Manifest() + src, err := image.FromUnparsedImage(unparsedImage) if err != nil { - return fmt.Errorf("Error reading manifest: %v", err) + return fmt.Errorf("Error initializing image from source %s: %v", transports.ImageName(srcRef), err) + } + unparsedImage = nil + defer src.Close() + + if src.IsMultiImage() { + return fmt.Errorf("can not copy %s: manifest contains multiple images", transports.ImageName(srcRef)) } var sigs [][]byte @@ -150,11 +150,7 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des } canModifyManifest := len(sigs) == 0 - writeReport("Getting image source configuration\n") - srcConfigInfo, err := src.ConfigInfo() - if err != nil { - return fmt.Errorf("Error parsing manifest: %v", err) - } + srcConfigInfo := src.ConfigInfo() if srcConfigInfo.Digest != "" { writeReport("Uploading blob %s\n", srcConfigInfo.Digest) destConfigInfo, err := copyBlob(dest, rawSource, srcConfigInfo, false, reportWriter) @@ -166,10 +162,7 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des } } - srcLayerInfos, err := src.LayerInfos() - if err != nil { - return fmt.Errorf("Error parsing manifest: %v", err) - } + srcLayerInfos := src.LayerInfos() destLayerInfos := []types.BlobInfo{} copiedLayers := map[string]types.BlobInfo{} for _, srcLayer := range srcLayerInfos { @@ -190,15 +183,20 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des manifestUpdates.LayerInfos = destLayerInfos } + pendingImage := src if !reflect.DeepEqual(manifestUpdates, types.ManifestUpdateOptions{}) { if !canModifyManifest { return fmt.Errorf("Internal error: copy needs an updated manifest but that was known to be forbidden") } - manifest, err = src.UpdatedManifest(manifestUpdates) + pendingImage, err = src.UpdatedImage(manifestUpdates) if err != nil { return fmt.Errorf("Error creating an updated manifest: %v", err) } } + manifest, _, err := pendingImage.Manifest() + if err != nil { + return fmt.Errorf("Error reading manifest: %v", err) + } if options != nil && options.SignBy != "" { mech, err := signature.NewGPGSigningMechanism() diff --git a/vendor/github.com/containers/image/directory/directory_transport.go b/vendor/github.com/containers/image/directory/directory_transport.go index 4979572d..5703127f 100644 --- a/vendor/github.com/containers/image/directory/directory_transport.go +++ b/vendor/github.com/containers/image/directory/directory_transport.go @@ -127,11 +127,13 @@ func (ref dirReference) PolicyConfigurationNamespaces() []string { return res } -// NewImage returns a types.Image for this reference. +// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport. // The caller must call .Close() on the returned Image. +// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource, +// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage. func (ref dirReference) NewImage(ctx *types.SystemContext) (types.Image, error) { src := newImageSource(ref) - return image.FromSource(src), nil + return image.FromSource(src) } // NewImageSource returns a types.ImageSource for this reference, diff --git a/vendor/github.com/containers/image/docker/docker_image.go b/vendor/github.com/containers/image/docker/docker_image.go index b02a8cd9..59468755 100644 --- a/vendor/github.com/containers/image/docker/docker_image.go +++ b/vendor/github.com/containers/image/docker/docker_image.go @@ -24,7 +24,11 @@ func newImage(ctx *types.SystemContext, ref dockerReference) (types.Image, error if err != nil { return nil, err } - return &Image{Image: image.FromSource(s), src: s}, nil + img, err := image.FromSource(s) + if err != nil { + return nil, err + } + return &Image{Image: img, src: s}, nil } // SourceRefFullName returns a fully expanded name for the repository this image is in. diff --git a/vendor/github.com/containers/image/docker/docker_transport.go b/vendor/github.com/containers/image/docker/docker_transport.go index 5ab5efe3..52c4ab73 100644 --- a/vendor/github.com/containers/image/docker/docker_transport.go +++ b/vendor/github.com/containers/image/docker/docker_transport.go @@ -115,8 +115,10 @@ func (ref dockerReference) PolicyConfigurationNamespaces() []string { return policyconfiguration.DockerReferenceNamespaces(ref.ref) } -// NewImage returns a types.Image for this reference. +// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport. // The caller must call .Close() on the returned Image. +// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource, +// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage. func (ref dockerReference) NewImage(ctx *types.SystemContext) (types.Image, error) { return newImage(ctx, ref) } diff --git a/vendor/github.com/containers/image/image/docker_schema1.go b/vendor/github.com/containers/image/image/docker_schema1.go index c3915fe7..7b3f894f 100644 --- a/vendor/github.com/containers/image/image/docker_schema1.go +++ b/vendor/github.com/containers/image/image/docker_schema1.go @@ -47,10 +47,27 @@ func manifestSchema1FromManifest(manifest []byte) (genericManifest, error) { return mschema1, nil } +func (m *manifestSchema1) serialize() ([]byte, error) { + // docker/distribution requires a signature even if the incoming data uses the nominally unsigned DockerV2Schema1MediaType. + unsigned, err := json.Marshal(*m) + if err != nil { + return nil, err + } + return manifest.AddDummyV2S1Signature(unsigned) +} + +func (m *manifestSchema1) manifestMIMEType() string { + return manifest.DockerV2Schema1SignedMediaType +} + +// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object. func (m *manifestSchema1) ConfigInfo() types.BlobInfo { return types.BlobInfo{} } +// LayerInfos returns a list of BlobInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers). +// The Digest field is guaranteed to be provided; Size may be -1. +// WARNING: The list may contain duplicates, and they are semantically relevant. func (m *manifestSchema1) LayerInfos() []types.BlobInfo { layers := make([]types.BlobInfo, len(m.FSLayers)) for i, layer := range m.FSLayers { // NOTE: This includes empty layers (where m.History.V1Compatibility->ThrowAway) @@ -59,13 +76,13 @@ func (m *manifestSchema1) LayerInfos() []types.BlobInfo { return layers } -func (m *manifestSchema1) Config() ([]byte, error) { +func (m *manifestSchema1) config() ([]byte, error) { return []byte(m.History[0].V1Compatibility), nil } -func (m *manifestSchema1) ImageInspectInfo() (*types.ImageInspectInfo, error) { +func (m *manifestSchema1) imageInspectInfo() (*types.ImageInspectInfo, error) { v1 := &v1Image{} - config, err := m.Config() + config, err := m.config() if err != nil { return nil, err } @@ -82,7 +99,9 @@ func (m *manifestSchema1) ImageInspectInfo() (*types.ImageInspectInfo, error) { }, nil } -func (m *manifestSchema1) UpdatedManifest(options types.ManifestUpdateOptions) ([]byte, error) { +// UpdatedImage returns a types.Image modified according to options. +// This does not change the state of the original Image object. +func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) { copy := *m if options.LayerInfos != nil { // Our LayerInfos includes empty layers (where m.History.V1Compatibility->ThrowAway), so expect them to be included here as well. @@ -96,12 +115,7 @@ func (m *manifestSchema1) UpdatedManifest(options types.ManifestUpdateOptions) ( copy.FSLayers[(len(options.LayerInfos)-1)-i].BlobSum = info.Digest } } - // docker/distribution requires a signature even if the incoming data uses the nominally unsigned DockerV2Schema1MediaType. - unsigned, err := json.Marshal(copy) - if err != nil { - return nil, err - } - return manifest.AddDummyV2S1Signature(unsigned) + return memoryImageFromManifest(©), nil } // fixManifestLayers, after validating the supplied manifest diff --git a/vendor/github.com/containers/image/image/docker_schema2.go b/vendor/github.com/containers/image/image/docker_schema2.go index 448dc38c..b803f1c7 100644 --- a/vendor/github.com/containers/image/image/docker_schema2.go +++ b/vendor/github.com/containers/image/image/docker_schema2.go @@ -30,10 +30,22 @@ func manifestSchema2FromManifest(src types.ImageSource, manifest []byte) (generi return &v2s2, nil } +func (m *manifestSchema2) serialize() ([]byte, error) { + return json.Marshal(*m) +} + +func (m *manifestSchema2) manifestMIMEType() string { + return m.MediaType +} + +// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object. func (m *manifestSchema2) ConfigInfo() types.BlobInfo { return types.BlobInfo{Digest: m.ConfigDescriptor.Digest, Size: m.ConfigDescriptor.Size} } +// LayerInfos returns a list of BlobInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers). +// The Digest field is guaranteed to be provided; Size may be -1. +// WARNING: The list may contain duplicates, and they are semantically relevant. func (m *manifestSchema2) LayerInfos() []types.BlobInfo { blobs := []types.BlobInfo{} for _, layer := range m.LayersDescriptors { @@ -42,7 +54,7 @@ func (m *manifestSchema2) LayerInfos() []types.BlobInfo { return blobs } -func (m *manifestSchema2) Config() ([]byte, error) { +func (m *manifestSchema2) config() ([]byte, error) { rawConfig, _, err := m.src.GetBlob(m.ConfigDescriptor.Digest) if err != nil { return nil, err @@ -52,8 +64,8 @@ func (m *manifestSchema2) Config() ([]byte, error) { return config, err } -func (m *manifestSchema2) ImageInspectInfo() (*types.ImageInspectInfo, error) { - config, err := m.Config() +func (m *manifestSchema2) imageInspectInfo() (*types.ImageInspectInfo, error) { + config, err := m.config() if err != nil { return nil, err } @@ -70,7 +82,9 @@ func (m *manifestSchema2) ImageInspectInfo() (*types.ImageInspectInfo, error) { }, nil } -func (m *manifestSchema2) UpdatedManifest(options types.ManifestUpdateOptions) ([]byte, error) { +// UpdatedImage returns a types.Image modified according to options. +// This does not change the state of the original Image object. +func (m *manifestSchema2) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) { copy := *m if options.LayerInfos != nil { if len(copy.LayersDescriptors) != len(options.LayerInfos) { @@ -81,5 +95,5 @@ func (m *manifestSchema2) UpdatedManifest(options types.ManifestUpdateOptions) ( copy.LayersDescriptors[i].Size = info.Size } } - return json.Marshal(copy) + return memoryImageFromManifest(©), nil } diff --git a/vendor/github.com/containers/image/image/image.go b/vendor/github.com/containers/image/image/image.go deleted file mode 100644 index 23c2a0a2..00000000 --- a/vendor/github.com/containers/image/image/image.go +++ /dev/null @@ -1,200 +0,0 @@ -// Package image consolidates knowledge about various container image formats -// (as opposed to image storage mechanisms, which are handled by types.ImageSource) -// and exposes all of them using an unified interface. -package image - -import ( - "errors" - "fmt" - "time" - - "github.com/containers/image/manifest" - "github.com/containers/image/types" -) - -// genericImage is a general set of utilities for working with container images, -// whatever is their underlying location (i.e. dockerImageSource-independent). -// Note the existence of skopeo/docker.Image: some instances of a `types.Image` -// may not be a `genericImage` directly. However, most users of `types.Image` -// do not care, and those who care about `skopeo/docker.Image` know they do. -type genericImage struct { - src types.ImageSource - // private cache for Manifest(); nil if not yet known. - cachedManifest []byte - // private cache for the manifest media type w/o having to guess it - // this may be the empty string in case the MIME Type wasn't guessed correctly - // this field is valid only if cachedManifest is not nil - cachedManifestMIMEType string - // private cache for Signatures(); nil if not yet known. - cachedSignatures [][]byte -} - -// FromSource returns a types.Image implementation for source. -// The caller must call .Close() on the returned Image. -// -// FromSource “takes ownership” of the input ImageSource and will call src.Close() -// when the image is closed. (This does not prevent callers from using both the -// Image and ImageSource objects simultaneously, but it means that they only need to -// the Image.) -func FromSource(src types.ImageSource) types.Image { - return &genericImage{src: src} -} - -// Reference returns the reference used to set up this source, _as specified by the user_ -// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. -func (i *genericImage) Reference() types.ImageReference { - return i.src.Reference() -} - -// Close removes resources associated with an initialized Image, if any. -func (i *genericImage) Close() { - i.src.Close() -} - -// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need. -// NOTE: It is essential for signature verification that Manifest returns the manifest from which ConfigInfo and LayerInfos is computed. -func (i *genericImage) Manifest() ([]byte, string, error) { - if i.cachedManifest == nil { - m, mt, err := i.src.GetManifest() - if err != nil { - return nil, "", err - } - i.cachedManifest = m - if mt == "" || mt == "text/plain" { - // Crane registries can return "text/plain". - // This makes no real sense, but it happens - // because requests for manifests are - // redirected to a content distribution - // network which is configured that way. - mt = manifest.GuessMIMEType(i.cachedManifest) - } - i.cachedManifestMIMEType = mt - } - return i.cachedManifest, i.cachedManifestMIMEType, nil -} - -// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need. -func (i *genericImage) Signatures() ([][]byte, error) { - if i.cachedSignatures == nil { - sigs, err := i.src.GetSignatures() - if err != nil { - return nil, err - } - i.cachedSignatures = sigs - } - return i.cachedSignatures, nil -} - -type config struct { - Labels map[string]string -} - -type v1Image struct { - // Config is the configuration of the container received from the client - Config *config `json:"config,omitempty"` - // DockerVersion specifies version on which image is built - DockerVersion string `json:"docker_version,omitempty"` - // Created timestamp when image was created - Created time.Time `json:"created"` - // Architecture is the hardware that the image is build and runs on - Architecture string `json:"architecture,omitempty"` - // OS is the operating system used to build and run the image - OS string `json:"os,omitempty"` -} - -// will support v1 one day... -type genericManifest interface { - Config() ([]byte, error) - ConfigInfo() types.BlobInfo - LayerInfos() []types.BlobInfo - ImageInspectInfo() (*types.ImageInspectInfo, error) // The caller will need to fill in Layers - UpdatedManifest(types.ManifestUpdateOptions) ([]byte, error) -} - -// getParsedManifest parses the manifest into a data structure, cleans it up, and returns it. -// NOTE: The manifest may have been modified in the process; DO NOT reserialize and store the return value -// if you want to preserve the original manifest; use the blob returned by Manifest() directly. -// NOTE: It is essential for signature verification that the object is computed from the same manifest which is returned by Manifest(). -func (i *genericImage) getParsedManifest() (genericManifest, error) { - manblob, mt, err := i.Manifest() - if err != nil { - return nil, err - } - return manifestInstanceFromBlob(i.src, manblob, mt) -} - -func (i *genericImage) IsMultiImage() (bool, error) { - _, mt, err := i.Manifest() - if err != nil { - return false, err - } - return mt == manifest.DockerV2ListMediaType, nil -} - -func manifestInstanceFromBlob(src types.ImageSource, manblob []byte, mt string) (genericManifest, error) { - switch mt { - // "application/json" is a valid v2s1 value per https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md . - // This works for now, when nothing else seems to return "application/json"; if that were not true, the mapping/detection might - // need to happen within the ImageSource. - case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType, "application/json": - return manifestSchema1FromManifest(manblob) - case manifest.DockerV2Schema2MediaType: - return manifestSchema2FromManifest(src, manblob) - case manifest.DockerV2ListMediaType: - return manifestSchema2FromManifestList(src, manblob) - case "": - return nil, errors.New("could not guess manifest media type") - default: - return nil, fmt.Errorf("unsupported manifest media type %s", mt) - } -} - -func (i *genericImage) Inspect() (*types.ImageInspectInfo, error) { - // TODO(runcom): unused version param for now, default to docker v2-1 - m, err := i.getParsedManifest() - if err != nil { - return nil, err - } - info, err := m.ImageInspectInfo() - if err != nil { - return nil, err - } - layers := m.LayerInfos() - info.Layers = make([]string, len(layers)) - for i, layer := range layers { - info.Layers[i] = layer.Digest - } - return info, nil -} - -// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object. -// NOTE: It is essential for signature verification that ConfigInfo is computed from the same manifest which is returned by Manifest(). -func (i *genericImage) ConfigInfo() (types.BlobInfo, error) { - m, err := i.getParsedManifest() - if err != nil { - return types.BlobInfo{}, err - } - return m.ConfigInfo(), nil -} - -// LayerInfos returns a list of BlobInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers). -// The Digest field is guaranteed to be provided; Size may be -1. -// NOTE: It is essential for signature verification that LayerInfos is computed from the same manifest which is returned by Manifest(). -// WARNING: The list may contain duplicates, and they are semantically relevant. -func (i *genericImage) LayerInfos() ([]types.BlobInfo, error) { - m, err := i.getParsedManifest() - if err != nil { - return nil, err - } - return m.LayerInfos(), nil -} - -// UpdatedManifest returns the image's manifest modified according to updateOptions. -// This does not change the state of the Image object. -func (i *genericImage) UpdatedManifest(options types.ManifestUpdateOptions) ([]byte, error) { - m, err := i.getParsedManifest() - if err != nil { - return nil, err - } - return m.UpdatedManifest(options) -} diff --git a/vendor/github.com/containers/image/image/manifest.go b/vendor/github.com/containers/image/image/manifest.go new file mode 100644 index 00000000..17b12235 --- /dev/null +++ b/vendor/github.com/containers/image/image/manifest.go @@ -0,0 +1,79 @@ +package image + +import ( + "errors" + "fmt" + "time" + + "github.com/containers/image/manifest" + "github.com/containers/image/types" +) + +type config struct { + Labels map[string]string +} + +type v1Image struct { + // Config is the configuration of the container received from the client + Config *config `json:"config,omitempty"` + // DockerVersion specifies version on which image is built + DockerVersion string `json:"docker_version,omitempty"` + // Created timestamp when image was created + Created time.Time `json:"created"` + // Architecture is the hardware that the image is build and runs on + Architecture string `json:"architecture,omitempty"` + // OS is the operating system used to build and run the image + OS string `json:"os,omitempty"` +} + +// genericManifest is an interface for parsing, modifying image manifests and related data. +// Note that the public methods are intended to be a subset of types.Image +// so that embedding a genericManifest into structs works. +// will support v1 one day... +type genericManifest interface { + serialize() ([]byte, error) + manifestMIMEType() string + config() ([]byte, error) + // ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object. + ConfigInfo() types.BlobInfo + // LayerInfos returns a list of BlobInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers). + // The Digest field is guaranteed to be provided; Size may be -1. + // WARNING: The list may contain duplicates, and they are semantically relevant. + LayerInfos() []types.BlobInfo + imageInspectInfo() (*types.ImageInspectInfo, error) // To be called by inspectManifest + // UpdatedImage returns a types.Image modified according to options. + // This does not change the state of the original Image object. + UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) +} + +func manifestInstanceFromBlob(src types.ImageSource, manblob []byte, mt string) (genericManifest, error) { + switch mt { + // "application/json" is a valid v2s1 value per https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md . + // This works for now, when nothing else seems to return "application/json"; if that were not true, the mapping/detection might + // need to happen within the ImageSource. + case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType, "application/json": + return manifestSchema1FromManifest(manblob) + case manifest.DockerV2Schema2MediaType: + return manifestSchema2FromManifest(src, manblob) + case manifest.DockerV2ListMediaType: + return manifestSchema2FromManifestList(src, manblob) + case "": + return nil, errors.New("could not guess manifest media type") + default: + return nil, fmt.Errorf("unsupported manifest media type %s", mt) + } +} + +// inspectManifest is an implementation of types.Image.Inspect +func inspectManifest(m genericManifest) (*types.ImageInspectInfo, error) { + info, err := m.imageInspectInfo() + if err != nil { + return nil, err + } + layers := m.LayerInfos() + info.Layers = make([]string, len(layers)) + for i, layer := range layers { + info.Layers[i] = layer.Digest + } + return info, nil +} diff --git a/vendor/github.com/containers/image/image/memory.go b/vendor/github.com/containers/image/image/memory.go new file mode 100644 index 00000000..568f855a --- /dev/null +++ b/vendor/github.com/containers/image/image/memory.go @@ -0,0 +1,65 @@ +package image + +import ( + "errors" + + "github.com/containers/image/types" +) + +// memoryImage is a mostly-implementation of types.Image assembled from data +// created in memory, used primarily as a return value of types.Image.UpdatedImage +// as a way to carry various structured information in a type-safe and easy-to-use way. +// Note that this _only_ carries the immediate metadata; it is _not_ a stand-alone +// collection of all related information, e.g. there is no way to get layer blobs +// from a memoryImage. +type memoryImage struct { + genericManifest + serializedManifest []byte // A private cache for Manifest() +} + +func memoryImageFromManifest(m genericManifest) types.Image { + return &memoryImage{ + genericManifest: m, + serializedManifest: nil, + } +} + +// Reference returns the reference used to set up this source, _as specified by the user_ +// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. +func (i *memoryImage) Reference() types.ImageReference { + // It would really be inappropriate to return the ImageReference of the image this was based on. + return nil +} + +// Close removes resources associated with an initialized UnparsedImage, if any. +func (i *memoryImage) Close() { +} + +// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need. +func (i *memoryImage) Manifest() ([]byte, string, error) { + if i.serializedManifest == nil { + m, err := i.genericManifest.serialize() + if err != nil { + return nil, "", err + } + i.serializedManifest = m + } + return i.serializedManifest, i.genericManifest.manifestMIMEType(), nil +} + +// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need. +func (i *memoryImage) Signatures() ([][]byte, error) { + // Modifying an image invalidates signatures; a caller asking the updated image for signatures + // is probably confused. + return nil, errors.New("Internal error: Image.Signatures() is not supported for images modified in memory") +} + +// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration. +func (i *memoryImage) Inspect() (*types.ImageInspectInfo, error) { + return inspectManifest(i.genericManifest) +} + +// IsMultiImage returns true if the image's manifest is a list of images, false otherwise. +func (i *memoryImage) IsMultiImage() bool { + return false +} diff --git a/vendor/github.com/containers/image/image/sourced.go b/vendor/github.com/containers/image/image/sourced.go new file mode 100644 index 00000000..1f21c68f --- /dev/null +++ b/vendor/github.com/containers/image/image/sourced.go @@ -0,0 +1,93 @@ +// Package image consolidates knowledge about various container image formats +// (as opposed to image storage mechanisms, which are handled by types.ImageSource) +// and exposes all of them using an unified interface. +package image + +import ( + "github.com/containers/image/manifest" + "github.com/containers/image/types" +) + +// FromSource returns a types.Image implementation for source. +// The caller must call .Close() on the returned Image. +// +// FromSource “takes ownership” of the input ImageSource and will call src.Close() +// when the image is closed. (This does not prevent callers from using both the +// Image and ImageSource objects simultaneously, but it means that they only need to +// the Image.) +// +// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource, +// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage instead of calling this function. +func FromSource(src types.ImageSource) (types.Image, error) { + return FromUnparsedImage(UnparsedFromSource(src)) +} + +// sourcedImage is a general set of utilities for working with container images, +// whatever is their underlying location (i.e. dockerImageSource-independent). +// Note the existence of skopeo/docker.Image: some instances of a `types.Image` +// may not be a `sourcedImage` directly. However, most users of `types.Image` +// do not care, and those who care about `skopeo/docker.Image` know they do. +type sourcedImage struct { + *UnparsedImage + manifestBlob []byte + manifestMIMEType string + // genericManifest contains data corresponding to manifestBlob. + // NOTE: The manifest may have been modified in the process; DO NOT reserialize and store genericManifest + // if you want to preserve the original manifest; use manifestBlob directly. + genericManifest +} + +// FromUnparsedImage returns a types.Image implementation for unparsed. +// The caller must call .Close() on the returned Image. +// +// FromSource “takes ownership” of the input UnparsedImage and will call uparsed.Close() +// when the image is closed. (This does not prevent callers from using both the +// UnparsedImage and ImageSource objects simultaneously, but it means that they only need to +// keep a reference to the Image.) +func FromUnparsedImage(unparsed *UnparsedImage) (types.Image, error) { + // Note that the input parameter above is specifically *image.UnparsedImage, not types.UnparsedImage: + // we want to be able to use unparsed.src. We could make that an explicit interface, but, well, + // this is the only UnparsedImage implementation around, anyway. + + // Also, we do not explicitly implement types.Image.Close; we let the implementation fall through to + // unparsed.Close. + + // NOTE: It is essential for signature verification that all parsing done in this object happens on the same manifest which is returned by unparsed.Manifest(). + manifestBlob, manifestMIMEType, err := unparsed.Manifest() + if err != nil { + return nil, err + } + if manifestMIMEType == "" || manifestMIMEType == "text/plain" { + // Crane registries can return "text/plain". + // This makes no real sense, but it happens + // because requests for manifests are + // redirected to a content distribution + // network which is configured that way. + manifestMIMEType = manifest.GuessMIMEType(manifestBlob) + } + + parsedManifest, err := manifestInstanceFromBlob(unparsed.src, manifestBlob, manifestMIMEType) + if err != nil { + return nil, err + } + + return &sourcedImage{ + UnparsedImage: unparsed, + manifestBlob: manifestBlob, + manifestMIMEType: manifestMIMEType, + genericManifest: parsedManifest, + }, nil +} + +// Manifest overrides the UnparsedImage.Manifest to use the fields which we have already fetched, after guessing and overrides. +func (i *sourcedImage) Manifest() ([]byte, string, error) { + return i.manifestBlob, i.manifestMIMEType, nil +} + +func (i *sourcedImage) Inspect() (*types.ImageInspectInfo, error) { + return inspectManifest(i.genericManifest) +} + +func (i *sourcedImage) IsMultiImage() bool { + return i.manifestMIMEType == manifest.DockerV2ListMediaType +} diff --git a/vendor/github.com/containers/image/image/unparsed.go b/vendor/github.com/containers/image/image/unparsed.go new file mode 100644 index 00000000..2682a5d8 --- /dev/null +++ b/vendor/github.com/containers/image/image/unparsed.go @@ -0,0 +1,60 @@ +package image + +import "github.com/containers/image/types" + +// UnparsedImage implements types.UnparsedImage . +type UnparsedImage struct { + src types.ImageSource + cachedManifest []byte // A private cache for Manifest(); nil if not yet known. + // A private cache for Manifest(), may be the empty string if guessing failed. + // Valid iff cachedManifest is not nil. + cachedManifestMIMEType string + cachedSignatures [][]byte // A private cache for Signatures(); nil if not yet known. +} + +// UnparsedFromSource returns a types.UnparsedImage implementation for source. +// The caller must call .Close() on the returned UnparsedImage. +// +// UnparsedFromSource “takes ownership” of the input ImageSource and will call src.Close() +// when the image is closed. (This does not prevent callers from using both the +// UnparsedImage and ImageSource objects simultaneously, but it means that they only need to +// keep a reference to the UnparsedImage.) +func UnparsedFromSource(src types.ImageSource) *UnparsedImage { + return &UnparsedImage{src: src} +} + +// Reference returns the reference used to set up this source, _as specified by the user_ +// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. +func (i *UnparsedImage) Reference() types.ImageReference { + return i.src.Reference() +} + +// Close removes resources associated with an initialized UnparsedImage, if any. +func (i *UnparsedImage) Close() { + i.src.Close() +} + +// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need. +func (i *UnparsedImage) Manifest() ([]byte, string, error) { + if i.cachedManifest == nil { + m, mt, err := i.src.GetManifest() + if err != nil { + return nil, "", err + } + i.cachedManifest = m + i.cachedManifestMIMEType = mt + } + return i.cachedManifest, i.cachedManifestMIMEType, nil +} + +// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need. +func (i *UnparsedImage) Signatures() ([][]byte, error) { + if i.cachedSignatures == nil { + sigs, err := i.src.GetSignatures() + if err != nil { + return nil, err + } + i.cachedSignatures = sigs + } + return i.cachedSignatures, nil +} 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 eee79957..fcd4c531 100644 --- a/vendor/github.com/containers/image/oci/layout/oci_transport.go +++ b/vendor/github.com/containers/image/oci/layout/oci_transport.go @@ -164,8 +164,10 @@ func (ref ociReference) PolicyConfigurationNamespaces() []string { return res } -// NewImage returns a types.Image for this reference. +// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport. // The caller must call .Close() on the returned Image. +// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource, +// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage. func (ref ociReference) NewImage(ctx *types.SystemContext) (types.Image, error) { return nil, errors.New("Full Image support not implemented for oci: image names") } diff --git a/vendor/github.com/containers/image/openshift/openshift_transport.go b/vendor/github.com/containers/image/openshift/openshift_transport.go index ae30d24c..213f1f72 100644 --- a/vendor/github.com/containers/image/openshift/openshift_transport.go +++ b/vendor/github.com/containers/image/openshift/openshift_transport.go @@ -118,14 +118,16 @@ func (ref openshiftReference) PolicyConfigurationNamespaces() []string { return policyconfiguration.DockerReferenceNamespaces(ref.dockerReference) } -// NewImage returns a types.Image for this reference. +// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport. // The caller must call .Close() on the returned Image. +// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource, +// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage. func (ref openshiftReference) NewImage(ctx *types.SystemContext) (types.Image, error) { src, err := newImageSource(ctx, ref, nil) if err != nil { return nil, err } - return genericImage.FromSource(src), nil + return genericImage.FromSource(src) } // NewImageSource returns a types.ImageSource for this reference, diff --git a/vendor/github.com/containers/image/signature/policy_eval.go b/vendor/github.com/containers/image/signature/policy_eval.go index ab3a50f9..4828ecc8 100644 --- a/vendor/github.com/containers/image/signature/policy_eval.go +++ b/vendor/github.com/containers/image/signature/policy_eval.go @@ -54,14 +54,14 @@ type PolicyRequirement interface { // a container based on this image; use IsRunningImageAllowed instead. // - Just because a signature is accepted does not automatically mean the contents of the // signature are authorized to run code as root, or to affect system or cluster configuration. - isSignatureAuthorAccepted(image types.Image, sig []byte) (signatureAcceptanceResult, *Signature, error) + isSignatureAuthorAccepted(image types.UnparsedImage, sig []byte) (signatureAcceptanceResult, *Signature, error) // isRunningImageAllowed returns true if the requirement allows running an image. // If it returns false, err must be non-nil, and should be an PolicyRequirementError if evaluation // succeeded but the result was rejection. // WARNING: This validates signatures and the manifest, but does not download or validate the // layers. Users must validate that the layers match their expected digests. - isRunningImageAllowed(image types.Image) (bool, error) + isRunningImageAllowed(image types.UnparsedImage) (bool, error) } // PolicyReferenceMatch specifies a set of image identities accepted in PolicyRequirement. @@ -70,7 +70,7 @@ type PolicyReferenceMatch interface { // matchesDockerReference decides whether a specific image identity is accepted for an image // (or, usually, for the image's Reference().DockerReference()). Note that // image.Reference().DockerReference() may be nil. - matchesDockerReference(image types.Image, signatureDockerReference string) bool + matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool } // PolicyContext encapsulates a policy and possible cached state @@ -174,7 +174,7 @@ func (pc *PolicyContext) requirementsForImageRef(ref types.ImageReference) Polic // a container based on this image; use IsRunningImageAllowed instead. // - Just because a signature is accepted does not automatically mean the contents of the // signature are authorized to run code as root, or to affect system or cluster configuration. -func (pc *PolicyContext) GetSignaturesWithAcceptedAuthor(image types.Image) (sigs []*Signature, finalErr error) { +func (pc *PolicyContext) GetSignaturesWithAcceptedAuthor(image types.UnparsedImage) (sigs []*Signature, finalErr error) { if err := pc.changeState(pcReady, pcInUse); err != nil { return nil, err } @@ -254,7 +254,7 @@ func (pc *PolicyContext) GetSignaturesWithAcceptedAuthor(image types.Image) (sig // succeeded but the result was rejection. // WARNING: This validates signatures and the manifest, but does not download or validate the // layers. Users must validate that the layers match their expected digests. -func (pc *PolicyContext) IsRunningImageAllowed(image types.Image) (res bool, finalErr error) { +func (pc *PolicyContext) IsRunningImageAllowed(image types.UnparsedImage) (res bool, finalErr error) { if err := pc.changeState(pcReady, pcInUse); err != nil { return false, err } diff --git a/vendor/github.com/containers/image/signature/policy_eval_baselayer.go b/vendor/github.com/containers/image/signature/policy_eval_baselayer.go index 8e5e628a..dec84c93 100644 --- a/vendor/github.com/containers/image/signature/policy_eval_baselayer.go +++ b/vendor/github.com/containers/image/signature/policy_eval_baselayer.go @@ -7,11 +7,11 @@ import ( "github.com/containers/image/types" ) -func (pr *prSignedBaseLayer) isSignatureAuthorAccepted(image types.Image, sig []byte) (signatureAcceptanceResult, *Signature, error) { +func (pr *prSignedBaseLayer) isSignatureAuthorAccepted(image types.UnparsedImage, sig []byte) (signatureAcceptanceResult, *Signature, error) { return sarUnknown, nil, nil } -func (pr *prSignedBaseLayer) isRunningImageAllowed(image types.Image) (bool, error) { +func (pr *prSignedBaseLayer) isRunningImageAllowed(image types.UnparsedImage) (bool, error) { // FIXME? Reject this at policy parsing time already? logrus.Errorf("signedBaseLayer not implemented yet!") return false, PolicyRequirementError("signedBaseLayer not implemented yet!") diff --git a/vendor/github.com/containers/image/signature/policy_eval_signedby.go b/vendor/github.com/containers/image/signature/policy_eval_signedby.go index e6889c89..7b1ffac7 100644 --- a/vendor/github.com/containers/image/signature/policy_eval_signedby.go +++ b/vendor/github.com/containers/image/signature/policy_eval_signedby.go @@ -13,7 +13,7 @@ import ( "github.com/containers/image/types" ) -func (pr *prSignedBy) isSignatureAuthorAccepted(image types.Image, sig []byte) (signatureAcceptanceResult, *Signature, error) { +func (pr *prSignedBy) isSignatureAuthorAccepted(image types.UnparsedImage, sig []byte) (signatureAcceptanceResult, *Signature, error) { switch pr.KeyType { case SBKeyTypeGPGKeys: case SBKeyTypeSignedByGPGKeys, SBKeyTypeX509Certificates, SBKeyTypeSignedByX509CAs: @@ -97,7 +97,7 @@ func (pr *prSignedBy) isSignatureAuthorAccepted(image types.Image, sig []byte) ( return sarAccepted, signature, nil } -func (pr *prSignedBy) isRunningImageAllowed(image types.Image) (bool, error) { +func (pr *prSignedBy) isRunningImageAllowed(image types.UnparsedImage) (bool, error) { sigs, err := image.Signatures() if err != nil { return false, err diff --git a/vendor/github.com/containers/image/signature/policy_eval_simple.go b/vendor/github.com/containers/image/signature/policy_eval_simple.go index 5d479f23..19a71e6d 100644 --- a/vendor/github.com/containers/image/signature/policy_eval_simple.go +++ b/vendor/github.com/containers/image/signature/policy_eval_simple.go @@ -9,20 +9,20 @@ import ( "github.com/containers/image/types" ) -func (pr *prInsecureAcceptAnything) isSignatureAuthorAccepted(image types.Image, sig []byte) (signatureAcceptanceResult, *Signature, error) { +func (pr *prInsecureAcceptAnything) isSignatureAuthorAccepted(image types.UnparsedImage, sig []byte) (signatureAcceptanceResult, *Signature, error) { // prInsecureAcceptAnything semantics: Every image is allowed to run, // but this does not consider the signature as verified. return sarUnknown, nil, nil } -func (pr *prInsecureAcceptAnything) isRunningImageAllowed(image types.Image) (bool, error) { +func (pr *prInsecureAcceptAnything) isRunningImageAllowed(image types.UnparsedImage) (bool, error) { return true, nil } -func (pr *prReject) isSignatureAuthorAccepted(image types.Image, sig []byte) (signatureAcceptanceResult, *Signature, error) { +func (pr *prReject) isSignatureAuthorAccepted(image types.UnparsedImage, sig []byte) (signatureAcceptanceResult, *Signature, error) { return sarRejected, nil, PolicyRequirementError(fmt.Sprintf("Any signatures for image %s are rejected by policy.", transports.ImageName(image.Reference()))) } -func (pr *prReject) isRunningImageAllowed(image types.Image) (bool, error) { +func (pr *prReject) isRunningImageAllowed(image types.UnparsedImage) (bool, error) { return false, PolicyRequirementError(fmt.Sprintf("Running image %s is rejected by policy.", transports.ImageName(image.Reference()))) } diff --git a/vendor/github.com/containers/image/signature/policy_reference_match.go b/vendor/github.com/containers/image/signature/policy_reference_match.go index 2b2d4109..df8eacea 100644 --- a/vendor/github.com/containers/image/signature/policy_reference_match.go +++ b/vendor/github.com/containers/image/signature/policy_reference_match.go @@ -11,7 +11,7 @@ import ( ) // parseImageAndDockerReference converts an image and a reference string into two parsed entities, failing on any error and handling unidentified images. -func parseImageAndDockerReference(image types.Image, s2 string) (reference.Named, reference.Named, error) { +func parseImageAndDockerReference(image types.UnparsedImage, s2 string) (reference.Named, reference.Named, error) { r1 := image.Reference().DockerReference() if r1 == nil { return nil, nil, PolicyRequirementError(fmt.Sprintf("Docker reference match attempted on image %s with no known Docker reference identity", @@ -24,7 +24,7 @@ func parseImageAndDockerReference(image types.Image, s2 string) (reference.Named return r1, r2, nil } -func (prm *prmMatchExact) matchesDockerReference(image types.Image, signatureDockerReference string) bool { +func (prm *prmMatchExact) matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool { intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference) if err != nil { return false @@ -36,7 +36,7 @@ func (prm *prmMatchExact) matchesDockerReference(image types.Image, signatureDoc return signature.String() == intended.String() } -func (prm *prmMatchRepository) matchesDockerReference(image types.Image, signatureDockerReference string) bool { +func (prm *prmMatchRepository) matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool { intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference) if err != nil { return false @@ -57,7 +57,7 @@ func parseDockerReferences(s1, s2 string) (reference.Named, reference.Named, err return r1, r2, nil } -func (prm *prmExactReference) matchesDockerReference(image types.Image, signatureDockerReference string) bool { +func (prm *prmExactReference) matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool { intended, signature, err := parseDockerReferences(prm.DockerReference, signatureDockerReference) if err != nil { return false @@ -69,7 +69,7 @@ func (prm *prmExactReference) matchesDockerReference(image types.Image, signatur return signature.String() == intended.String() } -func (prm *prmExactRepository) matchesDockerReference(image types.Image, signatureDockerReference string) bool { +func (prm *prmExactRepository) matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool { intended, signature, err := parseDockerReferences(prm.DockerRepository, signatureDockerReference) if err != nil { return false diff --git a/vendor/github.com/containers/image/types/types.go b/vendor/github.com/containers/image/types/types.go index ab185a6f..e1c7ec01 100644 --- a/vendor/github.com/containers/image/types/types.go +++ b/vendor/github.com/containers/image/types/types.go @@ -70,8 +70,10 @@ type ImageReference interface { // and each following element to be a prefix of the element preceding it. PolicyConfigurationNamespaces() []string - // NewImage returns a types.Image for this reference. + // NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport. // The caller must call .Close() on the returned Image. + // NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource, + // verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage. NewImage(ctx *SystemContext) (Image, error) // NewImageSource returns a types.ImageSource for this reference, // asking the backend to use a manifest from requestedManifestMIMETypes if possible. @@ -156,35 +158,41 @@ type ImageDestination interface { Commit() error } -// Image is the primary API for inspecting properties of images. -// Each Image should eventually be closed by calling Close(). -type Image interface { +// UnparsedImage is an Image-to-be; until it is verified and accepted, it only caries its identity and caches manifest and signature blobs. +// Thus, an UnparsedImage can be created from an ImageSource simply by fetching blobs without interpreting them, +// allowing cryptographic signature verification to happen first, before even fetching the manifest, or parsing anything else. +// This also makes the UnparsedImage→Image conversion an explicitly visible step. +// Each UnparsedImage should eventually be closed by calling Close(). +type UnparsedImage interface { // Reference returns the reference used to set up this source, _as specified by the user_ // (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. Reference() ImageReference - // Close removes resources associated with an initialized Image, if any. + // Close removes resources associated with an initialized UnparsedImage, if any. Close() - // ref to repository? // Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need. - // NOTE: It is essential for signature verification that Manifest returns the manifest from which ConfigInfo and LayerInfos is computed. Manifest() ([]byte, string, error) // Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need. Signatures() ([][]byte, error) +} + +// Image is the primary API for inspecting properties of images. +// Each Image should eventually be closed by calling Close(). +type Image interface { + // Note that Reference may return nil in the return value of UpdatedImage! + UnparsedImage // ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object. - // NOTE: It is essential for signature verification that ConfigInfo is computed from the same manifest which is returned by Manifest(). - ConfigInfo() (BlobInfo, error) + ConfigInfo() BlobInfo // LayerInfos returns a list of BlobInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers). // The Digest field is guaranteed to be provided; Size may be -1. - // NOTE: It is essential for signature verification that LayerInfos is computed from the same manifest which is returned by Manifest(). // WARNING: The list may contain duplicates, and they are semantically relevant. - LayerInfos() ([]BlobInfo, error) + LayerInfos() []BlobInfo // Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration. Inspect() (*ImageInspectInfo, error) - // UpdatedManifest returns the image's manifest modified according to options. - // This does not change the state of the Image object. - UpdatedManifest(options ManifestUpdateOptions) ([]byte, error) + // UpdatedImage returns a types.Image modified according to options. + // This does not change the state of the original Image object. + UpdatedImage(options ManifestUpdateOptions) (Image, error) // IsMultiImage returns true if the image's manifest is a list of images, false otherwise. - IsMultiImage() (bool, error) + IsMultiImage() bool } // ManifestUpdateOptions is a way to pass named optional arguments to Image.UpdatedManifest