From 88bf2ebdcf4529d6f64f1ffbd727eb73896cd739 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Tue, 16 May 2017 17:26:02 -0400 Subject: [PATCH] Also import configurations from V2S1 images Since V2S1 image manifests don't include a config blob, when we initialize a builder using an image with a V2S1 manifest, try to dig configuration information out of the manifest's layer history, and build a V2S2 image configuration using the results. Leave the diffID list unpopulated, because we compute the right values when we push the image, and to prevent us from mistakes if we ever try to use them before that in the future. Signed-off-by: Nalin Dahyabhai Closes: #118 Approved by: rhatdan --- config.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/config.go b/config.go index b6d9cacb..9e458ed1 100644 --- a/config.go +++ b/config.go @@ -2,9 +2,11 @@ package buildah import ( "encoding/json" + "fmt" "path/filepath" "runtime" "strings" + "time" digest "github.com/opencontainers/go-digest" ociv1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -117,6 +119,58 @@ func makeDockerV2S2Image(oimage *ociv1.Image) (docker.V2Image, error) { return image, nil } +// makeDockerV2S1Image builds the best docker image structure we can from the +// contents of the V2S1 image structure. +func makeDockerV2S1Image(manifest docker.V2S1Manifest) (docker.V2Image, error) { + // Treat the most recent (first) item in the history as a description of the image. + if len(manifest.History) == 0 { + return docker.V2Image{}, fmt.Errorf("error parsing image configuration from manifest") + } + dimage := docker.V2Image{} + err := json.Unmarshal([]byte(manifest.History[0].V1Compatibility), &dimage) + if err != nil { + return docker.V2Image{}, err + } + if dimage.DockerVersion == "" { + return docker.V2Image{}, fmt.Errorf("error parsing image configuration from history") + } + // The DiffID list is intended to contain the sums of _uncompressed_ blobs, and these are most + // likely compressed, so leave the list empty to avoid potential confusion later on. We can + // construct a list with the correct values when we prep layers for pushing, so we don't lose. + // information by leaving this part undone. + rootFS := &docker.V2S2RootFS{ + Type: docker.TypeLayers, + DiffIDs: []digest.Digest{}, + } + // Build a filesystem history. + history := []docker.V2S2History{} + for i := range manifest.History { + h := docker.V2S2History{ + Created: time.Now().UTC(), + Author: "", + CreatedBy: "", + Comment: "", + EmptyLayer: false, + } + dcompat := docker.V1Compatibility{} + if err2 := json.Unmarshal([]byte(manifest.History[i].V1Compatibility), &dcompat); err2 == nil { + h.Created = dcompat.Created.UTC() + h.Author = dcompat.Author + h.Comment = dcompat.Comment + if len(dcompat.ContainerConfig.Cmd) > 0 { + h.CreatedBy = fmt.Sprintf("%v", dcompat.ContainerConfig.Cmd) + } + h.EmptyLayer = dcompat.ThrowAway + } + // Prepend this layer to the list, because a v2s1 format manifest's list is in reverse order + // compared to v2s2, which lists earlier layers before later ones. + history = append([]docker.V2S2History{h}, history...) + } + dimage.RootFS = rootFS + dimage.History = history + return dimage, nil +} + func (b *Builder) initConfig() { image := ociv1.Image{} dimage := docker.V2Image{} @@ -135,6 +189,18 @@ func (b *Builder) initConfig() { } b.OCIv1 = image b.Docker = dimage + } else { + // Try to dig out the image configuration from the manifest. + manifest := docker.V2S1Manifest{} + if err := json.Unmarshal(b.Manifest, &manifest); err == nil && manifest.SchemaVersion == 1 { + if dimage, err = makeDockerV2S1Image(manifest); err == nil { + if image, err = makeOCIv1Image(&dimage); err != nil { + image = ociv1.Image{} + } + } + } + b.OCIv1 = image + b.Docker = dimage } if len(b.Manifest) > 0 { // Attempt to recover format-specific data from the manifest. @@ -152,6 +218,7 @@ func (b *Builder) fixupConfig() { b.Docker.ContainerConfig = *b.Docker.Config } b.Docker.Config = &b.Docker.ContainerConfig + b.Docker.DockerVersion = "" if b.FromImageID != "" { if d, err := digest.Parse(b.FromImageID); err == nil { b.Docker.Parent = docker.ID(d)