diff --git a/platform/files.go b/platform/files.go index 68a16b6e9..15134cc2c 100644 --- a/platform/files.go +++ b/platform/files.go @@ -229,6 +229,26 @@ type RunImageForRebase struct { Mirrors []string `toml:"mirrors,omitempty" json:"mirrors,omitempty"` } +func (r *RunImageForRebase) Contains(ref string) bool { + ref = parseMaybe(ref) + if parseMaybe(r.Image) == ref { + return true + } + for _, m := range r.Mirrors { + if parseMaybe(m) == ref { + return true + } + } + return false +} + +func parseMaybe(ref string) string { + if nameRef, err := name.ParseReference(ref); err == nil { + return nameRef.Context().Name() + } + return ref +} + func (r *RunImageForRebase) ToStackMetadata() StackMetadata { return StackMetadata{ RunImage: RunImageForExport{ diff --git a/rebaser.go b/rebaser.go index b128bfced..8246ff3a9 100644 --- a/rebaser.go +++ b/rebaser.go @@ -77,6 +77,16 @@ func (r *Rebaser) Rebase(workingImage imgutil.Image, newBaseImage imgutil.Image, } origMetadata.RunImage.Reference = identifier.String() + if r.PlatformAPI.AtLeast("0.12") { + // update stack and runImage if needed + if !origMetadata.RunImage.Contains(newBaseImage.Name()) { + origMetadata.RunImage.Image = newBaseImage.Name() + origMetadata.RunImage.Mirrors = []string{} + newStackMD := origMetadata.RunImage.ToStackMetadata() + origMetadata.Stack = &newStackMD + } + } + data, err := json.Marshal(origMetadata) if err != nil { return RebaseReport{}, errors.Wrap(err, "marshall metadata") diff --git a/rebaser_test.go b/rebaser_test.go index d9378ae6d..49af0a609 100644 --- a/rebaser_test.go +++ b/rebaser_test.go @@ -1,6 +1,7 @@ package lifecycle_test import ( + "encoding/json" "math/rand" "testing" "time" @@ -129,6 +130,117 @@ func testRebaser(t *testing.T, when spec.G, it spec.S) { h.AssertEq(t, md.App, []interface{}{map[string]interface{}{"sha": "123456"}}) }) + when("new base image has no run image metadata", func() { + it.Before(func() { + lifecycleMD := platform.LayersMetadata{ + RunImage: platform.RunImageForRebase{ + TopLayer: "some-top-layer", + Reference: "some-run-image-digest-reference", + Image: "some-run-image-tag-reference", + Mirrors: []string{"some-run-image-mirror"}, + }, + Stack: &platform.StackMetadata{RunImage: platform.RunImageForExport{ + Image: "some-run-image-tag-reference", + Mirrors: []string{"some-run-image-mirror"}, + }}, + } + label, err := json.Marshal(lifecycleMD) + h.AssertNil(t, err) + h.AssertNil(t, fakeAppImage.SetLabel(platform.LayerMetadataLabel, string(label))) + }) + + when("platform API < 0.12", func() { + it.Before(func() { + rebaser.PlatformAPI = api.MustParse("0.11") + }) + + it("preserves the existing metadata", func() { + _, err := rebaser.Rebase(fakeAppImage, fakeNewBaseImage, fakeAppImage.Name(), additionalNames) + h.AssertNil(t, err) + + h.AssertNil(t, image.DecodeLabel(fakeAppImage, platform.LayerMetadataLabel, &md)) + h.AssertEq(t, md.RunImage.TopLayer, "new-top-layer-sha") + h.AssertEq(t, md.RunImage.Reference, "new-run-id") + h.AssertEq(t, md.RunImage.Image, "some-run-image-tag-reference") + h.AssertEq(t, md.RunImage.Mirrors, []string{"some-run-image-mirror"}) + h.AssertEq(t, md.Stack.RunImage.Image, "some-run-image-tag-reference") + h.AssertEq(t, md.Stack.RunImage.Mirrors, []string{"some-run-image-mirror"}) + }) + }) + + when("platform API >= 0.12", func() { + it.Before(func() { + rebaser.PlatformAPI = api.MustParse("0.12") + }) + + it("overrides the existing metadata", func() { + _, err := rebaser.Rebase(fakeAppImage, fakeNewBaseImage, fakeAppImage.Name(), additionalNames) + h.AssertNil(t, err) + + h.AssertNil(t, image.DecodeLabel(fakeAppImage, platform.LayerMetadataLabel, &md)) + var empty []string + h.AssertEq(t, md.RunImage.TopLayer, "new-top-layer-sha") + h.AssertEq(t, md.RunImage.Reference, "new-run-id") + h.AssertEq(t, md.RunImage.Image, "some-repo/new-base-image") + h.AssertEq(t, md.RunImage.Mirrors, empty) + h.AssertEq(t, md.Stack.RunImage.Image, "some-repo/new-base-image") + h.AssertEq(t, md.Stack.RunImage.Mirrors, empty) + }) + + when("new base image is an existing mirror", func() { + it.Before(func() { + fakeNewBaseImage = fakes.NewImage( + "some-run-image-mirror", + "new-top-layer-sha", + local.IDIdentifier{ + ImageID: "new-run-id", + }, + ) + h.AssertNil(t, fakeNewBaseImage.SetLabel(platform.StackIDLabel, "io.buildpacks.stacks.bionic")) + }) + + it("preserves the existing metadata", func() { + _, err := rebaser.Rebase(fakeAppImage, fakeNewBaseImage, fakeAppImage.Name(), additionalNames) + h.AssertNil(t, err) + + h.AssertNil(t, image.DecodeLabel(fakeAppImage, platform.LayerMetadataLabel, &md)) + h.AssertEq(t, md.RunImage.TopLayer, "new-top-layer-sha") + h.AssertEq(t, md.RunImage.Reference, "new-run-id") + h.AssertEq(t, md.RunImage.Image, "some-run-image-tag-reference") + h.AssertEq(t, md.RunImage.Mirrors, []string{"some-run-image-mirror"}) + h.AssertEq(t, md.Stack.RunImage.Image, "some-run-image-tag-reference") + h.AssertEq(t, md.Stack.RunImage.Mirrors, []string{"some-run-image-mirror"}) + }) + }) + + when("reference includes docker registry", func() { + it.Before(func() { + fakeNewBaseImage = fakes.NewImage( + "index.docker.io/some-run-image-mirror", + "new-top-layer-sha", + local.IDIdentifier{ + ImageID: "new-run-id", + }, + ) + h.AssertNil(t, fakeNewBaseImage.SetLabel(platform.StackIDLabel, "io.buildpacks.stacks.bionic")) + }) + + it("still matches", func() { + _, err := rebaser.Rebase(fakeAppImage, fakeNewBaseImage, fakeAppImage.Name(), additionalNames) + h.AssertNil(t, err) + + h.AssertNil(t, image.DecodeLabel(fakeAppImage, platform.LayerMetadataLabel, &md)) + h.AssertEq(t, md.RunImage.TopLayer, "new-top-layer-sha") + h.AssertEq(t, md.RunImage.Reference, "new-run-id") + h.AssertEq(t, md.RunImage.Image, "some-run-image-tag-reference") + h.AssertEq(t, md.RunImage.Mirrors, []string{"some-run-image-mirror"}) + h.AssertEq(t, md.Stack.RunImage.Image, "some-run-image-tag-reference") + h.AssertEq(t, md.Stack.RunImage.Mirrors, []string{"some-run-image-mirror"}) + }) + }) + }) + }) + when("image has io.buildpacks.stack.* labels", func() { var tests = []struct { label string @@ -544,8 +656,9 @@ func testRebaser(t *testing.T, when spec.G, it spec.S) { }) when("outputImageRef is different than workingImage name", func() { + var outputImageRef = "fizz" + it("saves using outputImageRef, not the app image name", func() { - outputImageRef := "fizz" _, err := rebaser.Rebase(fakeAppImage, fakeNewBaseImage, outputImageRef, additionalNames) h.AssertNil(t, err) h.AssertContains(t, fakeAppImage.SavedNames(), append(additionalNames, outputImageRef)...)