diff --git a/api/apis.go b/api/apis.go index e46a04700..821df4576 100644 --- a/api/apis.go +++ b/api/apis.go @@ -11,7 +11,7 @@ var ( // Platform is a pair of lists of Platform API versions: // 1. All supported versions (including deprecated versions) // 2. The versions that are deprecated - Platform = newApisMustParse([]string{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13"}, []string{}) + Platform = newApisMustParse([]string{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13", "0.14"}, []string{}) // Buildpack is a pair of lists of Buildpack API versions: // 1. All supported versions (including deprecated versions) // 2. The versions that are deprecated diff --git a/cmd/lifecycle/restorer.go b/cmd/lifecycle/restorer.go index 0cc2eb293..e0798dbfd 100644 --- a/cmd/lifecycle/restorer.go +++ b/cmd/lifecycle/restorer.go @@ -305,7 +305,7 @@ func (r *restoreCmd) restore(layerMetadata files.LayersMetadata, group buildpack Buildpacks: group.Group, Logger: cmd.DefaultLogger, PlatformAPI: r.PlatformAPI, - LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(r.LayersDir, r.SkipLayers, cmd.DefaultLogger), + LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(r.LayersDir, r.SkipLayers, cmd.DefaultLogger, r.PlatformAPI), LayersMetadata: layerMetadata, SBOMRestorer: layer.NewSBOMRestorer(layer.SBOMRestorerOpts{ LayersDir: r.LayersDir, diff --git a/internal/layer/metadata_restorer.go b/internal/layer/metadata_restorer.go index f33e0e17e..6660e7a00 100644 --- a/internal/layer/metadata_restorer.go +++ b/internal/layer/metadata_restorer.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" + "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/internal/encoding" "github.com/buildpacks/lifecycle/launch" @@ -24,18 +25,21 @@ type MetadataRestorer interface { Restore(buildpacks []buildpack.GroupElement, appMeta files.LayersMetadata, cacheMeta platform.CacheMetadata, layerSHAStore SHAStore) error } -func NewDefaultMetadataRestorer(layersDir string, skipLayers bool, logger log.Logger) *DefaultMetadataRestorer { +// NewDefaultMetadataRestorer returns an instance of the DefaultMetadataRestorer struct +func NewDefaultMetadataRestorer(layersDir string, skipLayers bool, logger log.Logger, platformAPI *api.Version) *DefaultMetadataRestorer { return &DefaultMetadataRestorer{ - Logger: logger, - LayersDir: layersDir, - SkipLayers: skipLayers, + Logger: logger, + LayersDir: layersDir, + SkipLayers: skipLayers, + PlatformAPI: platformAPI, } } type DefaultMetadataRestorer struct { - LayersDir string - SkipLayers bool - Logger log.Logger + LayersDir string + SkipLayers bool + Logger log.Logger + PlatformAPI *api.Version } func (r *DefaultMetadataRestorer) Restore(buildpacks []buildpack.GroupElement, appMeta files.LayersMetadata, cacheMeta platform.CacheMetadata, layerSHAStore SHAStore) error { @@ -113,10 +117,12 @@ func (r *DefaultMetadataRestorer) restoreLayerMetadata(layerSHAStore SHAStore, a r.Logger.Debugf("Not restoring %q from cache, marked as cache=false", identifier) continue } - // If launch=true, the metadata was restored from the app image or the layer is stale. + // If launch=true, the metadata was restored from the appLayers if present. if layer.Launch { - r.Logger.Debugf("Not restoring %q from cache, marked as launch=true", identifier) - continue + if _, ok := appLayers[layerName]; ok || r.PlatformAPI.LessThan("0.14") { + r.Logger.Debugf("Not restoring %q from cache, marked as launch=true", identifier) + continue + } } r.Logger.Infof("Restoring metadata for %q from cache", identifier) if err := r.writeLayerMetadata(layerSHAStore, buildpackDir, layerName, layer, bp.ID); err != nil { diff --git a/internal/layer/metadata_restorer_test.go b/internal/layer/metadata_restorer_test.go index aec91d373..d1aec9219 100644 --- a/internal/layer/metadata_restorer_test.go +++ b/internal/layer/metadata_restorer_test.go @@ -42,7 +42,7 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) { layerDir, err = os.MkdirTemp("", "lifecycle-layer-dir") h.AssertNil(t, err) logger = log.Logger{Handler: &discard.Handler{}} - layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger) + layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger, api.Platform.Latest()) layerSHAStore = layer.NewSHAStore() }) @@ -136,6 +136,8 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) { {"no.cache.buildpack/some-layer.toml", "[metadata]\n some-layer-key = \"some-layer-value\""}, // Cache-image-only layers. {"metadata.buildpack/cache.toml", "[metadata]\n cache-key = \"cache-value\""}, + // Cached launch layers not in app + {"metadata.buildpack/launch-cache-not-in-app.toml", "[metadata]\n cache-only-key = \"cache-only-value\"\n launch-cache-key = \"cache-specific-value\""}, } { got := h.MustReadFile(t, filepath.Join(layerDir, data.name)) h.AssertStringContains(t, string(got), data.want) @@ -143,6 +145,33 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) { } }) + when("platformAPI is less than 0.14", func() { + it.Before(func() { + layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger, api.MustParse("0.13")) + }) + + it("ignores launch-cache-not-in-app", func() { + err := layerMetadataRestorer.Restore(buildpacks, layersMetadata, cacheMetadata, layerSHAStore) + h.AssertNil(t, err) + + h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "metadata.buildpack/launch-cache-not-in-app.toml")) + unsetFlags := "[types]" + for _, data := range []struct{ name, want string }{ + // App layers. + {"metadata.buildpack/launch.toml", "[metadata]\n launch-key = \"launch-value\""}, + {"metadata.buildpack/launch-build-cache.toml", "[metadata]\n launch-build-cache-key = \"launch-build-cache-value\""}, + {"metadata.buildpack/launch-cache.toml", "[metadata]\n launch-cache-key = \"launch-cache-value\""}, + {"no.cache.buildpack/some-layer.toml", "[metadata]\n some-layer-key = \"some-layer-value\""}, + // Cache-image-only layers. + {"metadata.buildpack/cache.toml", "[metadata]\n cache-key = \"cache-value\""}, + } { + got := h.MustReadFile(t, filepath.Join(layerDir, data.name)) + h.AssertStringContains(t, string(got), data.want) + h.AssertStringDoesNotContain(t, string(got), unsetFlags) // The [types] table shouldn't exist. The build, cache and launch flags are set to false. + } + }) + }) + it("restores layer metadata without the launch, build and cache flags", func() { buildpacks = []buildpack.GroupElement{ {ID: "metadata.buildpack", API: api.Buildpack.Latest().String()}, @@ -186,14 +215,6 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) { h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "no.group.buildpack")) }) - it("does not restore launch=true layer metadata", func() { - err := layerMetadataRestorer.Restore(buildpacks, layersMetadata, cacheMetadata, layerSHAStore) - h.AssertNil(t, err) - - h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "metadata.buildpack", "launch-cache-not-in-app.toml")) - h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "metadata.buildpack", "launch-cache-not-in-app.sha")) - }) - it("does not restore cache=false layer metadata", func() { err := layerMetadataRestorer.Restore(buildpacks, layersMetadata, cacheMetadata, layerSHAStore) h.AssertNil(t, err) @@ -299,7 +320,7 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) { when("skip layers is true", func() { it.Before(func() { skipLayers = true - layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger) + layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger, api.Platform.Latest()) }) it("does not write buildpack layer metadata", func() { diff --git a/internal/layer/testdata/cache_metadata.json b/internal/layer/testdata/cache_metadata.json index cc982aa90..a60ee35a1 100644 --- a/internal/layer/testdata/cache_metadata.json +++ b/internal/layer/testdata/cache_metadata.json @@ -36,7 +36,7 @@ "launch": true, "sha": "launch-cache-old-sha" }, - "launch-cache-not-in-app": { + "launch-cache-not-in-app": { "cache": true, "data": { "launch-cache-key": "cache-specific-value", diff --git a/phase/restorer.go b/phase/restorer.go index 48b6da59e..e6e6ee6f5 100644 --- a/phase/restorer.go +++ b/phase/restorer.go @@ -36,7 +36,7 @@ func (r *Restorer) Restore(cache Cache) error { } if r.LayerMetadataRestorer == nil { - r.LayerMetadataRestorer = layer.NewDefaultMetadataRestorer(r.LayersDir, false, r.Logger) + r.LayerMetadataRestorer = layer.NewDefaultMetadataRestorer(r.LayersDir, false, r.Logger, r.PlatformAPI) } if r.SBOMRestorer == nil { diff --git a/phase/restorer_test.go b/phase/restorer_test.go index 61066c883..94b3e1654 100644 --- a/phase/restorer_test.go +++ b/phase/restorer_test.go @@ -81,7 +81,7 @@ func testRestorer(buildpackAPI, platformAPI string) func(t *testing.T, when spec {ID: "buildpack.id", API: buildpackAPI}, {ID: "escaped/buildpack/id", API: buildpackAPI}, }, - LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(layersDir, skipLayers, &logger), + LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(layersDir, skipLayers, &logger, api.Platform.Latest()), SBOMRestorer: sbomRestorer, PlatformAPI: api.MustParse(platformAPI), }