Update containers/image and containers/storage
Bump containers/image to 3d0304a02154dddc8f97cc833aa0861cea5e9ade, and containers/storage to 0d32dfce498e06c132c60dac945081bf44c22464. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
parent
2fa1f3f74a
commit
0651d3a8de
64 changed files with 4121 additions and 1636 deletions
|
@ -12,10 +12,10 @@ github.com/gregjones/httpcache 787624de3eb7bd915c329cba748687a3b22666a6
|
||||||
github.com/json-iterator/go 1.0.0
|
github.com/json-iterator/go 1.0.0
|
||||||
github.com/peterbourgon/diskv v2.0.1
|
github.com/peterbourgon/diskv v2.0.1
|
||||||
github.com/sirupsen/logrus v1.0.0
|
github.com/sirupsen/logrus v1.0.0
|
||||||
github.com/containers/image 57b257d128d6075ea3287991ee408d24c7bd2758
|
github.com/containers/image 3d0304a02154dddc8f97cc833aa0861cea5e9ade
|
||||||
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
|
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
|
||||||
github.com/ostreedev/ostree-go master
|
github.com/ostreedev/ostree-go master
|
||||||
github.com/containers/storage d7921c6facc516358070a1306689eda18adaa20a
|
github.com/containers/storage 0d32dfce498e06c132c60dac945081bf44c22464
|
||||||
github.com/containernetworking/cni v0.4.0
|
github.com/containernetworking/cni v0.4.0
|
||||||
google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go
|
google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go
|
||||||
github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd
|
github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd
|
||||||
|
|
279
vendor/github.com/containers/image/copy/copy.go
generated
vendored
279
vendor/github.com/containers/image/copy/copy.go
generated
vendored
|
@ -12,8 +12,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
pb "gopkg.in/cheggaaa/pb.v1"
|
|
||||||
|
|
||||||
"github.com/containers/image/image"
|
"github.com/containers/image/image"
|
||||||
"github.com/containers/image/pkg/compression"
|
"github.com/containers/image/pkg/compression"
|
||||||
"github.com/containers/image/signature"
|
"github.com/containers/image/signature"
|
||||||
|
@ -22,6 +20,7 @@ import (
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
pb "gopkg.in/cheggaaa/pb.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type digestingReader struct {
|
type digestingReader struct {
|
||||||
|
@ -31,23 +30,6 @@ type digestingReader struct {
|
||||||
validationFailed bool
|
validationFailed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// imageCopier allows us to keep track of diffID values for blobs, and other
|
|
||||||
// data, that we're copying between images, and cache other information that
|
|
||||||
// might allow us to take some shortcuts
|
|
||||||
type imageCopier struct {
|
|
||||||
copiedBlobs map[digest.Digest]digest.Digest
|
|
||||||
cachedDiffIDs map[digest.Digest]digest.Digest
|
|
||||||
manifestUpdates *types.ManifestUpdateOptions
|
|
||||||
dest types.ImageDestination
|
|
||||||
src types.Image
|
|
||||||
rawSource types.ImageSource
|
|
||||||
diffIDsAreNeeded bool
|
|
||||||
canModifyManifest bool
|
|
||||||
reportWriter io.Writer
|
|
||||||
progressInterval time.Duration
|
|
||||||
progress chan types.ProgressProperties
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDigestingReader returns an io.Reader implementation with contents of source, which will eventually return a non-EOF error
|
// newDigestingReader returns an io.Reader implementation with contents of source, which will eventually return a non-EOF error
|
||||||
// and set validationFailed to true if the source stream does not match expectedDigest.
|
// and set validationFailed to true if the source stream does not match expectedDigest.
|
||||||
func newDigestingReader(source io.Reader, expectedDigest digest.Digest) (*digestingReader, error) {
|
func newDigestingReader(source io.Reader, expectedDigest digest.Digest) (*digestingReader, error) {
|
||||||
|
@ -86,6 +68,27 @@ func (d *digestingReader) Read(p []byte) (int, error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copier allows us to keep track of diffID values for blobs, and other
|
||||||
|
// data shared across one or more images in a possible manifest list.
|
||||||
|
type copier struct {
|
||||||
|
copiedBlobs map[digest.Digest]digest.Digest
|
||||||
|
cachedDiffIDs map[digest.Digest]digest.Digest
|
||||||
|
dest types.ImageDestination
|
||||||
|
rawSource types.ImageSource
|
||||||
|
reportWriter io.Writer
|
||||||
|
progressInterval time.Duration
|
||||||
|
progress chan types.ProgressProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
// imageCopier tracks state specific to a single image (possibly an item of a manifest list)
|
||||||
|
type imageCopier struct {
|
||||||
|
c *copier
|
||||||
|
manifestUpdates *types.ManifestUpdateOptions
|
||||||
|
src types.Image
|
||||||
|
diffIDsAreNeeded bool
|
||||||
|
canModifyManifest bool
|
||||||
|
}
|
||||||
|
|
||||||
// Options allows supplying non-default configuration modifying the behavior of CopyImage.
|
// Options allows supplying non-default configuration modifying the behavior of CopyImage.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
RemoveSignatures bool // Remove any pre-existing signatures. SignBy will still add a new signature.
|
RemoveSignatures bool // Remove any pre-existing signatures. SignBy will still add a new signature.
|
||||||
|
@ -95,6 +98,8 @@ type Options struct {
|
||||||
DestinationCtx *types.SystemContext
|
DestinationCtx *types.SystemContext
|
||||||
ProgressInterval time.Duration // time to wait between reports to signal the progress channel
|
ProgressInterval time.Duration // time to wait between reports to signal the progress channel
|
||||||
Progress chan types.ProgressProperties // Reported to when ProgressInterval has arrived for a single artifact+offset.
|
Progress chan types.ProgressProperties // Reported to when ProgressInterval has arrived for a single artifact+offset.
|
||||||
|
// manifest MIME type of image set by user. "" is default and means use the autodetection to the the manifest MIME type
|
||||||
|
ForceManifestMIMEType string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image copies image from srcRef to destRef, using policyContext to validate
|
// Image copies image from srcRef to destRef, using policyContext to validate
|
||||||
|
@ -115,10 +120,6 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
|
||||||
reportWriter = options.ReportWriter
|
reportWriter = options.ReportWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
writeReport := func(f string, a ...interface{}) {
|
|
||||||
fmt.Fprintf(reportWriter, f, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
dest, err := destRef.NewImageDestination(options.DestinationCtx)
|
dest, err := destRef.NewImageDestination(options.DestinationCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Error initializing destination %s", transports.ImageName(destRef))
|
return errors.Wrapf(err, "Error initializing destination %s", transports.ImageName(destRef))
|
||||||
|
@ -133,43 +134,89 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Error initializing source %s", transports.ImageName(srcRef))
|
return errors.Wrapf(err, "Error initializing source %s", transports.ImageName(srcRef))
|
||||||
}
|
}
|
||||||
unparsedImage := image.UnparsedFromSource(rawSource)
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if unparsedImage != nil {
|
if err := rawSource.Close(); err != nil {
|
||||||
if err := unparsedImage.Close(); err != nil {
|
retErr = errors.Wrapf(retErr, " (src: %v)", err)
|
||||||
retErr = errors.Wrapf(retErr, " (unparsed: %v)", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
c := &copier{
|
||||||
|
copiedBlobs: make(map[digest.Digest]digest.Digest),
|
||||||
|
cachedDiffIDs: make(map[digest.Digest]digest.Digest),
|
||||||
|
dest: dest,
|
||||||
|
rawSource: rawSource,
|
||||||
|
reportWriter: reportWriter,
|
||||||
|
progressInterval: options.ProgressInterval,
|
||||||
|
progress: options.Progress,
|
||||||
|
}
|
||||||
|
|
||||||
|
unparsedToplevel := image.UnparsedInstance(rawSource, nil)
|
||||||
|
multiImage, err := isMultiImage(unparsedToplevel)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Error determining manifest MIME type for %s", transports.ImageName(srcRef))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !multiImage {
|
||||||
|
// The simple case: Just copy a single image.
|
||||||
|
if err := c.copyOneImage(policyContext, options, unparsedToplevel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is a manifest list. Choose a single image and copy it.
|
||||||
|
// FIXME: Copy to destinations which support manifest lists, one image at a time.
|
||||||
|
instanceDigest, err := image.ChooseManifestInstanceFromManifestList(options.SourceCtx, unparsedToplevel)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Error choosing an image from manifest list %s", transports.ImageName(srcRef))
|
||||||
|
}
|
||||||
|
logrus.Debugf("Source is a manifest list; copying (only) instance %s", instanceDigest)
|
||||||
|
unparsedInstance := image.UnparsedInstance(rawSource, &instanceDigest)
|
||||||
|
|
||||||
|
if err := c.copyOneImage(policyContext, options, unparsedInstance); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.dest.Commit(); err != nil {
|
||||||
|
return errors.Wrap(err, "Error committing the finished image")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image copies a single (on-manifest-list) image unparsedImage, using policyContext to validate
|
||||||
|
// source image admissibility.
|
||||||
|
func (c *copier) copyOneImage(policyContext *signature.PolicyContext, options *Options, unparsedImage *image.UnparsedImage) (retErr error) {
|
||||||
|
// The caller is handling manifest lists; this could happen only if a manifest list contains a manifest list.
|
||||||
|
// Make sure we fail cleanly in such cases.
|
||||||
|
multiImage, err := isMultiImage(unparsedImage)
|
||||||
|
if err != nil {
|
||||||
|
// FIXME FIXME: How to name a reference for the sub-image?
|
||||||
|
return errors.Wrapf(err, "Error determining manifest MIME type for %s", transports.ImageName(unparsedImage.Reference()))
|
||||||
|
}
|
||||||
|
if multiImage {
|
||||||
|
return fmt.Errorf("Unexpectedly received a manifest list instead of a manifest for a single image")
|
||||||
|
}
|
||||||
|
|
||||||
// Please keep this policy check BEFORE reading any other information about the image.
|
// Please keep this policy check BEFORE reading any other information about the image.
|
||||||
|
// (the multiImage check above only matches the MIME type, which we have received anyway.
|
||||||
|
// Actual parsing of anything should be deferred.)
|
||||||
if allowed, err := policyContext.IsRunningImageAllowed(unparsedImage); !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 errors.Wrap(err, "Source image rejected")
|
return errors.Wrap(err, "Source image rejected")
|
||||||
}
|
}
|
||||||
src, err := image.FromUnparsedImage(unparsedImage)
|
src, err := image.FromUnparsedImage(options.SourceCtx, unparsedImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Error initializing image from source %s", transports.ImageName(srcRef))
|
return errors.Wrapf(err, "Error initializing image from source %s", transports.ImageName(c.rawSource.Reference()))
|
||||||
}
|
}
|
||||||
unparsedImage = nil
|
|
||||||
defer func() {
|
|
||||||
if err := src.Close(); err != nil {
|
|
||||||
retErr = errors.Wrapf(retErr, " (source: %v)", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := checkImageDestinationForCurrentRuntimeOS(src, dest); err != nil {
|
if err := checkImageDestinationForCurrentRuntimeOS(options.DestinationCtx, src, c.dest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if src.IsMultiImage() {
|
|
||||||
return errors.Errorf("can not copy %s: manifest contains multiple images", transports.ImageName(srcRef))
|
|
||||||
}
|
|
||||||
|
|
||||||
var sigs [][]byte
|
var sigs [][]byte
|
||||||
if options.RemoveSignatures {
|
if options.RemoveSignatures {
|
||||||
sigs = [][]byte{}
|
sigs = [][]byte{}
|
||||||
} else {
|
} else {
|
||||||
writeReport("Getting image source signatures\n")
|
c.Printf("Getting image source signatures\n")
|
||||||
s, err := src.Signatures(context.TODO())
|
s, err := src.Signatures(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Error reading signatures")
|
return errors.Wrap(err, "Error reading signatures")
|
||||||
|
@ -177,41 +224,33 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
|
||||||
sigs = s
|
sigs = s
|
||||||
}
|
}
|
||||||
if len(sigs) != 0 {
|
if len(sigs) != 0 {
|
||||||
writeReport("Checking if image destination supports signatures\n")
|
c.Printf("Checking if image destination supports signatures\n")
|
||||||
if err := dest.SupportsSignatures(); err != nil {
|
if err := c.dest.SupportsSignatures(); err != nil {
|
||||||
return errors.Wrap(err, "Can not copy signatures")
|
return errors.Wrap(err, "Can not copy signatures")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canModifyManifest := len(sigs) == 0
|
ic := imageCopier{
|
||||||
manifestUpdates := types.ManifestUpdateOptions{}
|
c: c,
|
||||||
manifestUpdates.InformationOnly.Destination = dest
|
manifestUpdates: &types.ManifestUpdateOptions{InformationOnly: types.ManifestUpdateInformation{Destination: c.dest}},
|
||||||
|
src: src,
|
||||||
|
// diffIDsAreNeeded is computed later
|
||||||
|
canModifyManifest: len(sigs) == 0,
|
||||||
|
}
|
||||||
|
|
||||||
if err := updateEmbeddedDockerReference(&manifestUpdates, dest, src, canModifyManifest); err != nil {
|
if err := ic.updateEmbeddedDockerReference(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// We compute preferredManifestMIMEType only to show it in error messages.
|
// We compute preferredManifestMIMEType only to show it in error messages.
|
||||||
// Without having to add this context in an error message, we would be happy enough to know only that no conversion is needed.
|
// Without having to add this context in an error message, we would be happy enough to know only that no conversion is needed.
|
||||||
preferredManifestMIMEType, otherManifestMIMETypeCandidates, err := determineManifestConversion(&manifestUpdates, src, dest.SupportedManifestMIMETypes(), canModifyManifest)
|
preferredManifestMIMEType, otherManifestMIMETypeCandidates, err := ic.determineManifestConversion(c.dest.SupportedManifestMIMETypes(), options.ForceManifestMIMEType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If src.UpdatedImageNeedsLayerDiffIDs(manifestUpdates) will be true, it needs to be true by the time we get here.
|
// If src.UpdatedImageNeedsLayerDiffIDs(ic.manifestUpdates) will be true, it needs to be true by the time we get here.
|
||||||
ic := imageCopier{
|
ic.diffIDsAreNeeded = src.UpdatedImageNeedsLayerDiffIDs(*ic.manifestUpdates)
|
||||||
copiedBlobs: make(map[digest.Digest]digest.Digest),
|
|
||||||
cachedDiffIDs: make(map[digest.Digest]digest.Digest),
|
|
||||||
manifestUpdates: &manifestUpdates,
|
|
||||||
dest: dest,
|
|
||||||
src: src,
|
|
||||||
rawSource: rawSource,
|
|
||||||
diffIDsAreNeeded: src.UpdatedImageNeedsLayerDiffIDs(manifestUpdates),
|
|
||||||
canModifyManifest: canModifyManifest,
|
|
||||||
reportWriter: reportWriter,
|
|
||||||
progressInterval: options.ProgressInterval,
|
|
||||||
progress: options.Progress,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ic.copyLayers(); err != nil {
|
if err := ic.copyLayers(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -233,9 +272,9 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
|
||||||
}
|
}
|
||||||
// If the original MIME type is acceptable, determineManifestConversion always uses it as preferredManifestMIMEType.
|
// If the original MIME type is acceptable, determineManifestConversion always uses it as preferredManifestMIMEType.
|
||||||
// So if we are here, we will definitely be trying to convert the manifest.
|
// So if we are here, we will definitely be trying to convert the manifest.
|
||||||
// With !canModifyManifest, that would just be a string of repeated failures for the same reason,
|
// With !ic.canModifyManifest, that would just be a string of repeated failures for the same reason,
|
||||||
// so let’s bail out early and with a better error message.
|
// so let’s bail out early and with a better error message.
|
||||||
if !canModifyManifest {
|
if !ic.canModifyManifest {
|
||||||
return errors.Wrap(err, "Writing manifest failed (and converting it is not possible)")
|
return errors.Wrap(err, "Writing manifest failed (and converting it is not possible)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +282,7 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
|
||||||
errs := []string{fmt.Sprintf("%s(%v)", preferredManifestMIMEType, err)}
|
errs := []string{fmt.Sprintf("%s(%v)", preferredManifestMIMEType, err)}
|
||||||
for _, manifestMIMEType := range otherManifestMIMETypeCandidates {
|
for _, manifestMIMEType := range otherManifestMIMETypeCandidates {
|
||||||
logrus.Debugf("Trying to use manifest type %s…", manifestMIMEType)
|
logrus.Debugf("Trying to use manifest type %s…", manifestMIMEType)
|
||||||
manifestUpdates.ManifestMIMEType = manifestMIMEType
|
ic.manifestUpdates.ManifestMIMEType = manifestMIMEType
|
||||||
attemptedManifest, err := ic.copyUpdatedConfigAndManifest()
|
attemptedManifest, err := ic.copyUpdatedConfigAndManifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Upload of manifest type %s failed: %v", manifestMIMEType, err)
|
logrus.Debugf("Upload of manifest type %s failed: %v", manifestMIMEType, err)
|
||||||
|
@ -262,35 +301,44 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.SignBy != "" {
|
if options.SignBy != "" {
|
||||||
newSig, err := createSignature(dest, manifest, options.SignBy, reportWriter)
|
newSig, err := c.createSignature(manifest, options.SignBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sigs = append(sigs, newSig)
|
sigs = append(sigs, newSig)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeReport("Storing signatures\n")
|
c.Printf("Storing signatures\n")
|
||||||
if err := dest.PutSignatures(sigs); err != nil {
|
if err := c.dest.PutSignatures(sigs); err != nil {
|
||||||
return errors.Wrap(err, "Error writing signatures")
|
return errors.Wrap(err, "Error writing signatures")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dest.Commit(); err != nil {
|
|
||||||
return errors.Wrap(err, "Error committing the finished image")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkImageDestinationForCurrentRuntimeOS(src types.Image, dest types.ImageDestination) error {
|
// Printf writes a formatted string to c.reportWriter.
|
||||||
|
// Note that the method name Printf is not entirely arbitrary: (go tool vet)
|
||||||
|
// has a built-in list of functions/methods (whatever object they are for)
|
||||||
|
// which have their format strings checked; for other names we would have
|
||||||
|
// to pass a parameter to every (go tool vet) invocation.
|
||||||
|
func (c *copier) Printf(format string, a ...interface{}) {
|
||||||
|
fmt.Fprintf(c.reportWriter, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkImageDestinationForCurrentRuntimeOS(ctx *types.SystemContext, src types.Image, dest types.ImageDestination) error {
|
||||||
if dest.MustMatchRuntimeOS() {
|
if dest.MustMatchRuntimeOS() {
|
||||||
|
wantedOS := runtime.GOOS
|
||||||
|
if ctx != nil && ctx.OSChoice != "" {
|
||||||
|
wantedOS = ctx.OSChoice
|
||||||
|
}
|
||||||
c, err := src.OCIConfig()
|
c, err := src.OCIConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Error parsing image configuration")
|
return errors.Wrapf(err, "Error parsing image configuration")
|
||||||
}
|
}
|
||||||
osErr := fmt.Errorf("image operating system %q cannot be used on %q", c.OS, runtime.GOOS)
|
osErr := fmt.Errorf("image operating system %q cannot be used on %q", c.OS, wantedOS)
|
||||||
if runtime.GOOS == "windows" && c.OS == "linux" {
|
if wantedOS == "windows" && c.OS == "linux" {
|
||||||
return osErr
|
return osErr
|
||||||
} else if runtime.GOOS != "windows" && c.OS == "windows" {
|
} else if wantedOS != "windows" && c.OS == "windows" {
|
||||||
return osErr
|
return osErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,35 +346,44 @@ func checkImageDestinationForCurrentRuntimeOS(src types.Image, dest types.ImageD
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateEmbeddedDockerReference handles the Docker reference embedded in Docker schema1 manifests.
|
// updateEmbeddedDockerReference handles the Docker reference embedded in Docker schema1 manifests.
|
||||||
func updateEmbeddedDockerReference(manifestUpdates *types.ManifestUpdateOptions, dest types.ImageDestination, src types.Image, canModifyManifest bool) error {
|
func (ic *imageCopier) updateEmbeddedDockerReference() error {
|
||||||
destRef := dest.Reference().DockerReference()
|
destRef := ic.c.dest.Reference().DockerReference()
|
||||||
if destRef == nil {
|
if destRef == nil {
|
||||||
return nil // Destination does not care about Docker references
|
return nil // Destination does not care about Docker references
|
||||||
}
|
}
|
||||||
if !src.EmbeddedDockerReferenceConflicts(destRef) {
|
if !ic.src.EmbeddedDockerReferenceConflicts(destRef) {
|
||||||
return nil // No reference embedded in the manifest, or it matches destRef already.
|
return nil // No reference embedded in the manifest, or it matches destRef already.
|
||||||
}
|
}
|
||||||
|
|
||||||
if !canModifyManifest {
|
if !ic.canModifyManifest {
|
||||||
return errors.Errorf("Copying a schema1 image with an embedded Docker reference to %s (Docker reference %s) would invalidate existing signatures. Explicitly enable signature removal to proceed anyway",
|
return errors.Errorf("Copying a schema1 image with an embedded Docker reference to %s (Docker reference %s) would invalidate existing signatures. Explicitly enable signature removal to proceed anyway",
|
||||||
transports.ImageName(dest.Reference()), destRef.String())
|
transports.ImageName(ic.c.dest.Reference()), destRef.String())
|
||||||
}
|
}
|
||||||
manifestUpdates.EmbeddedDockerReference = destRef
|
ic.manifestUpdates.EmbeddedDockerReference = destRef
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyLayers copies layers from src/rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest.
|
// copyLayers copies layers from ic.src/ic.c.rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest.
|
||||||
func (ic *imageCopier) copyLayers() error {
|
func (ic *imageCopier) copyLayers() error {
|
||||||
srcInfos := ic.src.LayerInfos()
|
srcInfos := ic.src.LayerInfos()
|
||||||
destInfos := []types.BlobInfo{}
|
destInfos := []types.BlobInfo{}
|
||||||
diffIDs := []digest.Digest{}
|
diffIDs := []digest.Digest{}
|
||||||
|
updatedSrcInfos := ic.src.LayerInfosForCopy()
|
||||||
|
srcInfosUpdated := false
|
||||||
|
if updatedSrcInfos != nil && !reflect.DeepEqual(srcInfos, updatedSrcInfos) {
|
||||||
|
if !ic.canModifyManifest {
|
||||||
|
return errors.Errorf("Internal error: copyLayers() needs to use an updated manifest but that was known to be forbidden")
|
||||||
|
}
|
||||||
|
srcInfos = updatedSrcInfos
|
||||||
|
srcInfosUpdated = true
|
||||||
|
}
|
||||||
for _, srcLayer := range srcInfos {
|
for _, srcLayer := range srcInfos {
|
||||||
var (
|
var (
|
||||||
destInfo types.BlobInfo
|
destInfo types.BlobInfo
|
||||||
diffID digest.Digest
|
diffID digest.Digest
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if ic.dest.AcceptsForeignLayerURLs() && len(srcLayer.URLs) != 0 {
|
if ic.c.dest.AcceptsForeignLayerURLs() && len(srcLayer.URLs) != 0 {
|
||||||
// DiffIDs are, currently, needed only when converting from schema1.
|
// DiffIDs are, currently, needed only when converting from schema1.
|
||||||
// In which case src.LayerInfos will not have URLs because schema1
|
// In which case src.LayerInfos will not have URLs because schema1
|
||||||
// does not support them.
|
// does not support them.
|
||||||
|
@ -334,7 +391,7 @@ func (ic *imageCopier) copyLayers() error {
|
||||||
return errors.New("getting DiffID for foreign layers is unimplemented")
|
return errors.New("getting DiffID for foreign layers is unimplemented")
|
||||||
}
|
}
|
||||||
destInfo = srcLayer
|
destInfo = srcLayer
|
||||||
fmt.Fprintf(ic.reportWriter, "Skipping foreign layer %q copy to %s\n", destInfo.Digest, ic.dest.Reference().Transport().Name())
|
ic.c.Printf("Skipping foreign layer %q copy to %s\n", destInfo.Digest, ic.c.dest.Reference().Transport().Name())
|
||||||
} else {
|
} else {
|
||||||
destInfo, diffID, err = ic.copyLayer(srcLayer)
|
destInfo, diffID, err = ic.copyLayer(srcLayer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -348,7 +405,7 @@ func (ic *imageCopier) copyLayers() error {
|
||||||
if ic.diffIDsAreNeeded {
|
if ic.diffIDsAreNeeded {
|
||||||
ic.manifestUpdates.InformationOnly.LayerDiffIDs = diffIDs
|
ic.manifestUpdates.InformationOnly.LayerDiffIDs = diffIDs
|
||||||
}
|
}
|
||||||
if layerDigestsDiffer(srcInfos, destInfos) {
|
if srcInfosUpdated || layerDigestsDiffer(srcInfos, destInfos) {
|
||||||
ic.manifestUpdates.LayerInfos = destInfos
|
ic.manifestUpdates.LayerInfos = destInfos
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -379,7 +436,7 @@ func (ic *imageCopier) copyUpdatedConfigAndManifest() ([]byte, error) {
|
||||||
// We have set ic.diffIDsAreNeeded based on the preferred MIME type returned by determineManifestConversion.
|
// We have set ic.diffIDsAreNeeded based on the preferred MIME type returned by determineManifestConversion.
|
||||||
// So, this can only happen if we are trying to upload using one of the other MIME type candidates.
|
// So, this can only happen if we are trying to upload using one of the other MIME type candidates.
|
||||||
// Because UpdatedImageNeedsLayerDiffIDs is true only when converting from s1 to s2, this case should only arise
|
// Because UpdatedImageNeedsLayerDiffIDs is true only when converting from s1 to s2, this case should only arise
|
||||||
// when ic.dest.SupportedManifestMIMETypes() includes both s1 and s2, the upload using s1 failed, and we are now trying s2.
|
// when ic.c.dest.SupportedManifestMIMETypes() includes both s1 and s2, the upload using s1 failed, and we are now trying s2.
|
||||||
// Supposedly s2-only registries do not exist or are extremely rare, so failing with this error message is good enough for now.
|
// Supposedly s2-only registries do not exist or are extremely rare, so failing with this error message is good enough for now.
|
||||||
// If handling such registries turns out to be necessary, we could compute ic.diffIDsAreNeeded based on the full list of manifest MIME type candidates.
|
// If handling such registries turns out to be necessary, we could compute ic.diffIDsAreNeeded based on the full list of manifest MIME type candidates.
|
||||||
return nil, errors.Errorf("Can not convert image to %s, preparing DiffIDs for this case is not supported", ic.manifestUpdates.ManifestMIMEType)
|
return nil, errors.Errorf("Can not convert image to %s, preparing DiffIDs for this case is not supported", ic.manifestUpdates.ManifestMIMEType)
|
||||||
|
@ -395,27 +452,27 @@ func (ic *imageCopier) copyUpdatedConfigAndManifest() ([]byte, error) {
|
||||||
return nil, errors.Wrap(err, "Error reading manifest")
|
return nil, errors.Wrap(err, "Error reading manifest")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ic.copyConfig(pendingImage); err != nil {
|
if err := ic.c.copyConfig(pendingImage); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(ic.reportWriter, "Writing manifest to image destination\n")
|
ic.c.Printf("Writing manifest to image destination\n")
|
||||||
if err := ic.dest.PutManifest(manifest); err != nil {
|
if err := ic.c.dest.PutManifest(manifest); err != nil {
|
||||||
return nil, errors.Wrap(err, "Error writing manifest")
|
return nil, errors.Wrap(err, "Error writing manifest")
|
||||||
}
|
}
|
||||||
return manifest, nil
|
return manifest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyConfig copies config.json, if any, from src to dest.
|
// copyConfig copies config.json, if any, from src to dest.
|
||||||
func (ic *imageCopier) copyConfig(src types.Image) error {
|
func (c *copier) copyConfig(src types.Image) error {
|
||||||
srcInfo := src.ConfigInfo()
|
srcInfo := src.ConfigInfo()
|
||||||
if srcInfo.Digest != "" {
|
if srcInfo.Digest != "" {
|
||||||
fmt.Fprintf(ic.reportWriter, "Copying config %s\n", srcInfo.Digest)
|
c.Printf("Copying config %s\n", srcInfo.Digest)
|
||||||
configBlob, err := src.ConfigBlob()
|
configBlob, err := src.ConfigBlob()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Error reading config blob %s", srcInfo.Digest)
|
return errors.Wrapf(err, "Error reading config blob %s", srcInfo.Digest)
|
||||||
}
|
}
|
||||||
destInfo, err := ic.copyBlobFromStream(bytes.NewReader(configBlob), srcInfo, nil, false)
|
destInfo, err := c.copyBlobFromStream(bytes.NewReader(configBlob), srcInfo, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -437,12 +494,12 @@ type diffIDResult struct {
|
||||||
// and returns a complete blobInfo of the copied layer, and a value for LayerDiffIDs if diffIDIsNeeded
|
// and returns a complete blobInfo of the copied layer, and a value for LayerDiffIDs if diffIDIsNeeded
|
||||||
func (ic *imageCopier) copyLayer(srcInfo types.BlobInfo) (types.BlobInfo, digest.Digest, error) {
|
func (ic *imageCopier) copyLayer(srcInfo types.BlobInfo) (types.BlobInfo, digest.Digest, error) {
|
||||||
// Check if we already have a blob with this digest
|
// Check if we already have a blob with this digest
|
||||||
haveBlob, extantBlobSize, err := ic.dest.HasBlob(srcInfo)
|
haveBlob, extantBlobSize, err := ic.c.dest.HasBlob(srcInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.BlobInfo{}, "", errors.Wrapf(err, "Error checking for blob %s at destination", srcInfo.Digest)
|
return types.BlobInfo{}, "", errors.Wrapf(err, "Error checking for blob %s at destination", srcInfo.Digest)
|
||||||
}
|
}
|
||||||
// If we already have a cached diffID for this blob, we don't need to compute it
|
// If we already have a cached diffID for this blob, we don't need to compute it
|
||||||
diffIDIsNeeded := ic.diffIDsAreNeeded && (ic.cachedDiffIDs[srcInfo.Digest] == "")
|
diffIDIsNeeded := ic.diffIDsAreNeeded && (ic.c.cachedDiffIDs[srcInfo.Digest] == "")
|
||||||
// If we already have the blob, and we don't need to recompute the diffID, then we might be able to avoid reading it again
|
// If we already have the blob, and we don't need to recompute the diffID, then we might be able to avoid reading it again
|
||||||
if haveBlob && !diffIDIsNeeded {
|
if haveBlob && !diffIDIsNeeded {
|
||||||
// Check the blob sizes match, if we were given a size this time
|
// Check the blob sizes match, if we were given a size this time
|
||||||
|
@ -451,17 +508,17 @@ func (ic *imageCopier) copyLayer(srcInfo types.BlobInfo) (types.BlobInfo, digest
|
||||||
}
|
}
|
||||||
srcInfo.Size = extantBlobSize
|
srcInfo.Size = extantBlobSize
|
||||||
// Tell the image destination that this blob's delta is being applied again. For some image destinations, this can be faster than using GetBlob/PutBlob
|
// Tell the image destination that this blob's delta is being applied again. For some image destinations, this can be faster than using GetBlob/PutBlob
|
||||||
blobinfo, err := ic.dest.ReapplyBlob(srcInfo)
|
blobinfo, err := ic.c.dest.ReapplyBlob(srcInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.BlobInfo{}, "", errors.Wrapf(err, "Error reapplying blob %s at destination", srcInfo.Digest)
|
return types.BlobInfo{}, "", errors.Wrapf(err, "Error reapplying blob %s at destination", srcInfo.Digest)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(ic.reportWriter, "Skipping fetch of repeat blob %s\n", srcInfo.Digest)
|
ic.c.Printf("Skipping fetch of repeat blob %s\n", srcInfo.Digest)
|
||||||
return blobinfo, ic.cachedDiffIDs[srcInfo.Digest], err
|
return blobinfo, ic.c.cachedDiffIDs[srcInfo.Digest], err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: copy the layer, computing the diffID if we need to do so
|
// Fallback: copy the layer, computing the diffID if we need to do so
|
||||||
fmt.Fprintf(ic.reportWriter, "Copying blob %s\n", srcInfo.Digest)
|
ic.c.Printf("Copying blob %s\n", srcInfo.Digest)
|
||||||
srcStream, srcBlobSize, err := ic.rawSource.GetBlob(srcInfo)
|
srcStream, srcBlobSize, err := ic.c.rawSource.GetBlob(srcInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.BlobInfo{}, "", errors.Wrapf(err, "Error reading blob %s", srcInfo.Digest)
|
return types.BlobInfo{}, "", errors.Wrapf(err, "Error reading blob %s", srcInfo.Digest)
|
||||||
}
|
}
|
||||||
|
@ -479,7 +536,7 @@ func (ic *imageCopier) copyLayer(srcInfo types.BlobInfo) (types.BlobInfo, digest
|
||||||
return types.BlobInfo{}, "", errors.Wrap(diffIDResult.err, "Error computing layer DiffID")
|
return types.BlobInfo{}, "", errors.Wrap(diffIDResult.err, "Error computing layer DiffID")
|
||||||
}
|
}
|
||||||
logrus.Debugf("Computed DiffID %s for layer %s", diffIDResult.digest, srcInfo.Digest)
|
logrus.Debugf("Computed DiffID %s for layer %s", diffIDResult.digest, srcInfo.Digest)
|
||||||
ic.cachedDiffIDs[srcInfo.Digest] = diffIDResult.digest
|
ic.c.cachedDiffIDs[srcInfo.Digest] = diffIDResult.digest
|
||||||
}
|
}
|
||||||
return blobInfo, diffIDResult.digest, nil
|
return blobInfo, diffIDResult.digest, nil
|
||||||
}
|
}
|
||||||
|
@ -513,7 +570,7 @@ func (ic *imageCopier) copyLayerFromStream(srcStream io.Reader, srcInfo types.Bl
|
||||||
return pipeWriter
|
return pipeWriter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
blobInfo, err := ic.copyBlobFromStream(srcStream, srcInfo, getDiffIDRecorder, ic.canModifyManifest) // Sets err to nil on success
|
blobInfo, err := ic.c.copyBlobFromStream(srcStream, srcInfo, getDiffIDRecorder, ic.canModifyManifest) // Sets err to nil on success
|
||||||
return blobInfo, diffIDChan, err
|
return blobInfo, diffIDChan, err
|
||||||
// We need the defer … pipeWriter.CloseWithError() to happen HERE so that the caller can block on reading from diffIDChan
|
// We need the defer … pipeWriter.CloseWithError() to happen HERE so that the caller can block on reading from diffIDChan
|
||||||
}
|
}
|
||||||
|
@ -547,7 +604,7 @@ func computeDiffID(stream io.Reader, decompressor compression.DecompressorFunc)
|
||||||
// perhaps sending a copy to an io.Writer if getOriginalLayerCopyWriter != nil,
|
// perhaps sending a copy to an io.Writer if getOriginalLayerCopyWriter != nil,
|
||||||
// perhaps compressing it if canCompress,
|
// perhaps compressing it if canCompress,
|
||||||
// and returns a complete blobInfo of the copied blob.
|
// and returns a complete blobInfo of the copied blob.
|
||||||
func (ic *imageCopier) copyBlobFromStream(srcStream io.Reader, srcInfo types.BlobInfo,
|
func (c *copier) copyBlobFromStream(srcStream io.Reader, srcInfo types.BlobInfo,
|
||||||
getOriginalLayerCopyWriter func(decompressor compression.DecompressorFunc) io.Writer,
|
getOriginalLayerCopyWriter func(decompressor compression.DecompressorFunc) io.Writer,
|
||||||
canCompress bool) (types.BlobInfo, error) {
|
canCompress bool) (types.BlobInfo, error) {
|
||||||
// The copying happens through a pipeline of connected io.Readers.
|
// The copying happens through a pipeline of connected io.Readers.
|
||||||
|
@ -575,7 +632,7 @@ func (ic *imageCopier) copyBlobFromStream(srcStream io.Reader, srcInfo types.Blo
|
||||||
|
|
||||||
// === Report progress using a pb.Reader.
|
// === Report progress using a pb.Reader.
|
||||||
bar := pb.New(int(srcInfo.Size)).SetUnits(pb.U_BYTES)
|
bar := pb.New(int(srcInfo.Size)).SetUnits(pb.U_BYTES)
|
||||||
bar.Output = ic.reportWriter
|
bar.Output = c.reportWriter
|
||||||
bar.SetMaxWidth(80)
|
bar.SetMaxWidth(80)
|
||||||
bar.ShowTimeLeft = false
|
bar.ShowTimeLeft = false
|
||||||
bar.ShowPercent = false
|
bar.ShowPercent = false
|
||||||
|
@ -592,7 +649,7 @@ func (ic *imageCopier) copyBlobFromStream(srcStream io.Reader, srcInfo types.Blo
|
||||||
|
|
||||||
// === Compress the layer if it is uncompressed and compression is desired
|
// === Compress the layer if it is uncompressed and compression is desired
|
||||||
var inputInfo types.BlobInfo
|
var inputInfo types.BlobInfo
|
||||||
if !canCompress || isCompressed || !ic.dest.ShouldCompressLayers() {
|
if !canCompress || isCompressed || !c.dest.ShouldCompressLayers() {
|
||||||
logrus.Debugf("Using original blob without modification")
|
logrus.Debugf("Using original blob without modification")
|
||||||
inputInfo = srcInfo
|
inputInfo = srcInfo
|
||||||
} else {
|
} else {
|
||||||
|
@ -609,19 +666,19 @@ func (ic *imageCopier) copyBlobFromStream(srcStream io.Reader, srcInfo types.Blo
|
||||||
inputInfo.Size = -1
|
inputInfo.Size = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Report progress using the ic.progress channel, if required.
|
// === Report progress using the c.progress channel, if required.
|
||||||
if ic.progress != nil && ic.progressInterval > 0 {
|
if c.progress != nil && c.progressInterval > 0 {
|
||||||
destStream = &progressReader{
|
destStream = &progressReader{
|
||||||
source: destStream,
|
source: destStream,
|
||||||
channel: ic.progress,
|
channel: c.progress,
|
||||||
interval: ic.progressInterval,
|
interval: c.progressInterval,
|
||||||
artifact: srcInfo,
|
artifact: srcInfo,
|
||||||
lastTime: time.Now(),
|
lastTime: time.Now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Finally, send the layer stream to dest.
|
// === Finally, send the layer stream to dest.
|
||||||
uploadedInfo, err := ic.dest.PutBlob(destStream, inputInfo)
|
uploadedInfo, err := c.dest.PutBlob(destStream, inputInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.BlobInfo{}, errors.Wrap(err, "Error writing blob")
|
return types.BlobInfo{}, errors.Wrap(err, "Error writing blob")
|
||||||
}
|
}
|
||||||
|
|
29
vendor/github.com/containers/image/copy/manifest.go
generated
vendored
29
vendor/github.com/containers/image/copy/manifest.go
generated
vendored
|
@ -37,16 +37,20 @@ func (os *orderedSet) append(s string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// determineManifestConversion updates manifestUpdates to convert manifest to a supported MIME type, if necessary and canModifyManifest.
|
// determineManifestConversion updates ic.manifestUpdates to convert manifest to a supported MIME type, if necessary and ic.canModifyManifest.
|
||||||
// Note that the conversion will only happen later, through src.UpdatedImage
|
// Note that the conversion will only happen later, through ic.src.UpdatedImage
|
||||||
// Returns the preferred manifest MIME type (whether we are converting to it or using it unmodified),
|
// Returns the preferred manifest MIME type (whether we are converting to it or using it unmodified),
|
||||||
// and a list of other possible alternatives, in order.
|
// and a list of other possible alternatives, in order.
|
||||||
func determineManifestConversion(manifestUpdates *types.ManifestUpdateOptions, src types.Image, destSupportedManifestMIMETypes []string, canModifyManifest bool) (string, []string, error) {
|
func (ic *imageCopier) determineManifestConversion(destSupportedManifestMIMETypes []string, forceManifestMIMEType string) (string, []string, error) {
|
||||||
_, srcType, err := src.Manifest()
|
_, srcType, err := ic.src.Manifest()
|
||||||
if err != nil { // This should have been cached?!
|
if err != nil { // This should have been cached?!
|
||||||
return "", nil, errors.Wrap(err, "Error reading manifest")
|
return "", nil, errors.Wrap(err, "Error reading manifest")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if forceManifestMIMEType != "" {
|
||||||
|
destSupportedManifestMIMETypes = []string{forceManifestMIMEType}
|
||||||
|
}
|
||||||
|
|
||||||
if len(destSupportedManifestMIMETypes) == 0 {
|
if len(destSupportedManifestMIMETypes) == 0 {
|
||||||
return srcType, []string{}, nil // Anything goes; just use the original as is, do not try any conversions.
|
return srcType, []string{}, nil // Anything goes; just use the original as is, do not try any conversions.
|
||||||
}
|
}
|
||||||
|
@ -67,10 +71,10 @@ func determineManifestConversion(manifestUpdates *types.ManifestUpdateOptions, s
|
||||||
if _, ok := supportedByDest[srcType]; ok {
|
if _, ok := supportedByDest[srcType]; ok {
|
||||||
prioritizedTypes.append(srcType)
|
prioritizedTypes.append(srcType)
|
||||||
}
|
}
|
||||||
if !canModifyManifest {
|
if !ic.canModifyManifest {
|
||||||
// We could also drop the !canModifyManifest parameter and have the caller
|
// We could also drop the !ic.canModifyManifest check and have the caller
|
||||||
// make the choice; it is already doing that to an extent, to improve error
|
// make the choice; it is already doing that to an extent, to improve error
|
||||||
// messages. But it is nice to hide the “if !canModifyManifest, do no conversion”
|
// messages. But it is nice to hide the “if !ic.canModifyManifest, do no conversion”
|
||||||
// special case in here; the caller can then worry (or not) only about a good UI.
|
// special case in here; the caller can then worry (or not) only about a good UI.
|
||||||
logrus.Debugf("We can't modify the manifest, hoping for the best...")
|
logrus.Debugf("We can't modify the manifest, hoping for the best...")
|
||||||
return srcType, []string{}, nil // Take our chances - FIXME? Or should we fail without trying?
|
return srcType, []string{}, nil // Take our chances - FIXME? Or should we fail without trying?
|
||||||
|
@ -94,9 +98,18 @@ func determineManifestConversion(manifestUpdates *types.ManifestUpdateOptions, s
|
||||||
}
|
}
|
||||||
preferredType := prioritizedTypes.list[0]
|
preferredType := prioritizedTypes.list[0]
|
||||||
if preferredType != srcType {
|
if preferredType != srcType {
|
||||||
manifestUpdates.ManifestMIMEType = preferredType
|
ic.manifestUpdates.ManifestMIMEType = preferredType
|
||||||
} else {
|
} else {
|
||||||
logrus.Debugf("... will first try using the original manifest unmodified")
|
logrus.Debugf("... will first try using the original manifest unmodified")
|
||||||
}
|
}
|
||||||
return preferredType, prioritizedTypes.list[1:], nil
|
return preferredType, prioritizedTypes.list[1:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isMultiImage returns true if img is a list of images
|
||||||
|
func isMultiImage(img types.UnparsedImage) (bool, error) {
|
||||||
|
_, mt, err := img.Manifest()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return manifest.MIMETypeIsMultiImage(mt), nil
|
||||||
|
}
|
||||||
|
|
14
vendor/github.com/containers/image/copy/sign.go
generated
vendored
14
vendor/github.com/containers/image/copy/sign.go
generated
vendored
|
@ -1,17 +1,13 @@
|
||||||
package copy
|
package copy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/containers/image/signature"
|
"github.com/containers/image/signature"
|
||||||
"github.com/containers/image/transports"
|
"github.com/containers/image/transports"
|
||||||
"github.com/containers/image/types"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// createSignature creates a new signature of manifest at (identified by) dest using keyIdentity.
|
// createSignature creates a new signature of manifest using keyIdentity.
|
||||||
func createSignature(dest types.ImageDestination, manifest []byte, keyIdentity string, reportWriter io.Writer) ([]byte, error) {
|
func (c *copier) createSignature(manifest []byte, keyIdentity string) ([]byte, error) {
|
||||||
mech, err := signature.NewGPGSigningMechanism()
|
mech, err := signature.NewGPGSigningMechanism()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Error initializing GPG")
|
return nil, errors.Wrap(err, "Error initializing GPG")
|
||||||
|
@ -21,12 +17,12 @@ func createSignature(dest types.ImageDestination, manifest []byte, keyIdentity s
|
||||||
return nil, errors.Wrap(err, "Signing not supported")
|
return nil, errors.Wrap(err, "Signing not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
dockerReference := dest.Reference().DockerReference()
|
dockerReference := c.dest.Reference().DockerReference()
|
||||||
if dockerReference == nil {
|
if dockerReference == nil {
|
||||||
return nil, errors.Errorf("Cannot determine canonical Docker reference for destination %s", transports.ImageName(dest.Reference()))
|
return nil, errors.Errorf("Cannot determine canonical Docker reference for destination %s", transports.ImageName(c.dest.Reference()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(reportWriter, "Signing manifest\n")
|
c.Printf("Signing manifest\n")
|
||||||
newSig, err := signature.SignDockerManifest(manifest, dockerReference.String(), mech, keyIdentity)
|
newSig, err := signature.SignDockerManifest(manifest, dockerReference.String(), mech, keyIdentity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Error creating signature")
|
return nil, errors.Wrap(err, "Error creating signature")
|
||||||
|
|
104
vendor/github.com/containers/image/directory/directory_dest.go
generated
vendored
104
vendor/github.com/containers/image/directory/directory_dest.go
generated
vendored
|
@ -4,19 +4,77 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const version = "Directory Transport Version: 1.0\n"
|
||||||
|
|
||||||
|
// ErrNotContainerImageDir indicates that the directory doesn't match the expected contents of a directory created
|
||||||
|
// using the 'dir' transport
|
||||||
|
var ErrNotContainerImageDir = errors.New("not a containers image directory, don't want to overwrite important data")
|
||||||
|
|
||||||
type dirImageDestination struct {
|
type dirImageDestination struct {
|
||||||
ref dirReference
|
ref dirReference
|
||||||
|
compress bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// newImageDestination returns an ImageDestination for writing to an existing directory.
|
// newImageDestination returns an ImageDestination for writing to a directory.
|
||||||
func newImageDestination(ref dirReference) types.ImageDestination {
|
func newImageDestination(ref dirReference, compress bool) (types.ImageDestination, error) {
|
||||||
return &dirImageDestination{ref}
|
d := &dirImageDestination{ref: ref, compress: compress}
|
||||||
|
|
||||||
|
// If directory exists check if it is empty
|
||||||
|
// if not empty, check whether the contents match that of a container image directory and overwrite the contents
|
||||||
|
// if the contents don't match throw an error
|
||||||
|
dirExists, err := pathExists(d.ref.resolvedPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error checking for path %q", d.ref.resolvedPath)
|
||||||
|
}
|
||||||
|
if dirExists {
|
||||||
|
isEmpty, err := isDirEmpty(d.ref.resolvedPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isEmpty {
|
||||||
|
versionExists, err := pathExists(d.ref.versionPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error checking if path exists %q", d.ref.versionPath())
|
||||||
|
}
|
||||||
|
if versionExists {
|
||||||
|
contents, err := ioutil.ReadFile(d.ref.versionPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// check if contents of version file is what we expect it to be
|
||||||
|
if string(contents) != version {
|
||||||
|
return nil, ErrNotContainerImageDir
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, ErrNotContainerImageDir
|
||||||
|
}
|
||||||
|
// delete directory contents so that only one image is in the directory at a time
|
||||||
|
if err = removeDirContents(d.ref.resolvedPath); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error erasing contents in %q", d.ref.resolvedPath)
|
||||||
|
}
|
||||||
|
logrus.Debugf("overwriting existing container image directory %q", d.ref.resolvedPath)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// create directory if it doesn't exist
|
||||||
|
if err := os.MkdirAll(d.ref.resolvedPath, 0755); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to create directory %q", d.ref.resolvedPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create version file
|
||||||
|
err = ioutil.WriteFile(d.ref.versionPath(), []byte(version), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error creating version file %q", d.ref.versionPath())
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
||||||
|
@ -42,7 +100,7 @@ func (d *dirImageDestination) SupportsSignatures() error {
|
||||||
|
|
||||||
// ShouldCompressLayers returns true iff it is desirable to compress layer blobs written to this destination.
|
// ShouldCompressLayers returns true iff it is desirable to compress layer blobs written to this destination.
|
||||||
func (d *dirImageDestination) ShouldCompressLayers() bool {
|
func (d *dirImageDestination) ShouldCompressLayers() bool {
|
||||||
return false
|
return d.compress
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptsForeignLayerURLs returns false iff foreign layers in manifest should be actually
|
// AcceptsForeignLayerURLs returns false iff foreign layers in manifest should be actually
|
||||||
|
@ -147,3 +205,39 @@ func (d *dirImageDestination) PutSignatures(signatures [][]byte) error {
|
||||||
func (d *dirImageDestination) Commit() error {
|
func (d *dirImageDestination) Commit() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns true if path exists
|
||||||
|
func pathExists(path string) (bool, error) {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if directory is empty
|
||||||
|
func isDirEmpty(path string) (bool, error) {
|
||||||
|
files, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return len(files) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deletes the contents of a directory
|
||||||
|
func removeDirContents(path string) error {
|
||||||
|
files, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
if err := os.RemoveAll(filepath.Join(path, file.Name())); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
25
vendor/github.com/containers/image/directory/directory_src.go
generated
vendored
25
vendor/github.com/containers/image/directory/directory_src.go
generated
vendored
|
@ -35,7 +35,12 @@ func (s *dirImageSource) Close() error {
|
||||||
|
|
||||||
// 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).
|
// 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).
|
||||||
// It may use a remote (= slow) service.
|
// It may use a remote (= slow) service.
|
||||||
func (s *dirImageSource) GetManifest() ([]byte, string, error) {
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list);
|
||||||
|
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *dirImageSource) GetManifest(instanceDigest *digest.Digest) ([]byte, string, error) {
|
||||||
|
if instanceDigest != nil {
|
||||||
|
return nil, "", errors.Errorf(`Getting target manifest not supported by "dir:"`)
|
||||||
|
}
|
||||||
m, err := ioutil.ReadFile(s.ref.manifestPath())
|
m, err := ioutil.ReadFile(s.ref.manifestPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
|
@ -43,10 +48,6 @@ func (s *dirImageSource) GetManifest() ([]byte, string, error) {
|
||||||
return m, manifest.GuessMIMEType(m), err
|
return m, manifest.GuessMIMEType(m), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *dirImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) {
|
|
||||||
return nil, "", errors.Errorf(`Getting target manifest not supported by "dir:"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
||||||
func (s *dirImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) {
|
func (s *dirImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) {
|
||||||
r, err := os.Open(s.ref.layerPath(info.Digest))
|
r, err := os.Open(s.ref.layerPath(info.Digest))
|
||||||
|
@ -60,7 +61,14 @@ func (s *dirImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, err
|
||||||
return r, fi.Size(), nil
|
return r, fi.Size(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *dirImageSource) GetSignatures(ctx context.Context) ([][]byte, error) {
|
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||||
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve signatures for
|
||||||
|
// (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list
|
||||||
|
// (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *dirImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||||
|
if instanceDigest != nil {
|
||||||
|
return nil, errors.Errorf(`Manifests lists are not supported by "dir:"`)
|
||||||
|
}
|
||||||
signatures := [][]byte{}
|
signatures := [][]byte{}
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
signature, err := ioutil.ReadFile(s.ref.signaturePath(i))
|
signature, err := ioutil.ReadFile(s.ref.signaturePath(i))
|
||||||
|
@ -74,3 +82,8 @@ func (s *dirImageSource) GetSignatures(ctx context.Context) ([][]byte, error) {
|
||||||
}
|
}
|
||||||
return signatures, nil
|
return signatures, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerInfosForCopy() returns updated layer info that should be used when copying, in preference to values in the manifest, if specified.
|
||||||
|
func (s *dirImageSource) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
20
vendor/github.com/containers/image/directory/directory_transport.go
generated
vendored
20
vendor/github.com/containers/image/directory/directory_transport.go
generated
vendored
|
@ -134,13 +134,14 @@ func (ref dirReference) PolicyConfigurationNamespaces() []string {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
// The caller must call .Close() on the returned Image.
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
|
// 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.
|
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
|
||||||
func (ref dirReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||||
|
func (ref dirReference) NewImage(ctx *types.SystemContext) (types.ImageCloser, error) {
|
||||||
src := newImageSource(ref)
|
src := newImageSource(ref)
|
||||||
return image.FromSource(src)
|
return image.FromSource(ctx, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImageSource returns a types.ImageSource for this reference.
|
// NewImageSource returns a types.ImageSource for this reference.
|
||||||
|
@ -152,7 +153,11 @@ func (ref dirReference) NewImageSource(ctx *types.SystemContext) (types.ImageSou
|
||||||
// NewImageDestination returns a types.ImageDestination for this reference.
|
// NewImageDestination returns a types.ImageDestination for this reference.
|
||||||
// The caller must call .Close() on the returned ImageDestination.
|
// The caller must call .Close() on the returned ImageDestination.
|
||||||
func (ref dirReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
func (ref dirReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||||
return newImageDestination(ref), nil
|
compress := false
|
||||||
|
if ctx != nil {
|
||||||
|
compress = ctx.DirForceCompress
|
||||||
|
}
|
||||||
|
return newImageDestination(ref, compress)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteImage deletes the named image from the registry, if supported.
|
// DeleteImage deletes the named image from the registry, if supported.
|
||||||
|
@ -175,3 +180,8 @@ func (ref dirReference) layerPath(digest digest.Digest) string {
|
||||||
func (ref dirReference) signaturePath(index int) string {
|
func (ref dirReference) signaturePath(index int) string {
|
||||||
return filepath.Join(ref.path, fmt.Sprintf("signature-%d", index+1))
|
return filepath.Join(ref.path, fmt.Sprintf("signature-%d", index+1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// versionPath returns a path for the version file within a directory using our conventions.
|
||||||
|
func (ref dirReference) versionPath() string {
|
||||||
|
return filepath.Join(ref.path, "version")
|
||||||
|
}
|
||||||
|
|
5
vendor/github.com/containers/image/docker/archive/src.go
generated
vendored
5
vendor/github.com/containers/image/docker/archive/src.go
generated
vendored
|
@ -34,3 +34,8 @@ func (s *archiveImageSource) Reference() types.ImageReference {
|
||||||
func (s *archiveImageSource) Close() error {
|
func (s *archiveImageSource) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerInfosForCopy() returns updated layer info that should be used when reading, in preference to values in the manifest, if specified.
|
||||||
|
func (s *archiveImageSource) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
9
vendor/github.com/containers/image/docker/archive/transport.go
generated
vendored
9
vendor/github.com/containers/image/docker/archive/transport.go
generated
vendored
|
@ -125,13 +125,14 @@ func (ref archiveReference) PolicyConfigurationNamespaces() []string {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
// The caller must call .Close() on the returned Image.
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
|
// 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.
|
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
|
||||||
func (ref archiveReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||||
|
func (ref archiveReference) NewImage(ctx *types.SystemContext) (types.ImageCloser, error) {
|
||||||
src := newImageSource(ctx, ref)
|
src := newImageSource(ctx, ref)
|
||||||
return ctrImage.FromSource(src)
|
return ctrImage.FromSource(ctx, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImageSource returns a types.ImageSource for this reference.
|
// NewImageSource returns a types.ImageSource for this reference.
|
||||||
|
|
69
vendor/github.com/containers/image/docker/daemon/client.go
generated
vendored
Normal file
69
vendor/github.com/containers/image/docker/daemon/client.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
dockerclient "github.com/docker/docker/client"
|
||||||
|
"github.com/docker/go-connections/tlsconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The default API version to be used in case none is explicitly specified
|
||||||
|
defaultAPIVersion = "1.22"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDockerClient initializes a new API client based on the passed SystemContext.
|
||||||
|
func newDockerClient(ctx *types.SystemContext) (*dockerclient.Client, error) {
|
||||||
|
host := dockerclient.DefaultDockerHost
|
||||||
|
if ctx != nil && ctx.DockerDaemonHost != "" {
|
||||||
|
host = ctx.DockerDaemonHost
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sadly, unix:// sockets don't work transparently with dockerclient.NewClient.
|
||||||
|
// They work fine with a nil httpClient; with a non-nil httpClient, the transport’s
|
||||||
|
// TLSClientConfig must be nil (or the client will try using HTTPS over the PF_UNIX socket
|
||||||
|
// regardless of the values in the *tls.Config), and we would have to call sockets.ConfigureTransport.
|
||||||
|
//
|
||||||
|
// We don't really want to configure anything for unix:// sockets, so just pass a nil *http.Client.
|
||||||
|
proto, _, _, err := dockerclient.ParseHost(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var httpClient *http.Client
|
||||||
|
if proto != "unix" {
|
||||||
|
hc, err := tlsConfig(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
httpClient = hc
|
||||||
|
}
|
||||||
|
|
||||||
|
return dockerclient.NewClient(host, defaultAPIVersion, httpClient, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tlsConfig(ctx *types.SystemContext) (*http.Client, error) {
|
||||||
|
options := tlsconfig.Options{}
|
||||||
|
if ctx != nil && ctx.DockerDaemonInsecureSkipTLSVerify {
|
||||||
|
options.InsecureSkipVerify = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx != nil && ctx.DockerDaemonCertPath != "" {
|
||||||
|
options.CAFile = filepath.Join(ctx.DockerDaemonCertPath, "ca.pem")
|
||||||
|
options.CertFile = filepath.Join(ctx.DockerDaemonCertPath, "cert.pem")
|
||||||
|
options.KeyFile = filepath.Join(ctx.DockerDaemonCertPath, "key.pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsc, err := tlsconfig.Client(options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: tlsc,
|
||||||
|
},
|
||||||
|
CheckRedirect: dockerclient.CheckRedirect,
|
||||||
|
}, nil
|
||||||
|
}
|
29
vendor/github.com/containers/image/docker/daemon/daemon_dest.go
generated
vendored
29
vendor/github.com/containers/image/docker/daemon/daemon_dest.go
generated
vendored
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
type daemonImageDestination struct {
|
type daemonImageDestination struct {
|
||||||
ref daemonReference
|
ref daemonReference
|
||||||
|
mustMatchRuntimeOS bool
|
||||||
*tarfile.Destination // Implements most of types.ImageDestination
|
*tarfile.Destination // Implements most of types.ImageDestination
|
||||||
// For talking to imageLoadGoroutine
|
// For talking to imageLoadGoroutine
|
||||||
goroutineCancel context.CancelFunc
|
goroutineCancel context.CancelFunc
|
||||||
|
@ -24,7 +25,7 @@ type daemonImageDestination struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newImageDestination returns a types.ImageDestination for the specified image reference.
|
// newImageDestination returns a types.ImageDestination for the specified image reference.
|
||||||
func newImageDestination(systemCtx *types.SystemContext, ref daemonReference) (types.ImageDestination, error) {
|
func newImageDestination(ctx *types.SystemContext, ref daemonReference) (types.ImageDestination, error) {
|
||||||
if ref.ref == nil {
|
if ref.ref == nil {
|
||||||
return nil, errors.Errorf("Invalid destination docker-daemon:%s: a destination must be a name:tag", ref.StringWithinTransport())
|
return nil, errors.Errorf("Invalid destination docker-daemon:%s: a destination must be a name:tag", ref.StringWithinTransport())
|
||||||
}
|
}
|
||||||
|
@ -33,7 +34,12 @@ func newImageDestination(systemCtx *types.SystemContext, ref daemonReference) (t
|
||||||
return nil, errors.Errorf("Invalid destination docker-daemon:%s: a destination must be a name:tag", ref.StringWithinTransport())
|
return nil, errors.Errorf("Invalid destination docker-daemon:%s: a destination must be a name:tag", ref.StringWithinTransport())
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := client.NewClient(client.DefaultDockerHost, "1.22", nil, nil) // FIXME: overridable host
|
var mustMatchRuntimeOS = true
|
||||||
|
if ctx != nil && ctx.DockerDaemonHost != client.DefaultDockerHost {
|
||||||
|
mustMatchRuntimeOS = false
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := newDockerClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Error initializing docker engine client")
|
return nil, errors.Wrap(err, "Error initializing docker engine client")
|
||||||
}
|
}
|
||||||
|
@ -42,16 +48,17 @@ func newImageDestination(systemCtx *types.SystemContext, ref daemonReference) (t
|
||||||
// Commit() may never be called, so we may never read from this channel; so, make this buffered to allow imageLoadGoroutine to write status and terminate even if we never read it.
|
// Commit() may never be called, so we may never read from this channel; so, make this buffered to allow imageLoadGoroutine to write status and terminate even if we never read it.
|
||||||
statusChannel := make(chan error, 1)
|
statusChannel := make(chan error, 1)
|
||||||
|
|
||||||
ctx, goroutineCancel := context.WithCancel(context.Background())
|
goroutineContext, goroutineCancel := context.WithCancel(context.Background())
|
||||||
go imageLoadGoroutine(ctx, c, reader, statusChannel)
|
go imageLoadGoroutine(goroutineContext, c, reader, statusChannel)
|
||||||
|
|
||||||
return &daemonImageDestination{
|
return &daemonImageDestination{
|
||||||
ref: ref,
|
ref: ref,
|
||||||
Destination: tarfile.NewDestination(writer, namedTaggedRef),
|
mustMatchRuntimeOS: mustMatchRuntimeOS,
|
||||||
goroutineCancel: goroutineCancel,
|
Destination: tarfile.NewDestination(writer, namedTaggedRef),
|
||||||
statusChannel: statusChannel,
|
goroutineCancel: goroutineCancel,
|
||||||
writer: writer,
|
statusChannel: statusChannel,
|
||||||
committed: false,
|
writer: writer,
|
||||||
|
committed: false,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +87,7 @@ func imageLoadGoroutine(ctx context.Context, c *client.Client, reader *io.PipeRe
|
||||||
|
|
||||||
// MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise.
|
// MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise.
|
||||||
func (d *daemonImageDestination) MustMatchRuntimeOS() bool {
|
func (d *daemonImageDestination) MustMatchRuntimeOS() bool {
|
||||||
return true
|
return d.mustMatchRuntimeOS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close removes resources associated with an initialized ImageDestination, if any.
|
// Close removes resources associated with an initialized ImageDestination, if any.
|
||||||
|
|
13
vendor/github.com/containers/image/docker/daemon/daemon_src.go
generated
vendored
13
vendor/github.com/containers/image/docker/daemon/daemon_src.go
generated
vendored
|
@ -6,14 +6,12 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containers/image/docker/tarfile"
|
"github.com/containers/image/docker/tarfile"
|
||||||
|
"github.com/containers/image/internal/tmpdir"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/docker/docker/client"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
const temporaryDirectoryForBigFiles = "/var/tmp" // Do not use the system default of os.TempDir(), usually /tmp, because with systemd it could be a tmpfs.
|
|
||||||
|
|
||||||
type daemonImageSource struct {
|
type daemonImageSource struct {
|
||||||
ref daemonReference
|
ref daemonReference
|
||||||
*tarfile.Source // Implements most of types.ImageSource
|
*tarfile.Source // Implements most of types.ImageSource
|
||||||
|
@ -35,7 +33,7 @@ type layerInfo struct {
|
||||||
// is the config, and that the following len(RootFS) files are the layers, but that feels
|
// is the config, and that the following len(RootFS) files are the layers, but that feels
|
||||||
// way too brittle.)
|
// way too brittle.)
|
||||||
func newImageSource(ctx *types.SystemContext, ref daemonReference) (types.ImageSource, error) {
|
func newImageSource(ctx *types.SystemContext, ref daemonReference) (types.ImageSource, error) {
|
||||||
c, err := client.NewClient(client.DefaultDockerHost, "1.22", nil, nil) // FIXME: overridable host
|
c, err := newDockerClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Error initializing docker engine client")
|
return nil, errors.Wrap(err, "Error initializing docker engine client")
|
||||||
}
|
}
|
||||||
|
@ -48,7 +46,7 @@ func newImageSource(ctx *types.SystemContext, ref daemonReference) (types.ImageS
|
||||||
defer inputStream.Close()
|
defer inputStream.Close()
|
||||||
|
|
||||||
// FIXME: use SystemContext here.
|
// FIXME: use SystemContext here.
|
||||||
tarCopyFile, err := ioutil.TempFile(temporaryDirectoryForBigFiles, "docker-daemon-tar")
|
tarCopyFile, err := ioutil.TempFile(tmpdir.TemporaryDirectoryForBigFiles(), "docker-daemon-tar")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -83,3 +81,8 @@ func (s *daemonImageSource) Reference() types.ImageReference {
|
||||||
func (s *daemonImageSource) Close() error {
|
func (s *daemonImageSource) Close() error {
|
||||||
return os.Remove(s.tarCopyPath)
|
return os.Remove(s.tarCopyPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerInfosForCopy() returns updated layer info that should be used when reading, in preference to values in the manifest, if specified.
|
||||||
|
func (s *daemonImageSource) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
11
vendor/github.com/containers/image/docker/daemon/daemon_transport.go
generated
vendored
11
vendor/github.com/containers/image/docker/daemon/daemon_transport.go
generated
vendored
|
@ -151,14 +151,17 @@ func (ref daemonReference) PolicyConfigurationNamespaces() []string {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImage returns a types.Image for this reference.
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
// The caller must call .Close() on the returned Image.
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
func (ref daemonReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
// 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.
|
||||||
|
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||||
|
func (ref daemonReference) NewImage(ctx *types.SystemContext) (types.ImageCloser, error) {
|
||||||
src, err := newImageSource(ctx, ref)
|
src, err := newImageSource(ctx, ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return image.FromSource(src)
|
return image.FromSource(ctx, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImageSource returns a types.ImageSource for this reference.
|
// NewImageSource returns a types.ImageSource for this reference.
|
||||||
|
|
64
vendor/github.com/containers/image/docker/docker_client.go
generated
vendored
64
vendor/github.com/containers/image/docker/docker_client.go
generated
vendored
|
@ -8,7 +8,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -125,69 +124,6 @@ func dockerCertDir(ctx *types.SystemContext, hostPort string) string {
|
||||||
return filepath.Join(hostCertDir, hostPort)
|
return filepath.Join(hostCertDir, hostPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupCertificates(dir string, tlsc *tls.Config) error {
|
|
||||||
logrus.Debugf("Looking for TLS certificates and private keys in %s", dir)
|
|
||||||
fs, err := ioutil.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range fs {
|
|
||||||
fullPath := filepath.Join(dir, f.Name())
|
|
||||||
if strings.HasSuffix(f.Name(), ".crt") {
|
|
||||||
systemPool, err := tlsconfig.SystemCertPool()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "unable to get system cert pool")
|
|
||||||
}
|
|
||||||
tlsc.RootCAs = systemPool
|
|
||||||
logrus.Debugf(" crt: %s", fullPath)
|
|
||||||
data, err := ioutil.ReadFile(fullPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tlsc.RootCAs.AppendCertsFromPEM(data)
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(f.Name(), ".cert") {
|
|
||||||
certName := f.Name()
|
|
||||||
keyName := certName[:len(certName)-5] + ".key"
|
|
||||||
logrus.Debugf(" cert: %s", fullPath)
|
|
||||||
if !hasFile(fs, keyName) {
|
|
||||||
return errors.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName)
|
|
||||||
}
|
|
||||||
cert, err := tls.LoadX509KeyPair(filepath.Join(dir, certName), filepath.Join(dir, keyName))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tlsc.Certificates = append(tlsc.Certificates, cert)
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(f.Name(), ".key") {
|
|
||||||
keyName := f.Name()
|
|
||||||
certName := keyName[:len(keyName)-4] + ".cert"
|
|
||||||
logrus.Debugf(" key: %s", fullPath)
|
|
||||||
if !hasFile(fs, certName) {
|
|
||||||
return errors.Errorf("missing client certificate %s for key %s", certName, keyName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasFile(files []os.FileInfo, name string) bool {
|
|
||||||
for _, f := range files {
|
|
||||||
if f.Name() == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
|
// newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
|
||||||
// “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection)
|
// “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection)
|
||||||
func newDockerClientFromRef(ctx *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) {
|
func newDockerClientFromRef(ctx *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) {
|
||||||
|
|
10
vendor/github.com/containers/image/docker/docker_image.go
generated
vendored
10
vendor/github.com/containers/image/docker/docker_image.go
generated
vendored
|
@ -12,26 +12,26 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Image is a Docker-specific implementation of types.Image with a few extra methods
|
// Image is a Docker-specific implementation of types.ImageCloser with a few extra methods
|
||||||
// which are specific to Docker.
|
// which are specific to Docker.
|
||||||
type Image struct {
|
type Image struct {
|
||||||
types.Image
|
types.ImageCloser
|
||||||
src *dockerImageSource
|
src *dockerImageSource
|
||||||
}
|
}
|
||||||
|
|
||||||
// newImage returns a new Image interface type after setting up
|
// newImage returns a new Image interface type after setting up
|
||||||
// a client to the registry hosting the given image.
|
// a client to the registry hosting the given image.
|
||||||
// The caller must call .Close() on the returned Image.
|
// The caller must call .Close() on the returned Image.
|
||||||
func newImage(ctx *types.SystemContext, ref dockerReference) (types.Image, error) {
|
func newImage(ctx *types.SystemContext, ref dockerReference) (types.ImageCloser, error) {
|
||||||
s, err := newImageSource(ctx, ref)
|
s, err := newImageSource(ctx, ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
img, err := image.FromSource(s)
|
img, err := image.FromSource(ctx, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Image{Image: img, src: s}, nil
|
return &Image{ImageCloser: img, src: s}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SourceRefFullName returns a fully expanded name for the repository this image is in.
|
// SourceRefFullName returns a fully expanded name for the repository this image is in.
|
||||||
|
|
8
vendor/github.com/containers/image/docker/docker_image_dest.go
generated
vendored
8
vendor/github.com/containers/image/docker/docker_image_dest.go
generated
vendored
|
@ -236,7 +236,7 @@ func (d *dockerImageDestination) PutManifest(m []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusCreated {
|
if !successStatus(res.StatusCode) {
|
||||||
err = errors.Wrapf(client.HandleErrorResponse(res), "Error uploading manifest to %s", path)
|
err = errors.Wrapf(client.HandleErrorResponse(res), "Error uploading manifest to %s", path)
|
||||||
if isManifestInvalidError(errors.Cause(err)) {
|
if isManifestInvalidError(errors.Cause(err)) {
|
||||||
err = types.ManifestTypeRejectedError{Err: err}
|
err = types.ManifestTypeRejectedError{Err: err}
|
||||||
|
@ -246,6 +246,12 @@ func (d *dockerImageDestination) PutManifest(m []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// successStatus returns true if the argument is a successful HTTP response
|
||||||
|
// code (in the range 200 - 399 inclusive).
|
||||||
|
func successStatus(status int) bool {
|
||||||
|
return status >= 200 && status <= 399
|
||||||
|
}
|
||||||
|
|
||||||
// isManifestInvalidError returns true iff err from client.HandleErrorReponse is a “manifest invalid” error.
|
// isManifestInvalidError returns true iff err from client.HandleErrorReponse is a “manifest invalid” error.
|
||||||
func isManifestInvalidError(err error) bool {
|
func isManifestInvalidError(err error) bool {
|
||||||
errors, ok := err.(errcode.Errors)
|
errors, ok := err.(errcode.Errors)
|
||||||
|
|
48
vendor/github.com/containers/image/docker/docker_image_src.go
generated
vendored
48
vendor/github.com/containers/image/docker/docker_image_src.go
generated
vendored
|
@ -52,6 +52,11 @@ func (s *dockerImageSource) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerInfosForCopy() returns updated layer info that should be used when reading, in preference to values in the manifest, if specified.
|
||||||
|
func (s *dockerImageSource) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// simplifyContentType drops parameters from a HTTP media type (see https://tools.ietf.org/html/rfc7231#section-3.1.1.1)
|
// simplifyContentType drops parameters from a HTTP media type (see https://tools.ietf.org/html/rfc7231#section-3.1.1.1)
|
||||||
// Alternatively, an empty string is returned unchanged, and invalid values are "simplified" to an empty string.
|
// Alternatively, an empty string is returned unchanged, and invalid values are "simplified" to an empty string.
|
||||||
func simplifyContentType(contentType string) string {
|
func simplifyContentType(contentType string) string {
|
||||||
|
@ -67,7 +72,12 @@ func simplifyContentType(contentType string) string {
|
||||||
|
|
||||||
// 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).
|
// 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).
|
||||||
// It may use a remote (= slow) service.
|
// It may use a remote (= slow) service.
|
||||||
func (s *dockerImageSource) GetManifest() ([]byte, string, error) {
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list);
|
||||||
|
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *dockerImageSource) GetManifest(instanceDigest *digest.Digest) ([]byte, string, error) {
|
||||||
|
if instanceDigest != nil {
|
||||||
|
return s.fetchManifest(context.TODO(), instanceDigest.String())
|
||||||
|
}
|
||||||
err := s.ensureManifestIsLoaded(context.TODO())
|
err := s.ensureManifestIsLoaded(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
|
@ -94,18 +104,12 @@ func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest strin
|
||||||
return manblob, simplifyContentType(res.Header.Get("Content-Type")), nil
|
return manblob, simplifyContentType(res.Header.Get("Content-Type")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTargetManifest returns an image's manifest given a digest.
|
|
||||||
// This is mainly used to retrieve a single image's manifest out of a manifest list.
|
|
||||||
func (s *dockerImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) {
|
|
||||||
return s.fetchManifest(context.TODO(), digest.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensureManifestIsLoaded sets s.cachedManifest and s.cachedManifestMIMEType
|
// ensureManifestIsLoaded sets s.cachedManifest and s.cachedManifestMIMEType
|
||||||
//
|
//
|
||||||
// ImageSource implementations are not required or expected to do any caching,
|
// ImageSource implementations are not required or expected to do any caching,
|
||||||
// but because our signatures are “attached” to the manifest digest,
|
// but because our signatures are “attached” to the manifest digest,
|
||||||
// we need to ensure that the digest of the manifest returned by GetManifest
|
// we need to ensure that the digest of the manifest returned by GetManifest(nil)
|
||||||
// and used by GetSignatures are consistent, otherwise we would get spurious
|
// and used by GetSignatures(ctx, nil) are consistent, otherwise we would get spurious
|
||||||
// signature verification failures when pulling while a tag is being updated.
|
// signature verification failures when pulling while a tag is being updated.
|
||||||
func (s *dockerImageSource) ensureManifestIsLoaded(ctx context.Context) error {
|
func (s *dockerImageSource) ensureManifestIsLoaded(ctx context.Context) error {
|
||||||
if s.cachedManifest != nil {
|
if s.cachedManifest != nil {
|
||||||
|
@ -176,22 +180,30 @@ func (s *dockerImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64,
|
||||||
return res.Body, getBlobSize(res), nil
|
return res.Body, getBlobSize(res), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *dockerImageSource) GetSignatures(ctx context.Context) ([][]byte, error) {
|
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||||
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve signatures for
|
||||||
|
// (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list
|
||||||
|
// (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *dockerImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||||
if err := s.c.detectProperties(ctx); err != nil {
|
if err := s.c.detectProperties(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case s.c.signatureBase != nil:
|
case s.c.signatureBase != nil:
|
||||||
return s.getSignaturesFromLookaside(ctx)
|
return s.getSignaturesFromLookaside(ctx, instanceDigest)
|
||||||
case s.c.supportsSignatures:
|
case s.c.supportsSignatures:
|
||||||
return s.getSignaturesFromAPIExtension(ctx)
|
return s.getSignaturesFromAPIExtension(ctx, instanceDigest)
|
||||||
default:
|
default:
|
||||||
return [][]byte{}, nil
|
return [][]byte{}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// manifestDigest returns a digest of the manifest, either from the supplied reference or from a fetched manifest.
|
// manifestDigest returns a digest of the manifest, from instanceDigest if non-nil; or from the supplied reference,
|
||||||
func (s *dockerImageSource) manifestDigest(ctx context.Context) (digest.Digest, error) {
|
// or finally, from a fetched manifest.
|
||||||
|
func (s *dockerImageSource) manifestDigest(ctx context.Context, instanceDigest *digest.Digest) (digest.Digest, error) {
|
||||||
|
if instanceDigest != nil {
|
||||||
|
return *instanceDigest, nil
|
||||||
|
}
|
||||||
if digested, ok := s.ref.ref.(reference.Digested); ok {
|
if digested, ok := s.ref.ref.(reference.Digested); ok {
|
||||||
d := digested.Digest()
|
d := digested.Digest()
|
||||||
if d.Algorithm() == digest.Canonical {
|
if d.Algorithm() == digest.Canonical {
|
||||||
|
@ -206,8 +218,8 @@ func (s *dockerImageSource) manifestDigest(ctx context.Context) (digest.Digest,
|
||||||
|
|
||||||
// getSignaturesFromLookaside implements GetSignatures() from the lookaside location configured in s.c.signatureBase,
|
// getSignaturesFromLookaside implements GetSignatures() from the lookaside location configured in s.c.signatureBase,
|
||||||
// which is not nil.
|
// which is not nil.
|
||||||
func (s *dockerImageSource) getSignaturesFromLookaside(ctx context.Context) ([][]byte, error) {
|
func (s *dockerImageSource) getSignaturesFromLookaside(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||||
manifestDigest, err := s.manifestDigest(ctx)
|
manifestDigest, err := s.manifestDigest(ctx, instanceDigest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -276,8 +288,8 @@ func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSignaturesFromAPIExtension implements GetSignatures() using the X-Registry-Supports-Signatures API extension.
|
// getSignaturesFromAPIExtension implements GetSignatures() using the X-Registry-Supports-Signatures API extension.
|
||||||
func (s *dockerImageSource) getSignaturesFromAPIExtension(ctx context.Context) ([][]byte, error) {
|
func (s *dockerImageSource) getSignaturesFromAPIExtension(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||||
manifestDigest, err := s.manifestDigest(ctx)
|
manifestDigest, err := s.manifestDigest(ctx, instanceDigest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
7
vendor/github.com/containers/image/docker/docker_transport.go
generated
vendored
7
vendor/github.com/containers/image/docker/docker_transport.go
generated
vendored
|
@ -122,11 +122,12 @@ func (ref dockerReference) PolicyConfigurationNamespaces() []string {
|
||||||
return policyconfiguration.DockerReferenceNamespaces(ref.ref)
|
return policyconfiguration.DockerReferenceNamespaces(ref.ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
// The caller must call .Close() on the returned Image.
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
|
// 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.
|
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
|
||||||
func (ref dockerReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||||
|
func (ref dockerReference) NewImage(ctx *types.SystemContext) (types.ImageCloser, error) {
|
||||||
return newImage(ctx, ref)
|
return newImage(ctx, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
vendor/github.com/containers/image/docker/tarfile/dest.go
generated
vendored
11
vendor/github.com/containers/image/docker/tarfile/dest.go
generated
vendored
|
@ -11,6 +11,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
|
"github.com/containers/image/internal/tmpdir"
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
|
@ -18,8 +19,6 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const temporaryDirectoryForBigFiles = "/var/tmp" // Do not use the system default of os.TempDir(), usually /tmp, because with systemd it could be a tmpfs.
|
|
||||||
|
|
||||||
// Destination is a partial implementation of types.ImageDestination for writing to an io.Writer.
|
// Destination is a partial implementation of types.ImageDestination for writing to an io.Writer.
|
||||||
type Destination struct {
|
type Destination struct {
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
|
@ -107,7 +106,7 @@ func (d *Destination) PutBlob(stream io.Reader, inputInfo types.BlobInfo) (types
|
||||||
|
|
||||||
if inputInfo.Size == -1 { // Ouch, we need to stream the blob into a temporary file just to determine the size.
|
if inputInfo.Size == -1 { // Ouch, we need to stream the blob into a temporary file just to determine the size.
|
||||||
logrus.Debugf("docker tarfile: input with unknown size, streaming to disk first ...")
|
logrus.Debugf("docker tarfile: input with unknown size, streaming to disk first ...")
|
||||||
streamCopy, err := ioutil.TempFile(temporaryDirectoryForBigFiles, "docker-tarfile-blob")
|
streamCopy, err := ioutil.TempFile(tmpdir.TemporaryDirectoryForBigFiles(), "docker-tarfile-blob")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.BlobInfo{}, err
|
return types.BlobInfo{}, err
|
||||||
}
|
}
|
||||||
|
@ -168,7 +167,7 @@ func (d *Destination) ReapplyBlob(info types.BlobInfo) (types.BlobInfo, error) {
|
||||||
func (d *Destination) PutManifest(m []byte) error {
|
func (d *Destination) PutManifest(m []byte) error {
|
||||||
// We do not bother with types.ManifestTypeRejectedError; our .SupportedManifestMIMETypes() above is already providing only one alternative,
|
// We do not bother with types.ManifestTypeRejectedError; our .SupportedManifestMIMETypes() above is already providing only one alternative,
|
||||||
// so the caller trying a different manifest kind would be pointless.
|
// so the caller trying a different manifest kind would be pointless.
|
||||||
var man schema2Manifest
|
var man manifest.Schema2
|
||||||
if err := json.Unmarshal(m, &man); err != nil {
|
if err := json.Unmarshal(m, &man); err != nil {
|
||||||
return errors.Wrap(err, "Error parsing manifest")
|
return errors.Wrap(err, "Error parsing manifest")
|
||||||
}
|
}
|
||||||
|
@ -177,12 +176,12 @@ func (d *Destination) PutManifest(m []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
layerPaths := []string{}
|
layerPaths := []string{}
|
||||||
for _, l := range man.Layers {
|
for _, l := range man.LayersDescriptors {
|
||||||
layerPaths = append(layerPaths, l.Digest.String())
|
layerPaths = append(layerPaths, l.Digest.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
items := []ManifestItem{{
|
items := []ManifestItem{{
|
||||||
Config: man.Config.Digest.String(),
|
Config: man.ConfigDescriptor.Digest.String(),
|
||||||
RepoTags: []string{d.repoTag},
|
RepoTags: []string{d.repoTag},
|
||||||
Layers: layerPaths,
|
Layers: layerPaths,
|
||||||
Parent: "",
|
Parent: "",
|
||||||
|
|
46
vendor/github.com/containers/image/docker/tarfile/src.go
generated
vendored
46
vendor/github.com/containers/image/docker/tarfile/src.go
generated
vendored
|
@ -24,8 +24,8 @@ type Source struct {
|
||||||
tarManifest *ManifestItem // nil if not available yet.
|
tarManifest *ManifestItem // nil if not available yet.
|
||||||
configBytes []byte
|
configBytes []byte
|
||||||
configDigest digest.Digest
|
configDigest digest.Digest
|
||||||
orderedDiffIDList []diffID
|
orderedDiffIDList []digest.Digest
|
||||||
knownLayers map[diffID]*layerInfo
|
knownLayers map[digest.Digest]*layerInfo
|
||||||
// Other state
|
// Other state
|
||||||
generatedManifest []byte // Private cache for GetManifest(), nil if not set yet.
|
generatedManifest []byte // Private cache for GetManifest(), nil if not set yet.
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ func (s *Source) ensureCachedDataIsPresent() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var parsedConfig image // Most fields ommitted, we only care about layer DiffIDs.
|
var parsedConfig manifest.Schema2Image // There's a lot of info there, but we only really care about layer DiffIDs.
|
||||||
if err := json.Unmarshal(configBytes, &parsedConfig); err != nil {
|
if err := json.Unmarshal(configBytes, &parsedConfig); err != nil {
|
||||||
return errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config)
|
return errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config)
|
||||||
}
|
}
|
||||||
|
@ -194,12 +194,12 @@ func (s *Source) LoadTarManifest() ([]ManifestItem, error) {
|
||||||
return s.loadTarManifest()
|
return s.loadTarManifest()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) prepareLayerData(tarManifest *ManifestItem, parsedConfig *image) (map[diffID]*layerInfo, error) {
|
func (s *Source) prepareLayerData(tarManifest *ManifestItem, parsedConfig *manifest.Schema2Image) (map[digest.Digest]*layerInfo, error) {
|
||||||
// Collect layer data available in manifest and config.
|
// Collect layer data available in manifest and config.
|
||||||
if len(tarManifest.Layers) != len(parsedConfig.RootFS.DiffIDs) {
|
if len(tarManifest.Layers) != len(parsedConfig.RootFS.DiffIDs) {
|
||||||
return nil, errors.Errorf("Inconsistent layer count: %d in manifest, %d in config", len(tarManifest.Layers), len(parsedConfig.RootFS.DiffIDs))
|
return nil, errors.Errorf("Inconsistent layer count: %d in manifest, %d in config", len(tarManifest.Layers), len(parsedConfig.RootFS.DiffIDs))
|
||||||
}
|
}
|
||||||
knownLayers := map[diffID]*layerInfo{}
|
knownLayers := map[digest.Digest]*layerInfo{}
|
||||||
unknownLayerSizes := map[string]*layerInfo{} // Points into knownLayers, a "to do list" of items with unknown sizes.
|
unknownLayerSizes := map[string]*layerInfo{} // Points into knownLayers, a "to do list" of items with unknown sizes.
|
||||||
for i, diffID := range parsedConfig.RootFS.DiffIDs {
|
for i, diffID := range parsedConfig.RootFS.DiffIDs {
|
||||||
if _, ok := knownLayers[diffID]; ok {
|
if _, ok := knownLayers[diffID]; ok {
|
||||||
|
@ -249,28 +249,34 @@ func (s *Source) prepareLayerData(tarManifest *ManifestItem, parsedConfig *image
|
||||||
|
|
||||||
// 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).
|
// 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).
|
||||||
// It may use a remote (= slow) service.
|
// It may use a remote (= slow) service.
|
||||||
func (s *Source) GetManifest() ([]byte, string, error) {
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list);
|
||||||
|
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *Source) GetManifest(instanceDigest *digest.Digest) ([]byte, string, error) {
|
||||||
|
if instanceDigest != nil {
|
||||||
|
// How did we even get here? GetManifest(nil) has returned a manifest.DockerV2Schema2MediaType.
|
||||||
|
return nil, "", errors.Errorf(`Manifest lists are not supported by "docker-daemon:"`)
|
||||||
|
}
|
||||||
if s.generatedManifest == nil {
|
if s.generatedManifest == nil {
|
||||||
if err := s.ensureCachedDataIsPresent(); err != nil {
|
if err := s.ensureCachedDataIsPresent(); err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
m := schema2Manifest{
|
m := manifest.Schema2{
|
||||||
SchemaVersion: 2,
|
SchemaVersion: 2,
|
||||||
MediaType: manifest.DockerV2Schema2MediaType,
|
MediaType: manifest.DockerV2Schema2MediaType,
|
||||||
Config: distributionDescriptor{
|
ConfigDescriptor: manifest.Schema2Descriptor{
|
||||||
MediaType: manifest.DockerV2Schema2ConfigMediaType,
|
MediaType: manifest.DockerV2Schema2ConfigMediaType,
|
||||||
Size: int64(len(s.configBytes)),
|
Size: int64(len(s.configBytes)),
|
||||||
Digest: s.configDigest,
|
Digest: s.configDigest,
|
||||||
},
|
},
|
||||||
Layers: []distributionDescriptor{},
|
LayersDescriptors: []manifest.Schema2Descriptor{},
|
||||||
}
|
}
|
||||||
for _, diffID := range s.orderedDiffIDList {
|
for _, diffID := range s.orderedDiffIDList {
|
||||||
li, ok := s.knownLayers[diffID]
|
li, ok := s.knownLayers[diffID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, "", errors.Errorf("Internal inconsistency: Information about layer %s missing", diffID)
|
return nil, "", errors.Errorf("Internal inconsistency: Information about layer %s missing", diffID)
|
||||||
}
|
}
|
||||||
m.Layers = append(m.Layers, distributionDescriptor{
|
m.LayersDescriptors = append(m.LayersDescriptors, manifest.Schema2Descriptor{
|
||||||
Digest: digest.Digest(diffID), // diffID is a digest of the uncompressed tarball
|
Digest: diffID, // diffID is a digest of the uncompressed tarball
|
||||||
MediaType: manifest.DockerV2Schema2LayerMediaType,
|
MediaType: manifest.DockerV2Schema2LayerMediaType,
|
||||||
Size: li.size,
|
Size: li.size,
|
||||||
})
|
})
|
||||||
|
@ -284,13 +290,6 @@ func (s *Source) GetManifest() ([]byte, string, error) {
|
||||||
return s.generatedManifest, manifest.DockerV2Schema2MediaType, nil
|
return s.generatedManifest, manifest.DockerV2Schema2MediaType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTargetManifest returns an image's manifest given a digest. This is mainly used to retrieve a single image's manifest
|
|
||||||
// out of a manifest list.
|
|
||||||
func (s *Source) GetTargetManifest(digest digest.Digest) ([]byte, string, error) {
|
|
||||||
// How did we even get here? GetManifest() above has returned a manifest.DockerV2Schema2MediaType.
|
|
||||||
return nil, "", errors.Errorf(`Manifest lists are not supported by "docker-daemon:"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
type readCloseWrapper struct {
|
type readCloseWrapper struct {
|
||||||
io.Reader
|
io.Reader
|
||||||
closeFunc func() error
|
closeFunc func() error
|
||||||
|
@ -313,7 +312,7 @@ func (s *Source) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) {
|
||||||
return ioutil.NopCloser(bytes.NewReader(s.configBytes)), int64(len(s.configBytes)), nil
|
return ioutil.NopCloser(bytes.NewReader(s.configBytes)), int64(len(s.configBytes)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if li, ok := s.knownLayers[diffID(info.Digest)]; ok { // diffID is a digest of the uncompressed tarball,
|
if li, ok := s.knownLayers[info.Digest]; ok { // diffID is a digest of the uncompressed tarball,
|
||||||
stream, err := s.openTarComponent(li.path)
|
stream, err := s.openTarComponent(li.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
|
@ -355,6 +354,13 @@ func (s *Source) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||||
func (s *Source) GetSignatures(ctx context.Context) ([][]byte, error) {
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve signatures for
|
||||||
|
// (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list
|
||||||
|
// (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *Source) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||||
|
if instanceDigest != nil {
|
||||||
|
// How did we even get here? GetManifest(nil) has returned a manifest.DockerV2Schema2MediaType.
|
||||||
|
return nil, errors.Errorf(`Manifest lists are not supported by "docker-daemon:"`)
|
||||||
|
}
|
||||||
return [][]byte{}, nil
|
return [][]byte{}, nil
|
||||||
}
|
}
|
||||||
|
|
38
vendor/github.com/containers/image/docker/tarfile/types.go
generated
vendored
38
vendor/github.com/containers/image/docker/tarfile/types.go
generated
vendored
|
@ -1,6 +1,9 @@
|
||||||
package tarfile
|
package tarfile
|
||||||
|
|
||||||
import "github.com/opencontainers/go-digest"
|
import (
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
// Various data structures.
|
// Various data structures.
|
||||||
|
|
||||||
|
@ -18,37 +21,8 @@ type ManifestItem struct {
|
||||||
Config string
|
Config string
|
||||||
RepoTags []string
|
RepoTags []string
|
||||||
Layers []string
|
Layers []string
|
||||||
Parent imageID `json:",omitempty"`
|
Parent imageID `json:",omitempty"`
|
||||||
LayerSources map[diffID]distributionDescriptor `json:",omitempty"`
|
LayerSources map[digest.Digest]manifest.Schema2Descriptor `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type imageID string
|
type imageID string
|
||||||
type diffID digest.Digest
|
|
||||||
|
|
||||||
// Based on github.com/docker/distribution/blobs.go
|
|
||||||
type distributionDescriptor struct {
|
|
||||||
MediaType string `json:"mediaType,omitempty"`
|
|
||||||
Size int64 `json:"size,omitempty"`
|
|
||||||
Digest digest.Digest `json:"digest,omitempty"`
|
|
||||||
URLs []string `json:"urls,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Based on github.com/docker/distribution/manifest/schema2/manifest.go
|
|
||||||
// FIXME: We are repeating this all over the place; make a public copy?
|
|
||||||
type schema2Manifest struct {
|
|
||||||
SchemaVersion int `json:"schemaVersion"`
|
|
||||||
MediaType string `json:"mediaType,omitempty"`
|
|
||||||
Config distributionDescriptor `json:"config"`
|
|
||||||
Layers []distributionDescriptor `json:"layers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Based on github.com/docker/docker/image/image.go
|
|
||||||
// MOST CONTENT OMITTED AS UNNECESSARY
|
|
||||||
type image struct {
|
|
||||||
RootFS *rootFS `json:"rootfs,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type rootFS struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
DiffIDs []diffID `json:"diff_ids,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
56
vendor/github.com/containers/image/image/docker_list.go
generated
vendored
56
vendor/github.com/containers/image/image/docker_list.go
generated
vendored
|
@ -2,6 +2,7 @@ package image
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
|
@ -21,7 +22,7 @@ type platformSpec struct {
|
||||||
|
|
||||||
// A manifestDescriptor references a platform-specific manifest.
|
// A manifestDescriptor references a platform-specific manifest.
|
||||||
type manifestDescriptor struct {
|
type manifestDescriptor struct {
|
||||||
descriptor
|
manifest.Schema2Descriptor
|
||||||
Platform platformSpec `json:"platform"`
|
Platform platformSpec `json:"platform"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,22 +32,36 @@ type manifestList struct {
|
||||||
Manifests []manifestDescriptor `json:"manifests"`
|
Manifests []manifestDescriptor `json:"manifests"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func manifestSchema2FromManifestList(src types.ImageSource, manblob []byte) (genericManifest, error) {
|
// chooseDigestFromManifestList parses blob as a schema2 manifest list,
|
||||||
list := manifestList{}
|
// and returns the digest of the image appropriate for the current environment.
|
||||||
if err := json.Unmarshal(manblob, &list); err != nil {
|
func chooseDigestFromManifestList(ctx *types.SystemContext, blob []byte) (digest.Digest, error) {
|
||||||
return nil, err
|
wantedArch := runtime.GOARCH
|
||||||
|
if ctx != nil && ctx.ArchitectureChoice != "" {
|
||||||
|
wantedArch = ctx.ArchitectureChoice
|
||||||
|
}
|
||||||
|
wantedOS := runtime.GOOS
|
||||||
|
if ctx != nil && ctx.OSChoice != "" {
|
||||||
|
wantedOS = ctx.OSChoice
|
||||||
|
}
|
||||||
|
|
||||||
|
list := manifestList{}
|
||||||
|
if err := json.Unmarshal(blob, &list); err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
var targetManifestDigest digest.Digest
|
|
||||||
for _, d := range list.Manifests {
|
for _, d := range list.Manifests {
|
||||||
if d.Platform.Architecture == runtime.GOARCH && d.Platform.OS == runtime.GOOS {
|
if d.Platform.Architecture == wantedArch && d.Platform.OS == wantedOS {
|
||||||
targetManifestDigest = d.Digest
|
return d.Digest, nil
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if targetManifestDigest == "" {
|
return "", fmt.Errorf("no image found in manifest list for architecture %s, OS %s", wantedArch, wantedOS)
|
||||||
return nil, errors.New("no supported platform found in manifest list")
|
}
|
||||||
|
|
||||||
|
func manifestSchema2FromManifestList(ctx *types.SystemContext, src types.ImageSource, manblob []byte) (genericManifest, error) {
|
||||||
|
targetManifestDigest, err := chooseDigestFromManifestList(ctx, manblob)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
manblob, mt, err := src.GetTargetManifest(targetManifestDigest)
|
manblob, mt, err := src.GetManifest(&targetManifestDigest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -59,5 +74,20 @@ func manifestSchema2FromManifestList(src types.ImageSource, manblob []byte) (gen
|
||||||
return nil, errors.Errorf("Manifest image does not match selected manifest digest %s", targetManifestDigest)
|
return nil, errors.Errorf("Manifest image does not match selected manifest digest %s", targetManifestDigest)
|
||||||
}
|
}
|
||||||
|
|
||||||
return manifestInstanceFromBlob(src, manblob, mt)
|
return manifestInstanceFromBlob(ctx, src, manblob, mt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChooseManifestInstanceFromManifestList returns a digest of a manifest appropriate
|
||||||
|
// for the current system from the manifest available from src.
|
||||||
|
func ChooseManifestInstanceFromManifestList(ctx *types.SystemContext, src types.UnparsedImage) (digest.Digest, error) {
|
||||||
|
// For now this only handles manifest.DockerV2ListMediaType; we can generalize it later,
|
||||||
|
// probably along with manifest list editing.
|
||||||
|
blob, mt, err := src.Manifest()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if mt != manifest.DockerV2ListMediaType {
|
||||||
|
return "", fmt.Errorf("Internal error: Trying to select an image from a non-manifest-list manifest type %s", mt)
|
||||||
|
}
|
||||||
|
return chooseDigestFromManifestList(ctx, blob)
|
||||||
}
|
}
|
||||||
|
|
292
vendor/github.com/containers/image/image/docker_schema1.go
generated
vendored
292
vendor/github.com/containers/image/image/docker_schema1.go
generated
vendored
|
@ -2,9 +2,6 @@ package image
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
|
@ -14,87 +11,25 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
|
||||||
)
|
|
||||||
|
|
||||||
type fsLayersSchema1 struct {
|
|
||||||
BlobSum digest.Digest `json:"blobSum"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type historySchema1 struct {
|
|
||||||
V1Compatibility string `json:"v1Compatibility"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// historySchema1 is a string containing this. It is similar to v1Image but not the same, in particular note the ThrowAway field.
|
|
||||||
type v1Compatibility struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Parent string `json:"parent,omitempty"`
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
Created time.Time `json:"created"`
|
|
||||||
ContainerConfig struct {
|
|
||||||
Cmd []string
|
|
||||||
} `json:"container_config,omitempty"`
|
|
||||||
Author string `json:"author,omitempty"`
|
|
||||||
ThrowAway bool `json:"throwaway,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type manifestSchema1 struct {
|
type manifestSchema1 struct {
|
||||||
Name string `json:"name"`
|
m *manifest.Schema1
|
||||||
Tag string `json:"tag"`
|
|
||||||
Architecture string `json:"architecture"`
|
|
||||||
FSLayers []fsLayersSchema1 `json:"fsLayers"`
|
|
||||||
History []historySchema1 `json:"history"`
|
|
||||||
SchemaVersion int `json:"schemaVersion"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func manifestSchema1FromManifest(manifest []byte) (genericManifest, error) {
|
func manifestSchema1FromManifest(manifestBlob []byte) (genericManifest, error) {
|
||||||
mschema1 := &manifestSchema1{}
|
m, err := manifest.Schema1FromManifest(manifestBlob)
|
||||||
if err := json.Unmarshal(manifest, mschema1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if mschema1.SchemaVersion != 1 {
|
|
||||||
return nil, errors.Errorf("unsupported schema version %d", mschema1.SchemaVersion)
|
|
||||||
}
|
|
||||||
if len(mschema1.FSLayers) != len(mschema1.History) {
|
|
||||||
return nil, errors.New("length of history not equal to number of layers")
|
|
||||||
}
|
|
||||||
if len(mschema1.FSLayers) == 0 {
|
|
||||||
return nil, errors.New("no FSLayers in manifest")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fixManifestLayers(mschema1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return mschema1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// manifestSchema1FromComponents builds a new manifestSchema1 from the supplied data.
|
|
||||||
func manifestSchema1FromComponents(ref reference.Named, fsLayers []fsLayersSchema1, history []historySchema1, architecture string) genericManifest {
|
|
||||||
var name, tag string
|
|
||||||
if ref != nil { // Well, what to do if it _is_ nil? Most consumers actually don't use these fields nowadays, so we might as well try not supplying them.
|
|
||||||
name = reference.Path(ref)
|
|
||||||
if tagged, ok := ref.(reference.NamedTagged); ok {
|
|
||||||
tag = tagged.Tag()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &manifestSchema1{
|
|
||||||
Name: name,
|
|
||||||
Tag: tag,
|
|
||||||
Architecture: architecture,
|
|
||||||
FSLayers: fsLayers,
|
|
||||||
History: history,
|
|
||||||
SchemaVersion: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return manifest.AddDummyV2S1Signature(unsigned)
|
return &manifestSchema1{m: m}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// manifestSchema1FromComponents builds a new manifestSchema1 from the supplied data.
|
||||||
|
func manifestSchema1FromComponents(ref reference.Named, fsLayers []manifest.Schema1FSLayers, history []manifest.Schema1History, architecture string) genericManifest {
|
||||||
|
return &manifestSchema1{m: manifest.Schema1FromComponents(ref, fsLayers, history, architecture)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manifestSchema1) serialize() ([]byte, error) {
|
||||||
|
return m.m.Serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manifestSchema1) manifestMIMEType() string {
|
func (m *manifestSchema1) manifestMIMEType() string {
|
||||||
|
@ -104,7 +39,7 @@ func (m *manifestSchema1) manifestMIMEType() string {
|
||||||
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
||||||
// Note that the config object may not exist in the underlying storage in the return value of UpdatedImage! Use ConfigBlob() below.
|
// Note that the config object may not exist in the underlying storage in the return value of UpdatedImage! Use ConfigBlob() below.
|
||||||
func (m *manifestSchema1) ConfigInfo() types.BlobInfo {
|
func (m *manifestSchema1) ConfigInfo() types.BlobInfo {
|
||||||
return types.BlobInfo{}
|
return m.m.ConfigInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigBlob returns the blob described by ConfigInfo, iff ConfigInfo().Digest != ""; nil otherwise.
|
// ConfigBlob returns the blob described by ConfigInfo, iff ConfigInfo().Digest != ""; nil otherwise.
|
||||||
|
@ -128,11 +63,7 @@ func (m *manifestSchema1) OCIConfig() (*imgspecv1.Image, error) {
|
||||||
// The Digest field is guaranteed to be provided; Size may be -1.
|
// The Digest field is guaranteed to be provided; Size may be -1.
|
||||||
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||||
func (m *manifestSchema1) LayerInfos() []types.BlobInfo {
|
func (m *manifestSchema1) LayerInfos() []types.BlobInfo {
|
||||||
layers := make([]types.BlobInfo, len(m.FSLayers))
|
return m.m.LayerInfos()
|
||||||
for i, layer := range m.FSLayers { // NOTE: This includes empty layers (where m.History.V1Compatibility->ThrowAway)
|
|
||||||
layers[(len(m.FSLayers)-1)-i] = types.BlobInfo{Digest: layer.BlobSum, Size: -1}
|
|
||||||
}
|
|
||||||
return layers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
||||||
|
@ -153,22 +84,11 @@ func (m *manifestSchema1) EmbeddedDockerReferenceConflicts(ref reference.Named)
|
||||||
} else {
|
} else {
|
||||||
tag = ""
|
tag = ""
|
||||||
}
|
}
|
||||||
return m.Name != name || m.Tag != tag
|
return m.m.Name != name || m.m.Tag != tag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manifestSchema1) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
func (m *manifestSchema1) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||||
v1 := &v1Image{}
|
return m.m.Inspect(nil)
|
||||||
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), v1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &types.ImageInspectInfo{
|
|
||||||
Tag: m.Tag,
|
|
||||||
DockerVersion: v1.DockerVersion,
|
|
||||||
Created: v1.Created,
|
|
||||||
Labels: v1.Config.Labels,
|
|
||||||
Architecture: v1.Architecture,
|
|
||||||
Os: v1.OS,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
|
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
|
||||||
|
@ -181,25 +101,18 @@ func (m *manifestSchema1) UpdatedImageNeedsLayerDiffIDs(options types.ManifestUp
|
||||||
// UpdatedImage returns a types.Image modified according to options.
|
// UpdatedImage returns a types.Image modified according to options.
|
||||||
// This does not change the state of the original Image object.
|
// This does not change the state of the original Image object.
|
||||||
func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) {
|
func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) {
|
||||||
copy := *m
|
copy := manifestSchema1{m: manifest.Schema1Clone(m.m)}
|
||||||
if options.LayerInfos != nil {
|
if options.LayerInfos != nil {
|
||||||
// Our LayerInfos includes empty layers (where m.History.V1Compatibility->ThrowAway), so expect them to be included here as well.
|
if err := copy.m.UpdateLayerInfos(options.LayerInfos); err != nil {
|
||||||
if len(copy.FSLayers) != len(options.LayerInfos) {
|
return nil, err
|
||||||
return nil, errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(copy.FSLayers), len(options.LayerInfos))
|
|
||||||
}
|
|
||||||
for i, info := range options.LayerInfos {
|
|
||||||
// (docker push) sets up m.History.V1Compatibility->{Id,Parent} based on values of info.Digest,
|
|
||||||
// but (docker pull) ignores them in favor of computing DiffIDs from uncompressed data, except verifying the child->parent links and uniqueness.
|
|
||||||
// So, we don't bother recomputing the IDs in m.History.V1Compatibility.
|
|
||||||
copy.FSLayers[(len(options.LayerInfos)-1)-i].BlobSum = info.Digest
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if options.EmbeddedDockerReference != nil {
|
if options.EmbeddedDockerReference != nil {
|
||||||
copy.Name = reference.Path(options.EmbeddedDockerReference)
|
copy.m.Name = reference.Path(options.EmbeddedDockerReference)
|
||||||
if tagged, isTagged := options.EmbeddedDockerReference.(reference.NamedTagged); isTagged {
|
if tagged, isTagged := options.EmbeddedDockerReference.(reference.NamedTagged); isTagged {
|
||||||
copy.Tag = tagged.Tag()
|
copy.m.Tag = tagged.Tag()
|
||||||
} else {
|
} else {
|
||||||
copy.Tag = ""
|
copy.m.Tag = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +122,21 @@ func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (typ
|
||||||
// We have 2 MIME types for schema 1, which are basically equivalent (even the un-"Signed" MIME type will be rejected if there isn’t a signature; so,
|
// We have 2 MIME types for schema 1, which are basically equivalent (even the un-"Signed" MIME type will be rejected if there isn’t a signature; so,
|
||||||
// handle conversions between them by doing nothing.
|
// handle conversions between them by doing nothing.
|
||||||
case manifest.DockerV2Schema2MediaType:
|
case manifest.DockerV2Schema2MediaType:
|
||||||
return copy.convertToManifestSchema2(options.InformationOnly.LayerInfos, options.InformationOnly.LayerDiffIDs)
|
m2, err := copy.convertToManifestSchema2(options.InformationOnly.LayerInfos, options.InformationOnly.LayerDiffIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return memoryImageFromManifest(m2), nil
|
||||||
|
case imgspecv1.MediaTypeImageManifest:
|
||||||
|
// We can't directly convert to OCI, but we can transitively convert via a Docker V2.2 Distribution manifest
|
||||||
|
m2, err := copy.convertToManifestSchema2(options.InformationOnly.LayerInfos, options.InformationOnly.LayerDiffIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m2.UpdatedImage(types.ManifestUpdateOptions{
|
||||||
|
ManifestMIMEType: imgspecv1.MediaTypeImageManifest,
|
||||||
|
InformationOnly: options.InformationOnly,
|
||||||
|
})
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("Conversion of image manifest from %s to %s is not implemented", manifest.DockerV2Schema1SignedMediaType, options.ManifestMIMEType)
|
return nil, errors.Errorf("Conversion of image manifest from %s to %s is not implemented", manifest.DockerV2Schema1SignedMediaType, options.ManifestMIMEType)
|
||||||
}
|
}
|
||||||
|
@ -217,102 +144,32 @@ func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (typ
|
||||||
return memoryImageFromManifest(©), nil
|
return memoryImageFromManifest(©), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixManifestLayers, after validating the supplied manifest
|
|
||||||
// (to use correctly-formatted IDs, and to not have non-consecutive ID collisions in manifest.History),
|
|
||||||
// modifies manifest to only have one entry for each layer ID in manifest.History (deleting the older duplicates,
|
|
||||||
// both from manifest.History and manifest.FSLayers).
|
|
||||||
// Note that even after this succeeds, manifest.FSLayers may contain duplicate entries
|
|
||||||
// (for Dockerfile operations which change the configuration but not the filesystem).
|
|
||||||
func fixManifestLayers(manifest *manifestSchema1) error {
|
|
||||||
type imageV1 struct {
|
|
||||||
ID string
|
|
||||||
Parent string
|
|
||||||
}
|
|
||||||
// Per the specification, we can assume that len(manifest.FSLayers) == len(manifest.History)
|
|
||||||
imgs := make([]*imageV1, len(manifest.FSLayers))
|
|
||||||
for i := range manifest.FSLayers {
|
|
||||||
img := &imageV1{}
|
|
||||||
|
|
||||||
if err := json.Unmarshal([]byte(manifest.History[i].V1Compatibility), img); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
imgs[i] = img
|
|
||||||
if err := validateV1ID(img.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if imgs[len(imgs)-1].Parent != "" {
|
|
||||||
return errors.New("Invalid parent ID in the base layer of the image")
|
|
||||||
}
|
|
||||||
// check general duplicates to error instead of a deadlock
|
|
||||||
idmap := make(map[string]struct{})
|
|
||||||
var lastID string
|
|
||||||
for _, img := range imgs {
|
|
||||||
// skip IDs that appear after each other, we handle those later
|
|
||||||
if _, exists := idmap[img.ID]; img.ID != lastID && exists {
|
|
||||||
return errors.Errorf("ID %+v appears multiple times in manifest", img.ID)
|
|
||||||
}
|
|
||||||
lastID = img.ID
|
|
||||||
idmap[lastID] = struct{}{}
|
|
||||||
}
|
|
||||||
// backwards loop so that we keep the remaining indexes after removing items
|
|
||||||
for i := len(imgs) - 2; i >= 0; i-- {
|
|
||||||
if imgs[i].ID == imgs[i+1].ID { // repeated ID. remove and continue
|
|
||||||
manifest.FSLayers = append(manifest.FSLayers[:i], manifest.FSLayers[i+1:]...)
|
|
||||||
manifest.History = append(manifest.History[:i], manifest.History[i+1:]...)
|
|
||||||
} else if imgs[i].Parent != imgs[i+1].ID {
|
|
||||||
return errors.Errorf("Invalid parent ID. Expected %v, got %v", imgs[i+1].ID, imgs[i].Parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateV1ID(id string) error {
|
|
||||||
if ok := validHex.MatchString(id); !ok {
|
|
||||||
return errors.Errorf("image ID %q is invalid", id)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Based on github.com/docker/docker/distribution/pull_v2.go
|
// Based on github.com/docker/docker/distribution/pull_v2.go
|
||||||
func (m *manifestSchema1) convertToManifestSchema2(uploadedLayerInfos []types.BlobInfo, layerDiffIDs []digest.Digest) (types.Image, error) {
|
func (m *manifestSchema1) convertToManifestSchema2(uploadedLayerInfos []types.BlobInfo, layerDiffIDs []digest.Digest) (genericManifest, error) {
|
||||||
if len(m.History) == 0 {
|
if len(m.m.History) == 0 {
|
||||||
// What would this even mean?! Anyhow, the rest of the code depends on fsLayers[0] and history[0] existing.
|
// What would this even mean?! Anyhow, the rest of the code depends on fsLayers[0] and history[0] existing.
|
||||||
return nil, errors.Errorf("Cannot convert an image with 0 history entries to %s", manifest.DockerV2Schema2MediaType)
|
return nil, errors.Errorf("Cannot convert an image with 0 history entries to %s", manifest.DockerV2Schema2MediaType)
|
||||||
}
|
}
|
||||||
if len(m.History) != len(m.FSLayers) {
|
if len(m.m.History) != len(m.m.FSLayers) {
|
||||||
return nil, errors.Errorf("Inconsistent schema 1 manifest: %d history entries, %d fsLayers entries", len(m.History), len(m.FSLayers))
|
return nil, errors.Errorf("Inconsistent schema 1 manifest: %d history entries, %d fsLayers entries", len(m.m.History), len(m.m.FSLayers))
|
||||||
}
|
}
|
||||||
if uploadedLayerInfos != nil && len(uploadedLayerInfos) != len(m.FSLayers) {
|
if uploadedLayerInfos != nil && len(uploadedLayerInfos) != len(m.m.FSLayers) {
|
||||||
return nil, errors.Errorf("Internal error: uploaded %d blobs, but schema1 manifest has %d fsLayers", len(uploadedLayerInfos), len(m.FSLayers))
|
return nil, errors.Errorf("Internal error: uploaded %d blobs, but schema1 manifest has %d fsLayers", len(uploadedLayerInfos), len(m.m.FSLayers))
|
||||||
}
|
}
|
||||||
if layerDiffIDs != nil && len(layerDiffIDs) != len(m.FSLayers) {
|
if layerDiffIDs != nil && len(layerDiffIDs) != len(m.m.FSLayers) {
|
||||||
return nil, errors.Errorf("Internal error: collected %d DiffID values, but schema1 manifest has %d fsLayers", len(layerDiffIDs), len(m.FSLayers))
|
return nil, errors.Errorf("Internal error: collected %d DiffID values, but schema1 manifest has %d fsLayers", len(layerDiffIDs), len(m.m.FSLayers))
|
||||||
}
|
}
|
||||||
|
|
||||||
rootFS := rootFS{
|
// Build a list of the diffIDs for the non-empty layers.
|
||||||
Type: "layers",
|
diffIDs := []digest.Digest{}
|
||||||
DiffIDs: []digest.Digest{},
|
var layers []manifest.Schema2Descriptor
|
||||||
BaseLayer: "",
|
for v1Index := len(m.m.History) - 1; v1Index >= 0; v1Index-- {
|
||||||
}
|
v2Index := (len(m.m.History) - 1) - v1Index
|
||||||
var layers []descriptor
|
|
||||||
history := make([]imageHistory, len(m.History))
|
|
||||||
for v1Index := len(m.History) - 1; v1Index >= 0; v1Index-- {
|
|
||||||
v2Index := (len(m.History) - 1) - v1Index
|
|
||||||
|
|
||||||
var v1compat v1Compatibility
|
var v1compat manifest.Schema1V1Compatibility
|
||||||
if err := json.Unmarshal([]byte(m.History[v1Index].V1Compatibility), &v1compat); err != nil {
|
if err := json.Unmarshal([]byte(m.m.History[v1Index].V1Compatibility), &v1compat); err != nil {
|
||||||
return nil, errors.Wrapf(err, "Error decoding history entry %d", v1Index)
|
return nil, errors.Wrapf(err, "Error decoding history entry %d", v1Index)
|
||||||
}
|
}
|
||||||
history[v2Index] = imageHistory{
|
|
||||||
Created: v1compat.Created,
|
|
||||||
Author: v1compat.Author,
|
|
||||||
CreatedBy: strings.Join(v1compat.ContainerConfig.Cmd, " "),
|
|
||||||
Comment: v1compat.Comment,
|
|
||||||
EmptyLayer: v1compat.ThrowAway,
|
|
||||||
}
|
|
||||||
|
|
||||||
if !v1compat.ThrowAway {
|
if !v1compat.ThrowAway {
|
||||||
var size int64
|
var size int64
|
||||||
if uploadedLayerInfos != nil {
|
if uploadedLayerInfos != nil {
|
||||||
|
@ -322,54 +179,23 @@ func (m *manifestSchema1) convertToManifestSchema2(uploadedLayerInfos []types.Bl
|
||||||
if layerDiffIDs != nil {
|
if layerDiffIDs != nil {
|
||||||
d = layerDiffIDs[v2Index]
|
d = layerDiffIDs[v2Index]
|
||||||
}
|
}
|
||||||
layers = append(layers, descriptor{
|
layers = append(layers, manifest.Schema2Descriptor{
|
||||||
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||||
Size: size,
|
Size: size,
|
||||||
Digest: m.FSLayers[v1Index].BlobSum,
|
Digest: m.m.FSLayers[v1Index].BlobSum,
|
||||||
})
|
})
|
||||||
rootFS.DiffIDs = append(rootFS.DiffIDs, d)
|
diffIDs = append(diffIDs, d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
configJSON, err := configJSONFromV1Config([]byte(m.History[0].V1Compatibility), rootFS, history)
|
configJSON, err := m.m.ToSchema2(diffIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
configDescriptor := descriptor{
|
configDescriptor := manifest.Schema2Descriptor{
|
||||||
MediaType: "application/vnd.docker.container.image.v1+json",
|
MediaType: "application/vnd.docker.container.image.v1+json",
|
||||||
Size: int64(len(configJSON)),
|
Size: int64(len(configJSON)),
|
||||||
Digest: digest.FromBytes(configJSON),
|
Digest: digest.FromBytes(configJSON),
|
||||||
}
|
}
|
||||||
|
|
||||||
m2 := manifestSchema2FromComponents(configDescriptor, nil, configJSON, layers)
|
return manifestSchema2FromComponents(configDescriptor, nil, configJSON, layers), nil
|
||||||
return memoryImageFromManifest(m2), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func configJSONFromV1Config(v1ConfigJSON []byte, rootFS rootFS, history []imageHistory) ([]byte, error) {
|
|
||||||
// github.com/docker/docker/image/v1/imagev1.go:MakeConfigFromV1Config unmarshals and re-marshals the input if docker_version is < 1.8.3 to remove blank fields;
|
|
||||||
// we don't do that here. FIXME? Should we? AFAICT it would only affect the digest value of the schema2 manifest, and we don't particularly need that to be
|
|
||||||
// a consistently reproducible value.
|
|
||||||
|
|
||||||
// Preserve everything we don't specifically know about.
|
|
||||||
// (This must be a *json.RawMessage, even though *[]byte is fairly redundant, because only *RawMessage implements json.Marshaler.)
|
|
||||||
rawContents := map[string]*json.RawMessage{}
|
|
||||||
if err := json.Unmarshal(v1ConfigJSON, &rawContents); err != nil { // We have already unmarshaled it before, using a more detailed schema?!
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(rawContents, "id")
|
|
||||||
delete(rawContents, "parent")
|
|
||||||
delete(rawContents, "Size")
|
|
||||||
delete(rawContents, "parent_id")
|
|
||||||
delete(rawContents, "layer_id")
|
|
||||||
delete(rawContents, "throwaway")
|
|
||||||
|
|
||||||
updates := map[string]interface{}{"rootfs": rootFS, "history": history}
|
|
||||||
for field, value := range updates {
|
|
||||||
encoded, err := json.Marshal(value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rawContents[field] = (*json.RawMessage)(&encoded)
|
|
||||||
}
|
|
||||||
return json.Marshal(rawContents)
|
|
||||||
}
|
}
|
||||||
|
|
149
vendor/github.com/containers/image/image/docker_schema2.go
generated
vendored
149
vendor/github.com/containers/image/image/docker_schema2.go
generated
vendored
|
@ -29,54 +29,44 @@ var gzippedEmptyLayer = []byte{
|
||||||
// gzippedEmptyLayerDigest is a digest of gzippedEmptyLayer
|
// gzippedEmptyLayerDigest is a digest of gzippedEmptyLayer
|
||||||
const gzippedEmptyLayerDigest = digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")
|
const gzippedEmptyLayerDigest = digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")
|
||||||
|
|
||||||
type descriptor struct {
|
|
||||||
MediaType string `json:"mediaType"`
|
|
||||||
Size int64 `json:"size"`
|
|
||||||
Digest digest.Digest `json:"digest"`
|
|
||||||
URLs []string `json:"urls,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type manifestSchema2 struct {
|
type manifestSchema2 struct {
|
||||||
src types.ImageSource // May be nil if configBlob is not nil
|
src types.ImageSource // May be nil if configBlob is not nil
|
||||||
configBlob []byte // If set, corresponds to contents of ConfigDescriptor.
|
configBlob []byte // If set, corresponds to contents of ConfigDescriptor.
|
||||||
SchemaVersion int `json:"schemaVersion"`
|
m *manifest.Schema2
|
||||||
MediaType string `json:"mediaType"`
|
|
||||||
ConfigDescriptor descriptor `json:"config"`
|
|
||||||
LayersDescriptors []descriptor `json:"layers"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func manifestSchema2FromManifest(src types.ImageSource, manifest []byte) (genericManifest, error) {
|
func manifestSchema2FromManifest(src types.ImageSource, manifestBlob []byte) (genericManifest, error) {
|
||||||
v2s2 := manifestSchema2{src: src}
|
m, err := manifest.Schema2FromManifest(manifestBlob)
|
||||||
if err := json.Unmarshal(manifest, &v2s2); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &v2s2, nil
|
return &manifestSchema2{
|
||||||
|
src: src,
|
||||||
|
m: m,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// manifestSchema2FromComponents builds a new manifestSchema2 from the supplied data:
|
// manifestSchema2FromComponents builds a new manifestSchema2 from the supplied data:
|
||||||
func manifestSchema2FromComponents(config descriptor, src types.ImageSource, configBlob []byte, layers []descriptor) genericManifest {
|
func manifestSchema2FromComponents(config manifest.Schema2Descriptor, src types.ImageSource, configBlob []byte, layers []manifest.Schema2Descriptor) genericManifest {
|
||||||
return &manifestSchema2{
|
return &manifestSchema2{
|
||||||
src: src,
|
src: src,
|
||||||
configBlob: configBlob,
|
configBlob: configBlob,
|
||||||
SchemaVersion: 2,
|
m: manifest.Schema2FromComponents(config, layers),
|
||||||
MediaType: manifest.DockerV2Schema2MediaType,
|
|
||||||
ConfigDescriptor: config,
|
|
||||||
LayersDescriptors: layers,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manifestSchema2) serialize() ([]byte, error) {
|
func (m *manifestSchema2) serialize() ([]byte, error) {
|
||||||
return json.Marshal(*m)
|
return m.m.Serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manifestSchema2) manifestMIMEType() string {
|
func (m *manifestSchema2) manifestMIMEType() string {
|
||||||
return m.MediaType
|
return m.m.MediaType
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
||||||
// Note that the config object may not exist in the underlying storage in the return value of UpdatedImage! Use ConfigBlob() below.
|
// Note that the config object may not exist in the underlying storage in the return value of UpdatedImage! Use ConfigBlob() below.
|
||||||
func (m *manifestSchema2) ConfigInfo() types.BlobInfo {
|
func (m *manifestSchema2) ConfigInfo() types.BlobInfo {
|
||||||
return types.BlobInfo{Digest: m.ConfigDescriptor.Digest, Size: m.ConfigDescriptor.Size}
|
return m.m.ConfigInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
// OCIConfig returns the image configuration as per OCI v1 image-spec. Information about
|
// OCIConfig returns the image configuration as per OCI v1 image-spec. Information about
|
||||||
|
@ -105,9 +95,9 @@ func (m *manifestSchema2) ConfigBlob() ([]byte, error) {
|
||||||
return nil, errors.Errorf("Internal error: neither src nor configBlob set in manifestSchema2")
|
return nil, errors.Errorf("Internal error: neither src nor configBlob set in manifestSchema2")
|
||||||
}
|
}
|
||||||
stream, _, err := m.src.GetBlob(types.BlobInfo{
|
stream, _, err := m.src.GetBlob(types.BlobInfo{
|
||||||
Digest: m.ConfigDescriptor.Digest,
|
Digest: m.m.ConfigDescriptor.Digest,
|
||||||
Size: m.ConfigDescriptor.Size,
|
Size: m.m.ConfigDescriptor.Size,
|
||||||
URLs: m.ConfigDescriptor.URLs,
|
URLs: m.m.ConfigDescriptor.URLs,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -118,8 +108,8 @@ func (m *manifestSchema2) ConfigBlob() ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
computedDigest := digest.FromBytes(blob)
|
computedDigest := digest.FromBytes(blob)
|
||||||
if computedDigest != m.ConfigDescriptor.Digest {
|
if computedDigest != m.m.ConfigDescriptor.Digest {
|
||||||
return nil, errors.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.ConfigDescriptor.Digest)
|
return nil, errors.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.m.ConfigDescriptor.Digest)
|
||||||
}
|
}
|
||||||
m.configBlob = blob
|
m.configBlob = blob
|
||||||
}
|
}
|
||||||
|
@ -130,15 +120,7 @@ func (m *manifestSchema2) ConfigBlob() ([]byte, error) {
|
||||||
// The Digest field is guaranteed to be provided; Size may be -1.
|
// The Digest field is guaranteed to be provided; Size may be -1.
|
||||||
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||||
func (m *manifestSchema2) LayerInfos() []types.BlobInfo {
|
func (m *manifestSchema2) LayerInfos() []types.BlobInfo {
|
||||||
blobs := []types.BlobInfo{}
|
return m.m.LayerInfos()
|
||||||
for _, layer := range m.LayersDescriptors {
|
|
||||||
blobs = append(blobs, types.BlobInfo{
|
|
||||||
Digest: layer.Digest,
|
|
||||||
Size: layer.Size,
|
|
||||||
URLs: layer.URLs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return blobs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
||||||
|
@ -149,21 +131,18 @@ func (m *manifestSchema2) EmbeddedDockerReferenceConflicts(ref reference.Named)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manifestSchema2) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
func (m *manifestSchema2) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||||
config, err := m.ConfigBlob()
|
getter := func(info types.BlobInfo) ([]byte, error) {
|
||||||
if err != nil {
|
if info.Digest != m.ConfigInfo().Digest {
|
||||||
return nil, err
|
// Shouldn't ever happen
|
||||||
|
return nil, errors.New("asked for a different config blob")
|
||||||
|
}
|
||||||
|
config, err := m.ConfigBlob()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
}
|
}
|
||||||
v1 := &v1Image{}
|
return m.m.Inspect(getter)
|
||||||
if err := json.Unmarshal(config, v1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &types.ImageInspectInfo{
|
|
||||||
DockerVersion: v1.DockerVersion,
|
|
||||||
Created: v1.Created,
|
|
||||||
Labels: v1.Config.Labels,
|
|
||||||
Architecture: v1.Architecture,
|
|
||||||
Os: v1.OS,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
|
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
|
||||||
|
@ -176,17 +155,14 @@ func (m *manifestSchema2) UpdatedImageNeedsLayerDiffIDs(options types.ManifestUp
|
||||||
// UpdatedImage returns a types.Image modified according to options.
|
// UpdatedImage returns a types.Image modified according to options.
|
||||||
// This does not change the state of the original Image object.
|
// This does not change the state of the original Image object.
|
||||||
func (m *manifestSchema2) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) {
|
func (m *manifestSchema2) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) {
|
||||||
copy := *m // NOTE: This is not a deep copy, it still shares slices etc.
|
copy := manifestSchema2{ // NOTE: This is not a deep copy, it still shares slices etc.
|
||||||
|
src: m.src,
|
||||||
|
configBlob: m.configBlob,
|
||||||
|
m: manifest.Schema2Clone(m.m),
|
||||||
|
}
|
||||||
if options.LayerInfos != nil {
|
if options.LayerInfos != nil {
|
||||||
if len(copy.LayersDescriptors) != len(options.LayerInfos) {
|
if err := copy.m.UpdateLayerInfos(options.LayerInfos); err != nil {
|
||||||
return nil, errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(copy.LayersDescriptors), len(options.LayerInfos))
|
return nil, err
|
||||||
}
|
|
||||||
copy.LayersDescriptors = make([]descriptor, len(options.LayerInfos))
|
|
||||||
for i, info := range options.LayerInfos {
|
|
||||||
copy.LayersDescriptors[i].MediaType = m.LayersDescriptors[i].MediaType
|
|
||||||
copy.LayersDescriptors[i].Digest = info.Digest
|
|
||||||
copy.LayersDescriptors[i].Size = info.Size
|
|
||||||
copy.LayersDescriptors[i].URLs = info.URLs
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1 to schema2, but we really don't care.
|
// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1 to schema2, but we really don't care.
|
||||||
|
@ -204,6 +180,15 @@ func (m *manifestSchema2) UpdatedImage(options types.ManifestUpdateOptions) (typ
|
||||||
return memoryImageFromManifest(©), nil
|
return memoryImageFromManifest(©), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func oci1DescriptorFromSchema2Descriptor(d manifest.Schema2Descriptor) imgspecv1.Descriptor {
|
||||||
|
return imgspecv1.Descriptor{
|
||||||
|
MediaType: d.MediaType,
|
||||||
|
Size: d.Size,
|
||||||
|
Digest: d.Digest,
|
||||||
|
URLs: d.URLs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *manifestSchema2) convertToManifestOCI1() (types.Image, error) {
|
func (m *manifestSchema2) convertToManifestOCI1() (types.Image, error) {
|
||||||
configOCI, err := m.OCIConfig()
|
configOCI, err := m.OCIConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -214,18 +199,16 @@ func (m *manifestSchema2) convertToManifestOCI1() (types.Image, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
config := descriptorOCI1{
|
config := imgspecv1.Descriptor{
|
||||||
descriptor: descriptor{
|
MediaType: imgspecv1.MediaTypeImageConfig,
|
||||||
MediaType: imgspecv1.MediaTypeImageConfig,
|
Size: int64(len(configOCIBytes)),
|
||||||
Size: int64(len(configOCIBytes)),
|
Digest: digest.FromBytes(configOCIBytes),
|
||||||
Digest: digest.FromBytes(configOCIBytes),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layers := make([]descriptorOCI1, len(m.LayersDescriptors))
|
layers := make([]imgspecv1.Descriptor, len(m.m.LayersDescriptors))
|
||||||
for idx := range layers {
|
for idx := range layers {
|
||||||
layers[idx] = descriptorOCI1{descriptor: m.LayersDescriptors[idx]}
|
layers[idx] = oci1DescriptorFromSchema2Descriptor(m.m.LayersDescriptors[idx])
|
||||||
if m.LayersDescriptors[idx].MediaType == manifest.DockerV2Schema2ForeignLayerMediaType {
|
if m.m.LayersDescriptors[idx].MediaType == manifest.DockerV2Schema2ForeignLayerMediaType {
|
||||||
layers[idx].MediaType = imgspecv1.MediaTypeImageLayerNonDistributable
|
layers[idx].MediaType = imgspecv1.MediaTypeImageLayerNonDistributable
|
||||||
} else {
|
} else {
|
||||||
// we assume layers are gzip'ed because docker v2s2 only deals with
|
// we assume layers are gzip'ed because docker v2s2 only deals with
|
||||||
|
@ -244,14 +227,14 @@ func (m *manifestSchema2) convertToManifestSchema1(dest types.ImageDestination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
imageConfig := &image{}
|
imageConfig := &manifest.Schema2Image{}
|
||||||
if err := json.Unmarshal(configBytes, imageConfig); err != nil {
|
if err := json.Unmarshal(configBytes, imageConfig); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build fsLayers and History, discarding all configs. We will patch the top-level config in later.
|
// Build fsLayers and History, discarding all configs. We will patch the top-level config in later.
|
||||||
fsLayers := make([]fsLayersSchema1, len(imageConfig.History))
|
fsLayers := make([]manifest.Schema1FSLayers, len(imageConfig.History))
|
||||||
history := make([]historySchema1, len(imageConfig.History))
|
history := make([]manifest.Schema1History, len(imageConfig.History))
|
||||||
nonemptyLayerIndex := 0
|
nonemptyLayerIndex := 0
|
||||||
var parentV1ID string // Set in the loop
|
var parentV1ID string // Set in the loop
|
||||||
v1ID := ""
|
v1ID := ""
|
||||||
|
@ -279,10 +262,10 @@ func (m *manifestSchema2) convertToManifestSchema1(dest types.ImageDestination)
|
||||||
}
|
}
|
||||||
blobDigest = gzippedEmptyLayerDigest
|
blobDigest = gzippedEmptyLayerDigest
|
||||||
} else {
|
} else {
|
||||||
if nonemptyLayerIndex >= len(m.LayersDescriptors) {
|
if nonemptyLayerIndex >= len(m.m.LayersDescriptors) {
|
||||||
return nil, errors.Errorf("Invalid image configuration, needs more than the %d distributed layers", len(m.LayersDescriptors))
|
return nil, errors.Errorf("Invalid image configuration, needs more than the %d distributed layers", len(m.m.LayersDescriptors))
|
||||||
}
|
}
|
||||||
blobDigest = m.LayersDescriptors[nonemptyLayerIndex].Digest
|
blobDigest = m.m.LayersDescriptors[nonemptyLayerIndex].Digest
|
||||||
nonemptyLayerIndex++
|
nonemptyLayerIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +276,7 @@ func (m *manifestSchema2) convertToManifestSchema1(dest types.ImageDestination)
|
||||||
}
|
}
|
||||||
v1ID = v
|
v1ID = v
|
||||||
|
|
||||||
fakeImage := v1Compatibility{
|
fakeImage := manifest.Schema1V1Compatibility{
|
||||||
ID: v1ID,
|
ID: v1ID,
|
||||||
Parent: parentV1ID,
|
Parent: parentV1ID,
|
||||||
Comment: historyEntry.Comment,
|
Comment: historyEntry.Comment,
|
||||||
|
@ -307,8 +290,8 @@ func (m *manifestSchema2) convertToManifestSchema1(dest types.ImageDestination)
|
||||||
return nil, errors.Errorf("Internal error: Error creating v1compatibility for %#v", fakeImage)
|
return nil, errors.Errorf("Internal error: Error creating v1compatibility for %#v", fakeImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fsLayers[v1Index] = fsLayersSchema1{BlobSum: blobDigest}
|
fsLayers[v1Index] = manifest.Schema1FSLayers{BlobSum: blobDigest}
|
||||||
history[v1Index] = historySchema1{V1Compatibility: string(v1CompatibilityBytes)}
|
history[v1Index] = manifest.Schema1History{V1Compatibility: string(v1CompatibilityBytes)}
|
||||||
// Note that parentV1ID of the top layer is preserved when exiting this loop
|
// Note that parentV1ID of the top layer is preserved when exiting this loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
82
vendor/github.com/containers/image/image/manifest.go
generated
vendored
82
vendor/github.com/containers/image/image/manifest.go
generated
vendored
|
@ -1,57 +1,14 @@
|
||||||
package image
|
package image
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"fmt"
|
||||||
|
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/pkg/strslice"
|
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/opencontainers/go-digest"
|
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type config struct {
|
|
||||||
Cmd strslice.StrSlice
|
|
||||||
Labels map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type v1Image struct {
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Parent string `json:"parent,omitempty"`
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
Created time.Time `json:"created"`
|
|
||||||
ContainerConfig *config `json:"container_config,omitempty"`
|
|
||||||
DockerVersion string `json:"docker_version,omitempty"`
|
|
||||||
Author string `json:"author,omitempty"`
|
|
||||||
// Config is the configuration of the container received from the client
|
|
||||||
Config *config `json:"config,omitempty"`
|
|
||||||
// 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"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type image struct {
|
|
||||||
v1Image
|
|
||||||
History []imageHistory `json:"history,omitempty"`
|
|
||||||
RootFS *rootFS `json:"rootfs,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type imageHistory struct {
|
|
||||||
Created time.Time `json:"created"`
|
|
||||||
Author string `json:"author,omitempty"`
|
|
||||||
CreatedBy string `json:"created_by,omitempty"`
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
EmptyLayer bool `json:"empty_layer,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type rootFS struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
DiffIDs []digest.Digest `json:"diff_ids,omitempty"`
|
|
||||||
BaseLayer string `json:"base_layer,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// genericManifest is an interface for parsing, modifying image manifests and related data.
|
// 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
|
// Note that the public methods are intended to be a subset of types.Image
|
||||||
// so that embedding a genericManifest into structs works.
|
// so that embedding a genericManifest into structs works.
|
||||||
|
@ -87,43 +44,24 @@ type genericManifest interface {
|
||||||
UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error)
|
UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func manifestInstanceFromBlob(src types.ImageSource, manblob []byte, mt string) (genericManifest, error) {
|
// manifestInstanceFromBlob returns a genericManifest implementation for (manblob, mt) in src.
|
||||||
switch mt {
|
// If manblob is a manifest list, it implicitly chooses an appropriate image from the list.
|
||||||
// "application/json" is a valid v2s1 value per https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md .
|
func manifestInstanceFromBlob(ctx *types.SystemContext, src types.ImageSource, manblob []byte, mt string) (genericManifest, error) {
|
||||||
// This works for now, when nothing else seems to return "application/json"; if that were not true, the mapping/detection might
|
switch manifest.NormalizedMIMEType(mt) {
|
||||||
// need to happen within the ImageSource.
|
case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType:
|
||||||
case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType, "application/json":
|
|
||||||
return manifestSchema1FromManifest(manblob)
|
return manifestSchema1FromManifest(manblob)
|
||||||
case imgspecv1.MediaTypeImageManifest:
|
case imgspecv1.MediaTypeImageManifest:
|
||||||
return manifestOCI1FromManifest(src, manblob)
|
return manifestOCI1FromManifest(src, manblob)
|
||||||
case manifest.DockerV2Schema2MediaType:
|
case manifest.DockerV2Schema2MediaType:
|
||||||
return manifestSchema2FromManifest(src, manblob)
|
return manifestSchema2FromManifest(src, manblob)
|
||||||
case manifest.DockerV2ListMediaType:
|
case manifest.DockerV2ListMediaType:
|
||||||
return manifestSchema2FromManifestList(src, manblob)
|
return manifestSchema2FromManifestList(ctx, src, manblob)
|
||||||
default:
|
default: // Note that this may not be reachable, manifest.NormalizedMIMEType has a default for unknown values.
|
||||||
// If it's not a recognized manifest media type, or we have failed determining the type, we'll try one last time
|
return nil, fmt.Errorf("Unimplemented manifest MIME type %s", mt)
|
||||||
// to deserialize using v2s1 as per https://github.com/docker/distribution/blob/master/manifests.go#L108
|
|
||||||
// and https://github.com/docker/distribution/blob/master/manifest/schema1/manifest.go#L50
|
|
||||||
//
|
|
||||||
// Crane registries can also return "text/plain", or pretty much anything else depending on a file extension “recognized” in the tag.
|
|
||||||
// This makes no real sense, but it happens
|
|
||||||
// because requests for manifests are
|
|
||||||
// redirected to a content distribution
|
|
||||||
// network which is configured that way. See https://bugzilla.redhat.com/show_bug.cgi?id=1389442
|
|
||||||
return manifestSchema1FromManifest(manblob)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// inspectManifest is an implementation of types.Image.Inspect
|
// inspectManifest is an implementation of types.Image.Inspect
|
||||||
func inspectManifest(m genericManifest) (*types.ImageInspectInfo, error) {
|
func inspectManifest(m genericManifest) (*types.ImageInspectInfo, error) {
|
||||||
info, err := m.imageInspectInfo()
|
return 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.String()
|
|
||||||
}
|
|
||||||
return info, nil
|
|
||||||
}
|
}
|
||||||
|
|
13
vendor/github.com/containers/image/image/memory.go
generated
vendored
13
vendor/github.com/containers/image/image/memory.go
generated
vendored
|
@ -33,11 +33,6 @@ func (i *memoryImage) Reference() types.ImageReference {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close removes resources associated with an initialized UnparsedImage, if any.
|
|
||||||
func (i *memoryImage) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the size of the image as stored, if known, or -1 if not.
|
// Size returns the size of the image as stored, if known, or -1 if not.
|
||||||
func (i *memoryImage) Size() (int64, error) {
|
func (i *memoryImage) Size() (int64, error) {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
|
@ -67,7 +62,9 @@ func (i *memoryImage) Inspect() (*types.ImageInspectInfo, error) {
|
||||||
return inspectManifest(i.genericManifest)
|
return inspectManifest(i.genericManifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsMultiImage returns true if the image's manifest is a list of images, false otherwise.
|
// LayerInfosForCopy returns an updated set of layer blob information which may not match the manifest.
|
||||||
func (i *memoryImage) IsMultiImage() bool {
|
// The Digest field is guaranteed to be provided; Size may be -1.
|
||||||
return false
|
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||||
|
func (i *memoryImage) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
113
vendor/github.com/containers/image/image/oci.go
generated
vendored
113
vendor/github.com/containers/image/image/oci.go
generated
vendored
|
@ -12,41 +12,34 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type descriptorOCI1 struct {
|
|
||||||
descriptor
|
|
||||||
Annotations map[string]string `json:"annotations,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type manifestOCI1 struct {
|
type manifestOCI1 struct {
|
||||||
src types.ImageSource // May be nil if configBlob is not nil
|
src types.ImageSource // May be nil if configBlob is not nil
|
||||||
configBlob []byte // If set, corresponds to contents of ConfigDescriptor.
|
configBlob []byte // If set, corresponds to contents of m.Config.
|
||||||
SchemaVersion int `json:"schemaVersion"`
|
m *manifest.OCI1
|
||||||
ConfigDescriptor descriptorOCI1 `json:"config"`
|
|
||||||
LayersDescriptors []descriptorOCI1 `json:"layers"`
|
|
||||||
Annotations map[string]string `json:"annotations,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func manifestOCI1FromManifest(src types.ImageSource, manifest []byte) (genericManifest, error) {
|
func manifestOCI1FromManifest(src types.ImageSource, manifestBlob []byte) (genericManifest, error) {
|
||||||
oci := manifestOCI1{src: src}
|
m, err := manifest.OCI1FromManifest(manifestBlob)
|
||||||
if err := json.Unmarshal(manifest, &oci); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &oci, nil
|
return &manifestOCI1{
|
||||||
|
src: src,
|
||||||
|
m: m,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// manifestOCI1FromComponents builds a new manifestOCI1 from the supplied data:
|
// manifestOCI1FromComponents builds a new manifestOCI1 from the supplied data:
|
||||||
func manifestOCI1FromComponents(config descriptorOCI1, src types.ImageSource, configBlob []byte, layers []descriptorOCI1) genericManifest {
|
func manifestOCI1FromComponents(config imgspecv1.Descriptor, src types.ImageSource, configBlob []byte, layers []imgspecv1.Descriptor) genericManifest {
|
||||||
return &manifestOCI1{
|
return &manifestOCI1{
|
||||||
src: src,
|
src: src,
|
||||||
configBlob: configBlob,
|
configBlob: configBlob,
|
||||||
SchemaVersion: 2,
|
m: manifest.OCI1FromComponents(config, layers),
|
||||||
ConfigDescriptor: config,
|
|
||||||
LayersDescriptors: layers,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manifestOCI1) serialize() ([]byte, error) {
|
func (m *manifestOCI1) serialize() ([]byte, error) {
|
||||||
return json.Marshal(*m)
|
return m.m.Serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manifestOCI1) manifestMIMEType() string {
|
func (m *manifestOCI1) manifestMIMEType() string {
|
||||||
|
@ -56,7 +49,7 @@ func (m *manifestOCI1) manifestMIMEType() string {
|
||||||
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
||||||
// Note that the config object may not exist in the underlying storage in the return value of UpdatedImage! Use ConfigBlob() below.
|
// Note that the config object may not exist in the underlying storage in the return value of UpdatedImage! Use ConfigBlob() below.
|
||||||
func (m *manifestOCI1) ConfigInfo() types.BlobInfo {
|
func (m *manifestOCI1) ConfigInfo() types.BlobInfo {
|
||||||
return types.BlobInfo{Digest: m.ConfigDescriptor.Digest, Size: m.ConfigDescriptor.Size, Annotations: m.ConfigDescriptor.Annotations}
|
return m.m.ConfigInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigBlob returns the blob described by ConfigInfo, iff ConfigInfo().Digest != ""; nil otherwise.
|
// ConfigBlob returns the blob described by ConfigInfo, iff ConfigInfo().Digest != ""; nil otherwise.
|
||||||
|
@ -67,9 +60,9 @@ func (m *manifestOCI1) ConfigBlob() ([]byte, error) {
|
||||||
return nil, errors.Errorf("Internal error: neither src nor configBlob set in manifestOCI1")
|
return nil, errors.Errorf("Internal error: neither src nor configBlob set in manifestOCI1")
|
||||||
}
|
}
|
||||||
stream, _, err := m.src.GetBlob(types.BlobInfo{
|
stream, _, err := m.src.GetBlob(types.BlobInfo{
|
||||||
Digest: m.ConfigDescriptor.Digest,
|
Digest: m.m.Config.Digest,
|
||||||
Size: m.ConfigDescriptor.Size,
|
Size: m.m.Config.Size,
|
||||||
URLs: m.ConfigDescriptor.URLs,
|
URLs: m.m.Config.URLs,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -80,8 +73,8 @@ func (m *manifestOCI1) ConfigBlob() ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
computedDigest := digest.FromBytes(blob)
|
computedDigest := digest.FromBytes(blob)
|
||||||
if computedDigest != m.ConfigDescriptor.Digest {
|
if computedDigest != m.m.Config.Digest {
|
||||||
return nil, errors.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.ConfigDescriptor.Digest)
|
return nil, errors.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.m.Config.Digest)
|
||||||
}
|
}
|
||||||
m.configBlob = blob
|
m.configBlob = blob
|
||||||
}
|
}
|
||||||
|
@ -107,11 +100,7 @@ func (m *manifestOCI1) OCIConfig() (*imgspecv1.Image, error) {
|
||||||
// The Digest field is guaranteed to be provided; Size may be -1.
|
// The Digest field is guaranteed to be provided; Size may be -1.
|
||||||
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||||
func (m *manifestOCI1) LayerInfos() []types.BlobInfo {
|
func (m *manifestOCI1) LayerInfos() []types.BlobInfo {
|
||||||
blobs := []types.BlobInfo{}
|
return m.m.LayerInfos()
|
||||||
for _, layer := range m.LayersDescriptors {
|
|
||||||
blobs = append(blobs, types.BlobInfo{Digest: layer.Digest, Size: layer.Size, Annotations: layer.Annotations, URLs: layer.URLs})
|
|
||||||
}
|
|
||||||
return blobs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
||||||
|
@ -122,21 +111,18 @@ func (m *manifestOCI1) EmbeddedDockerReferenceConflicts(ref reference.Named) boo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manifestOCI1) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
func (m *manifestOCI1) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||||
config, err := m.ConfigBlob()
|
getter := func(info types.BlobInfo) ([]byte, error) {
|
||||||
if err != nil {
|
if info.Digest != m.ConfigInfo().Digest {
|
||||||
return nil, err
|
// Shouldn't ever happen
|
||||||
|
return nil, errors.New("asked for a different config blob")
|
||||||
|
}
|
||||||
|
config, err := m.ConfigBlob()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
}
|
}
|
||||||
v1 := &v1Image{}
|
return m.m.Inspect(getter)
|
||||||
if err := json.Unmarshal(config, v1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &types.ImageInspectInfo{
|
|
||||||
DockerVersion: v1.DockerVersion,
|
|
||||||
Created: v1.Created,
|
|
||||||
Labels: v1.Config.Labels,
|
|
||||||
Architecture: v1.Architecture,
|
|
||||||
Os: v1.OS,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
|
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
|
||||||
|
@ -149,18 +135,14 @@ func (m *manifestOCI1) UpdatedImageNeedsLayerDiffIDs(options types.ManifestUpdat
|
||||||
// UpdatedImage returns a types.Image modified according to options.
|
// UpdatedImage returns a types.Image modified according to options.
|
||||||
// This does not change the state of the original Image object.
|
// This does not change the state of the original Image object.
|
||||||
func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) {
|
func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) {
|
||||||
copy := *m // NOTE: This is not a deep copy, it still shares slices etc.
|
copy := manifestOCI1{ // NOTE: This is not a deep copy, it still shares slices etc.
|
||||||
|
src: m.src,
|
||||||
|
configBlob: m.configBlob,
|
||||||
|
m: manifest.OCI1Clone(m.m),
|
||||||
|
}
|
||||||
if options.LayerInfos != nil {
|
if options.LayerInfos != nil {
|
||||||
if len(copy.LayersDescriptors) != len(options.LayerInfos) {
|
if err := copy.m.UpdateLayerInfos(options.LayerInfos); err != nil {
|
||||||
return nil, errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(copy.LayersDescriptors), len(options.LayerInfos))
|
return nil, err
|
||||||
}
|
|
||||||
copy.LayersDescriptors = make([]descriptorOCI1, len(options.LayerInfos))
|
|
||||||
for i, info := range options.LayerInfos {
|
|
||||||
copy.LayersDescriptors[i].MediaType = m.LayersDescriptors[i].MediaType
|
|
||||||
copy.LayersDescriptors[i].Digest = info.Digest
|
|
||||||
copy.LayersDescriptors[i].Size = info.Size
|
|
||||||
copy.LayersDescriptors[i].Annotations = info.Annotations
|
|
||||||
copy.LayersDescriptors[i].URLs = info.URLs
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1, but we really don't care.
|
// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1, but we really don't care.
|
||||||
|
@ -176,17 +158,26 @@ func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types.
|
||||||
return memoryImageFromManifest(©), nil
|
return memoryImageFromManifest(©), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func schema2DescriptorFromOCI1Descriptor(d imgspecv1.Descriptor) manifest.Schema2Descriptor {
|
||||||
|
return manifest.Schema2Descriptor{
|
||||||
|
MediaType: d.MediaType,
|
||||||
|
Size: d.Size,
|
||||||
|
Digest: d.Digest,
|
||||||
|
URLs: d.URLs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *manifestOCI1) convertToManifestSchema2() (types.Image, error) {
|
func (m *manifestOCI1) convertToManifestSchema2() (types.Image, error) {
|
||||||
// Create a copy of the descriptor.
|
// Create a copy of the descriptor.
|
||||||
config := m.ConfigDescriptor.descriptor
|
config := schema2DescriptorFromOCI1Descriptor(m.m.Config)
|
||||||
|
|
||||||
// The only difference between OCI and DockerSchema2 is the mediatypes. The
|
// The only difference between OCI and DockerSchema2 is the mediatypes. The
|
||||||
// media type of the manifest is handled by manifestSchema2FromComponents.
|
// media type of the manifest is handled by manifestSchema2FromComponents.
|
||||||
config.MediaType = manifest.DockerV2Schema2ConfigMediaType
|
config.MediaType = manifest.DockerV2Schema2ConfigMediaType
|
||||||
|
|
||||||
layers := make([]descriptor, len(m.LayersDescriptors))
|
layers := make([]manifest.Schema2Descriptor, len(m.m.Layers))
|
||||||
for idx := range layers {
|
for idx := range layers {
|
||||||
layers[idx] = m.LayersDescriptors[idx].descriptor
|
layers[idx] = schema2DescriptorFromOCI1Descriptor(m.m.Layers[idx])
|
||||||
layers[idx].MediaType = manifest.DockerV2Schema2LayerMediaType
|
layers[idx].MediaType = manifest.DockerV2Schema2LayerMediaType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
50
vendor/github.com/containers/image/image/sourced.go
generated
vendored
50
vendor/github.com/containers/image/image/sourced.go
generated
vendored
|
@ -4,12 +4,22 @@
|
||||||
package image
|
package image
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containers/image/manifest"
|
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FromSource returns a types.Image implementation for source.
|
// imageCloser implements types.ImageCloser, perhaps allowing simple users
|
||||||
// The caller must call .Close() on the returned Image.
|
// to use a single object without having keep a reference to a types.ImageSource
|
||||||
|
// only to call types.ImageSource.Close().
|
||||||
|
type imageCloser struct {
|
||||||
|
types.Image
|
||||||
|
src types.ImageSource
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromSource returns a types.ImageCloser implementation for the default instance of source.
|
||||||
|
// If source is a manifest list, .Manifest() still returns the manifest list,
|
||||||
|
// but other methods transparently return data from an appropriate image instance.
|
||||||
|
//
|
||||||
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
//
|
//
|
||||||
// FromSource “takes ownership” of the input ImageSource and will call src.Close()
|
// 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
|
// when the image is closed. (This does not prevent callers from using both the
|
||||||
|
@ -18,8 +28,19 @@ import (
|
||||||
//
|
//
|
||||||
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
|
// 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.
|
// 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) {
|
func FromSource(ctx *types.SystemContext, src types.ImageSource) (types.ImageCloser, error) {
|
||||||
return FromUnparsedImage(UnparsedFromSource(src))
|
img, err := FromUnparsedImage(ctx, UnparsedInstance(src, nil))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &imageCloser{
|
||||||
|
Image: img,
|
||||||
|
src: src,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ic *imageCloser) Close() error {
|
||||||
|
return ic.src.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// sourcedImage is a general set of utilities for working with container images,
|
// sourcedImage is a general set of utilities for working with container images,
|
||||||
|
@ -38,27 +59,22 @@ type sourcedImage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromUnparsedImage returns a types.Image implementation for unparsed.
|
// FromUnparsedImage returns a types.Image implementation for unparsed.
|
||||||
// The caller must call .Close() on the returned Image.
|
// If unparsed represents a manifest list, .Manifest() still returns the manifest list,
|
||||||
|
// but other methods transparently return data from an appropriate single image.
|
||||||
//
|
//
|
||||||
// FromSource “takes ownership” of the input UnparsedImage and will call uparsed.Close()
|
// The Image must not be used after the underlying ImageSource is Close()d.
|
||||||
// when the image is closed. (This does not prevent callers from using both the
|
func FromUnparsedImage(ctx *types.SystemContext, unparsed *UnparsedImage) (types.Image, error) {
|
||||||
// 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:
|
// 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,
|
// 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.
|
// 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().
|
// 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()
|
manifestBlob, manifestMIMEType, err := unparsed.Manifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedManifest, err := manifestInstanceFromBlob(unparsed.src, manifestBlob, manifestMIMEType)
|
parsedManifest, err := manifestInstanceFromBlob(ctx, unparsed.src, manifestBlob, manifestMIMEType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -85,6 +101,6 @@ func (i *sourcedImage) Inspect() (*types.ImageInspectInfo, error) {
|
||||||
return inspectManifest(i.genericManifest)
|
return inspectManifest(i.genericManifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *sourcedImage) IsMultiImage() bool {
|
func (i *sourcedImage) LayerInfosForCopy() []types.BlobInfo {
|
||||||
return i.manifestMIMEType == manifest.DockerV2ListMediaType
|
return i.UnparsedImage.LayerInfosForCopy()
|
||||||
}
|
}
|
||||||
|
|
69
vendor/github.com/containers/image/image/unparsed.go
generated
vendored
69
vendor/github.com/containers/image/image/unparsed.go
generated
vendored
|
@ -11,8 +11,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// UnparsedImage implements types.UnparsedImage .
|
// UnparsedImage implements types.UnparsedImage .
|
||||||
|
// An UnparsedImage is a pair of (ImageSource, instance digest); it can represent either a manifest list or a single image instance.
|
||||||
type UnparsedImage struct {
|
type UnparsedImage struct {
|
||||||
src types.ImageSource
|
src types.ImageSource
|
||||||
|
instanceDigest *digest.Digest
|
||||||
cachedManifest []byte // A private cache for Manifest(); nil if not yet known.
|
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.
|
// A private cache for Manifest(), may be the empty string if guessing failed.
|
||||||
// Valid iff cachedManifest is not nil.
|
// Valid iff cachedManifest is not nil.
|
||||||
|
@ -20,49 +22,41 @@ type UnparsedImage struct {
|
||||||
cachedSignatures [][]byte // A private cache for Signatures(); nil if not yet known.
|
cachedSignatures [][]byte // A private cache for Signatures(); nil if not yet known.
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnparsedFromSource returns a types.UnparsedImage implementation for source.
|
// UnparsedInstance returns a types.UnparsedImage implementation for (source, instanceDigest).
|
||||||
// The caller must call .Close() on the returned UnparsedImage.
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list).
|
||||||
//
|
//
|
||||||
// UnparsedFromSource “takes ownership” of the input ImageSource and will call src.Close()
|
// The UnparsedImage must not be used after the underlying ImageSource is Close()d.
|
||||||
// when the image is closed. (This does not prevent callers from using both the
|
func UnparsedInstance(src types.ImageSource, instanceDigest *digest.Digest) *UnparsedImage {
|
||||||
// UnparsedImage and ImageSource objects simultaneously, but it means that they only need to
|
return &UnparsedImage{
|
||||||
// keep a reference to the UnparsedImage.)
|
src: src,
|
||||||
func UnparsedFromSource(src types.ImageSource) *UnparsedImage {
|
instanceDigest: instanceDigest,
|
||||||
return &UnparsedImage{src: src}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference returns the reference used to set up this source, _as specified by the user_
|
// 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.
|
// (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 {
|
func (i *UnparsedImage) Reference() types.ImageReference {
|
||||||
|
// Note that this does not depend on instanceDigest; e.g. all instances within a manifest list need to be signed with the manifest list identity.
|
||||||
return i.src.Reference()
|
return i.src.Reference()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close removes resources associated with an initialized UnparsedImage, if any.
|
|
||||||
func (i *UnparsedImage) Close() error {
|
|
||||||
return i.src.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need.
|
// 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) {
|
func (i *UnparsedImage) Manifest() ([]byte, string, error) {
|
||||||
if i.cachedManifest == nil {
|
if i.cachedManifest == nil {
|
||||||
m, mt, err := i.src.GetManifest()
|
m, mt, err := i.src.GetManifest(i.instanceDigest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageSource.GetManifest does not do digest verification, but we do;
|
// ImageSource.GetManifest does not do digest verification, but we do;
|
||||||
// this immediately protects also any user of types.Image.
|
// this immediately protects also any user of types.Image.
|
||||||
ref := i.Reference().DockerReference()
|
if digest, haveDigest := i.expectedManifestDigest(); haveDigest {
|
||||||
if ref != nil {
|
matches, err := manifest.MatchesDigest(m, digest)
|
||||||
if canonical, ok := ref.(reference.Canonical); ok {
|
if err != nil {
|
||||||
digest := digest.Digest(canonical.Digest())
|
return nil, "", errors.Wrap(err, "Error computing manifest digest")
|
||||||
matches, err := manifest.MatchesDigest(m, digest)
|
}
|
||||||
if err != nil {
|
if !matches {
|
||||||
return nil, "", errors.Wrap(err, "Error computing manifest digest")
|
return nil, "", errors.Errorf("Manifest does not match provided manifest digest %s", digest)
|
||||||
}
|
|
||||||
if !matches {
|
|
||||||
return nil, "", errors.Errorf("Manifest does not match provided manifest digest %s", digest)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,10 +66,26 @@ func (i *UnparsedImage) Manifest() ([]byte, string, error) {
|
||||||
return i.cachedManifest, i.cachedManifestMIMEType, nil
|
return i.cachedManifest, i.cachedManifestMIMEType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expectedManifestDigest returns a the expected value of the manifest digest, and an indicator whether it is known.
|
||||||
|
// The bool return value seems redundant with digest != ""; it is used explicitly
|
||||||
|
// to refuse (unexpected) situations when the digest exists but is "".
|
||||||
|
func (i *UnparsedImage) expectedManifestDigest() (digest.Digest, bool) {
|
||||||
|
if i.instanceDigest != nil {
|
||||||
|
return *i.instanceDigest, true
|
||||||
|
}
|
||||||
|
ref := i.Reference().DockerReference()
|
||||||
|
if ref != nil {
|
||||||
|
if canonical, ok := ref.(reference.Canonical); ok {
|
||||||
|
return canonical.Digest(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
|
// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
|
||||||
func (i *UnparsedImage) Signatures(ctx context.Context) ([][]byte, error) {
|
func (i *UnparsedImage) Signatures(ctx context.Context) ([][]byte, error) {
|
||||||
if i.cachedSignatures == nil {
|
if i.cachedSignatures == nil {
|
||||||
sigs, err := i.src.GetSignatures(ctx)
|
sigs, err := i.src.GetSignatures(ctx, i.instanceDigest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -83,3 +93,10 @@ func (i *UnparsedImage) Signatures(ctx context.Context) ([][]byte, error) {
|
||||||
}
|
}
|
||||||
return i.cachedSignatures, nil
|
return i.cachedSignatures, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerInfosForCopy returns an updated set of layer blob information which may not match the manifest.
|
||||||
|
// The Digest field is guaranteed to be provided; Size may be -1.
|
||||||
|
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||||
|
func (i *UnparsedImage) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return i.src.LayerInfosForCopy()
|
||||||
|
}
|
||||||
|
|
19
vendor/github.com/containers/image/internal/tmpdir/tmpdir.go
generated
vendored
Normal file
19
vendor/github.com/containers/image/internal/tmpdir/tmpdir.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package tmpdir
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TemporaryDirectoryForBigFiles returns a directory for temporary (big) files.
|
||||||
|
// On non Windows systems it avoids the use of os.TempDir(), because the default temporary directory usually falls under /tmp
|
||||||
|
// which on systemd based systems could be the unsuitable tmpfs filesystem.
|
||||||
|
func TemporaryDirectoryForBigFiles() string {
|
||||||
|
var temporaryDirectoryForBigFiles string
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
temporaryDirectoryForBigFiles = os.TempDir()
|
||||||
|
} else {
|
||||||
|
temporaryDirectoryForBigFiles = "/var/tmp"
|
||||||
|
}
|
||||||
|
return temporaryDirectoryForBigFiles
|
||||||
|
}
|
310
vendor/github.com/containers/image/manifest/docker_schema1.go
generated
vendored
Normal file
310
vendor/github.com/containers/image/manifest/docker_schema1.go
generated
vendored
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/image/docker/reference"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
"github.com/docker/docker/api/types/versions"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Schema1FSLayers is an entry of the "fsLayers" array in docker/distribution schema 1.
|
||||||
|
type Schema1FSLayers struct {
|
||||||
|
BlobSum digest.Digest `json:"blobSum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema1History is an entry of the "history" array in docker/distribution schema 1.
|
||||||
|
type Schema1History struct {
|
||||||
|
V1Compatibility string `json:"v1Compatibility"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema1 is a manifest in docker/distribution schema 1.
|
||||||
|
type Schema1 struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
Architecture string `json:"architecture"`
|
||||||
|
FSLayers []Schema1FSLayers `json:"fsLayers"`
|
||||||
|
History []Schema1History `json:"history"`
|
||||||
|
SchemaVersion int `json:"schemaVersion"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema1V1Compatibility is a v1Compatibility in docker/distribution schema 1.
|
||||||
|
type Schema1V1Compatibility struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Parent string `json:"parent,omitempty"`
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
ContainerConfig struct {
|
||||||
|
Cmd []string
|
||||||
|
} `json:"container_config,omitempty"`
|
||||||
|
Author string `json:"author,omitempty"`
|
||||||
|
ThrowAway bool `json:"throwaway,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema1FromManifest creates a Schema1 manifest instance from a manifest blob.
|
||||||
|
// (NOTE: The instance is not necessary a literal representation of the original blob,
|
||||||
|
// layers with duplicate IDs are eliminated.)
|
||||||
|
func Schema1FromManifest(manifest []byte) (*Schema1, error) {
|
||||||
|
s1 := Schema1{}
|
||||||
|
if err := json.Unmarshal(manifest, &s1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s1.SchemaVersion != 1 {
|
||||||
|
return nil, errors.Errorf("unsupported schema version %d", s1.SchemaVersion)
|
||||||
|
}
|
||||||
|
if len(s1.FSLayers) != len(s1.History) {
|
||||||
|
return nil, errors.New("length of history not equal to number of layers")
|
||||||
|
}
|
||||||
|
if len(s1.FSLayers) == 0 {
|
||||||
|
return nil, errors.New("no FSLayers in manifest")
|
||||||
|
}
|
||||||
|
if err := s1.fixManifestLayers(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &s1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema1FromComponents creates an Schema1 manifest instance from the supplied data.
|
||||||
|
func Schema1FromComponents(ref reference.Named, fsLayers []Schema1FSLayers, history []Schema1History, architecture string) *Schema1 {
|
||||||
|
var name, tag string
|
||||||
|
if ref != nil { // Well, what to do if it _is_ nil? Most consumers actually don't use these fields nowadays, so we might as well try not supplying them.
|
||||||
|
name = reference.Path(ref)
|
||||||
|
if tagged, ok := ref.(reference.NamedTagged); ok {
|
||||||
|
tag = tagged.Tag()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Schema1{
|
||||||
|
Name: name,
|
||||||
|
Tag: tag,
|
||||||
|
Architecture: architecture,
|
||||||
|
FSLayers: fsLayers,
|
||||||
|
History: history,
|
||||||
|
SchemaVersion: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema1Clone creates a copy of the supplied Schema1 manifest.
|
||||||
|
func Schema1Clone(src *Schema1) *Schema1 {
|
||||||
|
copy := *src
|
||||||
|
return ©
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
||||||
|
func (m *Schema1) 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 *Schema1) 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)
|
||||||
|
layers[(len(m.FSLayers)-1)-i] = types.BlobInfo{Digest: layer.BlobSum, Size: -1}
|
||||||
|
}
|
||||||
|
return layers
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLayerInfos replaces the original layers with the specified BlobInfos (size+digest+urls), in order (the root layer first, and then successive layered layers)
|
||||||
|
func (m *Schema1) UpdateLayerInfos(layerInfos []types.BlobInfo) error {
|
||||||
|
// Our LayerInfos includes empty layers (where m.History.V1Compatibility->ThrowAway), so expect them to be included here as well.
|
||||||
|
if len(m.FSLayers) != len(layerInfos) {
|
||||||
|
return errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(m.FSLayers), len(layerInfos))
|
||||||
|
}
|
||||||
|
for i, info := range layerInfos {
|
||||||
|
// (docker push) sets up m.History.V1Compatibility->{Id,Parent} based on values of info.Digest,
|
||||||
|
// but (docker pull) ignores them in favor of computing DiffIDs from uncompressed data, except verifying the child->parent links and uniqueness.
|
||||||
|
// So, we don't bother recomputing the IDs in m.History.V1Compatibility.
|
||||||
|
m.FSLayers[(len(layerInfos)-1)-i].BlobSum = info.Digest
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize returns the manifest in a blob format.
|
||||||
|
// NOTE: Serialize() does not in general reproduce the original blob if this object was loaded from one, even if no modifications were made!
|
||||||
|
func (m *Schema1) 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 AddDummyV2S1Signature(unsigned)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixManifestLayers, after validating the supplied manifest
|
||||||
|
// (to use correctly-formatted IDs, and to not have non-consecutive ID collisions in m.History),
|
||||||
|
// modifies manifest to only have one entry for each layer ID in m.History (deleting the older duplicates,
|
||||||
|
// both from m.History and m.FSLayers).
|
||||||
|
// Note that even after this succeeds, m.FSLayers may contain duplicate entries
|
||||||
|
// (for Dockerfile operations which change the configuration but not the filesystem).
|
||||||
|
func (m *Schema1) fixManifestLayers() error {
|
||||||
|
type imageV1 struct {
|
||||||
|
ID string
|
||||||
|
Parent string
|
||||||
|
}
|
||||||
|
// Per the specification, we can assume that len(m.FSLayers) == len(m.History)
|
||||||
|
imgs := make([]*imageV1, len(m.FSLayers))
|
||||||
|
for i := range m.FSLayers {
|
||||||
|
img := &imageV1{}
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), img); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
imgs[i] = img
|
||||||
|
if err := validateV1ID(img.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if imgs[len(imgs)-1].Parent != "" {
|
||||||
|
return errors.New("Invalid parent ID in the base layer of the image")
|
||||||
|
}
|
||||||
|
// check general duplicates to error instead of a deadlock
|
||||||
|
idmap := make(map[string]struct{})
|
||||||
|
var lastID string
|
||||||
|
for _, img := range imgs {
|
||||||
|
// skip IDs that appear after each other, we handle those later
|
||||||
|
if _, exists := idmap[img.ID]; img.ID != lastID && exists {
|
||||||
|
return errors.Errorf("ID %+v appears multiple times in manifest", img.ID)
|
||||||
|
}
|
||||||
|
lastID = img.ID
|
||||||
|
idmap[lastID] = struct{}{}
|
||||||
|
}
|
||||||
|
// backwards loop so that we keep the remaining indexes after removing items
|
||||||
|
for i := len(imgs) - 2; i >= 0; i-- {
|
||||||
|
if imgs[i].ID == imgs[i+1].ID { // repeated ID. remove and continue
|
||||||
|
m.FSLayers = append(m.FSLayers[:i], m.FSLayers[i+1:]...)
|
||||||
|
m.History = append(m.History[:i], m.History[i+1:]...)
|
||||||
|
} else if imgs[i].Parent != imgs[i+1].ID {
|
||||||
|
return errors.Errorf("Invalid parent ID. Expected %v, got %v", imgs[i+1].ID, imgs[i].Parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
||||||
|
|
||||||
|
func validateV1ID(id string) error {
|
||||||
|
if ok := validHex.MatchString(id); !ok {
|
||||||
|
return errors.Errorf("image ID %q is invalid", id)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
|
||||||
|
func (m *Schema1) Inspect(_ func(types.BlobInfo) ([]byte, error)) (*types.ImageInspectInfo, error) {
|
||||||
|
s1 := &Schema2V1Image{}
|
||||||
|
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), s1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &types.ImageInspectInfo{
|
||||||
|
Tag: m.Tag,
|
||||||
|
Created: s1.Created,
|
||||||
|
DockerVersion: s1.DockerVersion,
|
||||||
|
Labels: make(map[string]string),
|
||||||
|
Architecture: s1.Architecture,
|
||||||
|
Os: s1.OS,
|
||||||
|
Layers: LayerInfosToStrings(m.LayerInfos()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSchema2 builds a schema2-style configuration blob using the supplied diffIDs.
|
||||||
|
func (m *Schema1) ToSchema2(diffIDs []digest.Digest) ([]byte, error) {
|
||||||
|
// Convert the schema 1 compat info into a schema 2 config, constructing some of the fields
|
||||||
|
// that aren't directly comparable using info from the manifest.
|
||||||
|
if len(m.History) == 0 {
|
||||||
|
return nil, errors.New("image has no layers")
|
||||||
|
}
|
||||||
|
s2 := struct {
|
||||||
|
Schema2Image
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Parent string `json:"parent,omitempty"`
|
||||||
|
ParentID string `json:"parent_id,omitempty"`
|
||||||
|
LayerID string `json:"layer_id,omitempty"`
|
||||||
|
ThrowAway bool `json:"throwaway,omitempty"`
|
||||||
|
Size int64 `json:",omitempty"`
|
||||||
|
}{}
|
||||||
|
config := []byte(m.History[0].V1Compatibility)
|
||||||
|
err := json.Unmarshal(config, &s2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error decoding configuration")
|
||||||
|
}
|
||||||
|
// Images created with versions prior to 1.8.3 require us to re-encode the encoded object,
|
||||||
|
// adding some fields that aren't "omitempty".
|
||||||
|
if s2.DockerVersion != "" && versions.LessThan(s2.DockerVersion, "1.8.3") {
|
||||||
|
config, err = json.Marshal(&s2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error re-encoding compat image config %#v", s2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Build the history.
|
||||||
|
convertedHistory := []Schema2History{}
|
||||||
|
for _, h := range m.History {
|
||||||
|
compat := Schema1V1Compatibility{}
|
||||||
|
if err := json.Unmarshal([]byte(h.V1Compatibility), &compat); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error decoding history information")
|
||||||
|
}
|
||||||
|
hitem := Schema2History{
|
||||||
|
Created: compat.Created,
|
||||||
|
CreatedBy: strings.Join(compat.ContainerConfig.Cmd, " "),
|
||||||
|
Author: compat.Author,
|
||||||
|
Comment: compat.Comment,
|
||||||
|
EmptyLayer: compat.ThrowAway,
|
||||||
|
}
|
||||||
|
convertedHistory = append([]Schema2History{hitem}, convertedHistory...)
|
||||||
|
}
|
||||||
|
// Build the rootfs information. We need the decompressed sums that we've been
|
||||||
|
// calculating to fill in the DiffIDs. It's expected (but not enforced by us)
|
||||||
|
// that the number of diffIDs corresponds to the number of non-EmptyLayer
|
||||||
|
// entries in the history.
|
||||||
|
rootFS := &Schema2RootFS{
|
||||||
|
Type: "layers",
|
||||||
|
DiffIDs: diffIDs,
|
||||||
|
}
|
||||||
|
// And now for some raw manipulation.
|
||||||
|
raw := make(map[string]*json.RawMessage)
|
||||||
|
err = json.Unmarshal(config, &raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error re-decoding compat image config %#v: %v", s2)
|
||||||
|
}
|
||||||
|
// Drop some fields.
|
||||||
|
delete(raw, "id")
|
||||||
|
delete(raw, "parent")
|
||||||
|
delete(raw, "parent_id")
|
||||||
|
delete(raw, "layer_id")
|
||||||
|
delete(raw, "throwaway")
|
||||||
|
delete(raw, "Size")
|
||||||
|
// Add the history and rootfs information.
|
||||||
|
rootfs, err := json.Marshal(rootFS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("error encoding rootfs information %#v: %v", rootFS, err)
|
||||||
|
}
|
||||||
|
rawRootfs := json.RawMessage(rootfs)
|
||||||
|
raw["rootfs"] = &rawRootfs
|
||||||
|
history, err := json.Marshal(convertedHistory)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("error encoding history information %#v: %v", convertedHistory, err)
|
||||||
|
}
|
||||||
|
rawHistory := json.RawMessage(history)
|
||||||
|
raw["history"] = &rawHistory
|
||||||
|
// Encode the result.
|
||||||
|
config, err = json.Marshal(raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("error re-encoding compat image config %#v: %v", s2, err)
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageID computes an ID which can uniquely identify this image by its contents.
|
||||||
|
func (m *Schema1) ImageID(diffIDs []digest.Digest) (string, error) {
|
||||||
|
image, err := m.ToSchema2(diffIDs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return digest.FromBytes(image).Hex(), nil
|
||||||
|
}
|
251
vendor/github.com/containers/image/manifest/docker_schema2.go
generated
vendored
Normal file
251
vendor/github.com/containers/image/manifest/docker_schema2.go
generated
vendored
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/image/pkg/strslice"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Schema2Descriptor is a “descriptor” in docker/distribution schema 2.
|
||||||
|
type Schema2Descriptor struct {
|
||||||
|
MediaType string `json:"mediaType"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Digest digest.Digest `json:"digest"`
|
||||||
|
URLs []string `json:"urls,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema2 is a manifest in docker/distribution schema 2.
|
||||||
|
type Schema2 struct {
|
||||||
|
SchemaVersion int `json:"schemaVersion"`
|
||||||
|
MediaType string `json:"mediaType"`
|
||||||
|
ConfigDescriptor Schema2Descriptor `json:"config"`
|
||||||
|
LayersDescriptors []Schema2Descriptor `json:"layers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema2Port is a Port, a string containing port number and protocol in the
|
||||||
|
// format "80/tcp", from docker/go-connections/nat.
|
||||||
|
type Schema2Port string
|
||||||
|
|
||||||
|
// Schema2PortSet is a PortSet, a collection of structs indexed by Port, from
|
||||||
|
// docker/go-connections/nat.
|
||||||
|
type Schema2PortSet map[Schema2Port]struct{}
|
||||||
|
|
||||||
|
// Schema2HealthConfig is a HealthConfig, which holds configuration settings
|
||||||
|
// for the HEALTHCHECK feature, from docker/docker/api/types/container.
|
||||||
|
type Schema2HealthConfig struct {
|
||||||
|
// Test is the test to perform to check that the container is healthy.
|
||||||
|
// An empty slice means to inherit the default.
|
||||||
|
// The options are:
|
||||||
|
// {} : inherit healthcheck
|
||||||
|
// {"NONE"} : disable healthcheck
|
||||||
|
// {"CMD", args...} : exec arguments directly
|
||||||
|
// {"CMD-SHELL", command} : run command with system's default shell
|
||||||
|
Test []string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Zero means to inherit. Durations are expressed as integer nanoseconds.
|
||||||
|
Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
|
||||||
|
Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.
|
||||||
|
|
||||||
|
// Retries is the number of consecutive failures needed to consider a container as unhealthy.
|
||||||
|
// Zero means inherit.
|
||||||
|
Retries int `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema2Config is a Config in docker/docker/api/types/container.
|
||||||
|
type Schema2Config struct {
|
||||||
|
Hostname string // Hostname
|
||||||
|
Domainname string // Domainname
|
||||||
|
User string // User that will run the command(s) inside the container, also support user:group
|
||||||
|
AttachStdin bool // Attach the standard input, makes possible user interaction
|
||||||
|
AttachStdout bool // Attach the standard output
|
||||||
|
AttachStderr bool // Attach the standard error
|
||||||
|
ExposedPorts Schema2PortSet `json:",omitempty"` // List of exposed ports
|
||||||
|
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||||
|
OpenStdin bool // Open stdin
|
||||||
|
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
||||||
|
Env []string // List of environment variable to set in the container
|
||||||
|
Cmd strslice.StrSlice // Command to run when starting the container
|
||||||
|
Healthcheck *Schema2HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
|
||||||
|
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific)
|
||||||
|
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
|
||||||
|
Volumes map[string]struct{} // List of volumes (mounts) used for the container
|
||||||
|
WorkingDir string // Current directory (PWD) in the command will be launched
|
||||||
|
Entrypoint strslice.StrSlice // Entrypoint to run when starting the container
|
||||||
|
NetworkDisabled bool `json:",omitempty"` // Is network disabled
|
||||||
|
MacAddress string `json:",omitempty"` // Mac Address of the container
|
||||||
|
OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
|
||||||
|
Labels map[string]string // List of labels set to this container
|
||||||
|
StopSignal string `json:",omitempty"` // Signal to stop a container
|
||||||
|
StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container
|
||||||
|
Shell strslice.StrSlice `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema2V1Image is a V1Image in docker/docker/image.
|
||||||
|
type Schema2V1Image struct {
|
||||||
|
// ID is a unique 64 character identifier of the image
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
// Parent is the ID of the parent image
|
||||||
|
Parent string `json:"parent,omitempty"`
|
||||||
|
// Comment is the commit message that was set when committing the image
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
// Created is the timestamp at which the image was created
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
// Container is the id of the container used to commit
|
||||||
|
Container string `json:"container,omitempty"`
|
||||||
|
// ContainerConfig is the configuration of the container that is committed into the image
|
||||||
|
ContainerConfig Schema2Config `json:"container_config,omitempty"`
|
||||||
|
// DockerVersion specifies the version of Docker that was used to build the image
|
||||||
|
DockerVersion string `json:"docker_version,omitempty"`
|
||||||
|
// Author is the name of the author that was specified when committing the image
|
||||||
|
Author string `json:"author,omitempty"`
|
||||||
|
// Config is the configuration of the container received from the client
|
||||||
|
Config *Schema2Config `json:"config,omitempty"`
|
||||||
|
// 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"`
|
||||||
|
// Size is the total size of the image including all layers it is composed of
|
||||||
|
Size int64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema2RootFS is a description of how to build up an image's root filesystem, from docker/docker/image.
|
||||||
|
type Schema2RootFS struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
DiffIDs []digest.Digest `json:"diff_ids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema2History stores build commands that were used to create an image, from docker/docker/image.
|
||||||
|
type Schema2History struct {
|
||||||
|
// Created is the timestamp at which the image was created
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
// Author is the name of the author that was specified when committing the image
|
||||||
|
Author string `json:"author,omitempty"`
|
||||||
|
// CreatedBy keeps the Dockerfile command used while building the image
|
||||||
|
CreatedBy string `json:"created_by,omitempty"`
|
||||||
|
// Comment is the commit message that was set when committing the image
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
// EmptyLayer is set to true if this history item did not generate a
|
||||||
|
// layer. Otherwise, the history item is associated with the next
|
||||||
|
// layer in the RootFS section.
|
||||||
|
EmptyLayer bool `json:"empty_layer,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema2Image is an Image in docker/docker/image.
|
||||||
|
type Schema2Image struct {
|
||||||
|
Schema2V1Image
|
||||||
|
Parent digest.Digest `json:"parent,omitempty"`
|
||||||
|
RootFS *Schema2RootFS `json:"rootfs,omitempty"`
|
||||||
|
History []Schema2History `json:"history,omitempty"`
|
||||||
|
OSVersion string `json:"os.version,omitempty"`
|
||||||
|
OSFeatures []string `json:"os.features,omitempty"`
|
||||||
|
|
||||||
|
// rawJSON caches the immutable JSON associated with this image.
|
||||||
|
rawJSON []byte
|
||||||
|
|
||||||
|
// computedID is the ID computed from the hash of the image config.
|
||||||
|
// Not to be confused with the legacy V1 ID in V1Image.
|
||||||
|
computedID digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema2FromManifest creates a Schema2 manifest instance from a manifest blob.
|
||||||
|
func Schema2FromManifest(manifest []byte) (*Schema2, error) {
|
||||||
|
s2 := Schema2{}
|
||||||
|
if err := json.Unmarshal(manifest, &s2); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &s2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema2FromComponents creates an Schema2 manifest instance from the supplied data.
|
||||||
|
func Schema2FromComponents(config Schema2Descriptor, layers []Schema2Descriptor) *Schema2 {
|
||||||
|
return &Schema2{
|
||||||
|
SchemaVersion: 2,
|
||||||
|
MediaType: DockerV2Schema2MediaType,
|
||||||
|
ConfigDescriptor: config,
|
||||||
|
LayersDescriptors: layers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema2Clone creates a copy of the supplied Schema2 manifest.
|
||||||
|
func Schema2Clone(src *Schema2) *Schema2 {
|
||||||
|
copy := *src
|
||||||
|
return ©
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
||||||
|
func (m *Schema2) 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 *Schema2) LayerInfos() []types.BlobInfo {
|
||||||
|
blobs := []types.BlobInfo{}
|
||||||
|
for _, layer := range m.LayersDescriptors {
|
||||||
|
blobs = append(blobs, types.BlobInfo{
|
||||||
|
Digest: layer.Digest,
|
||||||
|
Size: layer.Size,
|
||||||
|
URLs: layer.URLs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return blobs
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLayerInfos replaces the original layers with the specified BlobInfos (size+digest+urls), in order (the root layer first, and then successive layered layers)
|
||||||
|
func (m *Schema2) UpdateLayerInfos(layerInfos []types.BlobInfo) error {
|
||||||
|
if len(m.LayersDescriptors) != len(layerInfos) {
|
||||||
|
return errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(m.LayersDescriptors), len(layerInfos))
|
||||||
|
}
|
||||||
|
original := m.LayersDescriptors
|
||||||
|
m.LayersDescriptors = make([]Schema2Descriptor, len(layerInfos))
|
||||||
|
for i, info := range layerInfos {
|
||||||
|
m.LayersDescriptors[i].MediaType = original[i].MediaType
|
||||||
|
m.LayersDescriptors[i].Digest = info.Digest
|
||||||
|
m.LayersDescriptors[i].Size = info.Size
|
||||||
|
m.LayersDescriptors[i].URLs = info.URLs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize returns the manifest in a blob format.
|
||||||
|
// NOTE: Serialize() does not in general reproduce the original blob if this object was loaded from one, even if no modifications were made!
|
||||||
|
func (m *Schema2) Serialize() ([]byte, error) {
|
||||||
|
return json.Marshal(*m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
|
||||||
|
func (m *Schema2) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*types.ImageInspectInfo, error) {
|
||||||
|
config, err := configGetter(m.ConfigInfo())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s2 := &Schema2Image{}
|
||||||
|
if err := json.Unmarshal(config, s2); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
i := &types.ImageInspectInfo{
|
||||||
|
Tag: "",
|
||||||
|
Created: s2.Created,
|
||||||
|
DockerVersion: s2.DockerVersion,
|
||||||
|
Architecture: s2.Architecture,
|
||||||
|
Os: s2.OS,
|
||||||
|
Layers: LayerInfosToStrings(m.LayerInfos()),
|
||||||
|
}
|
||||||
|
if s2.Config != nil {
|
||||||
|
i.Labels = s2.Config.Labels
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageID computes an ID which can uniquely identify this image by its contents.
|
||||||
|
func (m *Schema2) ImageID([]digest.Digest) (string, error) {
|
||||||
|
if err := m.ConfigDescriptor.Digest.Validate(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return m.ConfigDescriptor.Digest.Hex(), nil
|
||||||
|
}
|
96
vendor/github.com/containers/image/manifest/manifest.go
generated
vendored
96
vendor/github.com/containers/image/manifest/manifest.go
generated
vendored
|
@ -2,7 +2,9 @@ package manifest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/image/types"
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
@ -35,7 +37,40 @@ var DefaultRequestedManifestMIMETypes = []string{
|
||||||
DockerV2Schema2MediaType,
|
DockerV2Schema2MediaType,
|
||||||
DockerV2Schema1SignedMediaType,
|
DockerV2Schema1SignedMediaType,
|
||||||
DockerV2Schema1MediaType,
|
DockerV2Schema1MediaType,
|
||||||
// DockerV2ListMediaType, // FIXME: Restore this ASAP
|
DockerV2ListMediaType,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manifest is an interface for parsing, modifying image manifests in isolation.
|
||||||
|
// Callers can either use this abstract interface without understanding the details of the formats,
|
||||||
|
// or instantiate a specific implementation (e.g. manifest.OCI1) and access the public members
|
||||||
|
// directly.
|
||||||
|
//
|
||||||
|
// See types.Image for functionality not limited to manifests, including format conversions and config parsing.
|
||||||
|
// This interface is similar to, but not strictly equivalent to, the equivalent methods in types.Image.
|
||||||
|
type Manifest interface {
|
||||||
|
// 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
|
||||||
|
// UpdateLayerInfos replaces the original layers with the specified BlobInfos (size+digest+urls), in order (the root layer first, and then successive layered layers)
|
||||||
|
UpdateLayerInfos(layerInfos []types.BlobInfo) error
|
||||||
|
|
||||||
|
// ImageID computes an ID which can uniquely identify this image by its contents, irrespective
|
||||||
|
// of which (of possibly more than one simultaneously valid) reference was used to locate the
|
||||||
|
// image, and unchanged by whether or how the layers are compressed. The result takes the form
|
||||||
|
// of the hexadecimal portion of a digest.Digest.
|
||||||
|
ImageID(diffIDs []digest.Digest) (string, error)
|
||||||
|
|
||||||
|
// Inspect returns various information for (skopeo inspect) parsed from the manifest,
|
||||||
|
// incorporating information from a configuration blob returned by configGetter, if
|
||||||
|
// the underlying image format is expected to include a configuration blob.
|
||||||
|
Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*types.ImageInspectInfo, error)
|
||||||
|
|
||||||
|
// Serialize returns the manifest in a blob format.
|
||||||
|
// NOTE: Serialize() does not in general reproduce the original blob if this object was loaded from one, even if no modifications were made!
|
||||||
|
Serialize() ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GuessMIMEType guesses MIME type of a manifest and returns it _if it is recognized_, or "" if unknown or unrecognized.
|
// GuessMIMEType guesses MIME type of a manifest and returns it _if it is recognized_, or "" if unknown or unrecognized.
|
||||||
|
@ -142,3 +177,62 @@ func AddDummyV2S1Signature(manifest []byte) ([]byte, error) {
|
||||||
}
|
}
|
||||||
return js.PrettySignature("signatures")
|
return js.PrettySignature("signatures")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MIMETypeIsMultiImage returns true if mimeType is a list of images
|
||||||
|
func MIMETypeIsMultiImage(mimeType string) bool {
|
||||||
|
return mimeType == DockerV2ListMediaType
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizedMIMEType returns the effective MIME type of a manifest MIME type returned by a server,
|
||||||
|
// centralizing various workarounds.
|
||||||
|
func NormalizedMIMEType(input string) string {
|
||||||
|
switch input {
|
||||||
|
// "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 "application/json":
|
||||||
|
return DockerV2Schema1SignedMediaType
|
||||||
|
case DockerV2Schema1MediaType, DockerV2Schema1SignedMediaType,
|
||||||
|
imgspecv1.MediaTypeImageManifest,
|
||||||
|
DockerV2Schema2MediaType,
|
||||||
|
DockerV2ListMediaType:
|
||||||
|
return input
|
||||||
|
default:
|
||||||
|
// If it's not a recognized manifest media type, or we have failed determining the type, we'll try one last time
|
||||||
|
// to deserialize using v2s1 as per https://github.com/docker/distribution/blob/master/manifests.go#L108
|
||||||
|
// and https://github.com/docker/distribution/blob/master/manifest/schema1/manifest.go#L50
|
||||||
|
//
|
||||||
|
// Crane registries can also return "text/plain", or pretty much anything else depending on a file extension “recognized” in the tag.
|
||||||
|
// This makes no real sense, but it happens
|
||||||
|
// because requests for manifests are
|
||||||
|
// redirected to a content distribution
|
||||||
|
// network which is configured that way. See https://bugzilla.redhat.com/show_bug.cgi?id=1389442
|
||||||
|
return DockerV2Schema1SignedMediaType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromBlob returns a Manifest instance for the specified manifest blob and the corresponding MIME type
|
||||||
|
func FromBlob(manblob []byte, mt string) (Manifest, error) {
|
||||||
|
switch NormalizedMIMEType(mt) {
|
||||||
|
case DockerV2Schema1MediaType, DockerV2Schema1SignedMediaType:
|
||||||
|
return Schema1FromManifest(manblob)
|
||||||
|
case imgspecv1.MediaTypeImageManifest:
|
||||||
|
return OCI1FromManifest(manblob)
|
||||||
|
case DockerV2Schema2MediaType:
|
||||||
|
return Schema2FromManifest(manblob)
|
||||||
|
case DockerV2ListMediaType:
|
||||||
|
return nil, fmt.Errorf("Treating manifest lists as individual manifests is not implemented")
|
||||||
|
default: // Note that this may not be reachable, NormalizedMIMEType has a default for unknown values.
|
||||||
|
return nil, fmt.Errorf("Unimplemented manifest MIME type %s", mt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerInfosToStrings converts a list of layer infos, presumably obtained from a Manifest.LayerInfos()
|
||||||
|
// method call, into a format suitable for inclusion in a types.ImageInspectInfo structure.
|
||||||
|
func LayerInfosToStrings(infos []types.BlobInfo) []string {
|
||||||
|
layers := make([]string, len(infos))
|
||||||
|
for i, info := range infos {
|
||||||
|
layers[i] = info.Digest.String()
|
||||||
|
}
|
||||||
|
return layers
|
||||||
|
}
|
||||||
|
|
120
vendor/github.com/containers/image/manifest/oci.go
generated
vendored
Normal file
120
vendor/github.com/containers/image/manifest/oci.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
"github.com/opencontainers/image-spec/specs-go"
|
||||||
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OCI1 is a manifest.Manifest implementation for OCI images.
|
||||||
|
// The underlying data from imgspecv1.Manifest is also available.
|
||||||
|
type OCI1 struct {
|
||||||
|
imgspecv1.Manifest
|
||||||
|
}
|
||||||
|
|
||||||
|
// OCI1FromManifest creates an OCI1 manifest instance from a manifest blob.
|
||||||
|
func OCI1FromManifest(manifest []byte) (*OCI1, error) {
|
||||||
|
oci1 := OCI1{}
|
||||||
|
if err := json.Unmarshal(manifest, &oci1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &oci1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OCI1FromComponents creates an OCI1 manifest instance from the supplied data.
|
||||||
|
func OCI1FromComponents(config imgspecv1.Descriptor, layers []imgspecv1.Descriptor) *OCI1 {
|
||||||
|
return &OCI1{
|
||||||
|
imgspecv1.Manifest{
|
||||||
|
Versioned: specs.Versioned{SchemaVersion: 2},
|
||||||
|
Config: config,
|
||||||
|
Layers: layers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OCI1Clone creates a copy of the supplied OCI1 manifest.
|
||||||
|
func OCI1Clone(src *OCI1) *OCI1 {
|
||||||
|
return &OCI1{
|
||||||
|
Manifest: src.Manifest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
||||||
|
func (m *OCI1) ConfigInfo() types.BlobInfo {
|
||||||
|
return types.BlobInfo{Digest: m.Config.Digest, Size: m.Config.Size, Annotations: m.Config.Annotations}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *OCI1) LayerInfos() []types.BlobInfo {
|
||||||
|
blobs := []types.BlobInfo{}
|
||||||
|
for _, layer := range m.Layers {
|
||||||
|
blobs = append(blobs, types.BlobInfo{Digest: layer.Digest, Size: layer.Size, Annotations: layer.Annotations, URLs: layer.URLs, MediaType: layer.MediaType})
|
||||||
|
}
|
||||||
|
return blobs
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLayerInfos replaces the original layers with the specified BlobInfos (size+digest+urls), in order (the root layer first, and then successive layered layers)
|
||||||
|
func (m *OCI1) UpdateLayerInfos(layerInfos []types.BlobInfo) error {
|
||||||
|
if len(m.Layers) != len(layerInfos) {
|
||||||
|
return errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(m.Layers), len(layerInfos))
|
||||||
|
}
|
||||||
|
original := m.Layers
|
||||||
|
m.Layers = make([]imgspecv1.Descriptor, len(layerInfos))
|
||||||
|
for i, info := range layerInfos {
|
||||||
|
m.Layers[i].MediaType = original[i].MediaType
|
||||||
|
m.Layers[i].Digest = info.Digest
|
||||||
|
m.Layers[i].Size = info.Size
|
||||||
|
m.Layers[i].Annotations = info.Annotations
|
||||||
|
m.Layers[i].URLs = info.URLs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize returns the manifest in a blob format.
|
||||||
|
// NOTE: Serialize() does not in general reproduce the original blob if this object was loaded from one, even if no modifications were made!
|
||||||
|
func (m *OCI1) Serialize() ([]byte, error) {
|
||||||
|
return json.Marshal(*m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
|
||||||
|
func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*types.ImageInspectInfo, error) {
|
||||||
|
config, err := configGetter(m.ConfigInfo())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v1 := &imgspecv1.Image{}
|
||||||
|
if err := json.Unmarshal(config, v1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d1 := &Schema2V1Image{}
|
||||||
|
json.Unmarshal(config, d1)
|
||||||
|
created := time.Time{}
|
||||||
|
if v1.Created != nil {
|
||||||
|
created = *v1.Created
|
||||||
|
}
|
||||||
|
i := &types.ImageInspectInfo{
|
||||||
|
Tag: "",
|
||||||
|
Created: created,
|
||||||
|
DockerVersion: d1.DockerVersion,
|
||||||
|
Labels: v1.Config.Labels,
|
||||||
|
Architecture: v1.Architecture,
|
||||||
|
Os: v1.OS,
|
||||||
|
Layers: LayerInfosToStrings(m.LayerInfos()),
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageID computes an ID which can uniquely identify this image by its contents.
|
||||||
|
func (m *OCI1) ImageID([]digest.Digest) (string, error) {
|
||||||
|
if err := m.Config.Digest.Validate(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return m.Config.Digest.Hex(), nil
|
||||||
|
}
|
27
vendor/github.com/containers/image/oci/archive/oci_src.go
generated
vendored
27
vendor/github.com/containers/image/oci/archive/oci_src.go
generated
vendored
|
@ -68,14 +68,12 @@ func (s *ociArchiveImageSource) Close() error {
|
||||||
return s.unpackedSrc.Close()
|
return s.unpackedSrc.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetManifest returns the image's manifest along with its MIME type
|
// 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).
|
||||||
// (which may be empty when it can't be determined but the manifest is available).
|
// It may use a remote (= slow) service.
|
||||||
func (s *ociArchiveImageSource) GetManifest() ([]byte, string, error) {
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list);
|
||||||
return s.unpackedSrc.GetManifest()
|
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
|
||||||
}
|
func (s *ociArchiveImageSource) GetManifest(instanceDigest *digest.Digest) ([]byte, string, error) {
|
||||||
|
return s.unpackedSrc.GetManifest(instanceDigest)
|
||||||
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.
|
// GetBlob returns a stream for the specified blob, and the blob's size.
|
||||||
|
@ -83,6 +81,15 @@ func (s *ociArchiveImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int
|
||||||
return s.unpackedSrc.GetBlob(info)
|
return s.unpackedSrc.GetBlob(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ociArchiveImageSource) GetSignatures(c context.Context) ([][]byte, error) {
|
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||||
return s.unpackedSrc.GetSignatures(c)
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve signatures for
|
||||||
|
// (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list
|
||||||
|
// (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *ociArchiveImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||||
|
return s.unpackedSrc.GetSignatures(ctx, instanceDigest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerInfosForCopy() returns updated layer info that should be used when reading, in preference to values in the manifest, if specified.
|
||||||
|
func (s *ociArchiveImageSource) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
73
vendor/github.com/containers/image/oci/archive/oci_transport.go
generated
vendored
73
vendor/github.com/containers/image/oci/archive/oci_transport.go
generated
vendored
|
@ -4,13 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/image/directory/explicitfilepath"
|
"github.com/containers/image/directory/explicitfilepath"
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/image"
|
"github.com/containers/image/image"
|
||||||
|
"github.com/containers/image/internal/tmpdir"
|
||||||
|
"github.com/containers/image/oci/internal"
|
||||||
ocilayout "github.com/containers/image/oci/layout"
|
ocilayout "github.com/containers/image/oci/layout"
|
||||||
"github.com/containers/image/transports"
|
"github.com/containers/image/transports"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
|
@ -48,51 +48,12 @@ func (t ociArchiveTransport) ParseReference(reference string) (types.ImageRefere
|
||||||
|
|
||||||
// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
|
// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
|
||||||
func (t ociArchiveTransport) ValidatePolicyConfigurationScope(scope string) error {
|
func (t ociArchiveTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||||
var file string
|
return internal.ValidateScope(scope)
|
||||||
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.
|
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OCI ImageReference.
|
||||||
func ParseReference(reference string) (types.ImageReference, error) {
|
func ParseReference(reference string) (types.ImageReference, error) {
|
||||||
var file, image string
|
file, image := internal.SplitPathAndImage(reference)
|
||||||
sep := strings.SplitN(reference, ":", 2)
|
|
||||||
file = sep[0]
|
|
||||||
|
|
||||||
if len(sep) == 2 {
|
|
||||||
image = sep[1]
|
|
||||||
}
|
|
||||||
return NewReference(file, image)
|
return NewReference(file, image)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,14 +63,15 @@ func NewReference(file, image string) (types.ImageReference, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// This is necessary to prevent directory paths returned by PolicyConfigurationNamespaces
|
|
||||||
// from being ambiguous with values of PolicyConfigurationIdentity.
|
if err := internal.ValidateOCIPath(file); err != nil {
|
||||||
if strings.Contains(resolved, ":") {
|
return nil, err
|
||||||
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)
|
if err := internal.ValidateImageName(image); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ociArchiveReference{file: file, resolvedFile: resolved, image: image}, nil
|
return ociArchiveReference{file: file, resolvedFile: resolved, image: image}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,14 +116,17 @@ func (ref ociArchiveReference) PolicyConfigurationNamespaces() []string {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
// The caller must call .Close() on the returned Image.
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
func (ref ociArchiveReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
// 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.
|
||||||
|
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||||
|
func (ref ociArchiveReference) NewImage(ctx *types.SystemContext) (types.ImageCloser, error) {
|
||||||
src, err := newImageSource(ctx, ref)
|
src, err := newImageSource(ctx, ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return image.FromSource(src)
|
return image.FromSource(ctx, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImageSource returns a types.ImageSource for this reference.
|
// NewImageSource returns a types.ImageSource for this reference.
|
||||||
|
@ -194,7 +159,7 @@ func (t *tempDirOCIRef) deleteTempDir() error {
|
||||||
|
|
||||||
// createOCIRef creates the oci reference of the image
|
// createOCIRef creates the oci reference of the image
|
||||||
func createOCIRef(image string) (tempDirOCIRef, error) {
|
func createOCIRef(image string) (tempDirOCIRef, error) {
|
||||||
dir, err := ioutil.TempDir("/var/tmp", "oci")
|
dir, err := ioutil.TempDir(tmpdir.TemporaryDirectoryForBigFiles(), "oci")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tempDirOCIRef{}, errors.Wrapf(err, "error creating temp directory")
|
return tempDirOCIRef{}, errors.Wrapf(err, "error creating temp directory")
|
||||||
}
|
}
|
||||||
|
|
126
vendor/github.com/containers/image/oci/internal/oci_util.go
generated
vendored
Normal file
126
vendor/github.com/containers/image/oci/internal/oci_util.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 + `)*$`)
|
||||||
|
var windowsRefRegexp = regexp.MustCompile(`^([a-zA-Z]:\\.+?):(.*)$`)
|
||||||
|
|
||||||
|
// ValidateImageName returns nil if the image name is empty or matches the open-containers image name specs.
|
||||||
|
// In any other case an error is returned.
|
||||||
|
func ValidateImageName(image string) error {
|
||||||
|
if len(image) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if !refRegexp.MatchString(image) {
|
||||||
|
err = errors.Errorf("Invalid image %s", image)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitPathAndImage tries to split the provided OCI reference into the OCI path and image.
|
||||||
|
// Neither path nor image parts are validated at this stage.
|
||||||
|
func SplitPathAndImage(reference string) (string, string) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return splitPathAndImageWindows(reference)
|
||||||
|
}
|
||||||
|
return splitPathAndImageNonWindows(reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitPathAndImageWindows(reference string) (string, string) {
|
||||||
|
groups := windowsRefRegexp.FindStringSubmatch(reference)
|
||||||
|
// nil group means no match
|
||||||
|
if groups == nil {
|
||||||
|
return reference, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// we expect three elements. First one full match, second the capture group for the path and
|
||||||
|
// the third the capture group for the image
|
||||||
|
if len(groups) != 3 {
|
||||||
|
return reference, ""
|
||||||
|
}
|
||||||
|
return groups[1], groups[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitPathAndImageNonWindows(reference string) (string, string) {
|
||||||
|
sep := strings.SplitN(reference, ":", 2)
|
||||||
|
path := sep[0]
|
||||||
|
|
||||||
|
var image string
|
||||||
|
if len(sep) == 2 {
|
||||||
|
image = sep[1]
|
||||||
|
}
|
||||||
|
return path, image
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateOCIPath takes the OCI path and validates it.
|
||||||
|
func ValidateOCIPath(path string) error {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// On Windows we must allow for a ':' as part of the path
|
||||||
|
if strings.Count(path, ":") > 1 {
|
||||||
|
return errors.Errorf("Invalid OCI reference: path %s contains more than one colon", path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if strings.Contains(path, ":") {
|
||||||
|
return errors.Errorf("Invalid OCI reference: path %s contains a colon", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateScope validates a policy configuration scope for an OCI transport.
|
||||||
|
func ValidateScope(scope string) error {
|
||||||
|
var err error
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
err = validateScopeWindows(scope)
|
||||||
|
} else {
|
||||||
|
err = validateScopeNonWindows(scope)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cleaned := filepath.Clean(scope)
|
||||||
|
if cleaned != scope {
|
||||||
|
return errors.Errorf(`Invalid scope %s: Uses non-canonical path format, perhaps try with path %s`, scope, cleaned)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateScopeWindows(scope string) error {
|
||||||
|
matched, _ := regexp.Match(`^[a-zA-Z]:\\`, []byte(scope))
|
||||||
|
if !matched {
|
||||||
|
return errors.Errorf("Invalid scope '%s'. Must be an absolute path", scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateScopeNonWindows(scope string) error {
|
||||||
|
if !strings.HasPrefix(scope, "/") {
|
||||||
|
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.
|
||||||
|
if scope == "/" {
|
||||||
|
return errors.New(`Invalid scope "/": Use the generic default scope ""`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
117
vendor/github.com/containers/image/oci/layout/oci_dest.go
generated
vendored
117
vendor/github.com/containers/image/oci/layout/oci_dest.go
generated
vendored
|
@ -18,21 +18,47 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ociImageDestination struct {
|
type ociImageDestination struct {
|
||||||
ref ociReference
|
ref ociReference
|
||||||
index imgspecv1.Index
|
index imgspecv1.Index
|
||||||
|
sharedBlobDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
// newImageDestination returns an ImageDestination for writing to an existing directory.
|
// newImageDestination returns an ImageDestination for writing to an existing directory.
|
||||||
func newImageDestination(ref ociReference) (types.ImageDestination, error) {
|
func newImageDestination(ctx *types.SystemContext, ref ociReference) (types.ImageDestination, error) {
|
||||||
if ref.image == "" {
|
if ref.image == "" {
|
||||||
return nil, errors.Errorf("cannot save image with empty image.ref.name")
|
return nil, errors.Errorf("cannot save image with empty image.ref.name")
|
||||||
}
|
}
|
||||||
index := imgspecv1.Index{
|
|
||||||
Versioned: imgspec.Versioned{
|
var index *imgspecv1.Index
|
||||||
SchemaVersion: 2,
|
if indexExists(ref) {
|
||||||
},
|
var err error
|
||||||
|
index, err = ref.getIndex()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
index = &imgspecv1.Index{
|
||||||
|
Versioned: imgspec.Versioned{
|
||||||
|
SchemaVersion: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &ociImageDestination{ref: ref, index: index}, nil
|
|
||||||
|
d := &ociImageDestination{ref: ref, index: *index}
|
||||||
|
if ctx != nil {
|
||||||
|
d.sharedBlobDir = ctx.OCISharedBlobDirPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ensureDirectoryExists(d.ref.dir); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Per the OCI image specification, layouts MUST have a "blobs" subdirectory,
|
||||||
|
// but it MAY be empty (e.g. if we never end up calling PutBlob)
|
||||||
|
// https://github.com/opencontainers/image-spec/blame/7c889fafd04a893f5c5f50b7ab9963d5d64e5242/image-layout.md#L19
|
||||||
|
if err := ensureDirectoryExists(filepath.Join(d.ref.dir, "blobs")); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
||||||
|
@ -81,16 +107,16 @@ func (d *ociImageDestination) MustMatchRuntimeOS() bool {
|
||||||
// to any other readers for download using the supplied digest.
|
// to any other readers for download using the supplied digest.
|
||||||
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
|
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
|
||||||
func (d *ociImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo) (types.BlobInfo, error) {
|
func (d *ociImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo) (types.BlobInfo, error) {
|
||||||
if err := ensureDirectoryExists(d.ref.dir); err != nil {
|
|
||||||
return types.BlobInfo{}, err
|
|
||||||
}
|
|
||||||
blobFile, err := ioutil.TempFile(d.ref.dir, "oci-put-blob")
|
blobFile, err := ioutil.TempFile(d.ref.dir, "oci-put-blob")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.BlobInfo{}, err
|
return types.BlobInfo{}, err
|
||||||
}
|
}
|
||||||
succeeded := false
|
succeeded := false
|
||||||
|
explicitClosed := false
|
||||||
defer func() {
|
defer func() {
|
||||||
blobFile.Close()
|
if !explicitClosed {
|
||||||
|
blobFile.Close()
|
||||||
|
}
|
||||||
if !succeeded {
|
if !succeeded {
|
||||||
os.Remove(blobFile.Name())
|
os.Remove(blobFile.Name())
|
||||||
}
|
}
|
||||||
|
@ -110,17 +136,28 @@ func (d *ociImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo
|
||||||
if err := blobFile.Sync(); err != nil {
|
if err := blobFile.Sync(); err != nil {
|
||||||
return types.BlobInfo{}, err
|
return types.BlobInfo{}, err
|
||||||
}
|
}
|
||||||
if err := blobFile.Chmod(0644); err != nil {
|
|
||||||
return types.BlobInfo{}, err
|
// On POSIX systems, blobFile was created with mode 0600, so we need to make it readable.
|
||||||
|
// On Windows, the “permissions of newly created files” argument to syscall.Open is
|
||||||
|
// ignored and the file is already readable; besides, blobFile.Chmod, i.e. syscall.Fchmod,
|
||||||
|
// always fails on Windows.
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
if err := blobFile.Chmod(0644); err != nil {
|
||||||
|
return types.BlobInfo{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blobPath, err := d.ref.blobPath(computedDigest)
|
blobPath, err := d.ref.blobPath(computedDigest, d.sharedBlobDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.BlobInfo{}, err
|
return types.BlobInfo{}, err
|
||||||
}
|
}
|
||||||
if err := ensureParentDirectoryExists(blobPath); err != nil {
|
if err := ensureParentDirectoryExists(blobPath); err != nil {
|
||||||
return types.BlobInfo{}, err
|
return types.BlobInfo{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// need to explicitly close the file, since a rename won't otherwise not work on Windows
|
||||||
|
blobFile.Close()
|
||||||
|
explicitClosed = true
|
||||||
if err := os.Rename(blobFile.Name(), blobPath); err != nil {
|
if err := os.Rename(blobFile.Name(), blobPath); err != nil {
|
||||||
return types.BlobInfo{}, err
|
return types.BlobInfo{}, err
|
||||||
}
|
}
|
||||||
|
@ -136,7 +173,7 @@ func (d *ociImageDestination) HasBlob(info types.BlobInfo) (bool, int64, error)
|
||||||
if info.Digest == "" {
|
if info.Digest == "" {
|
||||||
return false, -1, errors.Errorf(`"Can not check for a blob with unknown digest`)
|
return false, -1, errors.Errorf(`"Can not check for a blob with unknown digest`)
|
||||||
}
|
}
|
||||||
blobPath, err := d.ref.blobPath(info.Digest)
|
blobPath, err := d.ref.blobPath(info.Digest, d.sharedBlobDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, -1, err
|
return false, -1, err
|
||||||
}
|
}
|
||||||
|
@ -169,7 +206,7 @@ func (d *ociImageDestination) PutManifest(m []byte) error {
|
||||||
desc.MediaType = imgspecv1.MediaTypeImageManifest
|
desc.MediaType = imgspecv1.MediaTypeImageManifest
|
||||||
desc.Size = int64(len(m))
|
desc.Size = int64(len(m))
|
||||||
|
|
||||||
blobPath, err := d.ref.blobPath(digest)
|
blobPath, err := d.ref.blobPath(digest, d.sharedBlobDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -191,23 +228,20 @@ func (d *ociImageDestination) PutManifest(m []byte) error {
|
||||||
Architecture: runtime.GOARCH,
|
Architecture: runtime.GOARCH,
|
||||||
OS: runtime.GOOS,
|
OS: runtime.GOOS,
|
||||||
}
|
}
|
||||||
d.index.Manifests = append(d.index.Manifests, desc)
|
d.addManifest(&desc)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureDirectoryExists(path string) error {
|
func (d *ociImageDestination) addManifest(desc *imgspecv1.Descriptor) {
|
||||||
if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
|
for i, manifest := range d.index.Manifests {
|
||||||
if err := os.MkdirAll(path, 0755); err != nil {
|
if manifest.Annotations["org.opencontainers.image.ref.name"] == desc.Annotations["org.opencontainers.image.ref.name"] {
|
||||||
return err
|
// TODO Should there first be a cleanup based on the descriptor we are going to replace?
|
||||||
|
d.index.Manifests[i] = *desc
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
d.index.Manifests = append(d.index.Manifests, *desc)
|
||||||
}
|
|
||||||
|
|
||||||
// ensureParentDirectoryExists ensures the parent of the supplied path exists.
|
|
||||||
func ensureParentDirectoryExists(path string) error {
|
|
||||||
return ensureDirectoryExists(filepath.Dir(path))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ociImageDestination) PutSignatures(signatures [][]byte) error {
|
func (d *ociImageDestination) PutSignatures(signatures [][]byte) error {
|
||||||
|
@ -231,3 +265,30 @@ func (d *ociImageDestination) Commit() error {
|
||||||
}
|
}
|
||||||
return ioutil.WriteFile(d.ref.indexPath(), indexJSON, 0644)
|
return ioutil.WriteFile(d.ref.indexPath(), indexJSON, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureDirectoryExists(path string) error {
|
||||||
|
if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
|
||||||
|
if err := os.MkdirAll(path, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureParentDirectoryExists ensures the parent of the supplied path exists.
|
||||||
|
func ensureParentDirectoryExists(path string) error {
|
||||||
|
return ensureDirectoryExists(filepath.Dir(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
// indexExists checks whether the index location specified in the OCI reference exists.
|
||||||
|
// The implementation is opinionated, since in case of unexpected errors false is returned
|
||||||
|
func indexExists(ref ociReference) bool {
|
||||||
|
_, err := os.Stat(ref.indexPath())
|
||||||
|
if err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
69
vendor/github.com/containers/image/oci/layout/oci_src.go
generated
vendored
69
vendor/github.com/containers/image/oci/layout/oci_src.go
generated
vendored
|
@ -17,9 +17,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ociImageSource struct {
|
type ociImageSource struct {
|
||||||
ref ociReference
|
ref ociReference
|
||||||
descriptor imgspecv1.Descriptor
|
descriptor imgspecv1.Descriptor
|
||||||
client *http.Client
|
client *http.Client
|
||||||
|
sharedBlobDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
// newImageSource returns an ImageSource for reading from an existing directory.
|
// newImageSource returns an ImageSource for reading from an existing directory.
|
||||||
|
@ -40,7 +41,12 @@ func newImageSource(ctx *types.SystemContext, ref ociReference) (types.ImageSour
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &ociImageSource{ref: ref, descriptor: descriptor, client: client}, nil
|
d := &ociImageSource{ref: ref, descriptor: descriptor, client: client}
|
||||||
|
if ctx != nil {
|
||||||
|
// TODO(jonboulle): check dir existence?
|
||||||
|
d.sharedBlobDir = ctx.OCISharedBlobDirPath
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference returns the reference used to set up this source.
|
// Reference returns the reference used to set up this source.
|
||||||
|
@ -55,8 +61,26 @@ func (s *ociImageSource) Close() error {
|
||||||
|
|
||||||
// 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).
|
// 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).
|
||||||
// It may use a remote (= slow) service.
|
// It may use a remote (= slow) service.
|
||||||
func (s *ociImageSource) GetManifest() ([]byte, string, error) {
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list);
|
||||||
manifestPath, err := s.ref.blobPath(digest.Digest(s.descriptor.Digest))
|
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *ociImageSource) GetManifest(instanceDigest *digest.Digest) ([]byte, string, error) {
|
||||||
|
var dig digest.Digest
|
||||||
|
var mimeType string
|
||||||
|
if instanceDigest == nil {
|
||||||
|
dig = digest.Digest(s.descriptor.Digest)
|
||||||
|
mimeType = s.descriptor.MediaType
|
||||||
|
} else {
|
||||||
|
dig = *instanceDigest
|
||||||
|
// XXX: instanceDigest means that we don't immediately have the context of what
|
||||||
|
// mediaType the manifest has. In OCI this means that we don't know
|
||||||
|
// what reference it came from, so we just *assume* that its
|
||||||
|
// MediaTypeImageManifest.
|
||||||
|
// FIXME: We should actually be able to look up the manifest in the index,
|
||||||
|
// and see the MIME type there.
|
||||||
|
mimeType = imgspecv1.MediaTypeImageManifest
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestPath, err := s.ref.blobPath(dig, s.sharedBlobDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
@ -65,25 +89,7 @@ func (s *ociImageSource) GetManifest() ([]byte, string, error) {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, s.descriptor.MediaType, nil
|
return m, mimeType, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ociImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) {
|
|
||||||
manifestPath, err := s.ref.blobPath(digest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := ioutil.ReadFile(manifestPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX: GetTargetManifest means that we don't have the context of what
|
|
||||||
// mediaType the manifest has. In OCI this means that we don't know
|
|
||||||
// what reference it came from, so we just *assume* that its
|
|
||||||
// MediaTypeImageManifest.
|
|
||||||
return m, imgspecv1.MediaTypeImageManifest, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlob returns a stream for the specified blob, and the blob's size.
|
// GetBlob returns a stream for the specified blob, and the blob's size.
|
||||||
|
@ -92,7 +98,7 @@ func (s *ociImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, err
|
||||||
return s.getExternalBlob(info.URLs)
|
return s.getExternalBlob(info.URLs)
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err := s.ref.blobPath(info.Digest)
|
path, err := s.ref.blobPath(info.Digest, s.sharedBlobDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
@ -108,7 +114,11 @@ func (s *ociImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, err
|
||||||
return r, fi.Size(), nil
|
return r, fi.Size(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ociImageSource) GetSignatures(context.Context) ([][]byte, error) {
|
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||||
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve signatures for
|
||||||
|
// (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list
|
||||||
|
// (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *ociImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||||
return [][]byte{}, nil
|
return [][]byte{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +143,11 @@ func (s *ociImageSource) getExternalBlob(urls []string) (io.ReadCloser, int64, e
|
||||||
return nil, 0, errWrap
|
return nil, 0, errWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerInfosForCopy() returns updated layer info that should be used when reading, in preference to values in the manifest, if specified.
|
||||||
|
func (s *ociImageSource) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getBlobSize(resp *http.Response) int64 {
|
func getBlobSize(resp *http.Response) int64 {
|
||||||
size, err := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64)
|
size, err := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
100
vendor/github.com/containers/image/oci/layout/oci_transport.go
generated
vendored
100
vendor/github.com/containers/image/oci/layout/oci_transport.go
generated
vendored
|
@ -5,12 +5,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/image/directory/explicitfilepath"
|
"github.com/containers/image/directory/explicitfilepath"
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/image"
|
"github.com/containers/image/image"
|
||||||
|
"github.com/containers/image/oci/internal"
|
||||||
"github.com/containers/image/transports"
|
"github.com/containers/image/transports"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
|
@ -36,45 +36,12 @@ func (t ociTransport) ParseReference(reference string) (types.ImageReference, er
|
||||||
return ParseReference(reference)
|
return ParseReference(reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
|
||||||
// (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value).
|
// (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value).
|
||||||
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
|
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
|
||||||
// scope passed to this function will not be "", that value is always allowed.
|
// scope passed to this function will not be "", that value is always allowed.
|
||||||
func (t ociTransport) ValidatePolicyConfigurationScope(scope string) error {
|
func (t ociTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||||
var dir string
|
return internal.ValidateScope(scope)
|
||||||
sep := strings.SplitN(scope, ":", 2)
|
|
||||||
dir = sep[0]
|
|
||||||
|
|
||||||
if len(sep) == 2 {
|
|
||||||
image := sep[1]
|
|
||||||
if !refRegexp.MatchString(image) {
|
|
||||||
return errors.Errorf("Invalid image %s", image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(dir, "/") {
|
|
||||||
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(dir)
|
|
||||||
if cleaned != dir {
|
|
||||||
return errors.Errorf(`Invalid scope %s: Uses non-canonical path format, perhaps try with path %s`, scope, cleaned)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ociReference is an ImageReference for OCI directory paths.
|
// ociReference is an ImageReference for OCI directory paths.
|
||||||
|
@ -92,13 +59,7 @@ type ociReference struct {
|
||||||
|
|
||||||
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OCI ImageReference.
|
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OCI ImageReference.
|
||||||
func ParseReference(reference string) (types.ImageReference, error) {
|
func ParseReference(reference string) (types.ImageReference, error) {
|
||||||
var dir, image string
|
dir, image := internal.SplitPathAndImage(reference)
|
||||||
sep := strings.SplitN(reference, ":", 2)
|
|
||||||
dir = sep[0]
|
|
||||||
|
|
||||||
if len(sep) == 2 {
|
|
||||||
image = sep[1]
|
|
||||||
}
|
|
||||||
return NewReference(dir, image)
|
return NewReference(dir, image)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,14 +72,15 @@ func NewReference(dir, image string) (types.ImageReference, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// This is necessary to prevent directory paths returned by PolicyConfigurationNamespaces
|
|
||||||
// from being ambiguous with values of PolicyConfigurationIdentity.
|
if err := internal.ValidateOCIPath(dir); err != nil {
|
||||||
if strings.Contains(resolved, ":") {
|
return nil, err
|
||||||
return nil, errors.Errorf("Invalid OCI reference %s:%s: path %s contains a colon", dir, image, resolved)
|
|
||||||
}
|
}
|
||||||
if len(image) > 0 && !refRegexp.MatchString(image) {
|
|
||||||
return nil, errors.Errorf("Invalid image %s", image)
|
if err = internal.ValidateImageName(image); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ociReference{dir: dir, resolvedDir: resolved, image: image}, nil
|
return ociReference{dir: dir, resolvedDir: resolved, image: image}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,28 +139,40 @@ func (ref ociReference) PolicyConfigurationNamespaces() []string {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
// The caller must call .Close() on the returned Image.
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
|
// 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.
|
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
|
||||||
func (ref ociReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||||
|
func (ref ociReference) NewImage(ctx *types.SystemContext) (types.ImageCloser, error) {
|
||||||
src, err := newImageSource(ctx, ref)
|
src, err := newImageSource(ctx, ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return image.FromSource(src)
|
return image.FromSource(ctx, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getIndex returns a pointer to the index references by this ociReference. If an error occurs opening an index nil is returned together
|
||||||
|
// with an error.
|
||||||
|
func (ref ociReference) getIndex() (*imgspecv1.Index, error) {
|
||||||
|
indexJSON, err := os.Open(ref.indexPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer indexJSON.Close()
|
||||||
|
|
||||||
|
index := &imgspecv1.Index{}
|
||||||
|
if err := json.NewDecoder(indexJSON).Decode(index); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return index, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ref ociReference) getManifestDescriptor() (imgspecv1.Descriptor, error) {
|
func (ref ociReference) getManifestDescriptor() (imgspecv1.Descriptor, error) {
|
||||||
indexJSON, err := os.Open(ref.indexPath())
|
index, err := ref.getIndex()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return imgspecv1.Descriptor{}, err
|
return imgspecv1.Descriptor{}, err
|
||||||
}
|
}
|
||||||
defer indexJSON.Close()
|
|
||||||
index := imgspecv1.Index{}
|
|
||||||
if err := json.NewDecoder(indexJSON).Decode(&index); err != nil {
|
|
||||||
return imgspecv1.Descriptor{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var d *imgspecv1.Descriptor
|
var d *imgspecv1.Descriptor
|
||||||
if ref.image == "" {
|
if ref.image == "" {
|
||||||
|
@ -250,7 +224,7 @@ func (ref ociReference) NewImageSource(ctx *types.SystemContext) (types.ImageSou
|
||||||
// NewImageDestination returns a types.ImageDestination for this reference.
|
// NewImageDestination returns a types.ImageDestination for this reference.
|
||||||
// The caller must call .Close() on the returned ImageDestination.
|
// The caller must call .Close() on the returned ImageDestination.
|
||||||
func (ref ociReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
func (ref ociReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||||
return newImageDestination(ref)
|
return newImageDestination(ctx, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteImage deletes the named image from the registry, if supported.
|
// DeleteImage deletes the named image from the registry, if supported.
|
||||||
|
@ -269,9 +243,13 @@ func (ref ociReference) indexPath() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// blobPath returns a path for a blob within a directory using OCI image-layout conventions.
|
// blobPath returns a path for a blob within a directory using OCI image-layout conventions.
|
||||||
func (ref ociReference) blobPath(digest digest.Digest) (string, error) {
|
func (ref ociReference) blobPath(digest digest.Digest, sharedBlobDir string) (string, error) {
|
||||||
if err := digest.Validate(); err != nil {
|
if err := digest.Validate(); err != nil {
|
||||||
return "", errors.Wrapf(err, "unexpected digest reference %s", digest)
|
return "", errors.Wrapf(err, "unexpected digest reference %s", digest)
|
||||||
}
|
}
|
||||||
return filepath.Join(ref.dir, "blobs", digest.Algorithm().String(), digest.Hex()), nil
|
blobDir := filepath.Join(ref.dir, "blobs")
|
||||||
|
if sharedBlobDir != "" {
|
||||||
|
blobDir = sharedBlobDir
|
||||||
|
}
|
||||||
|
return filepath.Join(blobDir, digest.Algorithm().String(), digest.Hex()), nil
|
||||||
}
|
}
|
||||||
|
|
37
vendor/github.com/containers/image/openshift/openshift.go
generated
vendored
37
vendor/github.com/containers/image/openshift/openshift.go
generated
vendored
|
@ -200,20 +200,15 @@ func (s *openshiftImageSource) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *openshiftImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) {
|
|
||||||
if err := s.ensureImageIsResolved(context.TODO()); err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
return s.docker.GetTargetManifest(digest)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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).
|
// 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).
|
||||||
// It may use a remote (= slow) service.
|
// It may use a remote (= slow) service.
|
||||||
func (s *openshiftImageSource) GetManifest() ([]byte, string, error) {
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list);
|
||||||
|
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *openshiftImageSource) GetManifest(instanceDigest *digest.Digest) ([]byte, string, error) {
|
||||||
if err := s.ensureImageIsResolved(context.TODO()); err != nil {
|
if err := s.ensureImageIsResolved(context.TODO()); err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
return s.docker.GetManifest()
|
return s.docker.GetManifest(instanceDigest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
||||||
|
@ -224,12 +219,21 @@ func (s *openshiftImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int6
|
||||||
return s.docker.GetBlob(info)
|
return s.docker.GetBlob(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *openshiftImageSource) GetSignatures(ctx context.Context) ([][]byte, error) {
|
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||||
if err := s.ensureImageIsResolved(ctx); err != nil {
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve signatures for
|
||||||
return nil, err
|
// (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list
|
||||||
|
// (e.g. if the source never returns manifest lists).
|
||||||
|
func (s *openshiftImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||||
|
var imageName string
|
||||||
|
if instanceDigest == nil {
|
||||||
|
if err := s.ensureImageIsResolved(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
imageName = s.imageStreamImageName
|
||||||
|
} else {
|
||||||
|
imageName = instanceDigest.String()
|
||||||
}
|
}
|
||||||
|
image, err := s.client.getImage(ctx, imageName)
|
||||||
image, err := s.client.getImage(ctx, s.imageStreamImageName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -242,6 +246,11 @@ func (s *openshiftImageSource) GetSignatures(ctx context.Context) ([][]byte, err
|
||||||
return sigs, nil
|
return sigs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerInfosForCopy() returns updated layer info that should be used when reading, in preference to values in the manifest, if specified.
|
||||||
|
func (s *openshiftImageSource) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ensureImageIsResolved sets up s.docker and s.imageStreamImageName
|
// ensureImageIsResolved sets up s.docker and s.imageStreamImageName
|
||||||
func (s *openshiftImageSource) ensureImageIsResolved(ctx context.Context) error {
|
func (s *openshiftImageSource) ensureImageIsResolved(ctx context.Context) error {
|
||||||
if s.docker != nil {
|
if s.docker != nil {
|
||||||
|
|
9
vendor/github.com/containers/image/openshift/openshift_transport.go
generated
vendored
9
vendor/github.com/containers/image/openshift/openshift_transport.go
generated
vendored
|
@ -125,16 +125,17 @@ func (ref openshiftReference) PolicyConfigurationNamespaces() []string {
|
||||||
return policyconfiguration.DockerReferenceNamespaces(ref.dockerReference)
|
return policyconfiguration.DockerReferenceNamespaces(ref.dockerReference)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
// The caller must call .Close() on the returned Image.
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
|
// 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.
|
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
|
||||||
func (ref openshiftReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||||
|
func (ref openshiftReference) NewImage(ctx *types.SystemContext) (types.ImageCloser, error) {
|
||||||
src, err := newImageSource(ctx, ref)
|
src, err := newImageSource(ctx, ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return genericImage.FromSource(src)
|
return genericImage.FromSource(ctx, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImageSource returns a types.ImageSource for this reference.
|
// NewImageSource returns a types.ImageSource for this reference.
|
||||||
|
|
149
vendor/github.com/containers/image/ostree/ostree_dest.go
generated
vendored
149
vendor/github.com/containers/image/ostree/ostree_dest.go
generated
vendored
|
@ -4,6 +4,8 @@ package ostree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -12,18 +14,27 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/ostreedev/ostree-go/pkg/otbuiltin"
|
"github.com/ostreedev/ostree-go/pkg/otbuiltin"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/vbatts/tar-split/tar/asm"
|
||||||
|
"github.com/vbatts/tar-split/tar/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// #cgo pkg-config: glib-2.0 gobject-2.0 ostree-1
|
||||||
|
// #include <glib.h>
|
||||||
|
// #include <glib-object.h>
|
||||||
|
// #include <gio/gio.h>
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include <ostree.h>
|
||||||
|
// #include <gio/ginputstream.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
type blobToImport struct {
|
type blobToImport struct {
|
||||||
Size int64
|
Size int64
|
||||||
Digest digest.Digest
|
Digest digest.Digest
|
||||||
|
@ -35,18 +46,24 @@ type descriptor struct {
|
||||||
Digest digest.Digest `json:"digest"`
|
Digest digest.Digest `json:"digest"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fsLayersSchema1 struct {
|
||||||
|
BlobSum digest.Digest `json:"blobSum"`
|
||||||
|
}
|
||||||
|
|
||||||
type manifestSchema struct {
|
type manifestSchema struct {
|
||||||
ConfigDescriptor descriptor `json:"config"`
|
LayersDescriptors []descriptor `json:"layers"`
|
||||||
LayersDescriptors []descriptor `json:"layers"`
|
FSLayers []fsLayersSchema1 `json:"fsLayers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ostreeImageDestination struct {
|
type ostreeImageDestination struct {
|
||||||
ref ostreeReference
|
ref ostreeReference
|
||||||
manifest string
|
manifest string
|
||||||
schema manifestSchema
|
schema manifestSchema
|
||||||
tmpDirPath string
|
tmpDirPath string
|
||||||
blobs map[string]*blobToImport
|
blobs map[string]*blobToImport
|
||||||
digest digest.Digest
|
digest digest.Digest
|
||||||
|
signaturesLen int
|
||||||
|
repo *C.struct_OstreeRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
// newImageDestination returns an ImageDestination for writing to an existing ostree.
|
// newImageDestination returns an ImageDestination for writing to an existing ostree.
|
||||||
|
@ -55,7 +72,7 @@ func newImageDestination(ref ostreeReference, tmpDirPath string) (types.ImageDes
|
||||||
if err := ensureDirectoryExists(tmpDirPath); err != nil {
|
if err := ensureDirectoryExists(tmpDirPath); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &ostreeImageDestination{ref, "", manifestSchema{}, tmpDirPath, map[string]*blobToImport{}, ""}, nil
|
return &ostreeImageDestination{ref, "", manifestSchema{}, tmpDirPath, map[string]*blobToImport{}, "", 0, nil}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
|
||||||
|
@ -66,6 +83,9 @@ func (d *ostreeImageDestination) Reference() types.ImageReference {
|
||||||
|
|
||||||
// Close removes resources associated with an initialized ImageDestination, if any.
|
// Close removes resources associated with an initialized ImageDestination, if any.
|
||||||
func (d *ostreeImageDestination) Close() error {
|
func (d *ostreeImageDestination) Close() error {
|
||||||
|
if d.repo != nil {
|
||||||
|
C.g_object_unref(C.gpointer(d.repo))
|
||||||
|
}
|
||||||
return os.RemoveAll(d.tmpDirPath)
|
return os.RemoveAll(d.tmpDirPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,6 +194,35 @@ func (d *ostreeImageDestination) ostreeCommit(repo *otbuiltin.Repo, branch strin
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateTarSplitMetadata(output *bytes.Buffer, file string) error {
|
||||||
|
mfz := gzip.NewWriter(output)
|
||||||
|
defer mfz.Close()
|
||||||
|
metaPacker := storage.NewJSONPacker(mfz)
|
||||||
|
|
||||||
|
stream, err := os.OpenFile(file, os.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stream.Close()
|
||||||
|
|
||||||
|
gzReader, err := gzip.NewReader(stream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer gzReader.Close()
|
||||||
|
|
||||||
|
its, err := asm.NewInputTarStream(gzReader, metaPacker, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(ioutil.Discard, its)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *ostreeImageDestination) importBlob(repo *otbuiltin.Repo, blob *blobToImport) error {
|
func (d *ostreeImageDestination) importBlob(repo *otbuiltin.Repo, blob *blobToImport) error {
|
||||||
ostreeBranch := fmt.Sprintf("ociimage/%s", blob.Digest.Hex())
|
ostreeBranch := fmt.Sprintf("ociimage/%s", blob.Digest.Hex())
|
||||||
destinationPath := filepath.Join(d.tmpDirPath, blob.Digest.Hex(), "root")
|
destinationPath := filepath.Join(d.tmpDirPath, blob.Digest.Hex(), "root")
|
||||||
|
@ -185,6 +234,11 @@ func (d *ostreeImageDestination) importBlob(repo *otbuiltin.Repo, blob *blobToIm
|
||||||
os.RemoveAll(destinationPath)
|
os.RemoveAll(destinationPath)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
var tarSplitOutput bytes.Buffer
|
||||||
|
if err := generateTarSplitMetadata(&tarSplitOutput, blob.BlobPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if os.Getuid() == 0 {
|
if os.Getuid() == 0 {
|
||||||
if err := archive.UntarPath(blob.BlobPath, destinationPath); err != nil {
|
if err := archive.UntarPath(blob.BlobPath, destinationPath); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -202,28 +256,35 @@ func (d *ostreeImageDestination) importBlob(repo *otbuiltin.Repo, blob *blobToIm
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return d.ostreeCommit(repo, ostreeBranch, destinationPath, []string{fmt.Sprintf("docker.size=%d", blob.Size),
|
||||||
|
fmt.Sprintf("tarsplit.output=%s", base64.StdEncoding.EncodeToString(tarSplitOutput.Bytes()))})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ostreeImageDestination) importConfig(repo *otbuiltin.Repo, blob *blobToImport) error {
|
||||||
|
ostreeBranch := fmt.Sprintf("ociimage/%s", blob.Digest.Hex())
|
||||||
|
destinationPath := filepath.Dir(blob.BlobPath)
|
||||||
|
|
||||||
return d.ostreeCommit(repo, ostreeBranch, destinationPath, []string{fmt.Sprintf("docker.size=%d", blob.Size)})
|
return d.ostreeCommit(repo, ostreeBranch, destinationPath, []string{fmt.Sprintf("docker.size=%d", blob.Size)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ostreeImageDestination) importConfig(blob *blobToImport) error {
|
|
||||||
ostreeBranch := fmt.Sprintf("ociimage/%s", blob.Digest.Hex())
|
|
||||||
|
|
||||||
return exec.Command("ostree", "commit",
|
|
||||||
"--repo", d.ref.repo,
|
|
||||||
fmt.Sprintf("--add-metadata-string=docker.size=%d", blob.Size),
|
|
||||||
"--branch", ostreeBranch, filepath.Dir(blob.BlobPath)).Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ostreeImageDestination) HasBlob(info types.BlobInfo) (bool, int64, error) {
|
func (d *ostreeImageDestination) HasBlob(info types.BlobInfo) (bool, int64, error) {
|
||||||
branch := fmt.Sprintf("ociimage/%s", info.Digest.Hex())
|
|
||||||
output, err := exec.Command("ostree", "show", "--repo", d.ref.repo, "--print-metadata-key=docker.size", branch).CombinedOutput()
|
if d.repo == nil {
|
||||||
if err != nil {
|
repo, err := openRepo(d.ref.repo)
|
||||||
if bytes.Index(output, []byte("not found")) >= 0 || bytes.Index(output, []byte("No such")) >= 0 {
|
if err != nil {
|
||||||
return false, -1, nil
|
return false, 0, err
|
||||||
}
|
}
|
||||||
return false, -1, err
|
d.repo = repo
|
||||||
}
|
}
|
||||||
size, err := strconv.ParseInt(strings.Trim(string(output), "'\n"), 10, 64)
|
branch := fmt.Sprintf("ociimage/%s", info.Digest.Hex())
|
||||||
|
|
||||||
|
found, data, err := readMetadata(d.repo, branch, "docker.size")
|
||||||
|
if err != nil || !found {
|
||||||
|
return found, -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
size, err := strconv.ParseInt(data, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, -1, err
|
return false, -1, err
|
||||||
}
|
}
|
||||||
|
@ -272,6 +333,7 @@ func (d *ostreeImageDestination) PutSignatures(signatures [][]byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
d.signaturesLen = len(signatures)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,24 +348,37 @@ func (d *ostreeImageDestination) Commit() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, layer := range d.schema.LayersDescriptors {
|
checkLayer := func(hash string) error {
|
||||||
hash := layer.Digest.Hex()
|
|
||||||
blob := d.blobs[hash]
|
blob := d.blobs[hash]
|
||||||
// if the blob is not present in d.blobs then it is already stored in OSTree,
|
// if the blob is not present in d.blobs then it is already stored in OSTree,
|
||||||
// and we don't need to import it.
|
// and we don't need to import it.
|
||||||
if blob == nil {
|
if blob == nil {
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
err := d.importBlob(repo, blob)
|
err := d.importBlob(repo, blob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete(d.blobs, hash)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, layer := range d.schema.LayersDescriptors {
|
||||||
|
hash := layer.Digest.Hex()
|
||||||
|
if err = checkLayer(hash); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, layer := range d.schema.FSLayers {
|
||||||
|
hash := layer.BlobSum.Hex()
|
||||||
|
if err = checkLayer(hash); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := d.schema.ConfigDescriptor.Digest.Hex()
|
// Import the other blobs that are not layers
|
||||||
blob := d.blobs[hash]
|
for _, blob := range d.blobs {
|
||||||
if blob != nil {
|
err := d.importConfig(repo, blob)
|
||||||
err := d.importConfig(blob)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -311,7 +386,9 @@ func (d *ostreeImageDestination) Commit() error {
|
||||||
|
|
||||||
manifestPath := filepath.Join(d.tmpDirPath, "manifest")
|
manifestPath := filepath.Join(d.tmpDirPath, "manifest")
|
||||||
|
|
||||||
metadata := []string{fmt.Sprintf("docker.manifest=%s", string(d.manifest)), fmt.Sprintf("docker.digest=%s", string(d.digest))}
|
metadata := []string{fmt.Sprintf("docker.manifest=%s", string(d.manifest)),
|
||||||
|
fmt.Sprintf("signatures=%d", d.signaturesLen),
|
||||||
|
fmt.Sprintf("docker.digest=%s", string(d.digest))}
|
||||||
err = d.ostreeCommit(repo, fmt.Sprintf("ociimage/%s", d.ref.branchName), manifestPath, metadata)
|
err = d.ostreeCommit(repo, fmt.Sprintf("ociimage/%s", d.ref.branchName), manifestPath, metadata)
|
||||||
|
|
||||||
_, err = repo.CommitTransaction()
|
_, err = repo.CommitTransaction()
|
||||||
|
|
354
vendor/github.com/containers/image/ostree/ostree_src.go
generated
vendored
Normal file
354
vendor/github.com/containers/image/ostree/ostree_src.go
generated
vendored
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
// +build !containers_image_ostree_stub
|
||||||
|
|
||||||
|
package ostree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/containers/image/manifest"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
"github.com/containers/storage/pkg/ioutils"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
glib "github.com/ostreedev/ostree-go/pkg/glibobject"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/vbatts/tar-split/tar/asm"
|
||||||
|
"github.com/vbatts/tar-split/tar/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
// #cgo pkg-config: glib-2.0 gobject-2.0 ostree-1
|
||||||
|
// #include <glib.h>
|
||||||
|
// #include <glib-object.h>
|
||||||
|
// #include <gio/gio.h>
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include <ostree.h>
|
||||||
|
// #include <gio/ginputstream.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type ostreeImageSource struct {
|
||||||
|
ref ostreeReference
|
||||||
|
tmpDir string
|
||||||
|
repo *C.struct_OstreeRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
// newImageSource returns an ImageSource for reading from an existing directory.
|
||||||
|
func newImageSource(ctx *types.SystemContext, tmpDir string, ref ostreeReference) (types.ImageSource, error) {
|
||||||
|
return &ostreeImageSource{ref: ref, tmpDir: tmpDir}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference returns the reference used to set up this source.
|
||||||
|
func (s *ostreeImageSource) Reference() types.ImageReference {
|
||||||
|
return s.ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes resources associated with an initialized ImageSource, if any.
|
||||||
|
func (s *ostreeImageSource) Close() error {
|
||||||
|
if s.repo != nil {
|
||||||
|
C.g_object_unref(C.gpointer(s.repo))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ostreeImageSource) getLayerSize(blob string) (int64, error) {
|
||||||
|
b := fmt.Sprintf("ociimage/%s", blob)
|
||||||
|
found, data, err := readMetadata(s.repo, b, "docker.size")
|
||||||
|
if err != nil || !found {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return strconv.ParseInt(data, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ostreeImageSource) getLenSignatures() (int64, error) {
|
||||||
|
b := fmt.Sprintf("ociimage/%s", s.ref.branchName)
|
||||||
|
found, data, err := readMetadata(s.repo, b, "signatures")
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
// if 'signatures' is not present, just return 0 signatures.
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return strconv.ParseInt(data, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ostreeImageSource) getTarSplitData(blob string) ([]byte, error) {
|
||||||
|
b := fmt.Sprintf("ociimage/%s", blob)
|
||||||
|
found, out, err := readMetadata(s.repo, b, "tarsplit.output")
|
||||||
|
if err != nil || !found {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.DecodeString(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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).
|
||||||
|
// It may use a remote (= slow) service.
|
||||||
|
func (s *ostreeImageSource) GetManifest(instanceDigest *digest.Digest) ([]byte, string, error) {
|
||||||
|
if instanceDigest != nil {
|
||||||
|
return nil, "", errors.Errorf(`Manifest lists are not supported by "ostree:"`)
|
||||||
|
}
|
||||||
|
if s.repo == nil {
|
||||||
|
repo, err := openRepo(s.ref.repo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
s.repo = repo
|
||||||
|
}
|
||||||
|
|
||||||
|
b := fmt.Sprintf("ociimage/%s", s.ref.branchName)
|
||||||
|
found, out, err := readMetadata(s.repo, b, "docker.manifest")
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return nil, "", errors.New("manifest not found")
|
||||||
|
}
|
||||||
|
m := []byte(out)
|
||||||
|
return m, manifest.GuessMIMEType(m), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ostreeImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) {
|
||||||
|
return nil, "", errors.New("manifest lists are not supported by this transport")
|
||||||
|
}
|
||||||
|
|
||||||
|
func openRepo(path string) (*C.struct_OstreeRepo, error) {
|
||||||
|
var cerr *C.GError
|
||||||
|
cpath := C.CString(path)
|
||||||
|
defer C.free(unsafe.Pointer(cpath))
|
||||||
|
pathc := C.g_file_new_for_path(cpath)
|
||||||
|
defer C.g_object_unref(C.gpointer(pathc))
|
||||||
|
repo := C.ostree_repo_new(pathc)
|
||||||
|
r := glib.GoBool(glib.GBoolean(C.ostree_repo_open(repo, nil, &cerr)))
|
||||||
|
if !r {
|
||||||
|
C.g_object_unref(C.gpointer(repo))
|
||||||
|
return nil, glib.ConvertGError(glib.ToGError(unsafe.Pointer(cerr)))
|
||||||
|
}
|
||||||
|
return repo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ostreePathFileGetter struct {
|
||||||
|
repo *C.struct_OstreeRepo
|
||||||
|
parentRoot *C.GFile
|
||||||
|
}
|
||||||
|
|
||||||
|
type ostreeReader struct {
|
||||||
|
stream *C.GFileInputStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o ostreeReader) Close() error {
|
||||||
|
C.g_object_unref(C.gpointer(o.stream))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (o ostreeReader) Read(p []byte) (int, error) {
|
||||||
|
var cerr *C.GError
|
||||||
|
instanceCast := C.g_type_check_instance_cast((*C.GTypeInstance)(unsafe.Pointer(o.stream)), C.g_input_stream_get_type())
|
||||||
|
stream := (*C.GInputStream)(unsafe.Pointer(instanceCast))
|
||||||
|
|
||||||
|
b := C.g_input_stream_read_bytes(stream, (C.gsize)(cap(p)), nil, &cerr)
|
||||||
|
if b == nil {
|
||||||
|
return 0, glib.ConvertGError(glib.ToGError(unsafe.Pointer(cerr)))
|
||||||
|
}
|
||||||
|
defer C.g_bytes_unref(b)
|
||||||
|
|
||||||
|
count := int(C.g_bytes_get_size(b))
|
||||||
|
if count == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
data := (*[1 << 30]byte)(unsafe.Pointer(C.g_bytes_get_data(b, nil)))[:count:count]
|
||||||
|
copy(p, data)
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readMetadata(repo *C.struct_OstreeRepo, commit, key string) (bool, string, error) {
|
||||||
|
var cerr *C.GError
|
||||||
|
var ref *C.char
|
||||||
|
defer C.free(unsafe.Pointer(ref))
|
||||||
|
|
||||||
|
cCommit := C.CString(commit)
|
||||||
|
defer C.free(unsafe.Pointer(cCommit))
|
||||||
|
|
||||||
|
if !glib.GoBool(glib.GBoolean(C.ostree_repo_resolve_rev(repo, cCommit, C.gboolean(1), &ref, &cerr))) {
|
||||||
|
return false, "", glib.ConvertGError(glib.ToGError(unsafe.Pointer(cerr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ref == nil {
|
||||||
|
return false, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var variant *C.GVariant
|
||||||
|
if !glib.GoBool(glib.GBoolean(C.ostree_repo_load_variant(repo, C.OSTREE_OBJECT_TYPE_COMMIT, ref, &variant, &cerr))) {
|
||||||
|
return false, "", glib.ConvertGError(glib.ToGError(unsafe.Pointer(cerr)))
|
||||||
|
}
|
||||||
|
defer C.g_variant_unref(variant)
|
||||||
|
if variant != nil {
|
||||||
|
cKey := C.CString(key)
|
||||||
|
defer C.free(unsafe.Pointer(cKey))
|
||||||
|
|
||||||
|
metadata := C.g_variant_get_child_value(variant, 0)
|
||||||
|
defer C.g_variant_unref(metadata)
|
||||||
|
|
||||||
|
data := C.g_variant_lookup_value(metadata, (*C.gchar)(cKey), nil)
|
||||||
|
if data != nil {
|
||||||
|
defer C.g_variant_unref(data)
|
||||||
|
ptr := (*C.char)(C.g_variant_get_string(data, nil))
|
||||||
|
val := C.GoString(ptr)
|
||||||
|
return true, val, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOSTreePathFileGetter(repo *C.struct_OstreeRepo, commit string) (*ostreePathFileGetter, error) {
|
||||||
|
var cerr *C.GError
|
||||||
|
var parentRoot *C.GFile
|
||||||
|
cCommit := C.CString(commit)
|
||||||
|
defer C.free(unsafe.Pointer(cCommit))
|
||||||
|
if !glib.GoBool(glib.GBoolean(C.ostree_repo_read_commit(repo, cCommit, &parentRoot, nil, nil, &cerr))) {
|
||||||
|
return &ostreePathFileGetter{}, glib.ConvertGError(glib.ToGError(unsafe.Pointer(cerr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
C.g_object_ref(C.gpointer(repo))
|
||||||
|
|
||||||
|
return &ostreePathFileGetter{repo: repo, parentRoot: parentRoot}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o ostreePathFileGetter) Get(filename string) (io.ReadCloser, error) {
|
||||||
|
var file *C.GFile
|
||||||
|
if strings.HasPrefix(filename, "./") {
|
||||||
|
filename = filename[2:]
|
||||||
|
}
|
||||||
|
cfilename := C.CString(filename)
|
||||||
|
defer C.free(unsafe.Pointer(cfilename))
|
||||||
|
|
||||||
|
file = (*C.GFile)(C.g_file_resolve_relative_path(o.parentRoot, cfilename))
|
||||||
|
|
||||||
|
var cerr *C.GError
|
||||||
|
stream := C.g_file_read(file, nil, &cerr)
|
||||||
|
if stream == nil {
|
||||||
|
return nil, glib.ConvertGError(glib.ToGError(unsafe.Pointer(cerr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ostreeReader{stream: stream}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o ostreePathFileGetter) Close() {
|
||||||
|
C.g_object_unref(C.gpointer(o.repo))
|
||||||
|
C.g_object_unref(C.gpointer(o.parentRoot))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ostreeImageSource) readSingleFile(commit, path string) (io.ReadCloser, error) {
|
||||||
|
getter, err := newOSTreePathFileGetter(s.repo, commit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer getter.Close()
|
||||||
|
|
||||||
|
return getter.Get(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlob returns a stream for the specified blob, and the blob's size.
|
||||||
|
func (s *ostreeImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) {
|
||||||
|
blob := info.Digest.Hex()
|
||||||
|
branch := fmt.Sprintf("ociimage/%s", blob)
|
||||||
|
|
||||||
|
if s.repo == nil {
|
||||||
|
repo, err := openRepo(s.ref.repo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
s.repo = repo
|
||||||
|
}
|
||||||
|
|
||||||
|
layerSize, err := s.getLayerSize(blob)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tarsplit, err := s.getTarSplitData(blob)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if tarsplit is nil we are looking at the manifest. Return directly the file in /content
|
||||||
|
if tarsplit == nil {
|
||||||
|
file, err := s.readSingleFile(branch, "/content")
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
return file, layerSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mf := bytes.NewReader(tarsplit)
|
||||||
|
mfz, err := gzip.NewReader(mf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
defer mfz.Close()
|
||||||
|
metaUnpacker := storage.NewJSONUnpacker(mfz)
|
||||||
|
|
||||||
|
getter, err := newOSTreePathFileGetter(s.repo, branch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ots := asm.NewOutputTarStream(getter, metaUnpacker)
|
||||||
|
|
||||||
|
pipeReader, pipeWriter := io.Pipe()
|
||||||
|
go func() {
|
||||||
|
io.Copy(pipeWriter, ots)
|
||||||
|
pipeWriter.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
rc := ioutils.NewReadCloserWrapper(pipeReader, func() error {
|
||||||
|
getter.Close()
|
||||||
|
return ots.Close()
|
||||||
|
})
|
||||||
|
return rc, layerSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ostreeImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||||
|
if instanceDigest != nil {
|
||||||
|
return nil, errors.New("manifest lists are not supported by this transport")
|
||||||
|
}
|
||||||
|
lenSignatures, err := s.getLenSignatures()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
branch := fmt.Sprintf("ociimage/%s", s.ref.branchName)
|
||||||
|
|
||||||
|
if s.repo == nil {
|
||||||
|
repo, err := openRepo(s.ref.repo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.repo = repo
|
||||||
|
}
|
||||||
|
|
||||||
|
signatures := [][]byte{}
|
||||||
|
for i := int64(1); i <= lenSignatures; i++ {
|
||||||
|
sigReader, err := s.readSingleFile(branch, fmt.Sprintf("/signature-%d", i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer sigReader.Close()
|
||||||
|
|
||||||
|
sig, err := ioutil.ReadAll(sigReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signatures = append(signatures, sig)
|
||||||
|
}
|
||||||
|
return signatures, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerInfosForCopy() returns updated layer info that should be used when reading, in preference to values in the manifest, if specified.
|
||||||
|
func (s *ostreeImageSource) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return nil
|
||||||
|
}
|
41
vendor/github.com/containers/image/ostree/ostree_transport.go
generated
vendored
41
vendor/github.com/containers/image/ostree/ostree_transport.go
generated
vendored
|
@ -10,12 +10,12 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/containers/image/directory/explicitfilepath"
|
"github.com/containers/image/directory/explicitfilepath"
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
|
"github.com/containers/image/image"
|
||||||
"github.com/containers/image/transports"
|
"github.com/containers/image/transports"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultOSTreeRepo = "/ostree/repo"
|
const defaultOSTreeRepo = "/ostree/repo"
|
||||||
|
@ -66,6 +66,11 @@ type ostreeReference struct {
|
||||||
repo string
|
repo string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ostreeImageCloser struct {
|
||||||
|
types.ImageCloser
|
||||||
|
size int64
|
||||||
|
}
|
||||||
|
|
||||||
func (t ostreeTransport) ParseReference(ref string) (types.ImageReference, error) {
|
func (t ostreeTransport) ParseReference(ref string) (types.ImageReference, error) {
|
||||||
var repo = ""
|
var repo = ""
|
||||||
var image = ""
|
var image = ""
|
||||||
|
@ -110,7 +115,7 @@ func NewReference(image string, repo string) (types.ImageReference, error) {
|
||||||
// This is necessary to prevent directory paths returned by PolicyConfigurationNamespaces
|
// This is necessary to prevent directory paths returned by PolicyConfigurationNamespaces
|
||||||
// from being ambiguous with values of PolicyConfigurationIdentity.
|
// from being ambiguous with values of PolicyConfigurationIdentity.
|
||||||
if strings.Contains(resolved, ":") {
|
if strings.Contains(resolved, ":") {
|
||||||
return nil, errors.Errorf("Invalid OSTreeCI reference %s@%s: path %s contains a colon", image, repo, resolved)
|
return nil, errors.Errorf("Invalid OSTree reference %s@%s: path %s contains a colon", image, repo, resolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ostreeReference{
|
return ostreeReference{
|
||||||
|
@ -168,18 +173,38 @@ func (ref ostreeReference) PolicyConfigurationNamespaces() []string {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
|
func (s *ostreeImageCloser) Size() (int64, error) {
|
||||||
// The caller must call .Close() on the returned Image.
|
return s.size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
|
// 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.
|
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
|
||||||
func (ref ostreeReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
func (ref ostreeReference) NewImage(ctx *types.SystemContext) (types.ImageCloser, error) {
|
||||||
return nil, errors.New("Reading ostree: images is currently not supported")
|
var tmpDir string
|
||||||
|
if ctx == nil || ctx.OSTreeTmpDirPath == "" {
|
||||||
|
tmpDir = os.TempDir()
|
||||||
|
} else {
|
||||||
|
tmpDir = ctx.OSTreeTmpDirPath
|
||||||
|
}
|
||||||
|
src, err := newImageSource(ctx, tmpDir, ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return image.FromSource(ctx, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImageSource returns a types.ImageSource for this reference.
|
// NewImageSource returns a types.ImageSource for this reference.
|
||||||
// The caller must call .Close() on the returned ImageSource.
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
func (ref ostreeReference) NewImageSource(ctx *types.SystemContext) (types.ImageSource, error) {
|
func (ref ostreeReference) NewImageSource(ctx *types.SystemContext) (types.ImageSource, error) {
|
||||||
return nil, errors.New("Reading ostree: images is currently not supported")
|
var tmpDir string
|
||||||
|
if ctx == nil || ctx.OSTreeTmpDirPath == "" {
|
||||||
|
tmpDir = os.TempDir()
|
||||||
|
} else {
|
||||||
|
tmpDir = ctx.OSTreeTmpDirPath
|
||||||
|
}
|
||||||
|
return newImageSource(ctx, tmpDir, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImageDestination returns a types.ImageDestination for this reference.
|
// NewImageDestination returns a types.ImageDestination for this reference.
|
||||||
|
|
6
vendor/github.com/containers/image/signature/policy_config.go
generated
vendored
6
vendor/github.com/containers/image/signature/policy_config.go
generated
vendored
|
@ -70,7 +70,11 @@ func NewPolicyFromFile(fileName string) (*Policy, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewPolicyFromBytes(contents)
|
policy, err := NewPolicyFromBytes(contents)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "invalid policy in %q", fileName)
|
||||||
|
}
|
||||||
|
return policy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPolicyFromBytes returns a policy parsed from the specified blob.
|
// NewPolicyFromBytes returns a policy parsed from the specified blob.
|
||||||
|
|
970
vendor/github.com/containers/image/storage/storage_image.go
generated
vendored
970
vendor/github.com/containers/image/storage/storage_image.go
generated
vendored
File diff suppressed because it is too large
Load diff
76
vendor/github.com/containers/image/storage/storage_reference.go
generated
vendored
76
vendor/github.com/containers/image/storage/storage_reference.go
generated
vendored
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !containers_image_storage_stub
|
||||||
|
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -6,6 +8,7 @@ import (
|
||||||
"github.com/containers/image/docker/reference"
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
@ -18,9 +21,11 @@ type storageReference struct {
|
||||||
reference string
|
reference string
|
||||||
id string
|
id string
|
||||||
name reference.Named
|
name reference.Named
|
||||||
|
tag string
|
||||||
|
digest digest.Digest
|
||||||
}
|
}
|
||||||
|
|
||||||
func newReference(transport storageTransport, reference, id string, name reference.Named) *storageReference {
|
func newReference(transport storageTransport, reference, id string, name reference.Named, tag string, digest digest.Digest) *storageReference {
|
||||||
// We take a copy of the transport, which contains a pointer to the
|
// We take a copy of the transport, which contains a pointer to the
|
||||||
// store that it used for resolving this reference, so that the
|
// store that it used for resolving this reference, so that the
|
||||||
// transport that we'll return from Transport() won't be affected by
|
// transport that we'll return from Transport() won't be affected by
|
||||||
|
@ -30,6 +35,8 @@ func newReference(transport storageTransport, reference, id string, name referen
|
||||||
reference: reference,
|
reference: reference,
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
|
tag: tag,
|
||||||
|
digest: digest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,11 +44,32 @@ func newReference(transport storageTransport, reference, id string, name referen
|
||||||
// one present with the same name or ID, and return the image.
|
// one present with the same name or ID, and return the image.
|
||||||
func (s *storageReference) resolveImage() (*storage.Image, error) {
|
func (s *storageReference) resolveImage() (*storage.Image, error) {
|
||||||
if s.id == "" {
|
if s.id == "" {
|
||||||
|
// Look for an image that has the expanded reference name as an explicit Name value.
|
||||||
image, err := s.transport.store.Image(s.reference)
|
image, err := s.transport.store.Image(s.reference)
|
||||||
if image != nil && err == nil {
|
if image != nil && err == nil {
|
||||||
s.id = image.ID
|
s.id = image.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if s.id == "" && s.name != nil && s.digest != "" {
|
||||||
|
// Look for an image with the specified digest that has the same name,
|
||||||
|
// though possibly with a different tag or digest, as a Name value, so
|
||||||
|
// that the canonical reference can be implicitly resolved to the image.
|
||||||
|
images, err := s.transport.store.ImagesByDigest(s.digest)
|
||||||
|
if images != nil && err == nil {
|
||||||
|
repo := reference.FamiliarName(reference.TrimNamed(s.name))
|
||||||
|
search:
|
||||||
|
for _, image := range images {
|
||||||
|
for _, name := range image.Names {
|
||||||
|
if named, err := reference.ParseNormalizedNamed(name); err == nil {
|
||||||
|
if reference.FamiliarName(reference.TrimNamed(named)) == repo {
|
||||||
|
s.id = image.ID
|
||||||
|
break search
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if s.id == "" {
|
if s.id == "" {
|
||||||
logrus.Errorf("reference %q does not resolve to an image ID", s.StringWithinTransport())
|
logrus.Errorf("reference %q does not resolve to an image ID", s.StringWithinTransport())
|
||||||
return nil, ErrNoSuchImage
|
return nil, ErrNoSuchImage
|
||||||
|
@ -50,12 +78,15 @@ func (s *storageReference) resolveImage() (*storage.Image, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error reading image %q", s.id)
|
return nil, errors.Wrapf(err, "error reading image %q", s.id)
|
||||||
}
|
}
|
||||||
if s.reference != "" {
|
if s.name != nil {
|
||||||
|
repo := reference.FamiliarName(reference.TrimNamed(s.name))
|
||||||
nameMatch := false
|
nameMatch := false
|
||||||
for _, name := range img.Names {
|
for _, name := range img.Names {
|
||||||
if name == s.reference {
|
if named, err := reference.ParseNormalizedNamed(name); err == nil {
|
||||||
nameMatch = true
|
if reference.FamiliarName(reference.TrimNamed(named)) == repo {
|
||||||
break
|
nameMatch = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !nameMatch {
|
if !nameMatch {
|
||||||
|
@ -76,8 +107,21 @@ func (s storageReference) Transport() types.ImageTransport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a name with a tag, if we have a name to base them on.
|
// Return a name with a tag or digest, if we have either, else return it bare.
|
||||||
func (s storageReference) DockerReference() reference.Named {
|
func (s storageReference) DockerReference() reference.Named {
|
||||||
|
if s.name == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if s.tag != "" {
|
||||||
|
if namedTagged, err := reference.WithTag(s.name, s.tag); err == nil {
|
||||||
|
return namedTagged
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.digest != "" {
|
||||||
|
if canonical, err := reference.WithDigest(s.name, s.digest); err == nil {
|
||||||
|
return canonical
|
||||||
|
}
|
||||||
|
}
|
||||||
return s.name
|
return s.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +135,7 @@ func (s storageReference) StringWithinTransport() string {
|
||||||
optionsList = ":" + strings.Join(options, ",")
|
optionsList = ":" + strings.Join(options, ",")
|
||||||
}
|
}
|
||||||
storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "+" + s.transport.store.RunRoot() + optionsList + "]"
|
storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "+" + s.transport.store.RunRoot() + optionsList + "]"
|
||||||
if s.name == nil {
|
if s.reference == "" {
|
||||||
return storeSpec + "@" + s.id
|
return storeSpec + "@" + s.id
|
||||||
}
|
}
|
||||||
if s.id == "" {
|
if s.id == "" {
|
||||||
|
@ -120,11 +164,8 @@ func (s storageReference) PolicyConfigurationNamespaces() []string {
|
||||||
driverlessStoreSpec := "[" + s.transport.store.GraphRoot() + "]"
|
driverlessStoreSpec := "[" + s.transport.store.GraphRoot() + "]"
|
||||||
namespaces := []string{}
|
namespaces := []string{}
|
||||||
if s.name != nil {
|
if s.name != nil {
|
||||||
if s.id != "" {
|
name := reference.TrimNamed(s.name)
|
||||||
// The reference without the ID is also a valid namespace.
|
components := strings.Split(name.String(), "/")
|
||||||
namespaces = append(namespaces, storeSpec+s.reference)
|
|
||||||
}
|
|
||||||
components := strings.Split(s.name.Name(), "/")
|
|
||||||
for len(components) > 0 {
|
for len(components) > 0 {
|
||||||
namespaces = append(namespaces, storeSpec+strings.Join(components, "/"))
|
namespaces = append(namespaces, storeSpec+strings.Join(components, "/"))
|
||||||
components = components[:len(components)-1]
|
components = components[:len(components)-1]
|
||||||
|
@ -135,8 +176,13 @@ func (s storageReference) PolicyConfigurationNamespaces() []string {
|
||||||
return namespaces
|
return namespaces
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s storageReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
return newImage(s)
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
|
// 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.
|
||||||
|
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||||
|
func (s storageReference) NewImage(ctx *types.SystemContext) (types.ImageCloser, error) {
|
||||||
|
return newImage(ctx, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s storageReference) DeleteImage(ctx *types.SystemContext) error {
|
func (s storageReference) DeleteImage(ctx *types.SystemContext) error {
|
||||||
|
@ -159,5 +205,5 @@ func (s storageReference) NewImageSource(ctx *types.SystemContext) (types.ImageS
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s storageReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
func (s storageReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||||
return newImageDestination(s)
|
return newImageDestination(ctx, s)
|
||||||
}
|
}
|
||||||
|
|
209
vendor/github.com/containers/image/storage/storage_transport.go
generated
vendored
209
vendor/github.com/containers/image/storage/storage_transport.go
generated
vendored
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !containers_image_storage_stub
|
||||||
|
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -11,11 +13,14 @@ import (
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/idtools"
|
"github.com/containers/storage/pkg/idtools"
|
||||||
"github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
ddigest "github.com/opencontainers/go-digest"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
minimumTruncatedIDLength = 3
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
transports.Register(Transport)
|
transports.Register(Transport)
|
||||||
}
|
}
|
||||||
|
@ -101,69 +106,133 @@ func (s *storageTransport) DefaultGIDMap() []idtools.IDMap {
|
||||||
// relative to the given store, and returns it in a reference object.
|
// relative to the given store, and returns it in a reference object.
|
||||||
func (s storageTransport) ParseStoreReference(store storage.Store, ref string) (*storageReference, error) {
|
func (s storageTransport) ParseStoreReference(store storage.Store, ref string) (*storageReference, error) {
|
||||||
var name reference.Named
|
var name reference.Named
|
||||||
var sum digest.Digest
|
|
||||||
var err error
|
|
||||||
if ref == "" {
|
if ref == "" {
|
||||||
return nil, ErrInvalidReference
|
return nil, errors.Wrapf(ErrInvalidReference, "%q is an empty reference")
|
||||||
}
|
}
|
||||||
if ref[0] == '[' {
|
if ref[0] == '[' {
|
||||||
// Ignore the store specifier.
|
// Ignore the store specifier.
|
||||||
closeIndex := strings.IndexRune(ref, ']')
|
closeIndex := strings.IndexRune(ref, ']')
|
||||||
if closeIndex < 1 {
|
if closeIndex < 1 {
|
||||||
return nil, ErrInvalidReference
|
return nil, errors.Wrapf(ErrInvalidReference, "store specifier in %q did not end", ref)
|
||||||
}
|
}
|
||||||
ref = ref[closeIndex+1:]
|
ref = ref[closeIndex+1:]
|
||||||
}
|
}
|
||||||
refInfo := strings.SplitN(ref, "@", 2)
|
|
||||||
if len(refInfo) == 1 {
|
// The last segment, if there's more than one, is either a digest from a reference, or an image ID.
|
||||||
// A name.
|
split := strings.LastIndex(ref, "@")
|
||||||
name, err = reference.ParseNormalizedNamed(refInfo[0])
|
idOrDigest := ""
|
||||||
if err != nil {
|
if split != -1 {
|
||||||
return nil, err
|
// Peel off that last bit so that we can work on the rest.
|
||||||
|
idOrDigest = ref[split+1:]
|
||||||
|
if idOrDigest == "" {
|
||||||
|
return nil, errors.Wrapf(ErrInvalidReference, "%q does not look like a digest or image ID", idOrDigest)
|
||||||
}
|
}
|
||||||
} else if len(refInfo) == 2 {
|
ref = ref[:split]
|
||||||
// An ID, possibly preceded by a name.
|
|
||||||
if refInfo[0] != "" {
|
|
||||||
name, err = reference.ParseNormalizedNamed(refInfo[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sum, err = digest.Parse(refInfo[1])
|
|
||||||
if err != nil || sum.Validate() != nil {
|
|
||||||
sum, err = digest.Parse("sha256:" + refInfo[1])
|
|
||||||
if err != nil || sum.Validate() != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // Coverage: len(refInfo) is always 1 or 2
|
|
||||||
// Anything else: store specified in a form we don't
|
|
||||||
// recognize.
|
|
||||||
return nil, ErrInvalidReference
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The middle segment (now the last segment), if there is one, is a digest.
|
||||||
|
split = strings.LastIndex(ref, "@")
|
||||||
|
sum := digest.Digest("")
|
||||||
|
if split != -1 {
|
||||||
|
sum = digest.Digest(ref[split+1:])
|
||||||
|
if sum == "" {
|
||||||
|
return nil, errors.Wrapf(ErrInvalidReference, "%q does not look like an image digest", sum)
|
||||||
|
}
|
||||||
|
ref = ref[:split]
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have something that unambiguously should be a digest, validate it, and then the third part,
|
||||||
|
// if we have one, as an ID.
|
||||||
|
id := ""
|
||||||
|
if sum != "" {
|
||||||
|
if idSum, err := digest.Parse("sha256:" + idOrDigest); err != nil || idSum.Validate() != nil {
|
||||||
|
return nil, errors.Wrapf(ErrInvalidReference, "%q does not look like an image ID", idOrDigest)
|
||||||
|
}
|
||||||
|
if err := sum.Validate(); err != nil {
|
||||||
|
return nil, errors.Wrapf(ErrInvalidReference, "%q does not look like an image digest", sum)
|
||||||
|
}
|
||||||
|
id = idOrDigest
|
||||||
|
if img, err := store.Image(idOrDigest); err == nil && img != nil && len(idOrDigest) >= minimumTruncatedIDLength && strings.HasPrefix(img.ID, idOrDigest) {
|
||||||
|
// The ID is a truncated version of the ID of an image that's present in local storage,
|
||||||
|
// so we might as well use the expanded value.
|
||||||
|
id = img.ID
|
||||||
|
}
|
||||||
|
} else if idOrDigest != "" {
|
||||||
|
// There was no middle portion, so the final portion could be either a digest or an ID.
|
||||||
|
if idSum, err := digest.Parse("sha256:" + idOrDigest); err == nil && idSum.Validate() == nil {
|
||||||
|
// It's an ID.
|
||||||
|
id = idOrDigest
|
||||||
|
} else if idSum, err := digest.Parse(idOrDigest); err == nil && idSum.Validate() == nil {
|
||||||
|
// It's a digest.
|
||||||
|
sum = idSum
|
||||||
|
} else if img, err := store.Image(idOrDigest); err == nil && img != nil && len(idOrDigest) >= minimumTruncatedIDLength && strings.HasPrefix(img.ID, idOrDigest) {
|
||||||
|
// It's a truncated version of the ID of an image that's present in local storage,
|
||||||
|
// and we may need the expanded value.
|
||||||
|
id = img.ID
|
||||||
|
} else {
|
||||||
|
return nil, errors.Wrapf(ErrInvalidReference, "%q does not look like a digest or image ID", idOrDigest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we only had one portion, then _maybe_ it's a truncated image ID. Only check on that if it's
|
||||||
|
// at least of what we guess is a reasonable minimum length, because we don't want a really short value
|
||||||
|
// like "a" matching an image by ID prefix when the input was actually meant to specify an image name.
|
||||||
|
if len(ref) >= minimumTruncatedIDLength && sum == "" && id == "" {
|
||||||
|
if img, err := store.Image(ref); err == nil && img != nil && strings.HasPrefix(img.ID, ref) {
|
||||||
|
// It's a truncated version of the ID of an image that's present in local storage;
|
||||||
|
// we need to expand it.
|
||||||
|
id = img.ID
|
||||||
|
ref = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The initial portion is probably a name, possibly with a tag.
|
||||||
|
if ref != "" {
|
||||||
|
var err error
|
||||||
|
if name, err = reference.ParseNormalizedNamed(ref); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error parsing named reference %q", ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if name == nil && sum == "" && id == "" {
|
||||||
|
return nil, errors.Errorf("error parsing reference")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a copy of the store spec.
|
||||||
optionsList := ""
|
optionsList := ""
|
||||||
options := store.GraphOptions()
|
options := store.GraphOptions()
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
optionsList = ":" + strings.Join(options, ",")
|
optionsList = ":" + strings.Join(options, ",")
|
||||||
}
|
}
|
||||||
storeSpec := "[" + store.GraphDriverName() + "@" + store.GraphRoot() + "+" + store.RunRoot() + optionsList + "]"
|
storeSpec := "[" + store.GraphDriverName() + "@" + store.GraphRoot() + "+" + store.RunRoot() + optionsList + "]"
|
||||||
id := ""
|
|
||||||
if sum.Validate() == nil {
|
// Convert the name back into a reference string, if we got a name.
|
||||||
id = sum.Hex()
|
|
||||||
}
|
|
||||||
refname := ""
|
refname := ""
|
||||||
|
tag := ""
|
||||||
if name != nil {
|
if name != nil {
|
||||||
name = reference.TagNameOnly(name)
|
if sum.Validate() == nil {
|
||||||
refname = verboseName(name)
|
canonical, err := reference.WithDigest(name, sum)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error mixing name %q with digest %q", name, sum)
|
||||||
|
}
|
||||||
|
refname = verboseName(canonical)
|
||||||
|
} else {
|
||||||
|
name = reference.TagNameOnly(name)
|
||||||
|
tagged, ok := name.(reference.Tagged)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("error parsing possibly-tagless name %q", ref)
|
||||||
|
}
|
||||||
|
refname = verboseName(name)
|
||||||
|
tag = tagged.Tag()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if refname == "" {
|
if refname == "" {
|
||||||
logrus.Debugf("parsed reference into %q", storeSpec+"@"+id)
|
logrus.Debugf("parsed reference to id into %q", storeSpec+"@"+id)
|
||||||
} else if id == "" {
|
} else if id == "" {
|
||||||
logrus.Debugf("parsed reference into %q", storeSpec+refname)
|
logrus.Debugf("parsed reference to refname into %q", storeSpec+refname)
|
||||||
} else {
|
} else {
|
||||||
logrus.Debugf("parsed reference into %q", storeSpec+refname+"@"+id)
|
logrus.Debugf("parsed reference to refname@id into %q", storeSpec+refname+"@"+id)
|
||||||
}
|
}
|
||||||
return newReference(storageTransport{store: store, defaultUIDMap: s.defaultUIDMap, defaultGIDMap: s.defaultGIDMap}, refname, id, name), nil
|
return newReference(storageTransport{store: store, defaultUIDMap: s.defaultUIDMap, defaultGIDMap: s.defaultGIDMap}, refname, id, name, tag, sum), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *storageTransport) GetStore() (storage.Store, error) {
|
func (s *storageTransport) GetStore() (storage.Store, error) {
|
||||||
|
@ -182,11 +251,14 @@ func (s *storageTransport) GetStore() (storage.Store, error) {
|
||||||
return s.store, nil
|
return s.store, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseReference takes a name and/or an ID ("_name_"/"@_id_"/"_name_@_id_"),
|
// ParseReference takes a name and a tag or digest and/or ID
|
||||||
|
// ("_name_"/"@_id_"/"_name_:_tag_"/"_name_:_tag_@_id_"/"_name_@_digest_"/"_name_@_digest_@_id_"),
|
||||||
// possibly prefixed with a store specifier in the form "[_graphroot_]" or
|
// possibly prefixed with a store specifier in the form "[_graphroot_]" or
|
||||||
// "[_driver_@_graphroot_]" or "[_driver_@_graphroot_+_runroot_]" or
|
// "[_driver_@_graphroot_]" or "[_driver_@_graphroot_+_runroot_]" or
|
||||||
// "[_driver_@_graphroot_:_options_]" or "[_driver_@_graphroot_+_runroot_:_options_]",
|
// "[_driver_@_graphroot_:_options_]" or "[_driver_@_graphroot_+_runroot_:_options_]",
|
||||||
// tries to figure out which it is, and returns it in a reference object.
|
// tries to figure out which it is, and returns it in a reference object.
|
||||||
|
// If _id_ is the ID of an image that's present in local storage, it can be truncated, and
|
||||||
|
// even be specified as if it were a _name_, value.
|
||||||
func (s *storageTransport) ParseReference(reference string) (types.ImageReference, error) {
|
func (s *storageTransport) ParseReference(reference string) (types.ImageReference, error) {
|
||||||
var store storage.Store
|
var store storage.Store
|
||||||
// Check if there's a store location prefix. If there is, then it
|
// Check if there's a store location prefix. If there is, then it
|
||||||
|
@ -265,17 +337,23 @@ func (s *storageTransport) ParseReference(reference string) (types.ImageReferenc
|
||||||
|
|
||||||
func (s storageTransport) GetStoreImage(store storage.Store, ref types.ImageReference) (*storage.Image, error) {
|
func (s storageTransport) GetStoreImage(store storage.Store, ref types.ImageReference) (*storage.Image, error) {
|
||||||
dref := ref.DockerReference()
|
dref := ref.DockerReference()
|
||||||
if dref == nil {
|
if dref != nil {
|
||||||
if sref, ok := ref.(*storageReference); ok {
|
if img, err := store.Image(verboseName(dref)); err == nil {
|
||||||
if sref.id != "" {
|
return img, nil
|
||||||
if img, err := store.Image(sref.id); err == nil {
|
}
|
||||||
return img, nil
|
}
|
||||||
}
|
if sref, ok := ref.(*storageReference); ok {
|
||||||
|
if sref.id != "" {
|
||||||
|
if img, err := store.Image(sref.id); err == nil {
|
||||||
|
return img, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, ErrInvalidReference
|
tmpRef := *sref
|
||||||
|
if img, err := tmpRef.resolveImage(); err == nil {
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return store.Image(verboseName(dref))
|
return nil, storage.ErrImageUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *storageTransport) GetImage(ref types.ImageReference) (*storage.Image, error) {
|
func (s *storageTransport) GetImage(ref types.ImageReference) (*storage.Image, error) {
|
||||||
|
@ -335,7 +413,7 @@ func (s storageTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = ddigest.Parse("sha256:" + scopeInfo[1])
|
_, err = digest.Parse("sha256:" + scopeInfo[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -345,11 +423,28 @@ func (s storageTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func verboseName(name reference.Named) string {
|
func verboseName(r reference.Reference) string {
|
||||||
name = reference.TagNameOnly(name)
|
if r == nil {
|
||||||
tag := ""
|
return ""
|
||||||
if tagged, ok := name.(reference.NamedTagged); ok {
|
|
||||||
tag = ":" + tagged.Tag()
|
|
||||||
}
|
}
|
||||||
return name.Name() + tag
|
named, isNamed := r.(reference.Named)
|
||||||
|
digested, isDigested := r.(reference.Digested)
|
||||||
|
tagged, isTagged := r.(reference.Tagged)
|
||||||
|
name := ""
|
||||||
|
tag := ""
|
||||||
|
sum := ""
|
||||||
|
if isNamed {
|
||||||
|
name = (reference.TrimNamed(named)).String()
|
||||||
|
}
|
||||||
|
if isTagged {
|
||||||
|
if tagged.Tag() != "" {
|
||||||
|
tag = ":" + tagged.Tag()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isDigested {
|
||||||
|
if digested.Digest().Validate() == nil {
|
||||||
|
sum = "@" + digested.Digest().String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name + tag + sum
|
||||||
}
|
}
|
||||||
|
|
48
vendor/github.com/containers/image/tarball/doc.go
generated
vendored
Normal file
48
vendor/github.com/containers/image/tarball/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// Package tarball provides a way to generate images using one or more layer
|
||||||
|
// tarballs and an optional template configuration.
|
||||||
|
//
|
||||||
|
// An example:
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "fmt"
|
||||||
|
//
|
||||||
|
// cp "github.com/containers/image/copy"
|
||||||
|
// "github.com/containers/image/tarball"
|
||||||
|
// "github.com/containers/image/transports/alltransports"
|
||||||
|
//
|
||||||
|
// imgspecv1 "github.com/containers/image/transports/alltransports"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func imageFromTarball() {
|
||||||
|
// src, err := alltransports.ParseImageName("tarball:/var/cache/mock/fedora-26-x86_64/root_cache/cache.tar.gz")
|
||||||
|
// // - or -
|
||||||
|
// // src, err := tarball.Transport.ParseReference("/var/cache/mock/fedora-26-x86_64/root_cache/cache.tar.gz")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// updater, ok := src.(tarball.ConfigUpdater)
|
||||||
|
// if !ok {
|
||||||
|
// panic("unexpected: a tarball reference should implement tarball.ConfigUpdater")
|
||||||
|
// }
|
||||||
|
// config := imgspecv1.Image{
|
||||||
|
// Config: imgspecv1.ImageConfig{
|
||||||
|
// Cmd: []string{"/bin/bash"},
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// annotations := make(map[string]string)
|
||||||
|
// annotations[imgspecv1.AnnotationDescription] = "test image built from a mock root cache"
|
||||||
|
// err = updater.ConfigUpdate(config, annotations)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// dest, err := alltransports.ParseImageName("docker-daemon:mock:latest")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// err = cp.Image(nil, dest, src, nil)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
package tarball
|
93
vendor/github.com/containers/image/tarball/tarball_reference.go
generated
vendored
Normal file
93
vendor/github.com/containers/image/tarball/tarball_reference.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
package tarball
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/image/docker/reference"
|
||||||
|
"github.com/containers/image/image"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
|
||||||
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigUpdater is an interface that ImageReferences for "tarball" images also
|
||||||
|
// implement. It can be used to set values for a configuration, and to set
|
||||||
|
// image annotations which will be present in the images returned by the
|
||||||
|
// reference's NewImage() or NewImageSource() methods.
|
||||||
|
type ConfigUpdater interface {
|
||||||
|
ConfigUpdate(config imgspecv1.Image, annotations map[string]string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type tarballReference struct {
|
||||||
|
transport types.ImageTransport
|
||||||
|
config imgspecv1.Image
|
||||||
|
annotations map[string]string
|
||||||
|
filenames []string
|
||||||
|
stdin []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigUpdate updates the image's default configuration and adds annotations
|
||||||
|
// which will be visible in source images created using this reference.
|
||||||
|
func (r *tarballReference) ConfigUpdate(config imgspecv1.Image, annotations map[string]string) error {
|
||||||
|
r.config = config
|
||||||
|
if r.annotations == nil {
|
||||||
|
r.annotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
for k, v := range annotations {
|
||||||
|
r.annotations[k] = v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tarballReference) Transport() types.ImageTransport {
|
||||||
|
return r.transport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tarballReference) StringWithinTransport() string {
|
||||||
|
return strings.Join(r.filenames, ":")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tarballReference) DockerReference() reference.Named {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tarballReference) PolicyConfigurationIdentity() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tarballReference) PolicyConfigurationNamespaces() []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
|
// 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.
|
||||||
|
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||||
|
func (r *tarballReference) NewImage(ctx *types.SystemContext) (types.ImageCloser, error) {
|
||||||
|
src, err := r.NewImageSource(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
img, err := image.FromSource(ctx, src)
|
||||||
|
if err != nil {
|
||||||
|
src.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tarballReference) DeleteImage(ctx *types.SystemContext) error {
|
||||||
|
for _, filename := range r.filenames {
|
||||||
|
if err := os.Remove(filename); err != nil && !os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("error removing %q: %v", filename, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tarballReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||||
|
return nil, fmt.Errorf("destination not implemented yet")
|
||||||
|
}
|
260
vendor/github.com/containers/image/tarball/tarball_src.go
generated
vendored
Normal file
260
vendor/github.com/containers/image/tarball/tarball_src.go
generated
vendored
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
package tarball
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
|
imgspecs "github.com/opencontainers/image-spec/specs-go"
|
||||||
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tarballImageSource struct {
|
||||||
|
reference tarballReference
|
||||||
|
filenames []string
|
||||||
|
diffIDs []digest.Digest
|
||||||
|
diffSizes []int64
|
||||||
|
blobIDs []digest.Digest
|
||||||
|
blobSizes []int64
|
||||||
|
blobTypes []string
|
||||||
|
config []byte
|
||||||
|
configID digest.Digest
|
||||||
|
configSize int64
|
||||||
|
manifest []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tarballReference) NewImageSource(ctx *types.SystemContext) (types.ImageSource, error) {
|
||||||
|
// Gather up the digests, sizes, and date information for all of the files.
|
||||||
|
filenames := []string{}
|
||||||
|
diffIDs := []digest.Digest{}
|
||||||
|
diffSizes := []int64{}
|
||||||
|
blobIDs := []digest.Digest{}
|
||||||
|
blobSizes := []int64{}
|
||||||
|
blobTimes := []time.Time{}
|
||||||
|
blobTypes := []string{}
|
||||||
|
for _, filename := range r.filenames {
|
||||||
|
var file *os.File
|
||||||
|
var err error
|
||||||
|
var blobSize int64
|
||||||
|
var blobTime time.Time
|
||||||
|
var reader io.Reader
|
||||||
|
if filename == "-" {
|
||||||
|
blobSize = int64(len(r.stdin))
|
||||||
|
blobTime = time.Now()
|
||||||
|
reader = bytes.NewReader(r.stdin)
|
||||||
|
} else {
|
||||||
|
file, err = os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error opening %q for reading: %v", filename, err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
reader = file
|
||||||
|
fileinfo, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading size of %q: %v", filename, err)
|
||||||
|
}
|
||||||
|
blobSize = fileinfo.Size()
|
||||||
|
blobTime = fileinfo.ModTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to assuming the layer is compressed.
|
||||||
|
layerType := imgspecv1.MediaTypeImageLayerGzip
|
||||||
|
|
||||||
|
// Set up to digest the file as it is.
|
||||||
|
blobIDdigester := digest.Canonical.Digester()
|
||||||
|
reader = io.TeeReader(reader, blobIDdigester.Hash())
|
||||||
|
|
||||||
|
// Set up to digest the file after we maybe decompress it.
|
||||||
|
diffIDdigester := digest.Canonical.Digester()
|
||||||
|
uncompressed, err := gzip.NewReader(reader)
|
||||||
|
if err == nil {
|
||||||
|
// It is compressed, so the diffID is the digest of the uncompressed version
|
||||||
|
reader = io.TeeReader(uncompressed, diffIDdigester.Hash())
|
||||||
|
} else {
|
||||||
|
// It is not compressed, so the diffID and the blobID are going to be the same
|
||||||
|
diffIDdigester = blobIDdigester
|
||||||
|
layerType = imgspecv1.MediaTypeImageLayer
|
||||||
|
uncompressed = nil
|
||||||
|
}
|
||||||
|
n, err := io.Copy(ioutil.Discard, reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading %q: %v", filename, err)
|
||||||
|
}
|
||||||
|
if uncompressed != nil {
|
||||||
|
uncompressed.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab our uncompressed and possibly-compressed digests and sizes.
|
||||||
|
filenames = append(filenames, filename)
|
||||||
|
diffIDs = append(diffIDs, diffIDdigester.Digest())
|
||||||
|
diffSizes = append(diffSizes, n)
|
||||||
|
blobIDs = append(blobIDs, blobIDdigester.Digest())
|
||||||
|
blobSizes = append(blobSizes, blobSize)
|
||||||
|
blobTimes = append(blobTimes, blobTime)
|
||||||
|
blobTypes = append(blobTypes, layerType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the rootfs and history for the configuration blob.
|
||||||
|
rootfs := imgspecv1.RootFS{
|
||||||
|
Type: "layers",
|
||||||
|
DiffIDs: diffIDs,
|
||||||
|
}
|
||||||
|
created := time.Time{}
|
||||||
|
history := []imgspecv1.History{}
|
||||||
|
// Pick up the layer comment from the configuration's history list, if one is set.
|
||||||
|
comment := "imported from tarball"
|
||||||
|
if len(r.config.History) > 0 && r.config.History[0].Comment != "" {
|
||||||
|
comment = r.config.History[0].Comment
|
||||||
|
}
|
||||||
|
for i := range diffIDs {
|
||||||
|
createdBy := fmt.Sprintf("/bin/sh -c #(nop) ADD file:%s in %c", diffIDs[i].Hex(), os.PathSeparator)
|
||||||
|
history = append(history, imgspecv1.History{
|
||||||
|
Created: &blobTimes[i],
|
||||||
|
CreatedBy: createdBy,
|
||||||
|
Comment: comment,
|
||||||
|
})
|
||||||
|
// Use the mtime of the most recently modified file as the image's creation time.
|
||||||
|
if created.Before(blobTimes[i]) {
|
||||||
|
created = blobTimes[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick up other defaults from the config in the reference.
|
||||||
|
config := r.config
|
||||||
|
if config.Created == nil {
|
||||||
|
config.Created = &created
|
||||||
|
}
|
||||||
|
if config.Architecture == "" {
|
||||||
|
config.Architecture = runtime.GOARCH
|
||||||
|
}
|
||||||
|
if config.OS == "" {
|
||||||
|
config.OS = runtime.GOOS
|
||||||
|
}
|
||||||
|
config.RootFS = rootfs
|
||||||
|
config.History = history
|
||||||
|
|
||||||
|
// Encode and digest the image configuration blob.
|
||||||
|
configBytes, err := json.Marshal(&config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error generating configuration blob for %q: %v", strings.Join(r.filenames, separator), err)
|
||||||
|
}
|
||||||
|
configID := digest.Canonical.FromBytes(configBytes)
|
||||||
|
configSize := int64(len(configBytes))
|
||||||
|
|
||||||
|
// Populate a manifest with the configuration blob and the file as the single layer.
|
||||||
|
layerDescriptors := []imgspecv1.Descriptor{}
|
||||||
|
for i := range blobIDs {
|
||||||
|
layerDescriptors = append(layerDescriptors, imgspecv1.Descriptor{
|
||||||
|
Digest: blobIDs[i],
|
||||||
|
Size: blobSizes[i],
|
||||||
|
MediaType: blobTypes[i],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
annotations := make(map[string]string)
|
||||||
|
for k, v := range r.annotations {
|
||||||
|
annotations[k] = v
|
||||||
|
}
|
||||||
|
manifest := imgspecv1.Manifest{
|
||||||
|
Versioned: imgspecs.Versioned{
|
||||||
|
SchemaVersion: 2,
|
||||||
|
},
|
||||||
|
Config: imgspecv1.Descriptor{
|
||||||
|
Digest: configID,
|
||||||
|
Size: configSize,
|
||||||
|
MediaType: imgspecv1.MediaTypeImageConfig,
|
||||||
|
},
|
||||||
|
Layers: layerDescriptors,
|
||||||
|
Annotations: annotations,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode the manifest.
|
||||||
|
manifestBytes, err := json.Marshal(&manifest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error generating manifest for %q: %v", strings.Join(r.filenames, separator), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the image.
|
||||||
|
src := &tarballImageSource{
|
||||||
|
reference: *r,
|
||||||
|
filenames: filenames,
|
||||||
|
diffIDs: diffIDs,
|
||||||
|
diffSizes: diffSizes,
|
||||||
|
blobIDs: blobIDs,
|
||||||
|
blobSizes: blobSizes,
|
||||||
|
blobTypes: blobTypes,
|
||||||
|
config: configBytes,
|
||||||
|
configID: configID,
|
||||||
|
configSize: configSize,
|
||||||
|
manifest: manifestBytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
return src, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *tarballImageSource) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *tarballImageSource) GetBlob(blobinfo types.BlobInfo) (io.ReadCloser, int64, error) {
|
||||||
|
// We should only be asked about things in the manifest. Maybe the configuration blob.
|
||||||
|
if blobinfo.Digest == is.configID {
|
||||||
|
return ioutil.NopCloser(bytes.NewBuffer(is.config)), is.configSize, nil
|
||||||
|
}
|
||||||
|
// Maybe one of the layer blobs.
|
||||||
|
for i := range is.blobIDs {
|
||||||
|
if blobinfo.Digest == is.blobIDs[i] {
|
||||||
|
// We want to read that layer: open the file or memory block and hand it back.
|
||||||
|
if is.filenames[i] == "-" {
|
||||||
|
return ioutil.NopCloser(bytes.NewBuffer(is.reference.stdin)), int64(len(is.reference.stdin)), nil
|
||||||
|
}
|
||||||
|
reader, err := os.Open(is.filenames[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, -1, fmt.Errorf("error opening %q: %v", is.filenames[i], err)
|
||||||
|
}
|
||||||
|
return reader, is.blobSizes[i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, -1, fmt.Errorf("no blob with digest %q found", blobinfo.Digest.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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).
|
||||||
|
// It may use a remote (= slow) service.
|
||||||
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list);
|
||||||
|
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
|
||||||
|
func (is *tarballImageSource) GetManifest(instanceDigest *digest.Digest) ([]byte, string, error) {
|
||||||
|
if instanceDigest != nil {
|
||||||
|
return nil, "", fmt.Errorf("manifest lists are not supported by the %q transport", transportName)
|
||||||
|
}
|
||||||
|
return is.manifest, imgspecv1.MediaTypeImageManifest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||||
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve signatures for
|
||||||
|
// (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list
|
||||||
|
// (e.g. if the source never returns manifest lists).
|
||||||
|
func (*tarballImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||||
|
if instanceDigest != nil {
|
||||||
|
return nil, fmt.Errorf("manifest lists are not supported by the %q transport", transportName)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *tarballImageSource) Reference() types.ImageReference {
|
||||||
|
return &is.reference
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerInfosForCopy() returns updated layer info that should be used when reading, in preference to values in the manifest, if specified.
|
||||||
|
func (*tarballImageSource) LayerInfosForCopy() []types.BlobInfo {
|
||||||
|
return nil
|
||||||
|
}
|
66
vendor/github.com/containers/image/tarball/tarball_transport.go
generated
vendored
Normal file
66
vendor/github.com/containers/image/tarball/tarball_transport.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package tarball
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/image/transports"
|
||||||
|
"github.com/containers/image/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
transportName = "tarball"
|
||||||
|
separator = ":"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Transport implements the types.ImageTransport interface for "tarball:" images,
|
||||||
|
// which are makeshift images constructed using one or more possibly-compressed tar
|
||||||
|
// archives.
|
||||||
|
Transport = &tarballTransport{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type tarballTransport struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tarballTransport) Name() string {
|
||||||
|
return transportName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tarballTransport) ParseReference(reference string) (types.ImageReference, error) {
|
||||||
|
var stdin []byte
|
||||||
|
var err error
|
||||||
|
filenames := strings.Split(reference, separator)
|
||||||
|
for _, filename := range filenames {
|
||||||
|
if filename == "-" {
|
||||||
|
stdin, err = ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error buffering stdin: %v", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error opening %q: %v", filename, err)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
ref := &tarballReference{
|
||||||
|
transport: t,
|
||||||
|
filenames: filenames,
|
||||||
|
stdin: stdin,
|
||||||
|
}
|
||||||
|
return ref, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tarballTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||||
|
// See the explanation in daemonReference.PolicyConfigurationIdentity.
|
||||||
|
return errors.New(`tarball: does not support any scopes except the default "" one`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
transports.Register(Transport)
|
||||||
|
}
|
3
vendor/github.com/containers/image/transports/alltransports/alltransports.go
generated
vendored
3
vendor/github.com/containers/image/transports/alltransports/alltransports.go
generated
vendored
|
@ -13,8 +13,9 @@ import (
|
||||||
_ "github.com/containers/image/oci/archive"
|
_ "github.com/containers/image/oci/archive"
|
||||||
_ "github.com/containers/image/oci/layout"
|
_ "github.com/containers/image/oci/layout"
|
||||||
_ "github.com/containers/image/openshift"
|
_ "github.com/containers/image/openshift"
|
||||||
|
_ "github.com/containers/image/tarball"
|
||||||
// The ostree transport is registered by ostree*.go
|
// The ostree transport is registered by ostree*.go
|
||||||
_ "github.com/containers/image/storage"
|
// The storage transport is registered by storage*.go
|
||||||
"github.com/containers/image/transports"
|
"github.com/containers/image/transports"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
8
vendor/github.com/containers/image/transports/alltransports/storage.go
generated
vendored
Normal file
8
vendor/github.com/containers/image/transports/alltransports/storage.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// +build !containers_image_storage_stub
|
||||||
|
|
||||||
|
package alltransports
|
||||||
|
|
||||||
|
import (
|
||||||
|
// Register the storage transport
|
||||||
|
_ "github.com/containers/image/storage"
|
||||||
|
)
|
9
vendor/github.com/containers/image/transports/alltransports/storage_stub.go
generated
vendored
Normal file
9
vendor/github.com/containers/image/transports/alltransports/storage_stub.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// +build containers_image_storage_stub
|
||||||
|
|
||||||
|
package alltransports
|
||||||
|
|
||||||
|
import "github.com/containers/image/transports"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
transports.Register(transports.NewStubTransport("containers-storage"))
|
||||||
|
}
|
90
vendor/github.com/containers/image/types/types.go
generated
vendored
90
vendor/github.com/containers/image/types/types.go
generated
vendored
|
@ -73,11 +73,12 @@ type ImageReference interface {
|
||||||
// and each following element to be a prefix of the element preceding it.
|
// and each following element to be a prefix of the element preceding it.
|
||||||
PolicyConfigurationNamespaces() []string
|
PolicyConfigurationNamespaces() []string
|
||||||
|
|
||||||
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
|
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||||
// The caller must call .Close() on the returned Image.
|
// The caller must call .Close() on the returned ImageCloser.
|
||||||
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
|
// 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.
|
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
|
||||||
NewImage(ctx *SystemContext) (Image, error)
|
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||||
|
NewImage(ctx *SystemContext) (ImageCloser, error)
|
||||||
// NewImageSource returns a types.ImageSource for this reference.
|
// NewImageSource returns a types.ImageSource for this reference.
|
||||||
// The caller must call .Close() on the returned ImageSource.
|
// The caller must call .Close() on the returned ImageSource.
|
||||||
NewImageSource(ctx *SystemContext) (ImageSource, error)
|
NewImageSource(ctx *SystemContext) (ImageSource, error)
|
||||||
|
@ -96,9 +97,10 @@ type BlobInfo struct {
|
||||||
Size int64 // -1 if unknown
|
Size int64 // -1 if unknown
|
||||||
URLs []string
|
URLs []string
|
||||||
Annotations map[string]string
|
Annotations map[string]string
|
||||||
|
MediaType string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageSource is a service, possibly remote (= slow), to download components of a single image.
|
// ImageSource is a service, possibly remote (= slow), to download components of a single image or a named image set (manifest list).
|
||||||
// This is primarily useful for copying images around; for examining their properties, Image (below)
|
// This is primarily useful for copying images around; for examining their properties, Image (below)
|
||||||
// is usually more useful.
|
// is usually more useful.
|
||||||
// Each ImageSource should eventually be closed by calling Close().
|
// Each ImageSource should eventually be closed by calling Close().
|
||||||
|
@ -113,15 +115,21 @@ type ImageSource interface {
|
||||||
Close() error
|
Close() error
|
||||||
// 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).
|
// 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).
|
||||||
// It may use a remote (= slow) service.
|
// It may use a remote (= slow) service.
|
||||||
GetManifest() ([]byte, string, error)
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list);
|
||||||
// GetTargetManifest returns an image's manifest given a digest. This is mainly used to retrieve a single image's manifest
|
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
|
||||||
// out of a manifest list.
|
GetManifest(instanceDigest *digest.Digest) ([]byte, string, error)
|
||||||
GetTargetManifest(digest digest.Digest) ([]byte, string, error)
|
|
||||||
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
||||||
// The Digest field in BlobInfo is guaranteed to be provided; Size may be -1.
|
// The Digest field in BlobInfo is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided.
|
||||||
GetBlob(BlobInfo) (io.ReadCloser, int64, error)
|
GetBlob(BlobInfo) (io.ReadCloser, int64, error)
|
||||||
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||||
GetSignatures(context.Context) ([][]byte, error)
|
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve signatures for
|
||||||
|
// (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list
|
||||||
|
// (e.g. if the source never returns manifest lists).
|
||||||
|
GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error)
|
||||||
|
// LayerInfosForCopy returns either nil (meaning the values in the manifest are fine), or updated values for the layer blobsums that are listed in the image's manifest.
|
||||||
|
// The Digest field is guaranteed to be provided; Size may be -1.
|
||||||
|
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||||
|
LayerInfosForCopy() []BlobInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageDestination is a service, possibly remote (= slow), to store components of a single image.
|
// ImageDestination is a service, possibly remote (= slow), to store components of a single image.
|
||||||
|
@ -153,9 +161,10 @@ type ImageDestination interface {
|
||||||
AcceptsForeignLayerURLs() bool
|
AcceptsForeignLayerURLs() bool
|
||||||
// MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise.
|
// MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise.
|
||||||
MustMatchRuntimeOS() bool
|
MustMatchRuntimeOS() bool
|
||||||
// PutBlob writes contents of stream and returns data representing the result (with all data filled in).
|
// PutBlob writes contents of stream and returns data representing the result.
|
||||||
// inputInfo.Digest can be optionally provided if known; it is not mandatory for the implementation to verify it.
|
// 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.
|
// inputInfo.Size is the expected length of stream, if known.
|
||||||
|
// inputInfo.MediaType describes the blob format, if known.
|
||||||
// WARNING: The contents of stream are being verified on the fly. Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
|
// WARNING: The contents of stream are being verified on the fly. Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
|
||||||
// to any other readers for download using the supplied digest.
|
// to any other readers for download using the supplied digest.
|
||||||
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
|
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
|
||||||
|
@ -194,28 +203,35 @@ func (e ManifestTypeRejectedError) Error() string {
|
||||||
// Thus, an UnparsedImage can be created from an ImageSource simply by fetching blobs without interpreting them,
|
// 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.
|
// 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.
|
// This also makes the UnparsedImage→Image conversion an explicitly visible step.
|
||||||
// Each UnparsedImage should eventually be closed by calling Close().
|
//
|
||||||
|
// An UnparsedImage is a pair of (ImageSource, instance digest); it can represent either a manifest list or a single image instance.
|
||||||
|
//
|
||||||
|
// The UnparsedImage must not be used after the underlying ImageSource is Close()d.
|
||||||
type UnparsedImage interface {
|
type UnparsedImage interface {
|
||||||
// Reference returns the reference used to set up this source, _as specified by the user_
|
// 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.
|
// (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
|
Reference() ImageReference
|
||||||
// Close removes resources associated with an initialized UnparsedImage, if any.
|
|
||||||
Close() error
|
|
||||||
// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need.
|
// Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need.
|
||||||
Manifest() ([]byte, string, error)
|
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 is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
|
||||||
Signatures(ctx context.Context) ([][]byte, error)
|
Signatures(ctx context.Context) ([][]byte, error)
|
||||||
|
// LayerInfosForCopy returns either nil (meaning the values in the manifest are fine), or updated values for the layer blobsums that are listed in the image's manifest.
|
||||||
|
// The Digest field is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided.
|
||||||
|
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||||
|
LayerInfosForCopy() []BlobInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image is the primary API for inspecting properties of images.
|
// Image is the primary API for inspecting properties of images.
|
||||||
// Each Image should eventually be closed by calling Close().
|
// An Image is based on a pair of (ImageSource, instance digest); it can represent either a manifest list or a single image instance.
|
||||||
|
//
|
||||||
|
// The Image must not be used after the underlying ImageSource is Close()d.
|
||||||
type Image interface {
|
type Image interface {
|
||||||
// Note that Reference may return nil in the return value of UpdatedImage!
|
// Note that Reference may return nil in the return value of UpdatedImage!
|
||||||
UnparsedImage
|
UnparsedImage
|
||||||
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
|
||||||
// Note that the config object may not exist in the underlying storage in the return value of UpdatedImage! Use ConfigBlob() below.
|
// Note that the config object may not exist in the underlying storage in the return value of UpdatedImage! Use ConfigBlob() below.
|
||||||
ConfigInfo() BlobInfo
|
ConfigInfo() BlobInfo
|
||||||
// ConfigBlob returns the blob described by ConfigInfo, iff ConfigInfo().Digest != ""; nil otherwise.
|
// ConfigBlob returns the blob described by ConfigInfo, if ConfigInfo().Digest != ""; nil otherwise.
|
||||||
// The result is cached; it is OK to call this however often you need.
|
// The result is cached; it is OK to call this however often you need.
|
||||||
ConfigBlob() ([]byte, error)
|
ConfigBlob() ([]byte, error)
|
||||||
// OCIConfig returns the image configuration as per OCI v1 image-spec. Information about
|
// OCIConfig returns the image configuration as per OCI v1 image-spec. Information about
|
||||||
|
@ -223,7 +239,7 @@ type Image interface {
|
||||||
// old image manifests work (docker v2s1 especially).
|
// old image manifests work (docker v2s1 especially).
|
||||||
OCIConfig() (*v1.Image, error)
|
OCIConfig() (*v1.Image, error)
|
||||||
// LayerInfos returns a list of BlobInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers).
|
// 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.
|
// The Digest field is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided.
|
||||||
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||||
LayerInfos() []BlobInfo
|
LayerInfos() []BlobInfo
|
||||||
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
||||||
|
@ -240,16 +256,23 @@ type Image interface {
|
||||||
// Everything in options.InformationOnly should be provided, other fields should be set only if a modification is desired.
|
// Everything in options.InformationOnly should be provided, other fields should be set only if a modification is desired.
|
||||||
// This does not change the state of the original Image object.
|
// This does not change the state of the original Image object.
|
||||||
UpdatedImage(options ManifestUpdateOptions) (Image, error)
|
UpdatedImage(options ManifestUpdateOptions) (Image, error)
|
||||||
// IsMultiImage returns true if the image's manifest is a list of images, false otherwise.
|
|
||||||
IsMultiImage() bool
|
|
||||||
// Size returns an approximation of the amount of disk space which is consumed by the image in its current
|
// Size returns an approximation of the amount of disk space which is consumed by the image in its current
|
||||||
// location. If the size is not known, -1 will be returned.
|
// location. If the size is not known, -1 will be returned.
|
||||||
Size() (int64, error)
|
Size() (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImageCloser is an Image with a Close() method which must be called by the user.
|
||||||
|
// This is returned by ImageReference.NewImage, which transparently instantiates a types.ImageSource,
|
||||||
|
// to ensure that the ImageSource is closed.
|
||||||
|
type ImageCloser interface {
|
||||||
|
Image
|
||||||
|
// Close removes resources associated with an initialized ImageCloser.
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
// ManifestUpdateOptions is a way to pass named optional arguments to Image.UpdatedManifest
|
// ManifestUpdateOptions is a way to pass named optional arguments to Image.UpdatedManifest
|
||||||
type ManifestUpdateOptions struct {
|
type ManifestUpdateOptions struct {
|
||||||
LayerInfos []BlobInfo // Complete BlobInfos (size+digest+urls) which should replace the originals, in order (the root layer first, and then successive layered layers)
|
LayerInfos []BlobInfo // Complete BlobInfos (size+digest+urls+annotations) which should replace the originals, in order (the root layer first, and then successive layered layers). BlobInfos' MediaType fields are ignored.
|
||||||
EmbeddedDockerReference reference.Named
|
EmbeddedDockerReference reference.Named
|
||||||
ManifestMIMEType string
|
ManifestMIMEType string
|
||||||
// The values below are NOT requests to modify the image; they provide optional context which may or may not be used.
|
// The values below are NOT requests to modify the image; they provide optional context which may or may not be used.
|
||||||
|
@ -283,7 +306,7 @@ type DockerAuthConfig struct {
|
||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
// SystemContext allows parametrizing access to implicitly-accessed resources,
|
// SystemContext allows parameterizing access to implicitly-accessed resources,
|
||||||
// like configuration files in /etc and users' login state in their home directory.
|
// like configuration files in /etc and users' login state in their home directory.
|
||||||
// Various components can share the same field only if their semantics is exactly
|
// Various components can share the same field only if their semantics is exactly
|
||||||
// the same; if in doubt, add a new field.
|
// the same; if in doubt, add a new field.
|
||||||
|
@ -306,6 +329,10 @@ type SystemContext struct {
|
||||||
SystemRegistriesConfPath string
|
SystemRegistriesConfPath string
|
||||||
// If not "", overrides the default path for the authentication file
|
// If not "", overrides the default path for the authentication file
|
||||||
AuthFilePath string
|
AuthFilePath string
|
||||||
|
// If not "", overrides the use of platform.GOARCH when choosing an image or verifying architecture match.
|
||||||
|
ArchitectureChoice string
|
||||||
|
// If not "", overrides the use of platform.GOOS when choosing an image or verifying OS match.
|
||||||
|
OSChoice string
|
||||||
|
|
||||||
// === OCI.Transport overrides ===
|
// === OCI.Transport overrides ===
|
||||||
// If not "", a directory containing a CA certificate (ending with ".crt"),
|
// If not "", a directory containing a CA certificate (ending with ".crt"),
|
||||||
|
@ -314,6 +341,8 @@ type SystemContext struct {
|
||||||
OCICertPath string
|
OCICertPath string
|
||||||
// Allow downloading OCI image layers over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections.
|
// Allow downloading OCI image layers over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections.
|
||||||
OCIInsecureSkipTLSVerify bool
|
OCIInsecureSkipTLSVerify bool
|
||||||
|
// If not "", use a shared directory for storing blobs rather than within OCI layouts
|
||||||
|
OCISharedBlobDirPath string
|
||||||
|
|
||||||
// === docker.Transport overrides ===
|
// === docker.Transport overrides ===
|
||||||
// If not "", a directory containing a CA certificate (ending with ".crt"),
|
// If not "", a directory containing a CA certificate (ending with ".crt"),
|
||||||
|
@ -322,8 +351,9 @@ type SystemContext struct {
|
||||||
DockerCertPath string
|
DockerCertPath string
|
||||||
// If not "", overrides the system’s default path for a directory containing host[:port] subdirectories with the same structure as DockerCertPath above.
|
// If not "", overrides the system’s default path for a directory containing host[:port] subdirectories with the same structure as DockerCertPath above.
|
||||||
// Ignored if DockerCertPath is non-empty.
|
// Ignored if DockerCertPath is non-empty.
|
||||||
DockerPerHostCertDirPath string
|
DockerPerHostCertDirPath string
|
||||||
DockerInsecureSkipTLSVerify bool // Allow contacting docker registries over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections.
|
// Allow contacting docker registries over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections.
|
||||||
|
DockerInsecureSkipTLSVerify bool
|
||||||
// if nil, the library tries to parse ~/.docker/config.json to retrieve credentials
|
// if nil, the library tries to parse ~/.docker/config.json to retrieve credentials
|
||||||
DockerAuthConfig *DockerAuthConfig
|
DockerAuthConfig *DockerAuthConfig
|
||||||
// if not "", an User-Agent header is added to each request when contacting a registry.
|
// if not "", an User-Agent header is added to each request when contacting a registry.
|
||||||
|
@ -334,6 +364,20 @@ type SystemContext struct {
|
||||||
DockerDisableV1Ping bool
|
DockerDisableV1Ping bool
|
||||||
// Directory to use for OSTree temporary files
|
// Directory to use for OSTree temporary files
|
||||||
OSTreeTmpDirPath string
|
OSTreeTmpDirPath string
|
||||||
|
|
||||||
|
// === docker/daemon.Transport overrides ===
|
||||||
|
// A directory containing a CA certificate (ending with ".crt"),
|
||||||
|
// a client certificate (ending with ".cert") and a client certificate key
|
||||||
|
// (ending with ".key") used when talking to a Docker daemon.
|
||||||
|
DockerDaemonCertPath string
|
||||||
|
// The hostname or IP to the Docker daemon. If not set (aka ""), client.DefaultDockerHost is assumed.
|
||||||
|
DockerDaemonHost string
|
||||||
|
// Used to skip TLS verification, off by default. To take effect DockerDaemonCertPath needs to be specified as well.
|
||||||
|
DockerDaemonInsecureSkipTLSVerify bool
|
||||||
|
|
||||||
|
// === dir.Transport overrides ===
|
||||||
|
// DirForceCompress compresses the image layers if set to true
|
||||||
|
DirForceCompress bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProgressProperties is used to pass information from the copy code to a monitor which
|
// ProgressProperties is used to pass information from the copy code to a monitor which
|
||||||
|
|
7
vendor/github.com/containers/image/vendor.conf
generated
vendored
7
vendor/github.com/containers/image/vendor.conf
generated
vendored
|
@ -1,5 +1,5 @@
|
||||||
github.com/sirupsen/logrus v1.0.0
|
github.com/sirupsen/logrus v1.0.0
|
||||||
github.com/containers/storage 47536c89fcc545a87745e1a1573addc439409165
|
github.com/containers/storage master
|
||||||
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
|
||||||
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
|
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
|
||||||
github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716
|
github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716
|
||||||
|
@ -22,7 +22,7 @@ github.com/pborman/uuid 1b00554d822231195d1babd97ff4a781231955c9
|
||||||
github.com/pkg/errors 248dadf4e9068a0b3e79f02ed0a610d935de5302
|
github.com/pkg/errors 248dadf4e9068a0b3e79f02ed0a610d935de5302
|
||||||
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
|
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
|
||||||
github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
|
github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
|
||||||
github.com/vbatts/tar-split bd4c5d64c3e9297f410025a3b1bd0c58f659e721
|
github.com/vbatts/tar-split v0.10.2
|
||||||
golang.org/x/crypto 453249f01cfeb54c3d549ddb75ff152ca243f9d8
|
golang.org/x/crypto 453249f01cfeb54c3d549ddb75ff152ca243f9d8
|
||||||
golang.org/x/net 6b27048ae5e6ad1ef927e72e437531493de612fe
|
golang.org/x/net 6b27048ae5e6ad1ef927e72e437531493de612fe
|
||||||
golang.org/x/sys 43e60d72a8e2bd92ee98319ba9a384a0e9837c08
|
golang.org/x/sys 43e60d72a8e2bd92ee98319ba9a384a0e9837c08
|
||||||
|
@ -36,4 +36,5 @@ github.com/tchap/go-patricia v2.2.6
|
||||||
github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d
|
github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d
|
||||||
github.com/BurntSushi/toml b26d9c308763d68093482582cea63d69be07a0f0
|
github.com/BurntSushi/toml b26d9c308763d68093482582cea63d69be07a0f0
|
||||||
github.com/ostreedev/ostree-go aeb02c6b6aa2889db3ef62f7855650755befd460
|
github.com/ostreedev/ostree-go aeb02c6b6aa2889db3ef62f7855650755befd460
|
||||||
github.com/gogo/protobuf/proto fcdc5011193ff531a548e9b0301828d5a5b97fd8
|
github.com/gogo/protobuf fcdc5011193ff531a548e9b0301828d5a5b97fd8
|
||||||
|
github.com/pquerna/ffjson master
|
||||||
|
|
127
vendor/github.com/containers/storage/drivers/overlay/overlay.go
generated
vendored
127
vendor/github.com/containers/storage/drivers/overlay/overlay.go
generated
vendored
|
@ -3,7 +3,6 @@
|
||||||
package overlay
|
package overlay
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -26,7 +25,6 @@ import (
|
||||||
"github.com/containers/storage/pkg/locker"
|
"github.com/containers/storage/pkg/locker"
|
||||||
"github.com/containers/storage/pkg/mount"
|
"github.com/containers/storage/pkg/mount"
|
||||||
"github.com/containers/storage/pkg/parsers"
|
"github.com/containers/storage/pkg/parsers"
|
||||||
"github.com/containers/storage/pkg/parsers/kernel"
|
|
||||||
"github.com/containers/storage/pkg/system"
|
"github.com/containers/storage/pkg/system"
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
|
@ -124,22 +122,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := supportsOverlay(); err != nil {
|
|
||||||
return nil, errors.Wrap(graphdriver.ErrNotSupported, "kernel does not support overlay fs")
|
|
||||||
}
|
|
||||||
|
|
||||||
// require kernel 4.0.0 to ensure multiple lower dirs are supported
|
|
||||||
v, err := kernel.GetKernelVersion()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 0, Minor: 0}) < 0 {
|
|
||||||
if !opts.overrideKernelCheck {
|
|
||||||
return nil, errors.Wrap(graphdriver.ErrNotSupported, "kernel too old to provide multiple lowers feature for overlay")
|
|
||||||
}
|
|
||||||
logrus.Warn("Using pre-4.0.0 kernel for overlay, mount failures may require kernel update")
|
|
||||||
}
|
|
||||||
|
|
||||||
fsMagic, err := graphdriver.GetFSMagic(home)
|
fsMagic, err := graphdriver.GetFSMagic(home)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -153,22 +135,18 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
case graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs:
|
case graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs:
|
||||||
logrus.Errorf("'overlay' is not supported over %s", backingFs)
|
logrus.Errorf("'overlay' is not supported over %s", backingFs)
|
||||||
return nil, errors.Wrapf(graphdriver.ErrIncompatibleFS, "'overlay' is not supported over %s", backingFs)
|
return nil, errors.Wrapf(graphdriver.ErrIncompatibleFS, "'overlay' is not supported over %s", backingFs)
|
||||||
case graphdriver.FsMagicBtrfs:
|
|
||||||
// Support for OverlayFS on BTRFS was added in kernel 4.7
|
|
||||||
// See https://btrfs.wiki.kernel.org/index.php/Changelog
|
|
||||||
if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 7, Minor: 0}) < 0 {
|
|
||||||
if !opts.overrideKernelCheck {
|
|
||||||
logrus.Errorf("'overlay' requires kernel 4.7 to use on %s", backingFs)
|
|
||||||
return nil, errors.Wrapf(graphdriver.ErrIncompatibleFS, "'overlay' requires kernel 4.7 to use on %s", backingFs)
|
|
||||||
}
|
|
||||||
logrus.Warn("Using pre-4.7.0 kernel for overlay on btrfs, may require kernel update")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
|
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
supportsDType, err := supportsOverlay(home, fsMagic, rootUID, rootGID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(graphdriver.ErrNotSupported, "kernel does not support overlay fs")
|
||||||
|
}
|
||||||
|
|
||||||
// Create the driver home dir
|
// Create the driver home dir
|
||||||
if err := idtools.MkdirAllAs(path.Join(home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
|
if err := idtools.MkdirAllAs(path.Join(home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -178,16 +156,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
supportsDType, err := fsutils.SupportsDType(home)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !supportsDType {
|
|
||||||
logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs))
|
|
||||||
// TODO: Will make fatal when CRI-O Has AMI built on RHEL7.4
|
|
||||||
// return nil, overlayutils.ErrDTypeNotSupported("overlay", backingFs)
|
|
||||||
}
|
|
||||||
|
|
||||||
d := &Driver{
|
d := &Driver{
|
||||||
name: "overlay",
|
name: "overlay",
|
||||||
home: home,
|
home: home,
|
||||||
|
@ -210,10 +178,10 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
}
|
}
|
||||||
} else if opts.quota.Size > 0 {
|
} else if opts.quota.Size > 0 {
|
||||||
// if xfs is not the backing fs then error out if the storage-opt overlay.size is used.
|
// if xfs is not the backing fs then error out if the storage-opt overlay.size is used.
|
||||||
return nil, fmt.Errorf("Storage Option overlay.size only supported for backingFS XFS. Found %v", backingFs)
|
return nil, fmt.Errorf("Storage option overlay.size only supported for backingFS XFS. Found %v", backingFs)
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("backingFs=%s, projectQuotaSupported=%v", backingFs, projectQuotaSupported)
|
logrus.Debugf("backingFs=%s, projectQuotaSupported=%v", backingFs, projectQuotaSupported)
|
||||||
|
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
@ -227,20 +195,20 @@ func parseOptions(options []string) (*overlayOptions, error) {
|
||||||
}
|
}
|
||||||
key = strings.ToLower(key)
|
key = strings.ToLower(key)
|
||||||
switch key {
|
switch key {
|
||||||
case "overlay.override_kernel_check", "overlay2.override_kernel_check":
|
case ".override_kernel_check", "overlay.override_kernel_check", "overlay2.override_kernel_check":
|
||||||
logrus.Debugf("overlay: override_kernelcheck=%s", val)
|
logrus.Debugf("overlay: override_kernelcheck=%s", val)
|
||||||
o.overrideKernelCheck, err = strconv.ParseBool(val)
|
o.overrideKernelCheck, err = strconv.ParseBool(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case "overlay.size", "overlay2.size":
|
case ".size", "overlay.size", "overlay2.size":
|
||||||
logrus.Debugf("overlay: size=%s", val)
|
logrus.Debugf("overlay: size=%s", val)
|
||||||
size, err := units.RAMInBytes(val)
|
size, err := units.RAMInBytes(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
o.quota.Size = uint64(size)
|
o.quota.Size = uint64(size)
|
||||||
case "overlay.imagestore", "overlay2.imagestore":
|
case ".imagestore", "overlay.imagestore", "overlay2.imagestore":
|
||||||
logrus.Debugf("overlay: imagestore=%s", val)
|
logrus.Debugf("overlay: imagestore=%s", val)
|
||||||
// Additional read only image stores to use for lower paths
|
// Additional read only image stores to use for lower paths
|
||||||
for _, store := range strings.Split(val, ",") {
|
for _, store := range strings.Split(val, ",") {
|
||||||
|
@ -264,25 +232,59 @@ func parseOptions(options []string) (*overlayOptions, error) {
|
||||||
return o, nil
|
return o, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func supportsOverlay() error {
|
func supportsOverlay(home string, homeMagic graphdriver.FsMagic, rootUID, rootGID int) (supportsDType bool, err error) {
|
||||||
// We can try to modprobe overlay first before looking at
|
// We can try to modprobe overlay first
|
||||||
// proc/filesystems for when overlay is supported
|
|
||||||
exec.Command("modprobe", "overlay").Run()
|
exec.Command("modprobe", "overlay").Run()
|
||||||
|
|
||||||
f, err := os.Open("/proc/filesystems")
|
layerDir, err := ioutil.TempDir(home, "compat")
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return err
|
// Check if reading the directory's contents populates the d_type field, which is required
|
||||||
}
|
// for proper operation of the overlay filesystem.
|
||||||
defer f.Close()
|
supportsDType, err = fsutils.SupportsDType(layerDir)
|
||||||
|
if err != nil {
|
||||||
s := bufio.NewScanner(f)
|
return false, err
|
||||||
for s.Scan() {
|
|
||||||
if s.Text() == "nodev\toverlay" {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
if !supportsDType {
|
||||||
|
logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs))
|
||||||
|
// TODO: Will make fatal when CRI-O Has AMI built on RHEL7.4
|
||||||
|
// return nil, overlayutils.ErrDTypeNotSupported("overlay", backingFs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try a test mount in the specific location we're looking at using.
|
||||||
|
mergedDir := filepath.Join(layerDir, "merged")
|
||||||
|
lower1Dir := filepath.Join(layerDir, "lower1")
|
||||||
|
lower2Dir := filepath.Join(layerDir, "lower2")
|
||||||
|
defer func() {
|
||||||
|
// Permitted to fail, since the various subdirectories
|
||||||
|
// can be empty or not even there, and the home might
|
||||||
|
// legitimately be not empty
|
||||||
|
_ = unix.Unmount(mergedDir, unix.MNT_DETACH)
|
||||||
|
_ = os.RemoveAll(layerDir)
|
||||||
|
_ = os.Remove(home)
|
||||||
|
}()
|
||||||
|
_ = idtools.MkdirAs(mergedDir, 0700, rootUID, rootGID)
|
||||||
|
_ = idtools.MkdirAs(lower1Dir, 0700, rootUID, rootGID)
|
||||||
|
_ = idtools.MkdirAs(lower2Dir, 0700, rootUID, rootGID)
|
||||||
|
flags := fmt.Sprintf("lowerdir=%s:%s", lower1Dir, lower2Dir)
|
||||||
|
if len(flags) < unix.Getpagesize() {
|
||||||
|
if mountFrom(filepath.Dir(home), "overlay", mergedDir, "overlay", 0, flags) == nil {
|
||||||
|
logrus.Debugf("overlay test mount with multiple lowers succeeded")
|
||||||
|
return supportsDType, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flags = fmt.Sprintf("lowerdir=%s", lower1Dir)
|
||||||
|
if len(flags) < unix.Getpagesize() {
|
||||||
|
if mountFrom(filepath.Dir(home), "overlay", mergedDir, "overlay", 0, flags) == nil {
|
||||||
|
logrus.Errorf("overlay test mount with multiple lowers failed, but succeeded with a single lower")
|
||||||
|
return supportsDType, errors.Wrap(graphdriver.ErrNotSupported, "kernel too old to provide multiple lowers feature for overlay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logrus.Errorf("'overlay' is not supported over %s at %q", backingFs, home)
|
||||||
|
return supportsDType, errors.Wrapf(graphdriver.ErrIncompatibleFS, "'overlay' is not supported over %s at %q", backingFs, home)
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.")
|
logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.")
|
||||||
return errors.Wrap(graphdriver.ErrNotSupported, "'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.")
|
return supportsDType, errors.Wrap(graphdriver.ErrNotSupported, "'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func useNaiveDiff(home string) bool {
|
func useNaiveDiff(home string) bool {
|
||||||
|
@ -650,10 +652,21 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) {
|
||||||
func (d *Driver) Put(id string) error {
|
func (d *Driver) Put(id string) error {
|
||||||
d.locker.Lock(id)
|
d.locker.Lock(id)
|
||||||
defer d.locker.Unlock(id)
|
defer d.locker.Unlock(id)
|
||||||
|
dir := d.dir(id)
|
||||||
|
if _, err := os.Stat(dir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
mountpoint := path.Join(d.dir(id), "merged")
|
mountpoint := path.Join(d.dir(id), "merged")
|
||||||
if count := d.ctr.Decrement(mountpoint); count > 0 {
|
if count := d.ctr.Decrement(mountpoint); count > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if _, err := ioutil.ReadFile(path.Join(dir, lowerFile)); err != nil {
|
||||||
|
// If no lower, we used the diff directory, so no work to do
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil {
|
if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil {
|
||||||
logrus.Debugf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err)
|
logrus.Debugf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err)
|
||||||
}
|
}
|
||||||
|
|
5
vendor/github.com/containers/storage/drivers/vfs/driver.go
generated
vendored
5
vendor/github.com/containers/storage/drivers/vfs/driver.go
generated
vendored
|
@ -36,6 +36,11 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
if strings.HasPrefix(option, "vfs.imagestore=") {
|
if strings.HasPrefix(option, "vfs.imagestore=") {
|
||||||
d.homes = append(d.homes, strings.Split(option[15:], ",")...)
|
d.homes = append(d.homes, strings.Split(option[15:], ",")...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(option, ".imagestore=") {
|
||||||
|
d.homes = append(d.homes, strings.Split(option[12:], ",")...)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
|
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
|
||||||
|
|
103
vendor/github.com/containers/storage/images.go
generated
vendored
103
vendor/github.com/containers/storage/images.go
generated
vendored
|
@ -14,12 +14,22 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ImageDigestBigDataKey is the name of the big data item whose
|
||||||
|
// contents we consider useful for computing a "digest" of the
|
||||||
|
// image, by which we can locate the image later.
|
||||||
|
ImageDigestBigDataKey = "manifest"
|
||||||
|
)
|
||||||
|
|
||||||
// An Image is a reference to a layer and an associated metadata string.
|
// An Image is a reference to a layer and an associated metadata string.
|
||||||
type Image struct {
|
type Image struct {
|
||||||
// ID is either one which was specified at create-time, or a random
|
// ID is either one which was specified at create-time, or a random
|
||||||
// value which was generated by the library.
|
// value which was generated by the library.
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// Digest is a digest value that we can use to locate the image.
|
||||||
|
Digest digest.Digest `json:"digest,omitempty"`
|
||||||
|
|
||||||
// Names is an optional set of user-defined convenience values. The
|
// Names is an optional set of user-defined convenience values. The
|
||||||
// image can be referred to by its ID or any of its names. Names are
|
// image can be referred to by its ID or any of its names. Names are
|
||||||
// unique among images.
|
// unique among images.
|
||||||
|
@ -28,7 +38,7 @@ type Image struct {
|
||||||
// TopLayer is the ID of the topmost layer of the image itself, if the
|
// TopLayer is the ID of the topmost layer of the image itself, if the
|
||||||
// image contains one or more layers. Multiple images can refer to the
|
// image contains one or more layers. Multiple images can refer to the
|
||||||
// same top layer.
|
// same top layer.
|
||||||
TopLayer string `json:"layer"`
|
TopLayer string `json:"layer,omitempty"`
|
||||||
|
|
||||||
// Metadata is data we keep for the convenience of the caller. It is not
|
// Metadata is data we keep for the convenience of the caller. It is not
|
||||||
// expected to be large, since it is kept in memory.
|
// expected to be large, since it is kept in memory.
|
||||||
|
@ -74,6 +84,10 @@ type ROImageStore interface {
|
||||||
|
|
||||||
// Images returns a slice enumerating the known images.
|
// Images returns a slice enumerating the known images.
|
||||||
Images() ([]Image, error)
|
Images() ([]Image, error)
|
||||||
|
|
||||||
|
// Images returns a slice enumerating the images which have a big data
|
||||||
|
// item with the name ImageDigestBigDataKey and the specified digest.
|
||||||
|
ByDigest(d digest.Digest) ([]*Image, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageStore provides bookkeeping for information about Images.
|
// ImageStore provides bookkeeping for information about Images.
|
||||||
|
@ -87,7 +101,7 @@ type ImageStore interface {
|
||||||
// Create creates an image that has a specified ID (or a random one) and
|
// Create creates an image that has a specified ID (or a random one) and
|
||||||
// optional names, using the specified layer as its topmost (hopefully
|
// optional names, using the specified layer as its topmost (hopefully
|
||||||
// read-only) layer. That layer can be referenced by multiple images.
|
// read-only) layer. That layer can be referenced by multiple images.
|
||||||
Create(id string, names []string, layer, metadata string, created time.Time) (*Image, error)
|
Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (*Image, error)
|
||||||
|
|
||||||
// SetNames replaces the list of names associated with an image with the
|
// SetNames replaces the list of names associated with an image with the
|
||||||
// supplied values.
|
// supplied values.
|
||||||
|
@ -107,6 +121,7 @@ type imageStore struct {
|
||||||
idindex *truncindex.TruncIndex
|
idindex *truncindex.TruncIndex
|
||||||
byid map[string]*Image
|
byid map[string]*Image
|
||||||
byname map[string]*Image
|
byname map[string]*Image
|
||||||
|
bydigest map[digest.Digest][]*Image
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *imageStore) Images() ([]Image, error) {
|
func (r *imageStore) Images() ([]Image, error) {
|
||||||
|
@ -140,6 +155,7 @@ func (r *imageStore) Load() error {
|
||||||
idlist := []string{}
|
idlist := []string{}
|
||||||
ids := make(map[string]*Image)
|
ids := make(map[string]*Image)
|
||||||
names := make(map[string]*Image)
|
names := make(map[string]*Image)
|
||||||
|
digests := make(map[digest.Digest][]*Image)
|
||||||
if err = json.Unmarshal(data, &images); len(data) == 0 || err == nil {
|
if err = json.Unmarshal(data, &images); len(data) == 0 || err == nil {
|
||||||
idlist = make([]string, 0, len(images))
|
idlist = make([]string, 0, len(images))
|
||||||
for n, image := range images {
|
for n, image := range images {
|
||||||
|
@ -152,6 +168,16 @@ func (r *imageStore) Load() error {
|
||||||
}
|
}
|
||||||
names[name] = images[n]
|
names[name] = images[n]
|
||||||
}
|
}
|
||||||
|
// Implicit digest
|
||||||
|
if digest, ok := image.BigDataDigests[ImageDigestBigDataKey]; ok {
|
||||||
|
digests[digest] = append(digests[digest], images[n])
|
||||||
|
}
|
||||||
|
// Explicit digest
|
||||||
|
if image.Digest == "" {
|
||||||
|
image.Digest = image.BigDataDigests[ImageDigestBigDataKey]
|
||||||
|
} else if image.Digest != image.BigDataDigests[ImageDigestBigDataKey] {
|
||||||
|
digests[image.Digest] = append(digests[image.Digest], images[n])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if shouldSave && !r.IsReadWrite() {
|
if shouldSave && !r.IsReadWrite() {
|
||||||
|
@ -161,6 +187,7 @@ func (r *imageStore) Load() error {
|
||||||
r.idindex = truncindex.NewTruncIndex(idlist)
|
r.idindex = truncindex.NewTruncIndex(idlist)
|
||||||
r.byid = ids
|
r.byid = ids
|
||||||
r.byname = names
|
r.byname = names
|
||||||
|
r.bydigest = digests
|
||||||
if shouldSave {
|
if shouldSave {
|
||||||
return r.Save()
|
return r.Save()
|
||||||
}
|
}
|
||||||
|
@ -199,6 +226,7 @@ func newImageStore(dir string) (ImageStore, error) {
|
||||||
images: []*Image{},
|
images: []*Image{},
|
||||||
byid: make(map[string]*Image),
|
byid: make(map[string]*Image),
|
||||||
byname: make(map[string]*Image),
|
byname: make(map[string]*Image),
|
||||||
|
bydigest: make(map[digest.Digest][]*Image),
|
||||||
}
|
}
|
||||||
if err := istore.Load(); err != nil {
|
if err := istore.Load(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -219,6 +247,7 @@ func newROImageStore(dir string) (ROImageStore, error) {
|
||||||
images: []*Image{},
|
images: []*Image{},
|
||||||
byid: make(map[string]*Image),
|
byid: make(map[string]*Image),
|
||||||
byname: make(map[string]*Image),
|
byname: make(map[string]*Image),
|
||||||
|
bydigest: make(map[digest.Digest][]*Image),
|
||||||
}
|
}
|
||||||
if err := istore.Load(); err != nil {
|
if err := istore.Load(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -265,7 +294,7 @@ func (r *imageStore) SetFlag(id string, flag string, value interface{}) error {
|
||||||
return r.Save()
|
return r.Save()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *imageStore) Create(id string, names []string, layer, metadata string, created time.Time) (image *Image, err error) {
|
func (r *imageStore) Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (image *Image, err error) {
|
||||||
if !r.IsReadWrite() {
|
if !r.IsReadWrite() {
|
||||||
return nil, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new images at %q", r.imagespath())
|
return nil, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new images at %q", r.imagespath())
|
||||||
}
|
}
|
||||||
|
@ -292,6 +321,7 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c
|
||||||
if err == nil {
|
if err == nil {
|
||||||
image = &Image{
|
image = &Image{
|
||||||
ID: id,
|
ID: id,
|
||||||
|
Digest: searchableDigest,
|
||||||
Names: names,
|
Names: names,
|
||||||
TopLayer: layer,
|
TopLayer: layer,
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
|
@ -304,6 +334,10 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c
|
||||||
r.images = append(r.images, image)
|
r.images = append(r.images, image)
|
||||||
r.idindex.Add(id)
|
r.idindex.Add(id)
|
||||||
r.byid[id] = image
|
r.byid[id] = image
|
||||||
|
if searchableDigest != "" {
|
||||||
|
list := r.bydigest[searchableDigest]
|
||||||
|
r.bydigest[searchableDigest] = append(list, image)
|
||||||
|
}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
r.byname[name] = image
|
r.byname[name] = image
|
||||||
}
|
}
|
||||||
|
@ -383,6 +417,28 @@ func (r *imageStore) Delete(id string) error {
|
||||||
r.images = append(r.images[:toDeleteIndex], r.images[toDeleteIndex+1:]...)
|
r.images = append(r.images[:toDeleteIndex], r.images[toDeleteIndex+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if digest, ok := image.BigDataDigests[ImageDigestBigDataKey]; ok {
|
||||||
|
// remove the image from the digest-based index
|
||||||
|
if list, ok := r.bydigest[digest]; ok {
|
||||||
|
prunedList := imageSliceWithoutValue(list, image)
|
||||||
|
if len(prunedList) == 0 {
|
||||||
|
delete(r.bydigest, digest)
|
||||||
|
} else {
|
||||||
|
r.bydigest[digest] = prunedList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if image.Digest != "" {
|
||||||
|
// remove the image's hard-coded digest from the digest-based index
|
||||||
|
if list, ok := r.bydigest[image.Digest]; ok {
|
||||||
|
prunedList := imageSliceWithoutValue(list, image)
|
||||||
|
if len(prunedList) == 0 {
|
||||||
|
delete(r.bydigest, image.Digest)
|
||||||
|
} else {
|
||||||
|
r.bydigest[image.Digest] = prunedList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := r.Save(); err != nil {
|
if err := r.Save(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -411,6 +467,13 @@ func (r *imageStore) Exists(id string) bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *imageStore) ByDigest(d digest.Digest) ([]*Image, error) {
|
||||||
|
if images, ok := r.bydigest[d]; ok {
|
||||||
|
return images, nil
|
||||||
|
}
|
||||||
|
return nil, ErrImageUnknown
|
||||||
|
}
|
||||||
|
|
||||||
func (r *imageStore) BigData(id, key string) ([]byte, error) {
|
func (r *imageStore) BigData(id, key string) ([]byte, error) {
|
||||||
if key == "" {
|
if key == "" {
|
||||||
return nil, errors.Wrapf(ErrInvalidBigDataName, "can't retrieve image big data value for empty name")
|
return nil, errors.Wrapf(ErrInvalidBigDataName, "can't retrieve image big data value for empty name")
|
||||||
|
@ -486,6 +549,17 @@ func (r *imageStore) BigDataNames(id string) ([]string, error) {
|
||||||
return image.BigDataNames, nil
|
return image.BigDataNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func imageSliceWithoutValue(slice []*Image, value *Image) []*Image {
|
||||||
|
modified := make([]*Image, 0, len(slice))
|
||||||
|
for _, v := range slice {
|
||||||
|
if v == value {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
modified = append(modified, v)
|
||||||
|
}
|
||||||
|
return modified
|
||||||
|
}
|
||||||
|
|
||||||
func (r *imageStore) SetBigData(id, key string, data []byte) error {
|
func (r *imageStore) SetBigData(id, key string, data []byte) error {
|
||||||
if key == "" {
|
if key == "" {
|
||||||
return errors.Wrapf(ErrInvalidBigDataName, "can't set empty name for image big data item")
|
return errors.Wrapf(ErrInvalidBigDataName, "can't set empty name for image big data item")
|
||||||
|
@ -528,6 +602,29 @@ func (r *imageStore) SetBigData(id, key string, data []byte) error {
|
||||||
image.BigDataNames = append(image.BigDataNames, key)
|
image.BigDataNames = append(image.BigDataNames, key)
|
||||||
save = true
|
save = true
|
||||||
}
|
}
|
||||||
|
if key == ImageDigestBigDataKey {
|
||||||
|
if oldDigest != "" && oldDigest != newDigest && oldDigest != image.Digest {
|
||||||
|
// remove the image from the list of images in the digest-based
|
||||||
|
// index which corresponds to the old digest for this item, unless
|
||||||
|
// it's also the hard-coded digest
|
||||||
|
if list, ok := r.bydigest[oldDigest]; ok {
|
||||||
|
prunedList := imageSliceWithoutValue(list, image)
|
||||||
|
if len(prunedList) == 0 {
|
||||||
|
delete(r.bydigest, oldDigest)
|
||||||
|
} else {
|
||||||
|
r.bydigest[oldDigest] = prunedList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add the image to the list of images in the digest-based index which
|
||||||
|
// corresponds to the new digest for this item, unless it's already there
|
||||||
|
list := r.bydigest[newDigest]
|
||||||
|
if len(list) == len(imageSliceWithoutValue(list, image)) {
|
||||||
|
// the list isn't shortened by trying to prune this image from it,
|
||||||
|
// so it's not in there yet
|
||||||
|
r.bydigest[newDigest] = append(list, image)
|
||||||
|
}
|
||||||
|
}
|
||||||
if save {
|
if save {
|
||||||
err = r.Save()
|
err = r.Save()
|
||||||
}
|
}
|
||||||
|
|
60
vendor/github.com/containers/storage/images_ffjson.go
generated
vendored
60
vendor/github.com/containers/storage/images_ffjson.go
generated
vendored
|
@ -38,6 +38,11 @@ func (j *Image) MarshalJSONBuf(buf fflib.EncodingBuffer) error {
|
||||||
buf.WriteString(`{ "id":`)
|
buf.WriteString(`{ "id":`)
|
||||||
fflib.WriteJsonString(buf, string(j.ID))
|
fflib.WriteJsonString(buf, string(j.ID))
|
||||||
buf.WriteByte(',')
|
buf.WriteByte(',')
|
||||||
|
if len(j.Digest) != 0 {
|
||||||
|
buf.WriteString(`"digest":`)
|
||||||
|
fflib.WriteJsonString(buf, string(j.Digest))
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
if len(j.Names) != 0 {
|
if len(j.Names) != 0 {
|
||||||
buf.WriteString(`"names":`)
|
buf.WriteString(`"names":`)
|
||||||
if j.Names != nil {
|
if j.Names != nil {
|
||||||
|
@ -54,9 +59,11 @@ func (j *Image) MarshalJSONBuf(buf fflib.EncodingBuffer) error {
|
||||||
}
|
}
|
||||||
buf.WriteByte(',')
|
buf.WriteByte(',')
|
||||||
}
|
}
|
||||||
buf.WriteString(`"layer":`)
|
if len(j.TopLayer) != 0 {
|
||||||
fflib.WriteJsonString(buf, string(j.TopLayer))
|
buf.WriteString(`"layer":`)
|
||||||
buf.WriteByte(',')
|
fflib.WriteJsonString(buf, string(j.TopLayer))
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
if len(j.Metadata) != 0 {
|
if len(j.Metadata) != 0 {
|
||||||
buf.WriteString(`"metadata":`)
|
buf.WriteString(`"metadata":`)
|
||||||
fflib.WriteJsonString(buf, string(j.Metadata))
|
fflib.WriteJsonString(buf, string(j.Metadata))
|
||||||
|
@ -144,6 +151,8 @@ const (
|
||||||
|
|
||||||
ffjtImageID
|
ffjtImageID
|
||||||
|
|
||||||
|
ffjtImageDigest
|
||||||
|
|
||||||
ffjtImageNames
|
ffjtImageNames
|
||||||
|
|
||||||
ffjtImageTopLayer
|
ffjtImageTopLayer
|
||||||
|
@ -163,6 +172,8 @@ const (
|
||||||
|
|
||||||
var ffjKeyImageID = []byte("id")
|
var ffjKeyImageID = []byte("id")
|
||||||
|
|
||||||
|
var ffjKeyImageDigest = []byte("digest")
|
||||||
|
|
||||||
var ffjKeyImageNames = []byte("names")
|
var ffjKeyImageNames = []byte("names")
|
||||||
|
|
||||||
var ffjKeyImageTopLayer = []byte("layer")
|
var ffjKeyImageTopLayer = []byte("layer")
|
||||||
|
@ -266,6 +277,14 @@ mainparse:
|
||||||
goto mainparse
|
goto mainparse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
|
||||||
|
if bytes.Equal(ffjKeyImageDigest, kn) {
|
||||||
|
currentKey = ffjtImageDigest
|
||||||
|
state = fflib.FFParse_want_colon
|
||||||
|
goto mainparse
|
||||||
|
}
|
||||||
|
|
||||||
case 'f':
|
case 'f':
|
||||||
|
|
||||||
if bytes.Equal(ffjKeyImageFlags, kn) {
|
if bytes.Equal(ffjKeyImageFlags, kn) {
|
||||||
|
@ -356,6 +375,12 @@ mainparse:
|
||||||
goto mainparse
|
goto mainparse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fflib.EqualFoldRight(ffjKeyImageDigest, kn) {
|
||||||
|
currentKey = ffjtImageDigest
|
||||||
|
state = fflib.FFParse_want_colon
|
||||||
|
goto mainparse
|
||||||
|
}
|
||||||
|
|
||||||
if fflib.SimpleLetterEqualFold(ffjKeyImageID, kn) {
|
if fflib.SimpleLetterEqualFold(ffjKeyImageID, kn) {
|
||||||
currentKey = ffjtImageID
|
currentKey = ffjtImageID
|
||||||
state = fflib.FFParse_want_colon
|
state = fflib.FFParse_want_colon
|
||||||
|
@ -382,6 +407,9 @@ mainparse:
|
||||||
case ffjtImageID:
|
case ffjtImageID:
|
||||||
goto handle_ID
|
goto handle_ID
|
||||||
|
|
||||||
|
case ffjtImageDigest:
|
||||||
|
goto handle_Digest
|
||||||
|
|
||||||
case ffjtImageNames:
|
case ffjtImageNames:
|
||||||
goto handle_Names
|
goto handle_Names
|
||||||
|
|
||||||
|
@ -446,6 +474,32 @@ handle_ID:
|
||||||
state = fflib.FFParse_after_value
|
state = fflib.FFParse_after_value
|
||||||
goto mainparse
|
goto mainparse
|
||||||
|
|
||||||
|
handle_Digest:
|
||||||
|
|
||||||
|
/* handler: j.Digest type=digest.Digest kind=string quoted=false*/
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
{
|
||||||
|
if tok != fflib.FFTok_string && tok != fflib.FFTok_null {
|
||||||
|
return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for Digest", tok))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tok == fflib.FFTok_null {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
outBuf := fs.Output.Bytes()
|
||||||
|
|
||||||
|
j.Digest = digest.Digest(string(outBuf))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = fflib.FFParse_after_value
|
||||||
|
goto mainparse
|
||||||
|
|
||||||
handle_Names:
|
handle_Names:
|
||||||
|
|
||||||
/* handler: j.Names type=[]string kind=slice quoted=false*/
|
/* handler: j.Names type=[]string kind=slice quoted=false*/
|
||||||
|
|
50
vendor/github.com/containers/storage/store.go
generated
vendored
50
vendor/github.com/containers/storage/store.go
generated
vendored
|
@ -370,6 +370,10 @@ type Store interface {
|
||||||
// and may have different metadata, big data items, and flags.
|
// and may have different metadata, big data items, and flags.
|
||||||
ImagesByTopLayer(id string) ([]*Image, error)
|
ImagesByTopLayer(id string) ([]*Image, error)
|
||||||
|
|
||||||
|
// ImagesByDigest returns a list of images which contain a big data item
|
||||||
|
// named ImageDigestBigDataKey whose contents have the specified digest.
|
||||||
|
ImagesByDigest(d digest.Digest) ([]*Image, error)
|
||||||
|
|
||||||
// Container returns a specific container.
|
// Container returns a specific container.
|
||||||
Container(id string) (*Container, error)
|
Container(id string) (*Container, error)
|
||||||
|
|
||||||
|
@ -430,6 +434,8 @@ type ImageOptions struct {
|
||||||
// CreationDate, if not zero, will override the default behavior of marking the image as having been
|
// CreationDate, if not zero, will override the default behavior of marking the image as having been
|
||||||
// created when CreateImage() was called, recording CreationDate instead.
|
// created when CreateImage() was called, recording CreationDate instead.
|
||||||
CreationDate time.Time
|
CreationDate time.Time
|
||||||
|
// Digest is a hard-coded digest value that we can use to look up the image. It is optional.
|
||||||
|
Digest digest.Digest
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerOptions is used for passing options to a Store's CreateContainer() method.
|
// ContainerOptions is used for passing options to a Store's CreateContainer() method.
|
||||||
|
@ -487,11 +493,6 @@ func GetStore(options StoreOptions) (Store, error) {
|
||||||
if err := os.MkdirAll(options.RunRoot, 0700); err != nil && !os.IsExist(err) {
|
if err := os.MkdirAll(options.RunRoot, 0700); err != nil && !os.IsExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, subdir := range []string{} {
|
|
||||||
if err := os.MkdirAll(filepath.Join(options.RunRoot, subdir), 0700); err != nil && !os.IsExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(options.GraphRoot, 0700); err != nil && !os.IsExist(err) {
|
if err := os.MkdirAll(options.GraphRoot, 0700); err != nil && !os.IsExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -834,11 +835,11 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, o
|
||||||
}
|
}
|
||||||
|
|
||||||
creationDate := time.Now().UTC()
|
creationDate := time.Now().UTC()
|
||||||
if options != nil {
|
if options != nil && !options.CreationDate.IsZero() {
|
||||||
creationDate = options.CreationDate
|
creationDate = options.CreationDate
|
||||||
}
|
}
|
||||||
|
|
||||||
return ristore.Create(id, names, layer, metadata, creationDate)
|
return ristore.Create(id, names, layer, metadata, creationDate, options.Digest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *store) CreateContainer(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error) {
|
func (s *store) CreateContainer(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error) {
|
||||||
|
@ -1888,10 +1889,16 @@ func (s *store) layersByMappedDigest(m func(ROLayerStore, digest.Digest) ([]Laye
|
||||||
}
|
}
|
||||||
storeLayers, err := m(store, d)
|
storeLayers, err := m(store, d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
if errors.Cause(err) != ErrLayerUnknown {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
layers = append(layers, storeLayers...)
|
layers = append(layers, storeLayers...)
|
||||||
}
|
}
|
||||||
|
if len(layers) == 0 {
|
||||||
|
return nil, ErrLayerUnknown
|
||||||
|
}
|
||||||
return layers, nil
|
return layers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2080,6 +2087,33 @@ func (s *store) ImagesByTopLayer(id string) ([]*Image, error) {
|
||||||
return images, nil
|
return images, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *store) ImagesByDigest(d digest.Digest) ([]*Image, error) {
|
||||||
|
images := []*Image{}
|
||||||
|
|
||||||
|
istore, err := s.ImageStore()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
istores, err := s.ROImageStores()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, store := range append([]ROImageStore{istore}, istores...) {
|
||||||
|
store.Lock()
|
||||||
|
defer store.Unlock()
|
||||||
|
if modified, err := store.Modified(); modified || err != nil {
|
||||||
|
store.Load()
|
||||||
|
}
|
||||||
|
imageList, err := store.ByDigest(d)
|
||||||
|
if err != nil && err != ErrImageUnknown {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
images = append(images, imageList...)
|
||||||
|
}
|
||||||
|
return images, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *store) Container(id string) (*Container, error) {
|
func (s *store) Container(id string) (*Container, error) {
|
||||||
rcstore, err := s.ContainerStore()
|
rcstore, err := s.ContainerStore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
2
vendor/github.com/containers/storage/vendor.conf
generated
vendored
2
vendor/github.com/containers/storage/vendor.conf
generated
vendored
|
@ -15,7 +15,7 @@ github.com/pmezard/go-difflib v1.0.0
|
||||||
github.com/sirupsen/logrus v1.0.0
|
github.com/sirupsen/logrus v1.0.0
|
||||||
github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
|
github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
|
||||||
github.com/tchap/go-patricia v2.2.6
|
github.com/tchap/go-patricia v2.2.6
|
||||||
github.com/vbatts/tar-split bd4c5d64c3e9297f410025a3b1bd0c58f659e721
|
github.com/vbatts/tar-split v0.10.2
|
||||||
golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
|
golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
|
||||||
golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
|
golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
|
||||||
github.com/pquerna/ffjson d49c2bc1aa135aad0c6f4fc2056623ec78f5d5ac
|
github.com/pquerna/ffjson d49c2bc1aa135aad0c6f4fc2056623ec78f5d5ac
|
||||||
|
|
Loading…
Reference in a new issue