From 7a42d3c6d3e24304bce500e34111b647cb329675 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Wed, 17 May 2017 17:01:06 -0400 Subject: [PATCH] Learn to commit images in multiple formats Build configuration and manifest data for both output formats that we support, and select one or the other based on the list of manifest types that would be accepted as a source image. Signed-off-by: Nalin Dahyabhai Closes: #118 Approved by: rhatdan --- image.go | 151 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 114 insertions(+), 37 deletions(-) diff --git a/image.go b/image.go index 2245b4a2..da948f1f 100644 --- a/image.go +++ b/image.go @@ -21,6 +21,7 @@ import ( digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go" "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/projectatomic/buildah/docker" ) type containerImageRef struct { @@ -28,7 +29,8 @@ type containerImageRef struct { container *storage.Container compression archive.Compression name reference.Named - config []byte + oconfig []byte + dconfig []byte createdBy string annotations map[string]string } @@ -42,6 +44,7 @@ type containerImageSource struct { config []byte configDigest digest.Digest manifest []byte + manifestType string } func (i *containerImageRef) NewImage(sc *types.SystemContext) (types.Image, error) { @@ -53,18 +56,23 @@ func (i *containerImageRef) NewImage(sc *types.SystemContext) (types.Image, erro } func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestTypes []string) (src types.ImageSource, err error) { - if len(manifestTypes) > 0 { - ok := false - for _, mt := range manifestTypes { - if mt == v1.MediaTypeImageManifest { - ok = true - break - } + manifestType := "" + // Look for a supported format in the acceptable list. + for _, mt := range manifestTypes { + if mt == v1.MediaTypeImageManifest { + manifestType = mt + break } - if !ok { - return nil, fmt.Errorf("no supported manifest types") + if mt == docker.V2S2MediaTypeManifest { + manifestType = mt + break } } + // If it's not a format we support, return an error. + if manifestType != v1.MediaTypeImageManifest && manifestType != docker.V2S2MediaTypeManifest { + return nil, fmt.Errorf("no supported manifest types (attempted to use %q, only know %q and %q)", + manifestType, v1.MediaTypeImageManifest, docker.V2S2MediaTypeManifest) + } layers := []string{} layerID := i.container.LayerID layer, err := i.store.Layer(layerID) @@ -101,13 +109,18 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType } }() - image := v1.Image{} - err = json.Unmarshal(i.config, &image) + oimage := v1.Image{} + err = json.Unmarshal(i.oconfig, &oimage) + if err != nil { + return nil, err + } + dimage := docker.V2Image{} + err = json.Unmarshal(i.dconfig, &dimage) if err != nil { return nil, err } - manifest := v1.Manifest{ + omanifest := v1.Manifest{ Versioned: specs.Versioned{ SchemaVersion: 2, }, @@ -117,9 +130,22 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType Layers: []v1.Descriptor{}, Annotations: i.annotations, } + dmanifest := docker.V2S2Manifest{ + V2Versioned: docker.V2Versioned{ + SchemaVersion: 2, + MediaType: docker.V2S2MediaTypeManifest, + }, + Config: docker.V2S2Descriptor{ + MediaType: docker.V2S2MediaTypeImageConfig, + }, + Layers: []docker.V2S2Descriptor{}, + } - image.RootFS.Type = "layers" - image.RootFS.DiffIDs = []string{} + oimage.RootFS.Type = docker.TypeLayers + oimage.RootFS.DiffIDs = []string{} + dimage.RootFS = &docker.V2S2RootFS{} + dimage.RootFS.Type = docker.TypeLayers + dimage.RootFS.DiffIDs = []digest.Digest{} for _, layerID := range layers { rc, err := i.store.Diff("", layerID) @@ -141,11 +167,13 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType destHasher := digest.Canonical.Digester() counter := ioutils.NewWriteCounter(layerFile) multiWriter := io.MultiWriter(counter, destHasher.Hash()) - mediaType := v1.MediaTypeImageLayer + omediaType := v1.MediaTypeImageLayer + dmediaType := docker.V2S2MediaTypeUncompressedLayer if i.compression != archive.Uncompressed { switch i.compression { case archive.Gzip: - mediaType = v1.MediaTypeImageLayerGzip + omediaType = v1.MediaTypeImageLayerGzip + dmediaType = docker.V2S2MediaTypeLayer logrus.Debugf("compressing layer %q with gzip", layerID) case archive.Bzip2: logrus.Debugf("compressing layer %q with bzip2", layerID) @@ -175,48 +203,92 @@ func (i *containerImageRef) NewImageSource(sc *types.SystemContext, manifestType if err != nil { return nil, fmt.Errorf("error storing layer %q to file: %v", layerID, err) } - layerDescriptor := v1.Descriptor{ - MediaType: mediaType, + olayerDescriptor := v1.Descriptor{ + MediaType: omediaType, Digest: destHasher.Digest(), Size: size, } - manifest.Layers = append(manifest.Layers, layerDescriptor) - lastLayerDiffID := destHasher.Digest().String() - image.RootFS.DiffIDs = append(image.RootFS.DiffIDs, lastLayerDiffID) + omanifest.Layers = append(omanifest.Layers, olayerDescriptor) + dlayerDescriptor := docker.V2S2Descriptor{ + MediaType: dmediaType, + Digest: destHasher.Digest(), + Size: size, + } + dmanifest.Layers = append(dmanifest.Layers, dlayerDescriptor) + oimage.RootFS.DiffIDs = append(oimage.RootFS.DiffIDs, srcHasher.Digest().String()) + dimage.RootFS.DiffIDs = append(dimage.RootFS.DiffIDs, srcHasher.Digest()) } - news := v1.History{ + onews := v1.History{ Created: created, CreatedBy: i.createdBy, - Author: image.Author, + Author: oimage.Author, EmptyLayer: false, } - image.History = append(image.History, news) + oimage.History = append(oimage.History, onews) + dnews := docker.V2S2History{ + Created: created, + CreatedBy: i.createdBy, + Author: dimage.Author, + EmptyLayer: false, + } + dimage.History = append(dimage.History, dnews) - config, err := json.Marshal(&image) + oconfig, err := json.Marshal(&oimage) if err != nil { return nil, err } - logrus.Debugf("config = %s\n", config) - i.config = config + logrus.Debugf("OCIv1 config = %s", oconfig) + i.oconfig = oconfig - manifest.Config.Digest = digest.FromBytes(config) - manifest.Config.Size = int64(len(config)) + omanifest.Config.Digest = digest.FromBytes(oconfig) + omanifest.Config.Size = int64(len(oconfig)) + omanifest.Config.MediaType = v1.MediaTypeImageConfig - mfest, err := json.Marshal(&manifest) + omanifestbytes, err := json.Marshal(&omanifest) if err != nil { return nil, err } - logrus.Debugf("manifest = %s\n", mfest) + logrus.Debugf("OCIv1 manifest = %s", omanifestbytes) + dconfig, err := json.Marshal(&dimage) + if err != nil { + return nil, err + } + logrus.Debugf("Docker v2s2 config = %s", dconfig) + i.dconfig = dconfig + + dmanifest.Config.Digest = digest.FromBytes(dconfig) + dmanifest.Config.Size = int64(len(dconfig)) + dmanifest.Config.MediaType = docker.V2S2MediaTypeImageConfig + + dmanifestbytes, err := json.Marshal(&dmanifest) + if err != nil { + return nil, err + } + logrus.Debugf("Docker v2s2 manifest = %s", dmanifestbytes) + + var config []byte + var manifest []byte + switch manifestType { + case v1.MediaTypeImageManifest: + manifest = omanifestbytes + config = i.oconfig + case docker.V2S2MediaTypeManifest: + manifest = dmanifestbytes + config = i.dconfig + default: + panic("unreachable code: unsupported manifest type") + } src = &containerImageSource{ path: path, ref: i, store: i.store, container: i.container, compression: i.compression, - manifest: mfest, - config: i.config, + manifest: manifest, + manifestType: manifestType, + config: config, configDigest: digest.FromBytes(config), } return src, nil @@ -275,7 +347,7 @@ func (i *containerImageSource) GetTargetManifest(digest digest.Digest) ([]byte, } func (i *containerImageSource) GetManifest() ([]byte, string, error) { - return i.manifest, v1.MediaTypeImageManifest, nil + return i.manifest, i.manifestType, nil } func (i *containerImageSource) GetBlob(blob types.BlobInfo) (reader io.ReadCloser, size int64, err error) { @@ -321,7 +393,11 @@ func (b *Builder) makeContainerImageRef(compress archive.Compression) (types.Ima name = nil } } - config, err := json.Marshal(&b.OCIv1) + oconfig, err := json.Marshal(&b.OCIv1) + if err != nil { + return nil, err + } + dconfig, err := json.Marshal(&b.Docker) if err != nil { return nil, err } @@ -330,7 +406,8 @@ func (b *Builder) makeContainerImageRef(compress archive.Compression) (types.Ima container: container, compression: compress, name: name, - config: config, + oconfig: oconfig, + dconfig: dconfig, createdBy: b.CreatedBy(), annotations: b.Annotations(), }