From 2f230b88c4891ee3a71b01c1fa65e85e8d6b5f5b Mon Sep 17 00:00:00 2001 From: Jason Hall Date: Thu, 21 Jul 2022 15:04:34 -0400 Subject: [PATCH] Set layer media types consistently (#776) * Set layer media types consistently * Also test that base image mediaType is not changed --- pkg/build/gobuild.go | 25 +++++++++++--- pkg/build/gobuild_test.go | 73 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/pkg/build/gobuild.go b/pkg/build/gobuild.go index c40634390c..c140052ba4 100644 --- a/pkg/build/gobuild.go +++ b/pkg/build/gobuild.go @@ -731,6 +731,20 @@ func (g *gobuild) buildOne(ctx context.Context, refStr string, base v1.Image, pl ref := newRef(refStr) + // Layers should be typed to match the underlying image, since some + // registries reject mixed-type layers. + var layerMediaType types.MediaType + mt, err := base.MediaType() + if err != nil { + return nil, err + } + switch mt { + case types.OCIManifestSchema1: + layerMediaType = types.OCILayer + case types.DockerManifestSchema2: + layerMediaType = types.DockerLayer + } + cf, err := base.ConfigFile() if err != nil { return nil, err @@ -762,7 +776,7 @@ func (g *gobuild) buildOne(ctx context.Context, refStr string, base v1.Image, pl dataLayerBytes := dataLayerBuf.Bytes() dataLayer, err := tarball.LayerFromOpener(func() (io.ReadCloser, error) { return ioutil.NopCloser(bytes.NewBuffer(dataLayerBytes)), nil - }, tarball.WithCompressedCaching) + }, tarball.WithCompressedCaching, tarball.WithMediaType(layerMediaType)) if err != nil { return nil, err } @@ -780,7 +794,7 @@ func (g *gobuild) buildOne(ctx context.Context, refStr string, base v1.Image, pl appPath := path.Join(appDir, appFilename(ref.Path())) miss := func() (v1.Layer, error) { - return buildLayer(appPath, file, platform) + return buildLayer(appPath, file, platform, layerMediaType) } binaryLayer, err := g.cache.get(ctx, file, miss) @@ -789,7 +803,8 @@ func (g *gobuild) buildOne(ctx context.Context, refStr string, base v1.Image, pl } layers = append(layers, mutate.Addendum{ - Layer: binaryLayer, + Layer: binaryLayer, + MediaType: layerMediaType, History: v1.History{ Author: "ko", Created: g.creationTime, @@ -860,7 +875,7 @@ func (g *gobuild) buildOne(ctx context.Context, refStr string, base v1.Image, pl return si, nil } -func buildLayer(appPath, file string, platform *v1.Platform) (v1.Layer, error) { +func buildLayer(appPath, file string, platform *v1.Platform, layerMediaType types.MediaType) (v1.Layer, error) { // Construct a tarball with the binary and produce a layer. binaryLayerBuf, err := tarBinary(appPath, file, platform) if err != nil { @@ -872,7 +887,7 @@ func buildLayer(appPath, file string, platform *v1.Platform) (v1.Layer, error) { }, tarball.WithCompressedCaching, tarball.WithEstargzOptions(estargz.WithPrioritizedFiles([]string{ // When using estargz, prioritize downloading the binary entrypoint. appPath, - }))) + })), tarball.WithMediaType(layerMediaType)) } // Append appPath to the PATH environment variable, if it exists. Otherwise, diff --git a/pkg/build/gobuild_test.go b/pkg/build/gobuild_test.go index afe8e22be6..ccba6845cd 100644 --- a/pkg/build/gobuild_test.go +++ b/pkg/build/gobuild_test.go @@ -1152,3 +1152,76 @@ func TestMatchesPlatformSpec(t *testing.T) { } } } + +func TestGoBuildConsistentMediaTypes(t *testing.T) { + for _, c := range []struct { + desc string + mediaType, layerMediaType types.MediaType + }{{ + desc: "docker types", + mediaType: types.DockerManifestSchema2, + layerMediaType: types.DockerLayer, + }, { + desc: "oci types", + mediaType: types.OCIManifestSchema1, + layerMediaType: types.OCILayer, + }} { + t.Run(c.desc, func(t *testing.T) { + base := mutate.MediaType(empty.Image, c.mediaType) + l, err := random.Layer(10, c.layerMediaType) + if err != nil { + t.Fatal(err) + } + base, err = mutate.AppendLayers(base, l) + if err != nil { + t.Fatal(err) + } + + ng, err := NewGo( + context.Background(), + "", + WithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }), + withBuilder(writeTempFile), + withSBOMber(fauxSBOM), + ) + if err != nil { + t.Fatalf("NewGo() = %v", err) + } + + importpath := "github.com/google/ko" + + result, err := ng.Build(context.Background(), StrictScheme+importpath) + if err != nil { + t.Fatalf("Build() = %v", err) + } + + img, ok := result.(v1.Image) + if !ok { + t.Fatalf("Build() not an Image: %T", result) + } + + ls, err := img.Layers() + if err != nil { + t.Fatalf("Layers() = %v", err) + } + + for i, l := range ls { + gotMT, err := l.MediaType() + if err != nil { + t.Fatal(err) + } + if gotMT != c.layerMediaType { + t.Errorf("layer %d: got mediaType %q, want %q", i, gotMT, c.layerMediaType) + } + } + + gotMT, err := img.MediaType() + if err != nil { + t.Fatal(err) + } + if gotMT != c.mediaType { + t.Errorf("got image mediaType %q, want %q", gotMT, c.layerMediaType) + } + }) + } +}