From c828d031f6a9714980b5acc5cca6274dcaa4a806 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 26 Apr 2022 15:40:03 -0400 Subject: [PATCH] Updates per PR review Signed-off-by: Natalie Arellano --- acceptance/analyzer_test.go | 8 +- analyzer.go | 155 ++++++- analyzer_test.go | 333 +++++++++++++++- builder_test.go | 13 +- cmd/launcher/main.go | 2 +- .../launch => cmd/launcher/platform}/exit.go | 6 +- cmd/launcher/platform/exit_test.go | 34 ++ .../launcher/platform}/platform.go | 0 cmd/lifecycle/analyzer.go | 55 ++- cmd/lifecycle/builder.go | 13 +- cmd/lifecycle/creator.go | 68 ++-- cmd/lifecycle/detector.go | 21 +- cmd/lifecycle/exporter.go | 35 +- cmd/lifecycle/main.go | 148 ++++++- .../lifecycle/platform/analyze_inputs.go | 55 +-- .../lifecycle/platform/analyze_inputs_test.go | 68 ++-- .../lifecycle/platform}/default_paths.go | 23 +- {platform => cmd/lifecycle/platform}/exit.go | 4 +- cmd/lifecycle/platform/exit_test.go | 34 ++ cmd/lifecycle/platform/platform.go | 37 ++ .../platform}/testdata/layers/bad-group.toml | 0 .../platform}/testdata/layers/group.toml | 0 .../platform}/testdata/layers/stack.toml | 0 cmd/lifecycle/platform/utils.go | 59 +++ cmd/lifecycle/rebaser.go | 7 +- cmd/lifecycle/restorer.go | 9 +- detector_test.go | 49 +-- handlers.go | 29 ++ internal/layer/metadata_restorer.go | 2 +- internal/layer/sbom_restorer.go | 2 +- platform/exit_test.go | 75 ---- platform/inputs/analyzer_factory.go | 216 ---------- platform/inputs/analyzer_factory_test.go | 377 ------------------ platform/inputs/cache_handler.go | 41 -- platform/inputs/image_handler.go | 51 --- platform/inputs/registry_handler.go | 97 ----- platform/inputs/registry_handler_test.go | 124 ------ platform/inputs/testdata/registry/Dockerfile | 5 - .../inputs/testmock/analyzer_ops_manager.go | 136 ------- platform/launch/exit_test.go | 71 ---- platform/launch/platform.go | 21 - restorer_test.go | 23 +- .../testmock => testmock}/cache_handler.go | 29 +- testmock/config_handler.go | 51 +++ .../testmock => testmock}/image_handler.go | 2 +- .../metadata_restorer.go | 0 .../testmock => testmock}/registry_handler.go | 26 +- .../testmock => testmock}/sbom_restorer.go | 0 48 files changed, 1115 insertions(+), 1499 deletions(-) rename {platform/launch => cmd/launcher/platform}/exit.go (92%) create mode 100644 cmd/launcher/platform/exit_test.go rename {platform => cmd/launcher/platform}/platform.go (100%) rename platform/inputs/analyze.go => cmd/lifecycle/platform/analyze_inputs.go (65%) rename platform/inputs/analyze_test.go => cmd/lifecycle/platform/analyze_inputs_test.go (71%) rename {platform/inputs => cmd/lifecycle/platform}/default_paths.go (72%) rename {platform => cmd/lifecycle/platform}/exit.go (98%) create mode 100644 cmd/lifecycle/platform/exit_test.go create mode 100644 cmd/lifecycle/platform/platform.go rename {platform/inputs => cmd/lifecycle/platform}/testdata/layers/bad-group.toml (100%) rename {platform/inputs => cmd/lifecycle/platform}/testdata/layers/group.toml (100%) rename {platform/inputs => cmd/lifecycle/platform}/testdata/layers/stack.toml (100%) create mode 100644 cmd/lifecycle/platform/utils.go create mode 100644 handlers.go delete mode 100644 platform/exit_test.go delete mode 100644 platform/inputs/analyzer_factory.go delete mode 100644 platform/inputs/analyzer_factory_test.go delete mode 100644 platform/inputs/cache_handler.go delete mode 100644 platform/inputs/image_handler.go delete mode 100644 platform/inputs/registry_handler.go delete mode 100644 platform/inputs/registry_handler_test.go delete mode 100644 platform/inputs/testdata/registry/Dockerfile delete mode 100644 platform/inputs/testmock/analyzer_ops_manager.go delete mode 100644 platform/launch/exit_test.go delete mode 100644 platform/launch/platform.go rename {platform/inputs/testmock => testmock}/cache_handler.go (50%) create mode 100644 testmock/config_handler.go rename {platform/inputs/testmock => testmock}/image_handler.go (95%) rename {internal/layer/testmock => testmock}/metadata_restorer.go (100%) rename {platform/inputs/testmock => testmock}/registry_handler.go (74%) rename {internal/layer/testmock => testmock}/sbom_restorer.go (100%) diff --git a/acceptance/analyzer_test.go b/acceptance/analyzer_test.go index a606015a6..696462ecc 100644 --- a/acceptance/analyzer_test.go +++ b/acceptance/analyzer_test.go @@ -337,7 +337,7 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe output, err := cmd.CombinedOutput() h.AssertNotNil(t, err) - h.AssertStringContains(t, string(output), "initializing analyzer: validating registry read access: ensure registry read access to some-run-image") // TODO: update some-run-image to have explicit permissions when https://github.com/buildpacks/lifecycle/pull/685 is merged + h.AssertStringContains(t, string(output), "validating registry read access: ensure registry read access to some-run-image") // TODO: update some-run-image to have explicit permissions when https://github.com/buildpacks/lifecycle/pull/685 is merged }) when("stack.toml not present", func() { @@ -933,7 +933,7 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe output, err := cmd.CombinedOutput() h.AssertNotNil(t, err) - expected := "initializing analyzer: validating registry read access: ensure registry read access to " + analyzeRegFixtures.InaccessibleImage + expected := "validating registry read access: ensure registry read access to " + analyzeRegFixtures.InaccessibleImage h.AssertStringContains(t, string(output), expected) }) }) @@ -1033,7 +1033,7 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe output, err := cmd.CombinedOutput() h.AssertNotNil(t, err) - expected := "initializing analyzer: validating registry write access: ensure registry read/write access to " + analyzeRegFixtures.ReadOnlyCacheImage + expected := "validating registry write access: ensure registry read/write access to " + analyzeRegFixtures.ReadOnlyCacheImage h.AssertStringContains(t, string(output), expected) }) }) @@ -1106,7 +1106,7 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe output, err := cmd.CombinedOutput() h.AssertNotNil(t, err) - expected := "initializing analyzer: validating registry write access: ensure registry read/write access to " + analyzeRegFixtures.InaccessibleImage + expected := "validating registry write access: ensure registry read/write access to " + analyzeRegFixtures.InaccessibleImage h.AssertStringContains(t, string(output), expected) }) }) diff --git a/analyzer.go b/analyzer.go index c39744e97..ba50561b9 100644 --- a/analyzer.go +++ b/analyzer.go @@ -6,6 +6,7 @@ import ( "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/cache" "github.com/buildpacks/lifecycle/image" "github.com/buildpacks/lifecycle/internal/layer" "github.com/buildpacks/lifecycle/platform" @@ -15,17 +16,163 @@ type Platform interface { API() *api.Version } +type AnalyzerFactory struct { + platformAPI *api.Version + cacheHandler CacheHandler + configHandler ConfigHandler + imageHandler ImageHandler + registryHandler RegistryHandler +} + +func NewAnalyzerFactory(platformAPI *api.Version, cacheHandler CacheHandler, configHandler ConfigHandler, imageHandler ImageHandler, registryHandler RegistryHandler) *AnalyzerFactory { + return &AnalyzerFactory{ + platformAPI: platformAPI, + cacheHandler: cacheHandler, + configHandler: configHandler, + imageHandler: imageHandler, + registryHandler: registryHandler, + } +} + type Analyzer struct { PreviousImage imgutil.Image RunImage imgutil.Image Logger Logger - Platform Platform SBOMRestorer layer.SBOMRestorer // Platform API < 0.7 Buildpacks []buildpack.GroupBuildpack Cache Cache LayerMetadataRestorer layer.MetadataRestorer + RestoresLayerMetadata bool +} + +func (f *AnalyzerFactory) NewAnalyzer( + additionalTags []string, + cacheImageRef string, + launchCacheDir string, + layersDir string, + legacyCacheDir string, + legacyGroup buildpack.Group, + legacyGroupPath string, + outputImageRef string, + previousImageRef string, + runImageRef string, + skipLayers bool, + logger Logger, +) (*Analyzer, error) { + analyzer := &Analyzer{ + LayerMetadataRestorer: &layer.NopMetadataRestorer{}, + Logger: logger, + SBOMRestorer: &layer.NopSBOMRestorer{}, + } + + if f.platformAPI.AtLeast("0.7") { + if err := f.ensureRegistryAccess(additionalTags, cacheImageRef, outputImageRef, runImageRef, previousImageRef); err != nil { + return nil, err + } + } else { + if err := f.setBuildpacks(analyzer, legacyGroup, legacyGroupPath); err != nil { + return nil, err + } + + if err := f.setCache(analyzer, cacheImageRef, legacyCacheDir); err != nil { + return nil, err + } + + analyzer.LayerMetadataRestorer = &layer.DefaultMetadataRestorer{ + LayersDir: layersDir, + Logger: logger, + SkipLayers: skipLayers, + } + analyzer.RestoresLayerMetadata = true + } + if f.platformAPI.AtLeast("0.8") && !skipLayers { + analyzer.SBOMRestorer = &layer.DefaultSBOMRestorer{ + LayersDir: layersDir, + Logger: logger, + } + } + if err := f.setPrevious(analyzer, previousImageRef, launchCacheDir); err != nil { + return nil, err + } + if err := f.setRun(analyzer, runImageRef); err != nil { + return nil, err + } + return analyzer, nil +} + +func (f *AnalyzerFactory) ensureRegistryAccess( + additionalTags []string, + cacheImageRef string, + outputImageRef string, + runImageRef string, + previousImageRef string, +) error { + var readImages, writeImages []string + writeImages = append(writeImages, cacheImageRef) + if !f.imageHandler.Docker() { + readImages = append(readImages, previousImageRef, runImageRef) + writeImages = append(writeImages, outputImageRef) + writeImages = append(writeImages, additionalTags...) + } + + if err := f.registryHandler.EnsureReadAccess(readImages...); err != nil { + return errors.Wrap(err, "validating registry read access") + } + if err := f.registryHandler.EnsureWriteAccess(writeImages...); err != nil { + return errors.Wrap(err, "validating registry write access") + } + return nil +} + +func (f *AnalyzerFactory) setBuildpacks(analyzer *Analyzer, group buildpack.Group, path string) error { + if len(group.Group) > 0 { + analyzer.Buildpacks = group.Group + return nil + } + var err error + analyzer.Buildpacks, err = f.configHandler.ReadGroup(path) + return err +} + +func (f *AnalyzerFactory) setCache(analyzer *Analyzer, imageRef string, dir string) error { + var err error + analyzer.Cache, err = f.cacheHandler.InitCache(imageRef, dir) + return err +} + +func (f *AnalyzerFactory) setPrevious(analyzer *Analyzer, imageRef string, launchCacheDir string) error { + if imageRef == "" { + return nil + } + var err error + analyzer.PreviousImage, err = f.imageHandler.InitImage(imageRef) + if err != nil { + return errors.Wrap(err, "getting previous image") + } + if launchCacheDir == "" || !f.imageHandler.Docker() { + return nil + } + + volumeCache, err := cache.NewVolumeCache(launchCacheDir) + if err != nil { + return errors.Wrap(err, "creating launch cache") + } + analyzer.PreviousImage = cache.NewCachingImage(analyzer.PreviousImage, volumeCache) + return nil +} + +func (f *AnalyzerFactory) setRun(analyzer *Analyzer, imageRef string) error { + if imageRef == "" { + return nil + } + var err error + analyzer.RunImage, err = f.imageHandler.InitImage(imageRef) + if err != nil { + return errors.Wrap(err, "getting run image") + } + return nil } // Analyze fetches the layers metadata from the previous image and writes analyzed.toml. @@ -62,7 +209,7 @@ func (a *Analyzer) Analyze() (platform.AnalyzedMetadata, error) { } } - if a.restoresLayerMetadata() { + if a.RestoresLayerMetadata { cacheMeta, err = retrieveCacheMetadata(a.Cache, a.Logger) if err != nil { return platform.AnalyzedMetadata{}, err @@ -81,10 +228,6 @@ func (a *Analyzer) Analyze() (platform.AnalyzedMetadata, error) { }, nil } -func (a *Analyzer) restoresLayerMetadata() bool { - return a.Platform.API().LessThan("0.7") -} - func (a *Analyzer) getImageIdentifier(image imgutil.Image) (*platform.ImageIdentifier, error) { if !image.Found() { a.Logger.Infof("Previous image with name %q not found", image.Name()) diff --git a/analyzer_test.go b/analyzer_test.go index caba348a3..46cff2edf 100644 --- a/analyzer_test.go +++ b/analyzer_test.go @@ -13,7 +13,7 @@ import ( "github.com/buildpacks/imgutil/fakes" "github.com/buildpacks/imgutil/local" "github.com/golang/mock/gomock" - "github.com/sclevine/spec" + testspec "github.com/sclevine/spec" "github.com/sclevine/spec/report" "github.com/buildpacks/lifecycle" @@ -22,29 +22,325 @@ import ( "github.com/buildpacks/lifecycle/cache" "github.com/buildpacks/lifecycle/cmd" "github.com/buildpacks/lifecycle/internal/layer" - ltestmock "github.com/buildpacks/lifecycle/internal/layer/testmock" - "github.com/buildpacks/lifecycle/platform" + spec "github.com/buildpacks/lifecycle/platform" h "github.com/buildpacks/lifecycle/testhelpers" "github.com/buildpacks/lifecycle/testmock" ) func TestAnalyzer(t *testing.T) { for _, api := range api.Platform.Supported { - spec.Run(t, "unit-analyzer/"+api.String(), testAnalyzerBuilder(api.String()), spec.Parallel(), spec.Report(report.Terminal{})) + testspec.Run(t, "unit-analyzer/"+api.String(), testAnalyzer(api.String()), testspec.Parallel(), testspec.Report(report.Terminal{})) } + testspec.Run(t, "unit-new-analyzer", testAnalyzerFactory, testspec.Parallel(), testspec.Report(report.Terminal{})) } -func testAnalyzerBuilder(platformAPI string) func(t *testing.T, when spec.G, it spec.S) { - return func(t *testing.T, when spec.G, it spec.S) { +func testAnalyzerFactory(t *testing.T, when testspec.G, it testspec.S) { + when("#NewAnalyzer", func() { + var ( + analyzerFactory *lifecycle.AnalyzerFactory + fakeCacheHandler *testmock.MockCacheHandler + fakeConfigHandler *testmock.MockConfigHandler + fakeImageHandler *testmock.MockImageHandler + fakeRegistryHandler *testmock.MockRegistryHandler + logger *log.Logger + mockController *gomock.Controller + tempDir string + ) + + it.Before(func() { + mockController = gomock.NewController(t) + fakeCacheHandler = testmock.NewMockCacheHandler(mockController) + fakeConfigHandler = testmock.NewMockConfigHandler(mockController) + fakeImageHandler = testmock.NewMockImageHandler(mockController) + fakeRegistryHandler = testmock.NewMockRegistryHandler(mockController) + logger = &log.Logger{Handler: &discard.Handler{}} + var err error + tempDir, err = ioutil.TempDir("", "") + h.AssertNil(t, err) + }) + + it.After(func() { + mockController.Finish() + os.RemoveAll(tempDir) + }) + + when("platform api >= 0.8", func() { + it.Before(func() { + analyzerFactory = lifecycle.NewAnalyzerFactory( + api.Platform.Latest(), + fakeCacheHandler, + fakeConfigHandler, + fakeImageHandler, + fakeRegistryHandler, + ) + }) + + it("configures the analyzer", func() { + previousImage := fakes.NewImage("some-previous-image-ref", "", nil) + runImage := fakes.NewImage("some-run-image-ref", "", nil) + + t.Log("ensures registry access") + fakeImageHandler.EXPECT().Docker().Return(false) + fakeRegistryHandler.EXPECT().EnsureReadAccess([]string{"some-previous-image-ref", "some-run-image-ref"}) + fakeRegistryHandler.EXPECT().EnsureWriteAccess([]string{"some-cache-image-ref", "some-output-image-ref", "some-additional-tag"}) + + t.Log("does not process cache") + + t.Log("processes previous image") + fakeImageHandler.EXPECT().InitImage("some-previous-image-ref").Return(previousImage, nil) + fakeImageHandler.EXPECT().Docker().Return(false) + + t.Log("processes run image") + fakeImageHandler.EXPECT().InitImage("some-run-image-ref").Return(runImage, nil) + + analyzer, err := analyzerFactory.NewAnalyzer([]string{"some-additional-tag"}, "some-cache-image-ref", "some-launch-cache-dir", "some-layers-dir", "some-legacy-cache-dir", buildpack.Group{}, "some-legacy-group-path", "some-output-image-ref", "some-previous-image-ref", "some-run-image-ref", false, logger) + h.AssertNil(t, err) + h.AssertEq(t, analyzer.PreviousImage.Name(), previousImage.Name()) + h.AssertEq(t, analyzer.RunImage.Name(), runImage.Name()) + + t.Log("restores sbom data") + sbomRestorer, ok := analyzer.SBOMRestorer.(*layer.DefaultSBOMRestorer) + h.AssertEq(t, ok, true) + h.AssertEq(t, sbomRestorer.LayersDir, "some-layers-dir") + h.AssertEq(t, sbomRestorer.Logger, logger) + + t.Log("does not restore layer metadata") + _, ok = analyzer.LayerMetadataRestorer.(*layer.NopMetadataRestorer) + h.AssertEq(t, ok, true) + + t.Log("sets logger") + h.AssertEq(t, analyzer.Logger, logger) + }) + + when("daemon case", func() { + it("configures the analyzer", func() { + previousImage := fakes.NewImage("some-previous-image-ref", "", nil) + runImage := fakes.NewImage("some-run-image-ref", "", nil) + + t.Log("ensures registry access") + fakeImageHandler.EXPECT().Docker().Return(true) + fakeRegistryHandler.EXPECT().EnsureReadAccess() + fakeRegistryHandler.EXPECT().EnsureWriteAccess([]string{"some-cache-image-ref"}) + + t.Log("processes previous image") + fakeImageHandler.EXPECT().InitImage("some-previous-image-ref").Return(previousImage, nil) + fakeImageHandler.EXPECT().Docker().Return(true) + + t.Log("processes run image") + fakeImageHandler.EXPECT().InitImage("some-run-image-ref").Return(runImage, nil) + + launchCacheDir := filepath.Join(tempDir, "some-launch-cache-dir") + h.AssertNil(t, os.MkdirAll(launchCacheDir, 0777)) + analyzer, err := analyzerFactory.NewAnalyzer([]string{"some-additional-tag"}, "some-cache-image-ref", launchCacheDir, "some-layers-dir", "some-legacy-cache-dir", buildpack.Group{}, "some-legacy-group-path", "some-output-image-ref", "some-previous-image-ref", "some-run-image-ref", false, nil) + h.AssertNil(t, err) + h.AssertEq(t, analyzer.PreviousImage.Name(), previousImage.Name()) + h.AssertEq(t, analyzer.RunImage.Name(), runImage.Name()) + + t.Log("uses the provided launch cache") + _, ok := analyzer.PreviousImage.(*cache.CachingImage) + h.AssertEq(t, ok, true) + h.AssertPathExists(t, filepath.Join(launchCacheDir, "committed")) + h.AssertPathExists(t, filepath.Join(launchCacheDir, "staging")) + }) + }) + + when("skip layers", func() { + it("does not restore sbom data", func() { + fakeImageHandler.EXPECT().Docker() + fakeRegistryHandler.EXPECT().EnsureReadAccess(gomock.Any()) + fakeRegistryHandler.EXPECT().EnsureWriteAccess(gomock.Any()) + fakeImageHandler.EXPECT().InitImage(gomock.Any()) + fakeImageHandler.EXPECT().Docker() + fakeImageHandler.EXPECT().InitImage(gomock.Any()) + + analyzer, err := analyzerFactory.NewAnalyzer([]string{"some-additional-tag"}, "some-cache-image-ref", "some-launch-cache-dir", "some-layers-dir", "some-legacy-cache-dir", buildpack.Group{}, "some-legacy-group-path", "some-output-image-ref", "some-previous-image-ref", "some-run-image-ref", true, nil) + h.AssertNil(t, err) + + _, ok := analyzer.SBOMRestorer.(*layer.NopSBOMRestorer) + h.AssertEq(t, ok, true) + }) + }) + }) + + when("platform api = 0.7", func() { + it.Before(func() { + analyzerFactory = lifecycle.NewAnalyzerFactory( + api.MustParse("0.7"), + fakeCacheHandler, + fakeConfigHandler, + fakeImageHandler, + fakeRegistryHandler, + ) + }) + + it("configures the analyzer", func() { + previousImage := fakes.NewImage("some-previous-image-ref", "", nil) + runImage := fakes.NewImage("some-run-image-ref", "", nil) + + t.Log("ensures registry access") + fakeImageHandler.EXPECT().Docker().Return(false) + fakeRegistryHandler.EXPECT().EnsureReadAccess([]string{"some-previous-image-ref", "some-run-image-ref"}) + fakeRegistryHandler.EXPECT().EnsureWriteAccess([]string{"some-cache-image-ref", "some-output-image-ref", "some-additional-tag"}) + + t.Log("processes previous image") + fakeImageHandler.EXPECT().InitImage("some-previous-image-ref").Return(previousImage, nil) + fakeImageHandler.EXPECT().Docker().Return(false) + + t.Log("processes run image") + fakeImageHandler.EXPECT().InitImage("some-run-image-ref").Return(runImage, nil) + + analyzer, err := analyzerFactory.NewAnalyzer([]string{"some-additional-tag"}, "some-cache-image-ref", "some-launch-cache-dir", "some-layers-dir", "some-legacy-cache-dir", buildpack.Group{}, "some-legacy-group-path", "some-output-image-ref", "some-previous-image-ref", "some-run-image-ref", false, logger) + h.AssertNil(t, err) + h.AssertEq(t, analyzer.PreviousImage.Name(), previousImage.Name()) + h.AssertEq(t, analyzer.RunImage.Name(), runImage.Name()) + + t.Log("does not restore sbom data") + _, ok := analyzer.SBOMRestorer.(*layer.NopSBOMRestorer) + h.AssertEq(t, ok, true) + + t.Log("does not restore layer metadata") + _, ok = analyzer.LayerMetadataRestorer.(*layer.NopMetadataRestorer) + h.AssertEq(t, ok, true) + + t.Log("sets logger") + h.AssertEq(t, analyzer.Logger, logger) + }) + + when("daemon case", func() { + it("configures the analyzer", func() { + previousImage := fakes.NewImage("some-previous-image-ref", "", nil) + runImage := fakes.NewImage("some-run-image-ref", "", nil) + + t.Log("ensures registry access") + fakeImageHandler.EXPECT().Docker().Return(true) + fakeRegistryHandler.EXPECT().EnsureReadAccess() + fakeRegistryHandler.EXPECT().EnsureWriteAccess([]string{"some-cache-image-ref"}) + + t.Log("processes previous image") + fakeImageHandler.EXPECT().InitImage("some-previous-image-ref").Return(previousImage, nil) + fakeImageHandler.EXPECT().Docker().Return(true) + + t.Log("processes run image") + fakeImageHandler.EXPECT().InitImage("some-run-image-ref").Return(runImage, nil) + + launchCacheDir := filepath.Join(tempDir, "some-launch-cache-dir") + h.AssertNil(t, os.MkdirAll(launchCacheDir, 0777)) + analyzer, err := analyzerFactory.NewAnalyzer([]string{"some-additional-tag"}, "some-cache-image-ref", launchCacheDir, "some-layers-dir", "some-legacy-cache-dir", buildpack.Group{}, "some-legacy-group-path", "some-output-image-ref", "some-previous-image-ref", "some-run-image-ref", false, nil) + h.AssertNil(t, err) + h.AssertEq(t, analyzer.PreviousImage.Name(), previousImage.Name()) + h.AssertEq(t, analyzer.RunImage.Name(), runImage.Name()) + }) + }) + }) + + when("platform api < 0.7", func() { + it.Before(func() { + analyzerFactory = lifecycle.NewAnalyzerFactory( + api.MustParse("0.6"), + fakeCacheHandler, + fakeConfigHandler, + fakeImageHandler, + fakeRegistryHandler, + ) + }) + + it("configures the analyzer", func() { + previousImage := fakes.NewImage("some-previous-image-ref", "", nil) + runImage := fakes.NewImage("some-run-image-ref", "", nil) + + t.Log("does not ensure registry access") + + t.Log("processes group") + fakeConfigHandler.EXPECT().ReadGroup("some-legacy-group-path") + + t.Log("processes cache") + fakeCacheHandler.EXPECT().InitCache("some-cache-image-ref", "some-legacy-cache-dir") + + t.Log("processes previous image") + fakeImageHandler.EXPECT().InitImage("some-previous-image-ref").Return(previousImage, nil) + fakeImageHandler.EXPECT().Docker().Return(false) + + t.Log("processes run image") + fakeImageHandler.EXPECT().InitImage("some-run-image-ref").Return(runImage, nil) + + analyzer, err := analyzerFactory.NewAnalyzer([]string{"some-additional-tag"}, "some-cache-image-ref", "some-launch-cache-dir", "some-layers-dir", "some-legacy-cache-dir", buildpack.Group{}, "some-legacy-group-path", "some-output-image-ref", "some-previous-image-ref", "some-run-image-ref", false, logger) + h.AssertNil(t, err) + h.AssertEq(t, analyzer.PreviousImage.Name(), previousImage.Name()) + h.AssertEq(t, analyzer.RunImage.Name(), runImage.Name()) + + t.Log("does not restore sbom data") + _, ok := analyzer.SBOMRestorer.(*layer.NopSBOMRestorer) + h.AssertEq(t, ok, true) + + t.Log("restores layer metadata") + metadataRestorer, ok := analyzer.LayerMetadataRestorer.(*layer.DefaultMetadataRestorer) + h.AssertEq(t, ok, true) + h.AssertEq(t, metadataRestorer.LayersDir, "some-layers-dir") + h.AssertEq(t, metadataRestorer.Logger, logger) + h.AssertEq(t, metadataRestorer.SkipLayers, false) + + t.Log("sets logger") + h.AssertEq(t, analyzer.Logger, logger) + }) + + when("daemon case", func() { + it("configures the analyzer", func() { + previousImage := fakes.NewImage("some-previous-image-ref", "", nil) + runImage := fakes.NewImage("some-run-image-ref", "", nil) + + t.Log("does not ensure registry access") + + t.Log("processes group") + fakeConfigHandler.EXPECT().ReadGroup("some-legacy-group-path") + + t.Log("processes cache") + fakeCacheHandler.EXPECT().InitCache("some-cache-image-ref", "some-legacy-cache-dir") + + t.Log("processes previous image") + fakeImageHandler.EXPECT().InitImage("some-previous-image-ref").Return(previousImage, nil) + fakeImageHandler.EXPECT().Docker().Return(true) + + t.Log("processes run image") + fakeImageHandler.EXPECT().InitImage("some-run-image-ref").Return(runImage, nil) + + launchCacheDir := filepath.Join(tempDir, "some-launch-cache-dir") + h.AssertNil(t, os.MkdirAll(launchCacheDir, 0777)) + analyzer, err := analyzerFactory.NewAnalyzer([]string{"some-additional-tag"}, "some-cache-image-ref", launchCacheDir, "some-layers-dir", "some-legacy-cache-dir", buildpack.Group{}, "some-legacy-group-path", "some-output-image-ref", "some-previous-image-ref", "some-run-image-ref", false, nil) + h.AssertNil(t, err) + h.AssertEq(t, analyzer.PreviousImage.Name(), previousImage.Name()) + h.AssertEq(t, analyzer.RunImage.Name(), runImage.Name()) + }) + }) + + when("buildpack group is provided", func() { + it("uses the provided group", func() { + fakeCacheHandler.EXPECT().InitCache(gomock.Any(), gomock.Any()) + fakeImageHandler.EXPECT().InitImage(gomock.Any()) + fakeImageHandler.EXPECT().Docker() + fakeImageHandler.EXPECT().InitImage(gomock.Any()) + + providedGroup := buildpack.Group{Group: []buildpack.GroupBuildpack{{ID: "some-buildpack-id"}}} + analyzer, err := analyzerFactory.NewAnalyzer([]string{"some-additional-tag"}, "some-cache-image-ref", "some-launch-cache-dir", "some-layers-dir", "some-legacy-cache-dir", providedGroup, "some-legacy-group-path", "some-output-image-ref", "some-previous-image-ref", "some-run-image-ref", false, nil) + h.AssertNil(t, err) + + h.AssertEq(t, analyzer.Buildpacks, providedGroup.Group) + }) + }) + }) + }) +} + +func testAnalyzer(platformAPI string) func(t *testing.T, when testspec.G, it testspec.S) { + return func(t *testing.T, when testspec.G, it testspec.S) { var ( cacheDir string layersDir string tmpDir string analyzer *lifecycle.Analyzer image *fakes.Image - metadataRestorer *ltestmock.MockMetadataRestorer + metadataRestorer *testmock.MockMetadataRestorer mockCtrl *gomock.Controller - sbomRestorer *ltestmock.MockSBOMRestorer + sbomRestorer *testmock.MockSBOMRestorer testCache lifecycle.Cache ) @@ -70,16 +366,15 @@ func testAnalyzerBuilder(platformAPI string) func(t *testing.T, when spec.G, it discardLogger := log.Logger{Handler: &discard.Handler{}} mockCtrl = gomock.NewController(t) - metadataRestorer = ltestmock.NewMockMetadataRestorer(mockCtrl) + metadataRestorer = testmock.NewMockMetadataRestorer(mockCtrl) - sbomRestorer = ltestmock.NewMockSBOMRestorer(mockCtrl) + sbomRestorer = testmock.NewMockSBOMRestorer(mockCtrl) - p := platform.NewPlatform(platformAPI) h.AssertNil(t, err) analyzer = &lifecycle.Analyzer{ PreviousImage: image, Logger: &discardLogger, - Platform: p, + SBOMRestorer: sbomRestorer, Buildpacks: []buildpack.GroupBuildpack{ {ID: "metadata.buildpack", API: api.Buildpack.Latest().String()}, {ID: "no.cache.buildpack", API: api.Buildpack.Latest().String()}, @@ -87,7 +382,7 @@ func testAnalyzerBuilder(platformAPI string) func(t *testing.T, when spec.G, it }, Cache: testCache, LayerMetadataRestorer: metadataRestorer, - SBOMRestorer: sbomRestorer, + RestoresLayerMetadata: api.MustParse(platformAPI).LessThan("0.7"), } if testing.Verbose() { @@ -106,13 +401,13 @@ func testAnalyzerBuilder(platformAPI string) func(t *testing.T, when spec.G, it when("#Analyze", func() { var ( - expectedAppMetadata platform.LayersMetadata - expectedCacheMetadata platform.CacheMetadata + expectedAppMetadata spec.LayersMetadata + expectedCacheMetadata spec.CacheMetadata ref *testmock.MockReference ) expectRestoresLayerMetadataIfSupported := func() { - if analyzer.Platform.API().LessThan("0.7") { + if api.MustParse(platformAPI).LessThan("0.7") { useShaFiles := true layerSHAStore := layer.NewSHAStore(useShaFiles) metadataRestorer.EXPECT().Restore(analyzer.Buildpacks, expectedAppMetadata, expectedCacheMetadata, layerSHAStore) @@ -174,7 +469,7 @@ func testAnalyzerBuilder(platformAPI string) func(t *testing.T, when spec.G, it h.AssertNil(t, err) h.AssertNil(t, md.PreviousImage) - h.AssertEq(t, md.Metadata, platform.LayersMetadata{}) + h.AssertEq(t, md.Metadata, spec.LayersMetadata{}) }) }) @@ -188,7 +483,7 @@ func testAnalyzerBuilder(platformAPI string) func(t *testing.T, when spec.G, it it("returns empty analyzed metadata", func() { md, err := analyzer.Analyze() h.AssertNil(t, err) - h.AssertEq(t, md.Metadata, platform.LayersMetadata{}) + h.AssertEq(t, md.Metadata, spec.LayersMetadata{}) }) }) @@ -202,7 +497,7 @@ func testAnalyzerBuilder(platformAPI string) func(t *testing.T, when spec.G, it it("returns empty analyzed metadata", func() { md, err := analyzer.Analyze() h.AssertNil(t, err) - h.AssertEq(t, md.Metadata, platform.LayersMetadata{}) + h.AssertEq(t, md.Metadata, spec.LayersMetadata{}) }) }) diff --git a/builder_test.go b/builder_test.go index 845cdc865..1653fd825 100644 --- a/builder_test.go +++ b/builder_test.go @@ -15,29 +15,30 @@ import ( "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" "github.com/pkg/errors" - "github.com/sclevine/spec" + testspec "github.com/sclevine/spec" "github.com/sclevine/spec/report" "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/buildpack" + "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" "github.com/buildpacks/lifecycle/env" "github.com/buildpacks/lifecycle/launch" "github.com/buildpacks/lifecycle/layers" - "github.com/buildpacks/lifecycle/platform" + spec "github.com/buildpacks/lifecycle/platform" h "github.com/buildpacks/lifecycle/testhelpers" "github.com/buildpacks/lifecycle/testmock" ) func TestBuilder(t *testing.T) { - spec.Run(t, "Builder", testBuilder, spec.Report(report.Terminal{})) + testspec.Run(t, "Builder", testBuilder, testspec.Report(report.Terminal{})) } //go:generate mockgen -package testmock -destination testmock/env.go github.com/buildpacks/lifecycle BuildEnv //go:generate mockgen -package testmock -destination testmock/buildpack_store.go github.com/buildpacks/lifecycle BuildpackStore //go:generate mockgen -package testmock -destination testmock/buildpack.go github.com/buildpacks/lifecycle Buildpack -func testBuilder(t *testing.T, when spec.G, it spec.S) { +func testBuilder(t *testing.T, when testspec.G, it testspec.S) { var ( builder *lifecycle.Builder mockCtrl *gomock.Controller @@ -97,8 +98,8 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { when("#Build", func() { when("building succeeds", func() { it("should provide a subset of the build plan to each buildpack", func() { - builder.Plan = platform.BuildPlan{ - Entries: []platform.BuildPlanEntry{ + builder.Plan = spec.BuildPlan{ + Entries: []spec.BuildPlanEntry{ { Providers: []buildpack.GroupBuildpack{ {ID: "A", Version: "v1"}, diff --git a/cmd/launcher/main.go b/cmd/launcher/main.go index df36362c8..0edaf0dfd 100644 --- a/cmd/launcher/main.go +++ b/cmd/launcher/main.go @@ -10,9 +10,9 @@ import ( "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/cmd" + "github.com/buildpacks/lifecycle/cmd/launcher/platform" "github.com/buildpacks/lifecycle/env" "github.com/buildpacks/lifecycle/launch" - platform "github.com/buildpacks/lifecycle/platform/launch" ) func main() { diff --git a/platform/launch/exit.go b/cmd/launcher/platform/exit.go similarity index 92% rename from platform/launch/exit.go rename to cmd/launcher/platform/exit.go index 9ce357812..c6c15275d 100644 --- a/platform/launch/exit.go +++ b/cmd/launcher/platform/exit.go @@ -1,4 +1,4 @@ -package launch +package platform type LifecycleExitError int @@ -12,8 +12,8 @@ type Exiter interface { CodeFor(errType LifecycleExitError) int } -func NewExiter(apiStr string) Exiter { - switch apiStr { +func NewExiter(platformAPI string) Exiter { + switch platformAPI { case "0.3", "0.4", "0.5": return &LegacyExiter{} default: diff --git a/cmd/launcher/platform/exit_test.go b/cmd/launcher/platform/exit_test.go new file mode 100644 index 000000000..f9eb2fb64 --- /dev/null +++ b/cmd/launcher/platform/exit_test.go @@ -0,0 +1,34 @@ +package platform_test + +import ( + "testing" + + "github.com/sclevine/spec" + + "github.com/buildpacks/lifecycle/cmd/launcher/platform" + h "github.com/buildpacks/lifecycle/testhelpers" +) + +func TestExiter(t *testing.T) { + spec.Run(t, "Test Exiter", testExiter) +} + +func testExiter(t *testing.T, when spec.G, it spec.S) { + when("#NewExiter", func() { + when("platform api >= 0.6", func() { + it("returns a default exiter", func() { + foundExiter := platform.NewExiter("0.6") + _, ok := foundExiter.(*platform.DefaultExiter) + h.AssertEq(t, ok, true) + }) + }) + + when("platform api < 0.6", func() { + it("returns a legacy exiter", func() { + foundExiter := platform.NewExiter("0.5") + _, ok := foundExiter.(*platform.LegacyExiter) + h.AssertEq(t, ok, true) + }) + }) + }) +} diff --git a/platform/platform.go b/cmd/launcher/platform/platform.go similarity index 100% rename from platform/platform.go rename to cmd/launcher/platform/platform.go diff --git a/cmd/lifecycle/analyzer.go b/cmd/lifecycle/analyzer.go index 033bd5822..ba912c6a7 100644 --- a/cmd/lifecycle/analyzer.go +++ b/cmd/lifecycle/analyzer.go @@ -7,18 +7,18 @@ import ( "github.com/google/go-containerregistry/pkg/authn" "github.com/pkg/errors" + "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/auth" "github.com/buildpacks/lifecycle/cmd" + "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" "github.com/buildpacks/lifecycle/internal/encoding" - "github.com/buildpacks/lifecycle/platform" - "github.com/buildpacks/lifecycle/platform/inputs" "github.com/buildpacks/lifecycle/priv" ) type analyzeCmd struct { platform Platform + platform.AnalyzeInputs - inputs.Analyze docker client.CommonAPIClient // construct if necessary before dropping privileges keychain authn.Keychain // construct if necessary before dropping privileges } @@ -64,16 +64,22 @@ func (a *analyzeCmd) DefineFlags() { } // Args validates arguments and flags, and fills in default values. -func (a *analyzeCmd) Args(_ int, args []string) error { - resolver := &inputs.AnalyzeResolver{PlatformAPI: a.platform.API()} - resolvedInputs, err := resolver.Resolve(a.Analyze, args, cmd.DefaultLogger) +func (a *analyzeCmd) Args(nargs int, args []string) error { + if nargs != 1 { + err := fmt.Errorf("received %d arguments, but expected 1", nargs) + return cmd.FailErrCode(err, cmd.CodeInvalidArgs, "parse arguments") + } + a.AnalyzeInputs.OutputImageRef = args[0] + + var err error + a.AnalyzeInputs, err = a.platform.ResolveAnalyze(a.AnalyzeInputs, cmd.DefaultLogger) if err != nil { return cmd.FailErrCode(err, cmd.CodeInvalidArgs, "resolve inputs") } - a.Analyze = resolvedInputs return nil } +// Privileges validates the needed privileges func (a *analyzeCmd) Privileges() error { var err error a.keychain, err = auth.DefaultKeychain(a.RegistryImages()...) @@ -96,20 +102,29 @@ func (a *analyzeCmd) Privileges() error { return nil } +// Exec executes the command func (a *analyzeCmd) Exec() error { - factory := inputs.NewAnalyzerFactory(a.platform.API(), a.docker, a.keychain) - analyzer, err := factory.NewAnalyzer(inputs.ForAnalyzer{ - AdditionalTags: a.AdditionalTags, - CacheImageRef: a.CacheImageRef, - LaunchCacheDir: a.LaunchCacheDir, - LayersDir: a.LayersDir, - LegacyCacheDir: a.LegacyCacheDir, - LegacyGroupPath: a.LegacyGroupPath, - OutputImageRef: a.OutputImageRef, - PreviousImageRef: a.PreviousImageRef, - RunImageRef: a.RunImageRef, - SkipLayers: a.SkipLayers, - }, cmd.DefaultLogger) + factory := lifecycle.NewAnalyzerFactory( + a.platform.API(), + NewCacheHandler(a.keychain), + NewConfigHandler(), + NewImageHandler(a.docker, a.keychain), + NewRegistryHandler(a.keychain), + ) + analyzer, err := factory.NewAnalyzer( + a.ForAnalyzer.AdditionalTags, + a.ForAnalyzer.CacheImageRef, + a.ForAnalyzer.LaunchCacheDir, + a.ForAnalyzer.LayersDir, + a.ForAnalyzer.LegacyCacheDir, + a.ForAnalyzer.LegacyGroup, + a.ForAnalyzer.LegacyGroupPath, + a.ForAnalyzer.OutputImageRef, + a.ForAnalyzer.PreviousImageRef, + a.ForAnalyzer.RunImageRef, + a.ForAnalyzer.SkipLayers, + cmd.DefaultLogger, + ) if err != nil { return err } diff --git a/cmd/lifecycle/builder.go b/cmd/lifecycle/builder.go index ae3ca0931..242ae271d 100644 --- a/cmd/lifecycle/builder.go +++ b/cmd/lifecycle/builder.go @@ -8,9 +8,10 @@ import ( "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/cmd" + "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" "github.com/buildpacks/lifecycle/internal/encoding" "github.com/buildpacks/lifecycle/launch" - "github.com/buildpacks/lifecycle/platform" + spec "github.com/buildpacks/lifecycle/platform" "github.com/buildpacks/lifecycle/priv" ) @@ -77,7 +78,7 @@ func (b *buildCmd) Exec() error { return b.build(group, plan) } -func (ba buildArgs) build(group buildpack.Group, plan platform.BuildPlan) error { +func (ba buildArgs) build(group buildpack.Group, plan spec.BuildPlan) error { buildpackStore, err := buildpack.NewBuildpackStore(ba.buildpacksDir) if err != nil { return cmd.FailErrCode(err, ba.platform.CodeFor(platform.BuildError), "build") @@ -112,15 +113,15 @@ func (ba buildArgs) build(group buildpack.Group, plan platform.BuildPlan) error return nil } -func (b *buildCmd) readData() (buildpack.Group, platform.BuildPlan, error) { +func (b *buildCmd) readData() (buildpack.Group, spec.BuildPlan, error) { group, err := buildpack.ReadGroup(b.groupPath) if err != nil { - return buildpack.Group{}, platform.BuildPlan{}, cmd.FailErr(err, "read buildpack group") + return buildpack.Group{}, spec.BuildPlan{}, cmd.FailErr(err, "read buildpack group") } - var plan platform.BuildPlan + var plan spec.BuildPlan if _, err := toml.DecodeFile(b.planPath, &plan); err != nil { - return buildpack.Group{}, platform.BuildPlan{}, cmd.FailErr(err, "parse detect plan") + return buildpack.Group{}, spec.BuildPlan{}, cmd.FailErr(err, "parse detect plan") } return group, plan, nil } diff --git a/cmd/lifecycle/creator.go b/cmd/lifecycle/creator.go index b9df2d257..ce55b686a 100644 --- a/cmd/lifecycle/creator.go +++ b/cmd/lifecycle/creator.go @@ -9,13 +9,13 @@ import ( "github.com/google/go-containerregistry/pkg/authn" "github.com/pkg/errors" + "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/auth" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/cmd" "github.com/buildpacks/lifecycle/image" "github.com/buildpacks/lifecycle/internal/str" "github.com/buildpacks/lifecycle/platform" - "github.com/buildpacks/lifecycle/platform/inputs" "github.com/buildpacks/lifecycle/priv" ) @@ -170,17 +170,27 @@ func (c *createCmd) Exec() error { plan platform.BuildPlan ) if c.platform.API().AtLeast("0.7") { - factory := inputs.NewAnalyzerFactory(c.platform.API(), c.docker, c.keychain) - analyzer, err := factory.NewAnalyzer(inputs.ForAnalyzer{ - AdditionalTags: c.additionalTags, - CacheImageRef: c.cacheImageRef, - LaunchCacheDir: c.launchCacheDir, - LayersDir: c.layersDir, - OutputImageRef: c.outputImageRef, - PreviousImageRef: c.previousImageRef, - RunImageRef: c.runImageRef, - SkipLayers: c.skipRestore, - }, cmd.DefaultLogger) + factory := lifecycle.NewAnalyzerFactory( + c.platform.API(), + NewCacheHandler(c.keychain), + NewConfigHandler(), + NewImageHandler(c.docker, c.keychain), + NewRegistryHandler(c.keychain), + ) + analyzer, err := factory.NewAnalyzer( + c.additionalTags, + c.cacheImageRef, + c.launchCacheDir, + c.layersDir, + "", + buildpack.Group{}, + "", + c.outputImageRef, + c.previousImageRef, + c.runImageRef, + c.skipRestore, + cmd.DefaultLogger, + ) if err != nil { return errors.Wrap(err, "initializing analyzer") } @@ -216,19 +226,27 @@ func (c *createCmd) Exec() error { } cmd.DefaultLogger.Phase("ANALYZING") - factory := inputs.NewAnalyzerFactory(c.platform.API(), c.docker, c.keychain) - analyzer, err := factory.NewAnalyzer(inputs.ForAnalyzer{ - AdditionalTags: c.additionalTags, - CacheImageRef: c.cacheImageRef, - LaunchCacheDir: c.launchCacheDir, - LayersDir: c.layersDir, - LegacyCacheDir: c.cacheDir, - LegacyGroup: group, - OutputImageRef: c.outputImageRef, - PreviousImageRef: c.previousImageRef, - RunImageRef: c.runImageRef, - SkipLayers: c.skipRestore, - }, cmd.DefaultLogger) + factory := lifecycle.NewAnalyzerFactory( + c.platform.API(), + NewCacheHandler(c.keychain), + NewConfigHandler(), + NewImageHandler(c.docker, c.keychain), + NewRegistryHandler(c.keychain), + ) + analyzer, err := factory.NewAnalyzer( + c.additionalTags, + c.cacheImageRef, + c.launchCacheDir, + c.layersDir, + c.cacheDir, + group, + "", + c.outputImageRef, + c.previousImageRef, + c.runImageRef, + c.skipRestore, + cmd.DefaultLogger, + ) if err != nil { return errors.Wrap(err, "initializing analyzer") } diff --git a/cmd/lifecycle/detector.go b/cmd/lifecycle/detector.go index 9beb94883..52c09075c 100644 --- a/cmd/lifecycle/detector.go +++ b/cmd/lifecycle/detector.go @@ -7,8 +7,9 @@ import ( "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/cmd" + platform "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" "github.com/buildpacks/lifecycle/internal/encoding" - "github.com/buildpacks/lifecycle/platform" + spec "github.com/buildpacks/lifecycle/platform" "github.com/buildpacks/lifecycle/priv" ) @@ -80,13 +81,13 @@ func (d *detectCmd) Exec() error { return d.writeData(group, plan) } -func (da detectArgs) detect() (buildpack.Group, platform.BuildPlan, error) { +func (da detectArgs) detect() (buildpack.Group, spec.BuildPlan, error) { order, err := buildpack.ReadOrder(da.orderPath) if err != nil { - return buildpack.Group{}, platform.BuildPlan{}, cmd.FailErr(err, "read buildpack order file") + return buildpack.Group{}, spec.BuildPlan{}, cmd.FailErr(err, "read buildpack order file") } if err := da.verifyBuildpackApis(order); err != nil { - return buildpack.Group{}, platform.BuildPlan{}, err + return buildpack.Group{}, spec.BuildPlan{}, err } detector, err := lifecycle.NewDetector( @@ -99,7 +100,7 @@ func (da detectArgs) detect() (buildpack.Group, platform.BuildPlan, error) { da.platform, ) if err != nil { - return buildpack.Group{}, platform.BuildPlan{}, cmd.FailErr(err, "initialize detector") + return buildpack.Group{}, spec.BuildPlan{}, cmd.FailErr(err, "initialize detector") } group, plan, err := detector.Detect(order) if err != nil { @@ -109,15 +110,15 @@ func (da detectArgs) detect() (buildpack.Group, platform.BuildPlan, error) { case buildpack.ErrTypeFailedDetection: cmd.DefaultLogger.Error("No buildpack groups passed detection.") cmd.DefaultLogger.Error("Please check that you are running against the correct path.") - return buildpack.Group{}, platform.BuildPlan{}, cmd.FailErrCode(err, da.platform.CodeFor(platform.FailedDetect), "detect") + return buildpack.Group{}, spec.BuildPlan{}, cmd.FailErrCode(err, da.platform.CodeFor(platform.FailedDetect), "detect") case buildpack.ErrTypeBuildpack: cmd.DefaultLogger.Error("No buildpack groups passed detection.") - return buildpack.Group{}, platform.BuildPlan{}, cmd.FailErrCode(err, da.platform.CodeFor(platform.FailedDetectWithErrors), "detect") + return buildpack.Group{}, spec.BuildPlan{}, cmd.FailErrCode(err, da.platform.CodeFor(platform.FailedDetectWithErrors), "detect") default: - return buildpack.Group{}, platform.BuildPlan{}, cmd.FailErrCode(err, da.platform.CodeFor(platform.DetectError), "detect") + return buildpack.Group{}, spec.BuildPlan{}, cmd.FailErrCode(err, da.platform.CodeFor(platform.DetectError), "detect") } default: - return buildpack.Group{}, platform.BuildPlan{}, cmd.FailErrCode(err, da.platform.CodeFor(platform.DetectError), "detect") + return buildpack.Group{}, spec.BuildPlan{}, cmd.FailErrCode(err, da.platform.CodeFor(platform.DetectError), "detect") } } @@ -143,7 +144,7 @@ func (da detectArgs) verifyBuildpackApis(order buildpack.Order) error { return nil } -func (d *detectCmd) writeData(group buildpack.Group, plan platform.BuildPlan) error { +func (d *detectCmd) writeData(group buildpack.Group, plan spec.BuildPlan) error { if err := encoding.WriteTOML(d.groupPath, group); err != nil { return cmd.FailErr(err, "write buildpack group") } diff --git a/cmd/lifecycle/exporter.go b/cmd/lifecycle/exporter.go index 8f3747776..60051bef3 100644 --- a/cmd/lifecycle/exporter.go +++ b/cmd/lifecycle/exporter.go @@ -21,15 +21,16 @@ import ( "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/cache" "github.com/buildpacks/lifecycle/cmd" + "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" "github.com/buildpacks/lifecycle/image" "github.com/buildpacks/lifecycle/internal/encoding" "github.com/buildpacks/lifecycle/layers" - "github.com/buildpacks/lifecycle/platform" + spec "github.com/buildpacks/lifecycle/platform" "github.com/buildpacks/lifecycle/priv" ) type exportCmd struct { - analyzedMD platform.AnalyzedMetadata + analyzedMD spec.AnalyzedMetadata //flags: inputs cacheDir string @@ -55,7 +56,7 @@ type exportArgs struct { stackPath string targetRegistry string imageNames []string - stackMD platform.StackMetadata + stackMD spec.StackMetadata useDaemon bool uid, gid int @@ -156,16 +157,16 @@ func (e *exportCmd) Args(nargs int, args []string) error { return nil } -func readStack(stackPath string) (platform.StackMetadata, error) { +func readStack(stackPath string) (spec.StackMetadata, error) { var ( - stackMD platform.StackMetadata + stackMD spec.StackMetadata ) if _, err := toml.DecodeFile(stackPath, &stackMD); err != nil { if os.IsNotExist(err) { cmd.DefaultLogger.Infof("no stack metadata found at path '%s'\n", stackPath) } else { - return platform.StackMetadata{}, err + return spec.StackMetadata{}, err } } @@ -260,14 +261,14 @@ func (e *exportCmd) validateRunImageInput() error { } } -func (ea exportArgs) export(group buildpack.Group, cacheStore lifecycle.Cache, analyzedMD platform.AnalyzedMetadata) error { +func (ea exportArgs) export(group buildpack.Group, cacheStore lifecycle.Cache, analyzedMD spec.AnalyzedMetadata) error { artifactsDir, err := ioutil.TempDir("", "lifecycle.exporter.layer") if err != nil { return cmd.FailErr(err, "create temp directory") } defer os.RemoveAll(artifactsDir) - var projectMD platform.ProjectMetadata + var projectMD spec.ProjectMetadata _, err = toml.DecodeFile(ea.projectMetadataPath, &projectMD) if err != nil { if !os.IsNotExist(err) { @@ -327,7 +328,7 @@ func (ea exportArgs) export(group buildpack.Group, cacheStore lifecycle.Cache, a return nil } -func (ea exportArgs) initDaemonAppImage(analyzedMD platform.AnalyzedMetadata) (imgutil.Image, string, error) { +func (ea exportArgs) initDaemonAppImage(analyzedMD spec.AnalyzedMetadata) (imgutil.Image, string, error) { var opts = []local.ImageOption{ local.FromBaseImage(ea.runImageRef), } @@ -366,7 +367,7 @@ func (ea exportArgs) initDaemonAppImage(analyzedMD platform.AnalyzedMetadata) (i return appImage, runImageID.String(), nil } -func (ea exportArgs) initRemoteAppImage(analyzedMD platform.AnalyzedMetadata) (imgutil.Image, string, error) { +func (ea exportArgs) initRemoteAppImage(analyzedMD spec.AnalyzedMetadata) (imgutil.Image, string, error) { var opts = []remote.ImageOption{ remote.FromBaseImage(ea.runImageRef), } @@ -411,10 +412,10 @@ func (ea exportArgs) initRemoteAppImage(analyzedMD platform.AnalyzedMetadata) (i func launcherConfig(launcherPath string) lifecycle.LauncherConfig { return lifecycle.LauncherConfig{ Path: launcherPath, - Metadata: platform.LauncherMetadata{ + Metadata: spec.LauncherMetadata{ Version: cmd.Version, - Source: platform.SourceMetadata{ - Git: platform.GitMetadata{ + Source: spec.SourceMetadata{ + Git: spec.GitMetadata{ Repository: cmd.SCMRepository, Commit: cmd.SCMCommit, }, @@ -423,17 +424,17 @@ func launcherConfig(launcherPath string) lifecycle.LauncherConfig { } } -func parseAnalyzedMD(logger lifecycle.Logger, path string) (platform.AnalyzedMetadata, error) { - var analyzedMD platform.AnalyzedMetadata +func parseAnalyzedMD(logger lifecycle.Logger, path string) (spec.AnalyzedMetadata, error) { + var analyzedMD spec.AnalyzedMetadata _, err := toml.DecodeFile(path, &analyzedMD) if err != nil { if os.IsNotExist(err) { logger.Warnf("Warning: analyzed TOML file not found at '%s'", path) - return platform.AnalyzedMetadata{}, nil + return spec.AnalyzedMetadata{}, nil } - return platform.AnalyzedMetadata{}, err + return spec.AnalyzedMetadata{}, err } return analyzedMD, nil diff --git a/cmd/lifecycle/main.go b/cmd/lifecycle/main.go index 2202858f8..48e324a73 100644 --- a/cmd/lifecycle/main.go +++ b/cmd/lifecycle/main.go @@ -5,19 +5,25 @@ import ( "path/filepath" "strings" + "github.com/buildpacks/imgutil" + "github.com/buildpacks/imgutil/local" + "github.com/buildpacks/imgutil/remote" + "github.com/docker/docker/client" "github.com/google/go-containerregistry/pkg/authn" + "github.com/pkg/errors" "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/cache" "github.com/buildpacks/lifecycle/cmd" - lplatform "github.com/buildpacks/lifecycle/platform" + "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" ) type Platform interface { API() *api.Version - CodeFor(errType lplatform.LifecycleExitError) int + CodeFor(errType platform.LifecycleExitError) int + ResolveAnalyze(inputs platform.AnalyzeInputs, logger lifecycle.Logger) (platform.AnalyzeInputs, error) } func main() { @@ -26,7 +32,7 @@ func main() { cmd.Exit(err) } - p := lplatform.NewPlatform(platformAPI) + p := platform.NewPlatform(platformAPI) switch strings.TrimSuffix(filepath.Base(os.Args[0]), filepath.Ext(os.Args[0])) { case "detector": @@ -76,6 +82,90 @@ func subcommand(platform Platform) { } } +// handlers + +type DefaultCacheHandler struct { + keychain authn.Keychain +} + +func NewCacheHandler(keychain authn.Keychain) *DefaultCacheHandler { + return &DefaultCacheHandler{ + keychain: keychain, + } +} + +func (ch *DefaultCacheHandler) InitCache(cacheImageRef string, cacheDir string) (lifecycle.Cache, error) { + var ( + cacheStore lifecycle.Cache + err error + ) + if cacheImageRef != "" { + cacheStore, err = cache.NewImageCacheFromName(cacheImageRef, ch.keychain) + if err != nil { + return nil, errors.Wrap(err, "creating image cache") + } + } else if cacheDir != "" { + cacheStore, err = cache.NewVolumeCache(cacheDir) + if err != nil { + return nil, errors.Wrap(err, "creating volume cache") + } + } + return cacheStore, nil +} + +type DefaultImageHandler struct { + docker client.CommonAPIClient + keychain authn.Keychain +} + +func NewImageHandler(docker client.CommonAPIClient, keychain authn.Keychain) *DefaultImageHandler { + return &DefaultImageHandler{ + docker: docker, + keychain: keychain, + } +} + +func (h *DefaultImageHandler) InitImage(imageRef string) (imgutil.Image, error) { + if imageRef == "" { + return nil, nil + } + + if h.docker != nil { + return local.NewImage( + imageRef, + h.docker, + local.FromBaseImage(imageRef), + ) + } + + return remote.NewImage( + imageRef, + h.keychain, + remote.FromBaseImage(imageRef), + ) +} + +func (h *DefaultImageHandler) Docker() bool { + return h.docker != nil +} + +type DefaultConfigHandler struct{} + +func NewConfigHandler() *DefaultConfigHandler { + return &DefaultConfigHandler{} +} + +func (h *DefaultConfigHandler) ReadGroup(path string) ([]buildpack.GroupBuildpack, error) { + group, err := buildpack.ReadGroup(path) + if err != nil { + return nil, errors.Wrap(err, "reading buildpack group") + } + if err = verifyBuildpackApis(group); err != nil { + return nil, err + } + return group.Group, nil +} + func verifyBuildpackApis(group buildpack.Group) error { for _, bp := range group.Group { if bp.API == "" { @@ -90,6 +180,58 @@ func verifyBuildpackApis(group buildpack.Group) error { return nil } +type DefaultRegistryHandler struct { + keychain authn.Keychain +} + +func NewRegistryHandler(keychain authn.Keychain) *DefaultRegistryHandler { + return &DefaultRegistryHandler{ + keychain: keychain, + } +} + +func (rv *DefaultRegistryHandler) EnsureReadAccess(imageRefs ...string) error { + for _, imageRef := range imageRefs { + if err := verifyReadAccess(imageRef, rv.keychain); err != nil { + return err + } + } + return nil +} + +func (rv *DefaultRegistryHandler) EnsureWriteAccess(imageRefs ...string) error { + for _, imageRef := range imageRefs { + if err := verifyReadWriteAccess(imageRef, rv.keychain); err != nil { + return err + } + } + return nil +} + +func verifyReadAccess(imageRef string, keychain authn.Keychain) error { + if imageRef == "" { + return nil + } + img, _ := remote.NewImage(imageRef, keychain) + if !img.CheckReadAccess() { + return errors.Errorf("ensure registry read access to %s", imageRef) + } + return nil +} + +func verifyReadWriteAccess(imageRef string, keychain authn.Keychain) error { + if imageRef == "" { + return nil + } + img, _ := remote.NewImage(imageRef, keychain) + if !img.CheckReadWriteAccess() { + return errors.Errorf("ensure registry read/write access to %s", imageRef) + } + return nil +} + +// helpers + func initCache(cacheImageTag, cacheDir string, keychain authn.Keychain) (lifecycle.Cache, error) { var ( cacheStore lifecycle.Cache diff --git a/platform/inputs/analyze.go b/cmd/lifecycle/platform/analyze_inputs.go similarity index 65% rename from platform/inputs/analyze.go rename to cmd/lifecycle/platform/analyze_inputs.go index 4acf2a433..71a8ad1c3 100644 --- a/platform/inputs/analyze.go +++ b/cmd/lifecycle/platform/analyze_inputs.go @@ -1,20 +1,17 @@ -package inputs +package platform import ( - "fmt" - "github.com/pkg/errors" "github.com/buildpacks/lifecycle" - "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/image" "github.com/buildpacks/lifecycle/internal/str" ) -// Analyze holds the values of command-line flags and args. +// AnalyzeInputs holds the values of command-line flags and args. // Fields are the cumulative total of inputs across all supported platform APIs. -type Analyze struct { +type AnalyzeInputs struct { AnalyzedPath string StackPath string UID int @@ -37,11 +34,11 @@ type ForAnalyzer struct { PreviousImageRef string RunImageRef string SkipLayers bool - - LegacyGroup buildpack.Group // for creator + LegacyGroup buildpack.Group // for creator only } -func (a Analyze) RegistryImages() []string { +// RegistryImages returns the inputs that are images in a registry. +func (a AnalyzeInputs) RegistryImages() []string { var images []string images = appendNotEmpty(images, a.CacheImageRef) if !a.UseDaemon { @@ -51,49 +48,39 @@ func (a Analyze) RegistryImages() []string { return images } -type AnalyzeResolver struct { - PlatformAPI *api.Version -} - -// Resolve accepts AnalyzeInputs with flags filled in, and args. -// It returns AnalyzeInputs with default values filled in, or an error if the provided inputs are not valid. -func (av *AnalyzeResolver) Resolve(inputs Analyze, cmdArgs []string, logger lifecycle.Logger) (Analyze, error) { +// ResolveAnalyze accepts an AnalyzeInputs and returns a new AnalyzeInputs with default values filled in, +// or an error if the provided inputs are not valid. +func (r *InputsResolver) ResolveAnalyze(inputs AnalyzeInputs, logger lifecycle.Logger) (AnalyzeInputs, error) { resolvedInputs := inputs - nargs := len(cmdArgs) - if nargs != 1 { - return Analyze{}, fmt.Errorf("failed to parse arguments: received %d arguments, but expected 1", nargs) - } - resolvedInputs.OutputImageRef = cmdArgs[0] - - if err := av.fillDefaults(&resolvedInputs, logger); err != nil { - return Analyze{}, err + if err := r.fillDefaults(&resolvedInputs, logger); err != nil { + return AnalyzeInputs{}, err } - if err := av.validate(resolvedInputs, logger); err != nil { - return Analyze{}, err + if err := r.validate(resolvedInputs, logger); err != nil { + return AnalyzeInputs{}, err } return resolvedInputs, nil } -func (av *AnalyzeResolver) fillDefaults(inputs *Analyze, logger lifecycle.Logger) error { +func (r *InputsResolver) fillDefaults(inputs *AnalyzeInputs, logger lifecycle.Logger) error { if inputs.AnalyzedPath == PlaceholderAnalyzedPath { - inputs.AnalyzedPath = defaultPath(PlaceholderAnalyzedPath, inputs.LayersDir, av.PlatformAPI) + inputs.AnalyzedPath = defaultPath(PlaceholderAnalyzedPath, inputs.LayersDir, r.platformAPI) } if inputs.LegacyGroupPath == PlaceholderGroupPath { - inputs.LegacyGroupPath = defaultPath(PlaceholderGroupPath, inputs.LayersDir, av.PlatformAPI) + inputs.LegacyGroupPath = defaultPath(PlaceholderGroupPath, inputs.LayersDir, r.platformAPI) } if inputs.PreviousImageRef == "" { inputs.PreviousImageRef = inputs.OutputImageRef } - return av.fillRunImage(inputs, logger) + return r.fillRunImage(inputs, logger) } -func (av *AnalyzeResolver) fillRunImage(inputs *Analyze, logger lifecycle.Logger) error { - if av.PlatformAPI.LessThan("0.7") || inputs.RunImageRef != "" { +func (r *InputsResolver) fillRunImage(inputs *AnalyzeInputs, logger lifecycle.Logger) error { + if r.platformAPI.LessThan("0.7") || inputs.RunImageRef != "" { return nil } @@ -114,7 +101,7 @@ func (av *AnalyzeResolver) fillRunImage(inputs *Analyze, logger lifecycle.Logger return nil } -func (av *AnalyzeResolver) validate(inputs Analyze, logger lifecycle.Logger) error { +func (r *InputsResolver) validate(inputs AnalyzeInputs, logger lifecycle.Logger) error { if inputs.OutputImageRef == "" { return errors.New("image argument is required") } @@ -133,7 +120,7 @@ func (av *AnalyzeResolver) validate(inputs Analyze, logger lifecycle.Logger) err return errors.Wrap(err, "validating image tag(s)") } - if av.PlatformAPI.AtLeast("0.7") { + if r.platformAPI.AtLeast("0.7") { return nil } diff --git a/platform/inputs/analyze_test.go b/cmd/lifecycle/platform/analyze_inputs_test.go similarity index 71% rename from platform/inputs/analyze_test.go rename to cmd/lifecycle/platform/analyze_inputs_test.go index 39c21d551..06dd61198 100644 --- a/platform/inputs/analyze_test.go +++ b/cmd/lifecycle/platform/analyze_inputs_test.go @@ -1,4 +1,4 @@ -package inputs_test +package platform_test import ( "path/filepath" @@ -11,8 +11,8 @@ import ( "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/api" + "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" "github.com/buildpacks/lifecycle/internal/str" - "github.com/buildpacks/lifecycle/platform/inputs" h "github.com/buildpacks/lifecycle/testhelpers" ) @@ -25,25 +25,17 @@ func TestAnalyzeInputs(t *testing.T) { func testAnalyzeInputs(platformAPI string) func(t *testing.T, when spec.G, it spec.S) { return func(t *testing.T, when spec.G, it spec.S) { var ( - av *inputs.AnalyzeResolver + resolver *platform.InputsResolver logHandler *memory.Handler logger lifecycle.Logger ) + it.Before(func() { - av = &inputs.AnalyzeResolver{PlatformAPI: api.MustParse(platformAPI)} + resolver = platform.NewInputsResolver(api.MustParse(platformAPI)) logHandler = memory.New() logger = &log.Logger{Handler: logHandler} }) - when("called without an app image", func() { - it("errors", func() { - _, err := av.Resolve(inputs.Analyze{}, []string{}, logger) - h.AssertNotNil(t, err) - expected := "failed to parse arguments: received 0 arguments, but expected 1" - h.AssertStringContains(t, err.Error(), expected) - }) - }) - when("latest platform api(s)", func() { it.Before(func() { h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.7"), "") @@ -52,20 +44,22 @@ func testAnalyzeInputs(platformAPI string) func(t *testing.T, when spec.G, it sp when("run image", func() { when("not provided", func() { it("falls back to stack.toml", func() { - inputs := inputs.Analyze{ - StackPath: filepath.Join("testdata", "layers", "stack.toml"), + inputs := platform.AnalyzeInputs{ + StackPath: filepath.Join("testdata", "layers", "stack.toml"), + ForAnalyzer: platform.ForAnalyzer{OutputImageRef: "some-image"}, } - ret, err := av.Resolve(inputs, []string{"some-image"}, logger) + ret, err := resolver.ResolveAnalyze(inputs, logger) h.AssertNil(t, err) h.AssertEq(t, ret.RunImageRef, "some-run-image") }) when("stack.toml not present", func() { it("errors", func() { - inputs := inputs.Analyze{ - StackPath: "not-exist-stack.toml", + inputs := platform.AnalyzeInputs{ + StackPath: "not-exist-stack.toml", + ForAnalyzer: platform.ForAnalyzer{OutputImageRef: "some-image"}, } - _, err := av.Resolve(inputs, []string{"some-image"}, logger) + _, err := resolver.ResolveAnalyze(inputs, logger) h.AssertNotNil(t, err) expected := "-run-image is required when there is no stack metadata available" h.AssertStringContains(t, err.Error(), expected) @@ -76,8 +70,8 @@ func testAnalyzeInputs(platformAPI string) func(t *testing.T, when spec.G, it sp when("provided destination tags are on different registries", func() { it("errors", func() { - inputs := inputs.Analyze{ - ForAnalyzer: inputs.ForAnalyzer{ + inputs := platform.AnalyzeInputs{ + ForAnalyzer: platform.ForAnalyzer{ AdditionalTags: str.Slice{ "some-registry.io/some-namespace/some-image:tag", "some-other-registry.io/some-namespace/some-image", @@ -86,7 +80,7 @@ func testAnalyzeInputs(platformAPI string) func(t *testing.T, when spec.G, it sp RunImageRef: "some-run-image-ref", // ignore }, } - _, err := av.Resolve(inputs, []string{"some-image"}, logger) + _, err := resolver.ResolveAnalyze(inputs, logger) h.AssertNotNil(t, err) expected := "writing to multiple registries is unsupported" h.AssertStringContains(t, err.Error(), expected) @@ -101,7 +95,10 @@ func testAnalyzeInputs(platformAPI string) func(t *testing.T, when spec.G, it sp when("cache image tag and cache directory are both blank", func() { it("warns", func() { - _, err := av.Resolve(inputs.Analyze{}, []string{"some-image"}, logger) + inputs := platform.AnalyzeInputs{ + ForAnalyzer: platform.ForAnalyzer{OutputImageRef: "some-image"}, + } + _, err := resolver.ResolveAnalyze(inputs, logger) h.AssertNil(t, err) expected := "Not restoring cached layer metadata, no cache flag specified." h.AssertLogEntry(t, logHandler, expected) @@ -111,10 +108,11 @@ func testAnalyzeInputs(platformAPI string) func(t *testing.T, when spec.G, it sp when("run image", func() { when("not provided", func() { it("does not warn", func() { - inputs := inputs.Analyze{ - StackPath: "not-exist-stack.toml", + inputs := platform.AnalyzeInputs{ + StackPath: "not-exist-stack.toml", + ForAnalyzer: platform.ForAnalyzer{OutputImageRef: "some-image"}, } - _, err := av.Resolve(inputs, []string{"some-image"}, logger) + _, err := resolver.ResolveAnalyze(inputs, logger) h.AssertNil(t, err) h.AssertNoLogEntry(t, logHandler, `no stack metadata found at path ''`) h.AssertNoLogEntry(t, logHandler, `Previous image with name "" not found`) @@ -129,14 +127,15 @@ func testAnalyzeInputs(platformAPI string) func(t *testing.T, when spec.G, it sp "Platform API < 0.5 reads and writes to the working directory", ) - inputs := inputs.Analyze{ - AnalyzedPath: inputs.PlaceholderAnalyzedPath, - ForAnalyzer: inputs.ForAnalyzer{ - LegacyGroupPath: inputs.PlaceholderGroupPath, + inputs := platform.AnalyzeInputs{ + AnalyzedPath: platform.PlaceholderAnalyzedPath, + ForAnalyzer: platform.ForAnalyzer{ + LegacyGroupPath: platform.PlaceholderGroupPath, LayersDir: "some-layers-dir", + OutputImageRef: "some-image", }, } - ret, err := av.Resolve(inputs, []string{"some-image"}, logger) + ret, err := resolver.ResolveAnalyze(inputs, logger) h.AssertNil(t, err) h.AssertEq(t, ret.LegacyGroupPath, filepath.Join("some-layers-dir", "group.toml")) h.AssertEq(t, ret.AnalyzedPath, filepath.Join("some-layers-dir", "analyzed.toml")) @@ -151,14 +150,15 @@ func testAnalyzeInputs(platformAPI string) func(t *testing.T, when spec.G, it sp when("layers path is provided", func() { it("uses the group path at the working directory and writes analyzed.toml at the working directory", func() { - inputs := inputs.Analyze{ + inputs := platform.AnalyzeInputs{ AnalyzedPath: filepath.Join(".", "analyzed.toml"), - ForAnalyzer: inputs.ForAnalyzer{ + ForAnalyzer: platform.ForAnalyzer{ LegacyGroupPath: filepath.Join(".", "group.toml"), LayersDir: filepath.Join("testdata", "other-layers"), + OutputImageRef: "some-image", }, } - ret, err := av.Resolve(inputs, []string{"some-image"}, logger) + ret, err := resolver.ResolveAnalyze(inputs, logger) h.AssertNil(t, err) h.AssertEq(t, ret.LegacyGroupPath, filepath.Join(".", "group.toml")) h.AssertEq(t, ret.AnalyzedPath, filepath.Join(".", "analyzed.toml")) diff --git a/platform/inputs/default_paths.go b/cmd/lifecycle/platform/default_paths.go similarity index 72% rename from platform/inputs/default_paths.go rename to cmd/lifecycle/platform/default_paths.go index 2981d1f0d..b276bca5d 100644 --- a/platform/inputs/default_paths.go +++ b/cmd/lifecycle/platform/default_paths.go @@ -1,24 +1,21 @@ -package inputs +package platform import ( - "os" "path/filepath" - "github.com/BurntSushi/toml" - - "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/api" - "github.com/buildpacks/lifecycle/platform" ) -var ( +const ( DefaultAnalyzedFile = "analyzed.toml" DefaultGroupFile = "group.toml" DefaultOrderFile = "order.toml" DefaultPlanFile = "plan.toml" DefaultProjectMetadataFile = "project-metadata.toml" DefaultReportFile = "report.toml" +) +var ( PlaceholderAnalyzedPath = filepath.Join("", DefaultAnalyzedFile) PlaceholderGroupPath = filepath.Join("", DefaultGroupFile) PlaceholderPlanPath = filepath.Join("", DefaultPlanFile) @@ -36,15 +33,3 @@ func defaultPath(placeholderPath, layersDir string, platformAPI *api.Version) st } return filepath.Join(layersDir, filename) } - -func readStack(stackPath string, logger lifecycle.Logger) (platform.StackMetadata, error) { - var stackMD platform.StackMetadata - if _, err := toml.DecodeFile(stackPath, &stackMD); err != nil { - if os.IsNotExist(err) { - logger.Infof("no stack metadata found at path '%s'\n", stackPath) - } else { - return platform.StackMetadata{}, err - } - } - return stackMD, nil -} diff --git a/platform/exit.go b/cmd/lifecycle/platform/exit.go similarity index 98% rename from platform/exit.go rename to cmd/lifecycle/platform/exit.go index e3d9c888f..705809e27 100644 --- a/platform/exit.go +++ b/cmd/lifecycle/platform/exit.go @@ -20,8 +20,8 @@ type Exiter interface { CodeFor(errType LifecycleExitError) int } -func NewExiter(apiStr string) Exiter { - switch apiStr { +func NewExiter(platformAPI string) Exiter { + switch platformAPI { case "0.3", "0.4", "0.5": return &LegacyExiter{} default: diff --git a/cmd/lifecycle/platform/exit_test.go b/cmd/lifecycle/platform/exit_test.go new file mode 100644 index 000000000..cc9070ebd --- /dev/null +++ b/cmd/lifecycle/platform/exit_test.go @@ -0,0 +1,34 @@ +package platform_test + +import ( + "testing" + + "github.com/sclevine/spec" + + "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" + h "github.com/buildpacks/lifecycle/testhelpers" +) + +func TestExiter(t *testing.T) { + spec.Run(t, "Test Exiter", testExiter) +} + +func testExiter(t *testing.T, when spec.G, it spec.S) { + when("#NewExiter", func() { + when("platform api >= 0.6", func() { + it("returns a default exiter", func() { + foundExiter := platform.NewExiter("0.6") + _, ok := foundExiter.(*platform.DefaultExiter) + h.AssertEq(t, ok, true) + }) + }) + + when("platform api < 0.6", func() { + it("returns a legacy exiter", func() { + foundExiter := platform.NewExiter("0.5") + _, ok := foundExiter.(*platform.LegacyExiter) + h.AssertEq(t, ok, true) + }) + }) + }) +} diff --git a/cmd/lifecycle/platform/platform.go b/cmd/lifecycle/platform/platform.go new file mode 100644 index 000000000..941833bb2 --- /dev/null +++ b/cmd/lifecycle/platform/platform.go @@ -0,0 +1,37 @@ +package platform + +import ( + "github.com/buildpacks/lifecycle/api" +) + +// Platform handles logic pertaining to inputs and outputs from a platform (lifecycle invoker)'s perspective. +type Platform struct { + *InputsResolver + Exiter + api *api.Version +} + +// NewPlatform accepts a platform API and returns a new Platform. +func NewPlatform(apiStr string) *Platform { + platformAPI := api.MustParse(apiStr) + return &Platform{ + InputsResolver: NewInputsResolver(platformAPI), + Exiter: NewExiter(apiStr), + api: platformAPI, + } +} + +// API returns the platform API. +func (p *Platform) API() *api.Version { + return p.api +} + +// InputsResolver resolves inputs for each of the lifecycle phases. +type InputsResolver struct { + platformAPI *api.Version +} + +// NewInputsResolver accepts a platform API and returns a new InputsResolver. +func NewInputsResolver(platformAPI *api.Version) *InputsResolver { + return &InputsResolver{platformAPI: platformAPI} +} diff --git a/platform/inputs/testdata/layers/bad-group.toml b/cmd/lifecycle/platform/testdata/layers/bad-group.toml similarity index 100% rename from platform/inputs/testdata/layers/bad-group.toml rename to cmd/lifecycle/platform/testdata/layers/bad-group.toml diff --git a/platform/inputs/testdata/layers/group.toml b/cmd/lifecycle/platform/testdata/layers/group.toml similarity index 100% rename from platform/inputs/testdata/layers/group.toml rename to cmd/lifecycle/platform/testdata/layers/group.toml diff --git a/platform/inputs/testdata/layers/stack.toml b/cmd/lifecycle/platform/testdata/layers/stack.toml similarity index 100% rename from platform/inputs/testdata/layers/stack.toml rename to cmd/lifecycle/platform/testdata/layers/stack.toml diff --git a/cmd/lifecycle/platform/utils.go b/cmd/lifecycle/platform/utils.go new file mode 100644 index 000000000..98d889b5c --- /dev/null +++ b/cmd/lifecycle/platform/utils.go @@ -0,0 +1,59 @@ +package platform + +import ( + "fmt" + "os" + + "github.com/BurntSushi/toml" + "github.com/google/go-containerregistry/pkg/name" + + "github.com/buildpacks/lifecycle" + "github.com/buildpacks/lifecycle/platform" +) + +func appendNotEmpty(slice []string, elems ...string) []string { + for _, v := range elems { + if v != "" { + slice = append(slice, v) + } + } + return slice +} + +func ensureSameRegistry(firstRef string, secondRef string) error { + if firstRef == secondRef { + return nil + } + firstRegistry, err := parseRegistry(firstRef) + if err != nil { + return err + } + secondRegistry, err := parseRegistry(secondRef) + if err != nil { + return err + } + if firstRegistry != secondRegistry { + return fmt.Errorf("writing to multiple registries is unsupported: %s, %s", firstRegistry, secondRegistry) + } + return nil +} + +func parseRegistry(providedRef string) (string, error) { + ref, err := name.ParseReference(providedRef, name.WeakValidation) + if err != nil { + return "", err + } + return ref.Context().RegistryStr(), nil +} + +func readStack(stackPath string, logger lifecycle.Logger) (platform.StackMetadata, error) { + var stackMD platform.StackMetadata + if _, err := toml.DecodeFile(stackPath, &stackMD); err != nil { + if os.IsNotExist(err) { + logger.Infof("no stack metadata found at path '%s'\n", stackPath) + } else { + return platform.StackMetadata{}, err + } + } + return stackMD, nil +} diff --git a/cmd/lifecycle/rebaser.go b/cmd/lifecycle/rebaser.go index d2e0bab40..59b0e5df4 100644 --- a/cmd/lifecycle/rebaser.go +++ b/cmd/lifecycle/rebaser.go @@ -14,9 +14,10 @@ import ( "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/auth" "github.com/buildpacks/lifecycle/cmd" + platform "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" "github.com/buildpacks/lifecycle/image" "github.com/buildpacks/lifecycle/internal/encoding" - "github.com/buildpacks/lifecycle/platform" + spec "github.com/buildpacks/lifecycle/platform" "github.com/buildpacks/lifecycle/priv" ) @@ -168,8 +169,8 @@ func (r *rebaseCmd) setAppImage() error { return cmd.FailErr(err, "access image to rebase") } - var md platform.LayersMetadata - if err := image.DecodeLabel(r.appImage, platform.LayerMetadataLabel, &md); err != nil { + var md spec.LayersMetadata + if err := image.DecodeLabel(r.appImage, spec.LayerMetadataLabel, &md); err != nil { return err } diff --git a/cmd/lifecycle/restorer.go b/cmd/lifecycle/restorer.go index bf7d5d05c..964bd8531 100644 --- a/cmd/lifecycle/restorer.go +++ b/cmd/lifecycle/restorer.go @@ -11,8 +11,9 @@ import ( "github.com/buildpacks/lifecycle/auth" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/cmd" + "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" "github.com/buildpacks/lifecycle/internal/layer" - "github.com/buildpacks/lifecycle/platform" + spec "github.com/buildpacks/lifecycle/platform" "github.com/buildpacks/lifecycle/priv" ) @@ -99,9 +100,9 @@ func (r *restoreCmd) Exec() error { return err } - var appMeta platform.LayersMetadata + var appMeta spec.LayersMetadata if r.restoresLayerMetadata() { - var analyzedMd platform.AnalyzedMetadata + var analyzedMd spec.AnalyzedMetadata if _, err := toml.DecodeFile(r.analyzedPath, &analyzedMd); err == nil { appMeta = analyzedMd.Metadata } @@ -117,7 +118,7 @@ func (r *restoreCmd) registryImages() []string { return []string{} } -func (r restoreArgs) restore(layerMetadata platform.LayersMetadata, group buildpack.Group, cacheStore lifecycle.Cache) error { +func (r restoreArgs) restore(layerMetadata spec.LayersMetadata, group buildpack.Group, cacheStore lifecycle.Cache) error { restorer := &lifecycle.Restorer{ LayersDir: r.layersDir, Buildpacks: group.Group, diff --git a/detector_test.go b/detector_test.go index 57cb686d9..a5f6243cc 100644 --- a/detector_test.go +++ b/detector_test.go @@ -11,13 +11,14 @@ import ( "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" "github.com/pkg/errors" - "github.com/sclevine/spec" + testspec "github.com/sclevine/spec" "github.com/sclevine/spec/report" "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/buildpack" - "github.com/buildpacks/lifecycle/platform" + "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" + spec "github.com/buildpacks/lifecycle/platform" h "github.com/buildpacks/lifecycle/testhelpers" "github.com/buildpacks/lifecycle/testmock" ) @@ -25,10 +26,10 @@ import ( //go:generate mockgen -package testmock -destination testmock/resolver.go github.com/buildpacks/lifecycle Resolver func TestDetector(t *testing.T) { - spec.Run(t, "Detector", testDetector, spec.Report(report.Terminal{})) + testspec.Run(t, "Detector", testDetector, testspec.Report(report.Terminal{})) } -func testDetector(t *testing.T, when spec.G, it spec.S) { +func testDetector(t *testing.T, when testspec.G, it testspec.S) { when("#Detect", func() { var ( mockCtrl *gomock.Controller @@ -123,7 +124,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { detector.Runs, ).Return( []buildpack.GroupBuildpack{}, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, lifecycle.ErrFailedDetection, ) @@ -159,7 +160,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { detector.Runs, ).Return( []buildpack.GroupBuildpack{}, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, lifecycle.ErrFailedDetection, ).After(firstResolve) @@ -185,7 +186,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { detector.Runs, ).Return( []buildpack.GroupBuildpack{}, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, lifecycle.ErrFailedDetection, ).After(secondResolve) @@ -201,7 +202,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { detector.Runs, ).Return( []buildpack.GroupBuildpack{}, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, lifecycle.ErrFailedDetection, ).After(thirdResolve) @@ -222,7 +223,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { detector.Runs, ).Return( []buildpack.GroupBuildpack{}, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, lifecycle.ErrFailedDetection, ).After(fourthResolve) @@ -304,7 +305,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { detector.Runs, ).Return( []buildpack.GroupBuildpack{}, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, lifecycle.ErrFailedDetection, ) @@ -340,7 +341,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { detector.Runs, ).Return( []buildpack.GroupBuildpack{}, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, lifecycle.ErrFailedDetection, ).After(firstResolve) @@ -366,7 +367,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { detector.Runs, ).Return( []buildpack.GroupBuildpack{}, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, lifecycle.ErrFailedDetection, ).After(secondResolve) @@ -382,7 +383,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { detector.Runs, ).Return( fourthGroup, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, nil, ).After(thirdResolve) @@ -403,7 +404,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { t.Fatalf("Unexpected group:\n%s\n", s) } - if !hasEntries(plan.Entries, []platform.BuildPlanEntry(nil)) { + if !hasEntries(plan.Entries, []spec.BuildPlanEntry(nil)) { t.Fatalf("Unexpected entries:\n%+v\n", plan.Entries) } }) @@ -423,7 +424,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { {ID: "A", Version: "v1", API: "0.3"}, {ID: "B", Version: "v1", API: "0.2"}, } - resolver.EXPECT().Resolve(group, detector.Runs).Return(group, []platform.BuildPlanEntry{ + resolver.EXPECT().Resolve(group, detector.Runs).Return(group, []spec.BuildPlanEntry{ { Providers: []buildpack.GroupBuildpack{ {ID: "A", Version: "v1"}, @@ -458,7 +459,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { t.Fatalf("Unexpected group:\n%s\n", s) } - if !hasEntries(plan.Entries, []platform.BuildPlanEntry{ + if !hasEntries(plan.Entries, []spec.BuildPlanEntry{ { Providers: []buildpack.GroupBuildpack{ {ID: "A", Version: "v1"}, @@ -515,7 +516,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { {ID: "A", Version: "v1", API: "0.3"}, {ID: "B", Version: "v1", API: "0.2"}, } - resolver.EXPECT().Resolve(group, detector.Runs).Return(group, []platform.BuildPlanEntry{}, nil) + resolver.EXPECT().Resolve(group, detector.Runs).Return(group, []spec.BuildPlanEntry{}, nil) _, _, err := detector.Detect(buildpack.Order{{Group: group}}) if err != nil { @@ -572,7 +573,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { } resolver.EXPECT().Resolve(group, detector.Runs).Return( []buildpack.GroupBuildpack{}, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, lifecycle.ErrBuildpack, ) @@ -595,7 +596,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { } resolver.EXPECT().Resolve(group, detector.Runs).Return( []buildpack.GroupBuildpack{}, - []platform.BuildPlanEntry{}, + []spec.BuildPlanEntry{}, lifecycle.ErrFailedDetection, ) @@ -817,7 +818,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { t.Fatalf("Unexpected group:\n%s\n", s) } - if !hasEntries(entries, []platform.BuildPlanEntry{ + if !hasEntries(entries, []spec.BuildPlanEntry{ { Providers: []buildpack.GroupBuildpack{ {ID: "A", Version: "v1"}, @@ -1017,7 +1018,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { t.Fatalf("Unexpected group:\n%s\n", s) } - if !hasEntries(entries, []platform.BuildPlanEntry{ + if !hasEntries(entries, []spec.BuildPlanEntry{ { Providers: []buildpack.GroupBuildpack{{ID: "B", Version: "v1"}}, Requires: []buildpack.Require{{Name: "dep-present"}}, @@ -1140,7 +1141,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { t.Fatalf("Unexpected group:\n%s\n", s) } - if !hasEntries(entries, []platform.BuildPlanEntry{ + if !hasEntries(entries, []spec.BuildPlanEntry{ { Providers: []buildpack.GroupBuildpack{{ID: "A", Version: "v1"}}, Requires: []buildpack.Require{{Name: "dep1-present"}}, @@ -1168,7 +1169,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { }) } -func hasEntry(l []platform.BuildPlanEntry, entry platform.BuildPlanEntry) bool { +func hasEntry(l []spec.BuildPlanEntry, entry spec.BuildPlanEntry) bool { for _, e := range l { if reflect.DeepEqual(e, entry) { return true @@ -1177,7 +1178,7 @@ func hasEntry(l []platform.BuildPlanEntry, entry platform.BuildPlanEntry) bool { return false } -func hasEntries(a, b []platform.BuildPlanEntry) bool { +func hasEntries(a, b []spec.BuildPlanEntry) bool { if len(a) != len(b) { return false } diff --git a/handlers.go b/handlers.go new file mode 100644 index 000000000..316eb653e --- /dev/null +++ b/handlers.go @@ -0,0 +1,29 @@ +package lifecycle + +import ( + "github.com/buildpacks/imgutil" + + "github.com/buildpacks/lifecycle/buildpack" +) + +//go:generate mockgen -package testmock -destination testmock/cache_handler.go github.com/buildpacks/lifecycle CacheHandler +type CacheHandler interface { + InitCache(imageRef, dir string) (Cache, error) +} + +//go:generate mockgen -package testmock -destination testmock/registry_handler.go github.com/buildpacks/lifecycle RegistryHandler +type ConfigHandler interface { + ReadGroup(path string) ([]buildpack.GroupBuildpack, error) +} + +//go:generate mockgen -package testmock -destination testmock/image_handler.go github.com/buildpacks/lifecycle ImageHandler +type ImageHandler interface { + InitImage(imageRef string) (imgutil.Image, error) + Docker() bool +} + +//go:generate mockgen -package testmock -destination testmock/registry_handler.go github.com/buildpacks/lifecycle RegistryHandler +type RegistryHandler interface { + EnsureReadAccess(imageRefs ...string) error + EnsureWriteAccess(imageRefs ...string) error +} diff --git a/internal/layer/metadata_restorer.go b/internal/layer/metadata_restorer.go index 973469e71..b0c8df6d5 100644 --- a/internal/layer/metadata_restorer.go +++ b/internal/layer/metadata_restorer.go @@ -12,7 +12,7 @@ import ( "github.com/buildpacks/lifecycle/platform" ) -//go:generate mockgen -package testmock -destination testmock/metadata_restorer.go github.com/buildpacks/lifecycle/internal/layer MetadataRestorer +//go:generate mockgen -package testmock -destination ../../testmock/metadata_restorer.go github.com/buildpacks/lifecycle/internal/layer MetadataRestorer type MetadataRestorer interface { Restore(buildpacks []buildpack.GroupBuildpack, appMeta platform.LayersMetadata, cacheMeta platform.CacheMetadata, layerSHAStore SHAStore) error } diff --git a/internal/layer/sbom_restorer.go b/internal/layer/sbom_restorer.go index aa16f7979..a65bea810 100644 --- a/internal/layer/sbom_restorer.go +++ b/internal/layer/sbom_restorer.go @@ -19,7 +19,7 @@ import ( "github.com/buildpacks/lifecycle/layers" ) -//go:generate mockgen -package testmock -destination testmock/sbom_restorer.go github.com/buildpacks/lifecycle/internal/layer SBOMRestorer +//go:generate mockgen -package testmock -destination ../../testmock/sbom_restorer.go github.com/buildpacks/lifecycle/internal/layer SBOMRestorer type SBOMRestorer interface { RestoreFromPrevious(image imgutil.Image, layerDigest string) error RestoreFromCache(cache Cache, layerDigest string) error diff --git a/platform/exit_test.go b/platform/exit_test.go deleted file mode 100644 index 5bbc0dbb6..000000000 --- a/platform/exit_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package platform_test - -import ( - "fmt" - "testing" - - "github.com/sclevine/spec" - - "github.com/buildpacks/lifecycle/api" - "github.com/buildpacks/lifecycle/platform" - h "github.com/buildpacks/lifecycle/testhelpers" -) - -func TestExiter(t *testing.T) { - spec.Run(t, "Test Exiter", testExiter) -} - -func testExiter(t *testing.T, when spec.G, it spec.S) { - type expected struct { - version string - exiter interface{} - } - toTest := []expected{ - { - version: "0.3", - exiter: &platform.LegacyExiter{}, - }, - { - version: "0.4", - exiter: &platform.LegacyExiter{}, - }, - { - version: "0.5", - exiter: &platform.LegacyExiter{}, - }, - { - version: "0.6", - exiter: &platform.DefaultExiter{}, - }, - { - version: "0.7", - exiter: &platform.DefaultExiter{}, - }, - { - version: "0.8", - exiter: &platform.DefaultExiter{}, - }, - { - version: "0.9", - exiter: &platform.DefaultExiter{}, - }, - } - for _, apiVersion := range api.Platform.Supported { - for _, expected := range toTest { - if expected.version == apiVersion.String() { - when(fmt.Sprintf("NewExiter for platform %s", expected.version), func() { - it("returns the right type", func() { - foundExiter := platform.NewExiter(expected.version) - - switch expected.exiter.(type) { - case *platform.DefaultExiter: - _, ok := foundExiter.(*platform.DefaultExiter) - h.AssertEq(t, ok, true) - case *platform.LegacyExiter: - _, ok := foundExiter.(*platform.LegacyExiter) - h.AssertEq(t, ok, true) - default: - t.Fatalf("unexpected exiter") - } - }) - }) - } - } - } -} diff --git a/platform/inputs/analyzer_factory.go b/platform/inputs/analyzer_factory.go deleted file mode 100644 index f4ca0dd2d..000000000 --- a/platform/inputs/analyzer_factory.go +++ /dev/null @@ -1,216 +0,0 @@ -package inputs - -import ( - "github.com/docker/docker/client" - "github.com/google/go-containerregistry/pkg/authn" - "github.com/pkg/errors" - - "github.com/buildpacks/lifecycle" - "github.com/buildpacks/lifecycle/api" - "github.com/buildpacks/lifecycle/buildpack" - "github.com/buildpacks/lifecycle/cache" - "github.com/buildpacks/lifecycle/cmd" - "github.com/buildpacks/lifecycle/internal/layer" - "github.com/buildpacks/lifecycle/platform" -) - -type AnalyzerFactory struct { - PlatformAPI *api.Version - AnalyzerOpsManager -} - -//go:generate mockgen -package testmock -destination testmock/analyzer_ops_manager.go github.com/buildpacks/lifecycle/platform/inputs AnalyzerOpsManager -type AnalyzerOpsManager interface { - EnsureRegistryAccess(opts ForAnalyzer) AnalyzerOp - WithBuildpacks(group buildpack.Group, path string) AnalyzerOp - WithCache(cacheImageRef, cacheDir string) AnalyzerOp - WithLayerMetadataRestorer(layersDir string, skipLayers bool, logger lifecycle.Logger) AnalyzerOp - WithPrevious(imageRef string, launchCacheDir string) AnalyzerOp - WithRun(imageRef string) AnalyzerOp - WithSBOMRestorer(layersDir string, logger lifecycle.Logger) AnalyzerOp -} - -type AnalyzerOp func(*lifecycle.Analyzer) error - -func NewAnalyzerFactory(platformAPI *api.Version, docker client.CommonAPIClient, keychain authn.Keychain) *AnalyzerFactory { - return &AnalyzerFactory{ - PlatformAPI: platformAPI, - AnalyzerOpsManager: &DefaultAnalyzerOpsManager{ - CacheHandler: NewCacheHandler(keychain), - ImageHandler: NewImageHandler(docker, keychain), - RegistryHandler: NewRegistryHandler(keychain), - }, - } -} - -type DefaultAnalyzerOpsManager struct { - CacheHandler CacheHandler - ImageHandler ImageHandler - RegistryHandler RegistryHandler -} - -func (af *AnalyzerFactory) NewAnalyzer(opts ForAnalyzer, logger lifecycle.Logger) (*lifecycle.Analyzer, error) { - analyzer := &lifecycle.Analyzer{ - Platform: platform.NewPlatform(af.PlatformAPI.String()), // TODO: this should be removed eventually in favor of just passing the api - Logger: logger, - SBOMRestorer: &layer.NopSBOMRestorer{}, - LayerMetadataRestorer: &layer.NopMetadataRestorer{}, - } - - var ops []AnalyzerOp - switch { - case af.PlatformAPI.AtLeast("0.8"): - ops = append(ops, - af.EnsureRegistryAccess(opts), - af.WithPrevious(opts.PreviousImageRef, opts.LaunchCacheDir), - af.WithRun(opts.RunImageRef), - af.WithSBOMRestorer(opts.LayersDir, logger), - ) - case af.PlatformAPI.AtLeast("0.7"): - ops = append(ops, - af.EnsureRegistryAccess(opts), - af.WithPrevious(opts.PreviousImageRef, opts.LaunchCacheDir), - af.WithRun(opts.RunImageRef), - ) - default: - ops = append(ops, - af.WithBuildpacks(opts.LegacyGroup, opts.LegacyGroupPath), - af.WithCache(opts.CacheImageRef, opts.LegacyCacheDir), - af.WithLayerMetadataRestorer(opts.LayersDir, opts.SkipLayers, logger), - af.WithPrevious(opts.PreviousImageRef, opts.LaunchCacheDir), - ) - } - - var err error - for _, op := range ops { - if err = op(analyzer); err != nil { - if err, ok := err.(*cmd.ErrorFail); ok { - return nil, err - } - return nil, errors.Wrap(err, "initializing analyzer") - } - } - return analyzer, nil -} - -func (om *DefaultAnalyzerOpsManager) EnsureRegistryAccess(opts ForAnalyzer) AnalyzerOp { - return func(_ *lifecycle.Analyzer) error { - var readImages, writeImages []string - writeImages = appendNotEmpty(writeImages, opts.CacheImageRef) - if !om.ImageHandler.Docker() { - readImages = appendNotEmpty(readImages, opts.PreviousImageRef, opts.RunImageRef) - writeImages = appendNotEmpty(writeImages, opts.OutputImageRef) - writeImages = appendNotEmpty(writeImages, opts.AdditionalTags...) - } - - if err := om.RegistryHandler.EnsureReadAccess(readImages); err != nil { - return errors.Wrap(err, "validating registry read access") - } - if err := om.RegistryHandler.EnsureWriteAccess(writeImages); err != nil { - return errors.Wrap(err, "validating registry write access") - } - return nil - } -} - -func (om *DefaultAnalyzerOpsManager) WithBuildpacks(group buildpack.Group, path string) AnalyzerOp { - return func(analyzer *lifecycle.Analyzer) error { - if len(group.Group) > 0 { - return nil - } - group, err := buildpack.ReadGroup(path) - if err != nil { - return errors.Wrap(err, "reading buildpack group") - } - if err := verifyBuildpackApis(group); err != nil { - return err - } - analyzer.Buildpacks = group.Group - return nil - } -} - -func (om *DefaultAnalyzerOpsManager) WithCache(cacheImageRef, cacheDir string) AnalyzerOp { - return func(analyzer *lifecycle.Analyzer) error { - var err error - if cacheImageRef != "" { - analyzer.Cache, err = om.CacheHandler.InitImageCache(cacheImageRef) - } - if cacheDir != "" { - analyzer.Cache, err = om.CacheHandler.InitVolumeCache(cacheDir) - } - return err - } -} - -func (om *DefaultAnalyzerOpsManager) WithLayerMetadataRestorer(layersDir string, skipLayers bool, logger lifecycle.Logger) AnalyzerOp { - return func(analyzer *lifecycle.Analyzer) error { - analyzer.LayerMetadataRestorer = &layer.DefaultMetadataRestorer{ - LayersDir: layersDir, - SkipLayers: skipLayers, - Logger: logger, - } - return nil - } -} - -func (om *DefaultAnalyzerOpsManager) WithPrevious(imageRef string, launchCacheDir string) AnalyzerOp { - return func(analyzer *lifecycle.Analyzer) error { - if imageRef == "" { - return nil - } - var err error - analyzer.PreviousImage, err = om.ImageHandler.InitImage(imageRef) - if err != nil { - return errors.Wrap(err, "getting previous image") - } - if launchCacheDir == "" || !om.ImageHandler.Docker() { - return nil - } - - volumeCache, err := cache.NewVolumeCache(launchCacheDir) - if err != nil { - return errors.Wrap(err, "creating launch cache") - } - analyzer.PreviousImage = cache.NewCachingImage(analyzer.PreviousImage, volumeCache) - return nil - } -} - -func (om *DefaultAnalyzerOpsManager) WithRun(imageRef string) AnalyzerOp { - return func(analyzer *lifecycle.Analyzer) error { - if imageRef == "" { - return nil - } - var err error - analyzer.RunImage, err = om.ImageHandler.InitImage(imageRef) - if err != nil { - return errors.Wrap(err, "getting run image") - } - return nil - } -} - -func (om *DefaultAnalyzerOpsManager) WithSBOMRestorer(layersDir string, logger lifecycle.Logger) AnalyzerOp { - return func(analyzer *lifecycle.Analyzer) error { - analyzer.SBOMRestorer = &layer.DefaultSBOMRestorer{ - LayersDir: layersDir, - Logger: logger, - } - return nil - } -} - -func verifyBuildpackApis(group buildpack.Group) error { - for _, bp := range group.Group { - if bp.API == "" { - // if this group was generated by this lifecycle bp.API should be set - // but if for some reason it isn't default to 0.2 - bp.API = "0.2" - } - if err := cmd.VerifyBuildpackAPI(bp.String(), bp.API); err != nil { - return err - } - } - return nil -} diff --git a/platform/inputs/analyzer_factory_test.go b/platform/inputs/analyzer_factory_test.go deleted file mode 100644 index 054769a69..000000000 --- a/platform/inputs/analyzer_factory_test.go +++ /dev/null @@ -1,377 +0,0 @@ -package inputs_test - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/apex/log" - "github.com/apex/log/handlers/discard" - "github.com/apex/log/handlers/memory" - "github.com/buildpacks/imgutil/fakes" - "github.com/golang/mock/gomock" - "github.com/sclevine/spec" - "github.com/sclevine/spec/report" - - "github.com/buildpacks/lifecycle" - "github.com/buildpacks/lifecycle/api" - "github.com/buildpacks/lifecycle/buildpack" - "github.com/buildpacks/lifecycle/cache" - "github.com/buildpacks/lifecycle/internal/layer" - "github.com/buildpacks/lifecycle/platform/inputs" - "github.com/buildpacks/lifecycle/platform/inputs/testmock" - h "github.com/buildpacks/lifecycle/testhelpers" -) - -func TestAnalyzerFactory(t *testing.T) { - spec.Run(t, "unit-analyzer-ops-manager", testAnalyzerOpsManager, spec.Parallel(), spec.Report(report.Terminal{})) - for _, api := range api.Platform.Supported { - spec.Run(t, "unit-analyzer-factory/"+api.String(), testAnalyzerFactory(api.String()), spec.Parallel(), spec.Report(report.Terminal{})) - } -} - -func testAnalyzerOpsManager(t *testing.T, when spec.G, it spec.S) { - var ( - om *inputs.DefaultAnalyzerOpsManager - fakeCacheHandler *testmock.MockCacheHandler - fakeImageHandler *testmock.MockImageHandler - fakeRegistryHandler *testmock.MockRegistryHandler - logHandler *memory.Handler - logger *log.Logger - mockController *gomock.Controller - tempDir string - ) - - it.Before(func() { - mockController = gomock.NewController(t) - fakeCacheHandler = testmock.NewMockCacheHandler(mockController) - fakeImageHandler = testmock.NewMockImageHandler(mockController) - fakeRegistryHandler = testmock.NewMockRegistryHandler(mockController) - om = &inputs.DefaultAnalyzerOpsManager{ - CacheHandler: fakeCacheHandler, - ImageHandler: fakeImageHandler, - RegistryHandler: fakeRegistryHandler, - } - logHandler = memory.New() - logger = &log.Logger{Handler: logHandler} - var err error - tempDir, err = ioutil.TempDir("", "") - h.AssertNil(t, err) - h.AssertNil(t, os.Mkdir(filepath.Join(tempDir, "launch-cache"), 0755)) - h.AssertNil(t, os.Mkdir(filepath.Join(tempDir, "cache"), 0755)) - }) - - it.After(func() { - mockController.Finish() - os.RemoveAll(tempDir) - }) - - when("EnsureRegistryAccess", func() { - when("exporting to a daemon", func() { - it.Before(func() { - fakeImageHandler.EXPECT().Docker().Return(true).AnyTimes() - fakeImageHandler.EXPECT().InitImage(gomock.Any()).AnyTimes() - }) - - it("validates access", func() { - opts := inputs.ForAnalyzer{ - AdditionalTags: []string{"some-additional-tag"}, - CacheImageRef: "some-cache-image-ref", - OutputImageRef: "some-output-image-ref", - PreviousImageRef: "some-previous-image-ref", - RunImageRef: "some-run-image-ref", - } - var none []string - fakeRegistryHandler.EXPECT().EnsureReadAccess(none) - fakeRegistryHandler.EXPECT().EnsureWriteAccess([]string{"some-cache-image-ref"}) - - h.AssertNil(t, om.EnsureRegistryAccess(opts)(&lifecycle.Analyzer{})) - }) - }) - - when("exporting to a registry", func() { - it.Before(func() { - fakeImageHandler.EXPECT().Docker().Return(false).AnyTimes() - fakeImageHandler.EXPECT().InitImage(gomock.Any()).AnyTimes() - }) - - it("validates access", func() { - opts := inputs.ForAnalyzer{ - AdditionalTags: []string{"some-additional-tag"}, - CacheImageRef: "some-cache-image-ref", - OutputImageRef: "some-output-image-ref", - PreviousImageRef: "some-previous-image-ref", - RunImageRef: "some-run-image-ref", - } - expectedReadImages := []string{ - "some-previous-image-ref", - "some-run-image-ref", - } - expectedWriteImages := []string{ - "some-cache-image-ref", - "some-output-image-ref", - "some-additional-tag", - } - fakeRegistryHandler.EXPECT().EnsureReadAccess(expectedReadImages) - fakeRegistryHandler.EXPECT().EnsureWriteAccess(expectedWriteImages) - - h.AssertNil(t, om.EnsureRegistryAccess(opts)(&lifecycle.Analyzer{})) - }) - }) - }) - - when("WithBuildpacks", func() { - it("reads group.toml", func() { - groupPath := filepath.Join("testdata", "layers", "group.toml") - analyzer := &lifecycle.Analyzer{} - - h.AssertNil(t, om.WithBuildpacks(buildpack.Group{}, groupPath)(analyzer)) - h.AssertEq(t, analyzer.Buildpacks, []buildpack.GroupBuildpack{ - {ID: "some-buildpack-id", Version: "some-buildpack-version", API: "0.7", Homepage: "some-buildpack-homepage"}, - }) - }) - - it("validates buildpack apis", func() { - groupPath := filepath.Join("testdata", "layers", "bad-group.toml") - analyzer := &lifecycle.Analyzer{} - - h.AssertNotNil(t, om.WithBuildpacks(buildpack.Group{}, groupPath)(analyzer)) - }) - }) - - when("WithCache", func() { - when("provided a cache image", func() { - it("provides it to the analyzer", func() { - origImage := fakes.NewImage("some-cache-image", "", nil) - cacheImage := cache.NewImageCache(origImage, &fakes.Image{}) - fakeCacheHandler.EXPECT().InitImageCache("some-cache-image").Return(cacheImage, nil) - analyzer := &lifecycle.Analyzer{} - - h.AssertNil(t, om.WithCache("some-cache-image", "")(analyzer)) - cacheImage, ok := analyzer.Cache.(*cache.ImageCache) - h.AssertEq(t, ok, true) - h.AssertEq(t, cacheImage.Name(), "some-cache-image") - }) - }) - - when("provided a cache directory", func() { - it.Before(func() { - om.CacheHandler = inputs.NewCacheHandler(nil) // use a real cache handler - }) - - it("provides it to the analyzer", func() { - analyzer := &lifecycle.Analyzer{} - - h.AssertNil(t, om.WithCache("", filepath.Join(tempDir, "cache"))(analyzer)) - cacheDir, ok := analyzer.Cache.(*cache.VolumeCache) - h.AssertEq(t, ok, true) - h.AssertEq(t, cacheDir.Name(), filepath.Join(tempDir, "cache")) - }) - }) - }) - - when("WithLayerMetadataRestorer", func() { - it("provides a layer metadata restorer to the analyzer", func() { - analyzer := &lifecycle.Analyzer{} - - h.AssertNil(t, om.WithLayerMetadataRestorer("some-layers-dir", false, logger)(analyzer)) - defaultRestorer, ok := analyzer.LayerMetadataRestorer.(*layer.DefaultMetadataRestorer) - h.AssertEq(t, ok, true) - h.AssertEq(t, defaultRestorer.LayersDir, "some-layers-dir") - }) - }) - - when("WithPrevious", func() { - it("provides it to the analyzer", func() { - previousImageRef := "some-previous-image-ref" - previousImage := fakes.NewImage(previousImageRef, "", nil) - fakeImageHandler.EXPECT().InitImage(previousImageRef).Return(previousImage, nil) - analyzer := &lifecycle.Analyzer{} - - h.AssertNil(t, om.WithPrevious(previousImageRef, "")(analyzer)) - h.AssertEq(t, analyzer.PreviousImage.Name(), previousImageRef) - }) - - when("daemon case", func() { - it.Before(func() { - fakeImageHandler.EXPECT().Docker().Return(true).AnyTimes() - fakeRegistryHandler.EXPECT().EnsureReadAccess(gomock.Any()).AnyTimes() - fakeRegistryHandler.EXPECT().EnsureWriteAccess(gomock.Any()).AnyTimes() - }) - - when("provided a launch cache dir", func() { - it("previous image is a caching image", func() { - previousImageRef := "some-previous-image-ref" - launchCacheDir := filepath.Join(tempDir, "launch-cache") - previousImage := fakes.NewImage(previousImageRef, "", nil) - fakeImageHandler.EXPECT().InitImage(previousImageRef).Return(previousImage, nil) - analyzer := &lifecycle.Analyzer{} - - h.AssertNil(t, om.WithPrevious(previousImageRef, launchCacheDir)(analyzer)) - h.AssertEq(t, analyzer.PreviousImage.Name(), previousImageRef) - _, ok := analyzer.PreviousImage.(*cache.CachingImage) - h.AssertEq(t, ok, true) - h.AssertPathExists(t, filepath.Join(tempDir, "launch-cache", "committed")) - h.AssertPathExists(t, filepath.Join(tempDir, "launch-cache", "staging")) - }) - }) - - when("not provided a launch cache dir", func() { - it("previous image is a regular image", func() { - previousImageRef := "some-previous-image-ref" - previousImage := fakes.NewImage(previousImageRef, "", nil) - fakeImageHandler.EXPECT().InitImage(previousImageRef).Return(previousImage, nil) - analyzer := &lifecycle.Analyzer{} - - h.AssertNil(t, om.WithPrevious(previousImageRef, "")(analyzer)) - h.AssertEq(t, analyzer.PreviousImage.Name(), previousImageRef) - _, ok := analyzer.PreviousImage.(*fakes.Image) - h.AssertEq(t, ok, true) - }) - }) - }) - }) - - when("WithRun", func() { - it("provides it to the analyzer", func() { - runImageRef := "some-run-image-ref" - runImage := fakes.NewImage(runImageRef, "", nil) - fakeImageHandler.EXPECT().InitImage(runImageRef).Return(runImage, nil) - analyzer := &lifecycle.Analyzer{} - - h.AssertNil(t, om.WithRun(runImageRef)(analyzer)) - h.AssertEq(t, analyzer.RunImage.Name(), runImageRef) - }) - }) - - when("WithSBOMRestorer", func() { - it("provides an sbom restorer to the analyzer", func() { - analyzer := &lifecycle.Analyzer{} - - h.AssertNil(t, om.WithSBOMRestorer("some-layers-dir", logger)(analyzer)) - defaultRestorer, ok := analyzer.SBOMRestorer.(*layer.DefaultSBOMRestorer) - h.AssertEq(t, ok, true) - h.AssertEq(t, defaultRestorer.LayersDir, "some-layers-dir") - }) - }) -} - -func testAnalyzerFactory(platformAPI string) func(t *testing.T, when spec.G, it spec.S) { - return func(t *testing.T, when spec.G, it spec.S) { - var ( - af *inputs.AnalyzerFactory - om *testmock.MockAnalyzerOpsManager - logger *log.Logger - callCount int - mockController *gomock.Controller - ) - wasCalled := func(_ *lifecycle.Analyzer) error { - callCount++ - return nil - } - - it.Before(func() { - mockController = gomock.NewController(t) - om = testmock.NewMockAnalyzerOpsManager(mockController) - af = &inputs.AnalyzerFactory{ - PlatformAPI: api.MustParse(platformAPI), - AnalyzerOpsManager: om, - } - logger = &log.Logger{Handler: &discard.Handler{}} - }) - - it.After(func() { - mockController.Finish() - }) - - it("provides platform and logger to the analyzer", func() { - opts := inputs.ForAnalyzer{} - om.EXPECT().EnsureRegistryAccess(opts).Return(wasCalled).AnyTimes() - om.EXPECT().WithBuildpacks(opts.LegacyGroup, opts.LegacyGroupPath).Return(wasCalled).AnyTimes() - om.EXPECT().WithCache(opts.CacheImageRef, opts.LegacyCacheDir).Return(wasCalled).AnyTimes() - om.EXPECT().WithLayerMetadataRestorer(opts.LayersDir, opts.SkipLayers, logger).Return(wasCalled).AnyTimes() - om.EXPECT().WithPrevious(opts.PreviousImageRef, opts.LaunchCacheDir).Return(wasCalled).AnyTimes() - om.EXPECT().WithRun(opts.RunImageRef).Return(wasCalled).AnyTimes() - om.EXPECT().WithSBOMRestorer(opts.LayersDir, logger).Return(wasCalled).AnyTimes() - - analyzer, err := af.NewAnalyzer(opts, logger) - h.AssertNil(t, err) - h.AssertEq(t, analyzer.Platform.API().String(), af.PlatformAPI.String()) - h.AssertEq(t, analyzer.Logger, logger) - }) - - when("latest platform api(s)", func() { - it.Before(func() { - h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.8"), "") - }) - - it("calls the expected operations", func() { - opts := inputs.ForAnalyzer{ - LaunchCacheDir: "some-launch-cache-dir", - LayersDir: "some-layers-dir", - LegacyCacheDir: "some-ignored-cache-dir", - LegacyGroupPath: "some-ignored-group.toml", - PreviousImageRef: "some-previous-image", - RunImageRef: "some-run-image", - } - om.EXPECT().EnsureRegistryAccess(opts).Return(wasCalled) - om.EXPECT().WithPrevious(opts.PreviousImageRef, opts.LaunchCacheDir).Return(wasCalled) - om.EXPECT().WithRun(opts.RunImageRef).Return(wasCalled) - om.EXPECT().WithSBOMRestorer(opts.LayersDir, logger).Return(wasCalled) - - _, err := af.NewAnalyzer(opts, logger) - h.AssertNil(t, err) - h.AssertEq(t, callCount, 4) - }) - }) - - when("platform api 0.7", func() { - it.Before(func() { - h.SkipIf(t, api.MustParse(platformAPI).AtLeast("0.8"), "") - h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.7"), "") - }) - - it("calls the expected operations", func() { - opts := inputs.ForAnalyzer{ - LayersDir: "some-layers-dir", - LegacyCacheDir: "some-ignored-cache-dir", - LegacyGroupPath: "some-ignored-group.toml", - PreviousImageRef: "some-previous-image", - RunImageRef: "some-run-image", - } - om.EXPECT().EnsureRegistryAccess(opts).Return(wasCalled) - om.EXPECT().WithPrevious(opts.PreviousImageRef, opts.LaunchCacheDir).Return(wasCalled) - om.EXPECT().WithRun(opts.RunImageRef).Return(wasCalled) - - _, err := af.NewAnalyzer(opts, logger) - h.AssertNil(t, err) - h.AssertEq(t, callCount, 3) - }) - }) - - when("platform api < 0.7", func() { - it.Before(func() { - h.SkipIf(t, api.MustParse(platformAPI).AtLeast("0.7"), "") - }) - - it("calls the expected operations", func() { - opts := inputs.ForAnalyzer{ - LayersDir: "some-layers-dir", - LegacyCacheDir: "some-cache-dir", - LegacyGroupPath: "some-group.toml", - PreviousImageRef: "some-previous-image", - RunImageRef: "some-ignored-run-image", - } - om.EXPECT().WithBuildpacks(opts.LegacyGroup, opts.LegacyGroupPath).Return(wasCalled) - om.EXPECT().WithCache(opts.CacheImageRef, opts.LegacyCacheDir).Return(wasCalled) - om.EXPECT().WithLayerMetadataRestorer(opts.LayersDir, opts.SkipLayers, logger).Return(wasCalled) - om.EXPECT().WithPrevious(opts.PreviousImageRef, opts.LaunchCacheDir).Return(wasCalled) - - _, err := af.NewAnalyzer(opts, logger) - h.AssertNil(t, err) - h.AssertEq(t, callCount, 4) - }) - }) - } -} diff --git a/platform/inputs/cache_handler.go b/platform/inputs/cache_handler.go deleted file mode 100644 index a3bd3b54d..000000000 --- a/platform/inputs/cache_handler.go +++ /dev/null @@ -1,41 +0,0 @@ -package inputs - -import ( - "github.com/google/go-containerregistry/pkg/authn" - "github.com/pkg/errors" - - "github.com/buildpacks/lifecycle" - "github.com/buildpacks/lifecycle/cache" -) - -//go:generate mockgen -package testmock -destination testmock/cache_handler.go github.com/buildpacks/lifecycle/platform/inputs CacheHandler -type CacheHandler interface { - InitImageCache(cacheImageRef string) (lifecycle.Cache, error) - InitVolumeCache(cacheDir string) (lifecycle.Cache, error) -} - -type DefaultCacheHandler struct { - keychain authn.Keychain -} - -func NewCacheHandler(keychain authn.Keychain) *DefaultCacheHandler { - return &DefaultCacheHandler{ - keychain: keychain, - } -} - -func (ch *DefaultCacheHandler) InitImageCache(cacheImageRef string) (lifecycle.Cache, error) { - cacheStore, err := cache.NewImageCacheFromName(cacheImageRef, ch.keychain) - if err != nil { - return nil, errors.Wrap(err, "creating image cache") - } - return cacheStore, nil -} - -func (ch *DefaultCacheHandler) InitVolumeCache(cacheDir string) (lifecycle.Cache, error) { - cacheStore, err := cache.NewVolumeCache(cacheDir) - if err != nil { - return nil, errors.Wrap(err, "creating volume cache") - } - return cacheStore, nil -} diff --git a/platform/inputs/image_handler.go b/platform/inputs/image_handler.go deleted file mode 100644 index 822f416df..000000000 --- a/platform/inputs/image_handler.go +++ /dev/null @@ -1,51 +0,0 @@ -package inputs - -import ( - "github.com/buildpacks/imgutil" - "github.com/buildpacks/imgutil/local" - "github.com/buildpacks/imgutil/remote" - "github.com/docker/docker/client" - "github.com/google/go-containerregistry/pkg/authn" -) - -//go:generate mockgen -package testmock -destination testmock/image_handler.go github.com/buildpacks/lifecycle/platform/inputs ImageHandler -type ImageHandler interface { - InitImage(imageRef string) (imgutil.Image, error) - Docker() bool -} - -type DefaultImageHandler struct { - docker client.CommonAPIClient - keychain authn.Keychain -} - -func NewImageHandler(docker client.CommonAPIClient, keychain authn.Keychain) *DefaultImageHandler { - return &DefaultImageHandler{ - docker: docker, - keychain: keychain, - } -} - -func (h *DefaultImageHandler) InitImage(imageRef string) (imgutil.Image, error) { - if imageRef == "" { - return nil, nil - } - - if h.docker != nil { - return local.NewImage( - imageRef, - h.docker, - local.FromBaseImage(imageRef), - ) - } - - return remote.NewImage( - imageRef, - h.keychain, - remote.FromBaseImage(imageRef), - ) -} - -func (h *DefaultImageHandler) Docker() bool { - return h.docker != nil -} diff --git a/platform/inputs/registry_handler.go b/platform/inputs/registry_handler.go deleted file mode 100644 index e68275e69..000000000 --- a/platform/inputs/registry_handler.go +++ /dev/null @@ -1,97 +0,0 @@ -package inputs - -import ( - "fmt" - - "github.com/buildpacks/imgutil/remote" - "github.com/google/go-containerregistry/pkg/authn" - "github.com/google/go-containerregistry/pkg/name" - "github.com/pkg/errors" -) - -//go:generate mockgen -package testmock -destination testmock/registry_handler.go github.com/buildpacks/lifecycle/platform/inputs RegistryHandler -type RegistryHandler interface { - EnsureReadAccess(imageRefs []string) error - EnsureWriteAccess(imageRefs []string) error -} - -type DefaultRegistryHandler struct { - keychain authn.Keychain -} - -func NewRegistryHandler(keychain authn.Keychain) *DefaultRegistryHandler { - return &DefaultRegistryHandler{ - keychain: keychain, - } -} - -func (rv *DefaultRegistryHandler) EnsureReadAccess(imageRefs []string) error { - for _, imageRef := range imageRefs { - if err := verifyReadAccess(imageRef, rv.keychain); err != nil { - return err - } - } - return nil -} - -func (rv *DefaultRegistryHandler) EnsureWriteAccess(imageRefs []string) error { - for _, imageRef := range imageRefs { - if err := verifyReadWriteAccess(imageRef, rv.keychain); err != nil { - return err - } - } - return nil -} - -func verifyReadAccess(imageRef string, keychain authn.Keychain) error { - img, _ := remote.NewImage(imageRef, keychain) - if !img.CheckReadAccess() { - return errors.Errorf("ensure registry read access to %s", imageRef) - } - return nil -} - -func verifyReadWriteAccess(imageRef string, keychain authn.Keychain) error { - img, _ := remote.NewImage(imageRef, keychain) - if !img.CheckReadWriteAccess() { - return errors.Errorf("ensure registry read/write access to %s", imageRef) - } - return nil -} - -// registry helpers - -func appendNotEmpty(slice []string, elems ...string) []string { - for _, v := range elems { - if v != "" { - slice = append(slice, v) - } - } - return slice -} - -func ensureSameRegistry(firstRef string, secondRef string) error { - if firstRef == secondRef { - return nil - } - firstRegistry, err := parseRegistry(firstRef) - if err != nil { - return err - } - secondRegistry, err := parseRegistry(secondRef) - if err != nil { - return err - } - if firstRegistry != secondRegistry { - return fmt.Errorf("writing to multiple registries is unsupported: %s, %s", firstRegistry, secondRegistry) - } - return nil -} - -func parseRegistry(providedRef string) (string, error) { - ref, err := name.ParseReference(providedRef, name.WeakValidation) - if err != nil { - return "", err - } - return ref.Context().RegistryStr(), nil -} diff --git a/platform/inputs/registry_handler_test.go b/platform/inputs/registry_handler_test.go deleted file mode 100644 index f669ceb2c..000000000 --- a/platform/inputs/registry_handler_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package inputs_test - -import ( - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "testing" - - ih "github.com/buildpacks/imgutil/testhelpers" - "github.com/sclevine/spec" - "github.com/sclevine/spec/report" - - "github.com/buildpacks/lifecycle/auth" - "github.com/buildpacks/lifecycle/platform/inputs" - h "github.com/buildpacks/lifecycle/testhelpers" -) - -func TestRegistryHandler(t *testing.T) { - spec.Run(t, "unit-registry-handler", testRegistryHandler, spec.Parallel(), spec.Report(report.Terminal{})) -} - -func testRegistryHandler(t *testing.T, when spec.G, it spec.S) { - const ( - imageReadOnly = "some-read-only-image" - imageReadWrite = "some-read-write-image" - imageInaccessible = "some-inaccessible-image" - ) - - var ( - registryHandler *inputs.DefaultRegistryHandler - dockerConfigDir string - registry *ih.DockerRegistry - containerBaseImage string - ) - - it.Before(func() { - var err error - dockerConfigDir, err = ioutil.TempDir("", "test.docker.config.dir") - h.AssertNil(t, err) - - registry = ih.NewDockerRegistry( - ih.WithAuth(dockerConfigDir), - ih.WithImagePrivileges(), - ) - registry.Start(t) - - os.Setenv("DOCKER_CONFIG", dockerConfigDir) - keychain, err := auth.DefaultKeychain(registry.RepoName("some-image")) - h.AssertNil(t, err) - - if runtime.GOOS == "windows" { - containerBaseImage = "mcr.microsoft.com/windows/nanoserver:1809" - } else { - containerBaseImage = "scratch" - } - createFixtures(t, registry, containerBaseImage, imageReadOnly, imageReadWrite, imageInaccessible) - registry.SetReadOnly(imageReadOnly) - registry.SetReadWrite(imageReadWrite) - registry.SetInaccessible(imageInaccessible) - - registryHandler = inputs.NewRegistryHandler(keychain) - }) - - it.After(func() { - registry.Stop(t) - h.AssertNil(t, os.RemoveAll(dockerConfigDir)) - os.Unsetenv("DOCKER_CONFIG") - removeFixtures(t, imageReadOnly, imageReadWrite, imageInaccessible) - }) - - when("EnsureReadAccess", func() { - when("image is readable", func() { - it("returns nil", func() { - h.AssertNil(t, registryHandler.EnsureReadAccess([]string{registry.RepoName(imageReadOnly)})) - }) - }) - - when("image is not readable", func() { - it("returns an error", func() { - h.AssertNotNil(t, registryHandler.EnsureReadAccess([]string{registry.RepoName(imageInaccessible)})) - }) - }) - }) - - when("EnsureWriteAccess", func() { - when("image is writable", func() { - it("returns nil", func() { - h.AssertNil(t, registryHandler.EnsureWriteAccess([]string{registry.RepoName(imageReadWrite)})) - }) - }) - - when("image is not writable", func() { - it("returns an error", func() { - h.AssertNotNil(t, registryHandler.EnsureWriteAccess([]string{registry.RepoName(imageReadOnly)})) - }) - }) - }) -} - -func createFixtures(t *testing.T, registry *ih.DockerRegistry, baseImage string, imageNames ...string) { - for _, imageName := range imageNames { - buildRegistryImage(t, imageName, filepath.Join("testdata", "registry"), registry, "--build-arg", "base_image="+baseImage) - } -} - -func buildRegistryImage(t *testing.T, repoName, context string, registry *ih.DockerRegistry, buildArgs ...string) string { - // Build image - regRepoName := registry.RepoName(repoName) - h.DockerBuild(t, regRepoName, context, h.WithArgs(buildArgs...)) - - // Push image - h.AssertNil(t, h.PushImage(h.DockerCli(t), regRepoName, registry.EncodedLabeledAuth())) - - // Return registry repo name - return regRepoName -} - -func removeFixtures(t *testing.T, imageNames ...string) { - for _, imageName := range imageNames { - _, _, _ = h.RunE(exec.Command("docker", "rmi", imageName)) // #nosec G204 - } -} diff --git a/platform/inputs/testdata/registry/Dockerfile b/platform/inputs/testdata/registry/Dockerfile deleted file mode 100644 index 53067a45e..000000000 --- a/platform/inputs/testdata/registry/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -ARG base_image - -FROM $base_image - -LABEL some.label="some-label-value" diff --git a/platform/inputs/testmock/analyzer_ops_manager.go b/platform/inputs/testmock/analyzer_ops_manager.go deleted file mode 100644 index 737621e1e..000000000 --- a/platform/inputs/testmock/analyzer_ops_manager.go +++ /dev/null @@ -1,136 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/buildpacks/lifecycle/platform/inputs (interfaces: AnalyzerOpsManager) - -// Package testmock is a generated GoMock package. -package testmock - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - - lifecycle "github.com/buildpacks/lifecycle" - buildpack "github.com/buildpacks/lifecycle/buildpack" - inputs "github.com/buildpacks/lifecycle/platform/inputs" -) - -// MockAnalyzerOpsManager is a mock of AnalyzerOpsManager interface. -type MockAnalyzerOpsManager struct { - ctrl *gomock.Controller - recorder *MockAnalyzerOpsManagerMockRecorder -} - -// MockAnalyzerOpsManagerMockRecorder is the mock recorder for MockAnalyzerOpsManager. -type MockAnalyzerOpsManagerMockRecorder struct { - mock *MockAnalyzerOpsManager -} - -// NewMockAnalyzerOpsManager creates a new mock instance. -func NewMockAnalyzerOpsManager(ctrl *gomock.Controller) *MockAnalyzerOpsManager { - mock := &MockAnalyzerOpsManager{ctrl: ctrl} - mock.recorder = &MockAnalyzerOpsManagerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockAnalyzerOpsManager) EXPECT() *MockAnalyzerOpsManagerMockRecorder { - return m.recorder -} - -// EnsureRegistryAccess mocks base method. -func (m *MockAnalyzerOpsManager) EnsureRegistryAccess(arg0 inputs.ForAnalyzer) inputs.AnalyzerOp { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureRegistryAccess", arg0) - ret0, _ := ret[0].(inputs.AnalyzerOp) - return ret0 -} - -// EnsureRegistryAccess indicates an expected call of EnsureRegistryAccess. -func (mr *MockAnalyzerOpsManagerMockRecorder) EnsureRegistryAccess(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureRegistryAccess", reflect.TypeOf((*MockAnalyzerOpsManager)(nil).EnsureRegistryAccess), arg0) -} - -// WithBuildpacks mocks base method. -func (m *MockAnalyzerOpsManager) WithBuildpacks(arg0 buildpack.Group, arg1 string) inputs.AnalyzerOp { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WithBuildpacks", arg0, arg1) - ret0, _ := ret[0].(inputs.AnalyzerOp) - return ret0 -} - -// WithBuildpacks indicates an expected call of WithBuildpacks. -func (mr *MockAnalyzerOpsManagerMockRecorder) WithBuildpacks(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithBuildpacks", reflect.TypeOf((*MockAnalyzerOpsManager)(nil).WithBuildpacks), arg0, arg1) -} - -// WithCache mocks base method. -func (m *MockAnalyzerOpsManager) WithCache(arg0, arg1 string) inputs.AnalyzerOp { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WithCache", arg0, arg1) - ret0, _ := ret[0].(inputs.AnalyzerOp) - return ret0 -} - -// WithCache indicates an expected call of WithCache. -func (mr *MockAnalyzerOpsManagerMockRecorder) WithCache(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithCache", reflect.TypeOf((*MockAnalyzerOpsManager)(nil).WithCache), arg0, arg1) -} - -// WithLayerMetadataRestorer mocks base method. -func (m *MockAnalyzerOpsManager) WithLayerMetadataRestorer(arg0 string, arg1 bool, arg2 lifecycle.Logger) inputs.AnalyzerOp { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WithLayerMetadataRestorer", arg0, arg1, arg2) - ret0, _ := ret[0].(inputs.AnalyzerOp) - return ret0 -} - -// WithLayerMetadataRestorer indicates an expected call of WithLayerMetadataRestorer. -func (mr *MockAnalyzerOpsManagerMockRecorder) WithLayerMetadataRestorer(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithLayerMetadataRestorer", reflect.TypeOf((*MockAnalyzerOpsManager)(nil).WithLayerMetadataRestorer), arg0, arg1, arg2) -} - -// WithPrevious mocks base method. -func (m *MockAnalyzerOpsManager) WithPrevious(arg0, arg1 string) inputs.AnalyzerOp { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WithPrevious", arg0, arg1) - ret0, _ := ret[0].(inputs.AnalyzerOp) - return ret0 -} - -// WithPrevious indicates an expected call of WithPrevious. -func (mr *MockAnalyzerOpsManagerMockRecorder) WithPrevious(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithPrevious", reflect.TypeOf((*MockAnalyzerOpsManager)(nil).WithPrevious), arg0, arg1) -} - -// WithRun mocks base method. -func (m *MockAnalyzerOpsManager) WithRun(arg0 string) inputs.AnalyzerOp { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WithRun", arg0) - ret0, _ := ret[0].(inputs.AnalyzerOp) - return ret0 -} - -// WithRun indicates an expected call of WithRun. -func (mr *MockAnalyzerOpsManagerMockRecorder) WithRun(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithRun", reflect.TypeOf((*MockAnalyzerOpsManager)(nil).WithRun), arg0) -} - -// WithSBOMRestorer mocks base method. -func (m *MockAnalyzerOpsManager) WithSBOMRestorer(arg0 string, arg1 lifecycle.Logger) inputs.AnalyzerOp { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WithSBOMRestorer", arg0, arg1) - ret0, _ := ret[0].(inputs.AnalyzerOp) - return ret0 -} - -// WithSBOMRestorer indicates an expected call of WithSBOMRestorer. -func (mr *MockAnalyzerOpsManagerMockRecorder) WithSBOMRestorer(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithSBOMRestorer", reflect.TypeOf((*MockAnalyzerOpsManager)(nil).WithSBOMRestorer), arg0, arg1) -} diff --git a/platform/launch/exit_test.go b/platform/launch/exit_test.go deleted file mode 100644 index 67a7e338f..000000000 --- a/platform/launch/exit_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package launch_test - -import ( - "fmt" - "testing" - - "github.com/sclevine/spec" - - "github.com/buildpacks/lifecycle/api" - platform "github.com/buildpacks/lifecycle/platform/launch" - h "github.com/buildpacks/lifecycle/testhelpers" -) - -func TestExiter(t *testing.T) { - spec.Run(t, "Test Exiter", testExiter) -} - -func testExiter(t *testing.T, when spec.G, it spec.S) { - type expected struct { - version string - exiter interface{} - } - toTest := []expected{ - { - version: "0.3", - exiter: &platform.LegacyExiter{}, - }, - { - version: "0.4", - exiter: &platform.LegacyExiter{}, - }, - { - version: "0.5", - exiter: &platform.LegacyExiter{}, - }, - { - version: "0.6", - exiter: &platform.DefaultExiter{}, - }, - { - version: "0.7", - exiter: &platform.DefaultExiter{}, - }, - { - version: "0.8", - exiter: &platform.DefaultExiter{}, - }, - } - for _, apiVersion := range api.Platform.Supported { - for _, expected := range toTest { - if expected.version == apiVersion.String() { - when(fmt.Sprintf("NewExiter for platform %s", expected.version), func() { - it("returns the right type", func() { - foundExiter := platform.NewExiter(expected.version) - - switch expected.exiter.(type) { - case *platform.DefaultExiter: - _, ok := foundExiter.(*platform.DefaultExiter) - h.AssertEq(t, ok, true) - case *platform.LegacyExiter: - _, ok := foundExiter.(*platform.LegacyExiter) - h.AssertEq(t, ok, true) - default: - t.Fatalf("unexpected exiter") - } - }) - }) - } - } - } -} diff --git a/platform/launch/platform.go b/platform/launch/platform.go deleted file mode 100644 index 44f8886f6..000000000 --- a/platform/launch/platform.go +++ /dev/null @@ -1,21 +0,0 @@ -package launch - -import ( - "github.com/buildpacks/lifecycle/api" -) - -type Platform struct { - Exiter - api *api.Version -} - -func NewPlatform(apiStr string) *Platform { - return &Platform{ - Exiter: NewExiter(apiStr), - api: api.MustParse(apiStr), - } -} - -func (p *Platform) API() *api.Version { - return p.api -} diff --git a/restorer_test.go b/restorer_test.go index a4534b63c..cb6a8cb5f 100644 --- a/restorer_test.go +++ b/restorer_test.go @@ -12,35 +12,36 @@ import ( "github.com/apex/log/handlers/memory" "github.com/golang/mock/gomock" "github.com/pkg/errors" - "github.com/sclevine/spec" + testspec "github.com/sclevine/spec" "github.com/sclevine/spec/report" "github.com/buildpacks/lifecycle" "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/cache" + "github.com/buildpacks/lifecycle/cmd/lifecycle/platform" "github.com/buildpacks/lifecycle/internal/layer" - ltestmock "github.com/buildpacks/lifecycle/internal/layer/testmock" "github.com/buildpacks/lifecycle/layers" - "github.com/buildpacks/lifecycle/platform" + spec "github.com/buildpacks/lifecycle/platform" h "github.com/buildpacks/lifecycle/testhelpers" + "github.com/buildpacks/lifecycle/testmock" ) func TestRestorer(t *testing.T) { for _, buildpackAPIStr := range []string{"0.5", api.Buildpack.Latest().String()} { for _, platformAPI := range api.Platform.Supported { platformAPIStr := platformAPI.String() - spec.Run( + testspec.Run( t, "unit-restorer/buildpack-"+buildpackAPIStr+"/platform-"+platformAPIStr, - testRestorerBuilder(buildpackAPIStr, platformAPIStr), spec.Report(report.Terminal{}), + testRestorer(buildpackAPIStr, platformAPIStr), testspec.Report(report.Terminal{}), ) } } } -func testRestorerBuilder(buildpackAPI, platformAPI string) func(t *testing.T, when spec.G, it spec.S) { - return func(t *testing.T, when spec.G, it spec.S) { +func testRestorer(buildpackAPI, platformAPI string) func(t *testing.T, when testspec.G, it testspec.S) { + return func(t *testing.T, when testspec.G, it testspec.S) { when("#Restore", func() { var ( cacheDir string @@ -50,7 +51,7 @@ func testRestorerBuilder(buildpackAPI, platformAPI string) func(t *testing.T, wh testCache lifecycle.Cache restorer *lifecycle.Restorer mockCtrl *gomock.Controller - sbomRestorer *ltestmock.MockSBOMRestorer + sbomRestorer *testmock.MockSBOMRestorer ) it.Before(func() { @@ -73,7 +74,7 @@ func testRestorerBuilder(buildpackAPI, platformAPI string) func(t *testing.T, wh h.AssertNil(t, err) mockCtrl = gomock.NewController(t) - sbomRestorer = ltestmock.NewMockSBOMRestorer(mockCtrl) + sbomRestorer = testmock.NewMockSBOMRestorer(mockCtrl) if api.MustParse(platformAPI).AtLeast("0.8") { sbomRestorer.EXPECT().RestoreToBuildpackLayers(gomock.Any()).AnyTimes() } @@ -652,7 +653,7 @@ func testRestorerBuilder(buildpackAPI, platformAPI string) func(t *testing.T, wh h.AssertNil(t, err) h.Mkfile(t, "some-data", filepath.Join(tmpDir, "some.tar")) h.AssertNil(t, testCache.AddLayerFile(filepath.Join(tmpDir, "some.tar"), "some-digest")) - h.AssertNil(t, testCache.SetMetadata(platform.CacheMetadata{BOM: platform.LayerMetadata{SHA: "some-digest"}})) + h.AssertNil(t, testCache.SetMetadata(spec.CacheMetadata{BOM: spec.LayerMetadata{SHA: "some-digest"}})) h.AssertNil(t, testCache.Commit()) }) @@ -669,7 +670,7 @@ func testRestorerBuilder(buildpackAPI, platformAPI string) func(t *testing.T, wh when("there is no app image metadata", func() { it.Before(func() { - restorer.LayersMetadata = platform.LayersMetadata{} + restorer.LayersMetadata = spec.LayersMetadata{} }) it("analyzes with no layer metadata", func() { diff --git a/platform/inputs/testmock/cache_handler.go b/testmock/cache_handler.go similarity index 50% rename from platform/inputs/testmock/cache_handler.go rename to testmock/cache_handler.go index faa9f4345..e13106b79 100644 --- a/platform/inputs/testmock/cache_handler.go +++ b/testmock/cache_handler.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/buildpacks/lifecycle/platform/inputs (interfaces: CacheHandler) +// Source: github.com/buildpacks/lifecycle (interfaces: CacheHandler) // Package testmock is a generated GoMock package. package testmock @@ -35,32 +35,17 @@ func (m *MockCacheHandler) EXPECT() *MockCacheHandlerMockRecorder { return m.recorder } -// InitImageCache mocks base method. -func (m *MockCacheHandler) InitImageCache(arg0 string) (lifecycle.Cache, error) { +// InitCache mocks base method. +func (m *MockCacheHandler) InitCache(arg0, arg1 string) (lifecycle.Cache, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InitImageCache", arg0) + ret := m.ctrl.Call(m, "InitCache", arg0, arg1) ret0, _ := ret[0].(lifecycle.Cache) ret1, _ := ret[1].(error) return ret0, ret1 } -// InitImageCache indicates an expected call of InitImageCache. -func (mr *MockCacheHandlerMockRecorder) InitImageCache(arg0 interface{}) *gomock.Call { +// InitCache indicates an expected call of InitCache. +func (mr *MockCacheHandlerMockRecorder) InitCache(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitImageCache", reflect.TypeOf((*MockCacheHandler)(nil).InitImageCache), arg0) -} - -// InitVolumeCache mocks base method. -func (m *MockCacheHandler) InitVolumeCache(arg0 string) (lifecycle.Cache, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InitVolumeCache", arg0) - ret0, _ := ret[0].(lifecycle.Cache) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// InitVolumeCache indicates an expected call of InitVolumeCache. -func (mr *MockCacheHandlerMockRecorder) InitVolumeCache(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitVolumeCache", reflect.TypeOf((*MockCacheHandler)(nil).InitVolumeCache), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitCache", reflect.TypeOf((*MockCacheHandler)(nil).InitCache), arg0, arg1) } diff --git a/testmock/config_handler.go b/testmock/config_handler.go new file mode 100644 index 000000000..2bb8a4bd8 --- /dev/null +++ b/testmock/config_handler.go @@ -0,0 +1,51 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/buildpacks/lifecycle (interfaces: ConfigHandler) + +// Package testmock is a generated GoMock package. +package testmock + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + + buildpack "github.com/buildpacks/lifecycle/buildpack" +) + +// MockConfigHandler is a mock of ConfigHandler interface. +type MockConfigHandler struct { + ctrl *gomock.Controller + recorder *MockConfigHandlerMockRecorder +} + +// MockConfigHandlerMockRecorder is the mock recorder for MockConfigHandler. +type MockConfigHandlerMockRecorder struct { + mock *MockConfigHandler +} + +// NewMockConfigHandler creates a new mock instance. +func NewMockConfigHandler(ctrl *gomock.Controller) *MockConfigHandler { + mock := &MockConfigHandler{ctrl: ctrl} + mock.recorder = &MockConfigHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockConfigHandler) EXPECT() *MockConfigHandlerMockRecorder { + return m.recorder +} + +// ReadGroup mocks base method. +func (m *MockConfigHandler) ReadGroup(arg0 string) ([]buildpack.GroupBuildpack, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadGroup", arg0) + ret0, _ := ret[0].([]buildpack.GroupBuildpack) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadGroup indicates an expected call of ReadGroup. +func (mr *MockConfigHandlerMockRecorder) ReadGroup(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadGroup", reflect.TypeOf((*MockConfigHandler)(nil).ReadGroup), arg0) +} diff --git a/platform/inputs/testmock/image_handler.go b/testmock/image_handler.go similarity index 95% rename from platform/inputs/testmock/image_handler.go rename to testmock/image_handler.go index 84dcff4f8..2a1c4fad5 100644 --- a/platform/inputs/testmock/image_handler.go +++ b/testmock/image_handler.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/buildpacks/lifecycle/platform/inputs (interfaces: ImageHandler) +// Source: github.com/buildpacks/lifecycle (interfaces: ImageHandler) // Package testmock is a generated GoMock package. package testmock diff --git a/internal/layer/testmock/metadata_restorer.go b/testmock/metadata_restorer.go similarity index 100% rename from internal/layer/testmock/metadata_restorer.go rename to testmock/metadata_restorer.go diff --git a/platform/inputs/testmock/registry_handler.go b/testmock/registry_handler.go similarity index 74% rename from platform/inputs/testmock/registry_handler.go rename to testmock/registry_handler.go index 41d3dfb68..737a15aa3 100644 --- a/platform/inputs/testmock/registry_handler.go +++ b/testmock/registry_handler.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/buildpacks/lifecycle/platform/inputs (interfaces: RegistryHandler) +// Source: github.com/buildpacks/lifecycle (interfaces: RegistryHandler) // Package testmock is a generated GoMock package. package testmock @@ -34,29 +34,37 @@ func (m *MockRegistryHandler) EXPECT() *MockRegistryHandlerMockRecorder { } // EnsureReadAccess mocks base method. -func (m *MockRegistryHandler) EnsureReadAccess(arg0 []string) error { +func (m *MockRegistryHandler) EnsureReadAccess(arg0 ...string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureReadAccess", arg0) + varargs := []interface{}{} + for _, a := range arg0 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "EnsureReadAccess", varargs...) ret0, _ := ret[0].(error) return ret0 } // EnsureReadAccess indicates an expected call of EnsureReadAccess. -func (mr *MockRegistryHandlerMockRecorder) EnsureReadAccess(arg0 interface{}) *gomock.Call { +func (mr *MockRegistryHandlerMockRecorder) EnsureReadAccess(arg0 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureReadAccess", reflect.TypeOf((*MockRegistryHandler)(nil).EnsureReadAccess), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureReadAccess", reflect.TypeOf((*MockRegistryHandler)(nil).EnsureReadAccess), arg0...) } // EnsureWriteAccess mocks base method. -func (m *MockRegistryHandler) EnsureWriteAccess(arg0 []string) error { +func (m *MockRegistryHandler) EnsureWriteAccess(arg0 ...string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureWriteAccess", arg0) + varargs := []interface{}{} + for _, a := range arg0 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "EnsureWriteAccess", varargs...) ret0, _ := ret[0].(error) return ret0 } // EnsureWriteAccess indicates an expected call of EnsureWriteAccess. -func (mr *MockRegistryHandlerMockRecorder) EnsureWriteAccess(arg0 interface{}) *gomock.Call { +func (mr *MockRegistryHandlerMockRecorder) EnsureWriteAccess(arg0 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureWriteAccess", reflect.TypeOf((*MockRegistryHandler)(nil).EnsureWriteAccess), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureWriteAccess", reflect.TypeOf((*MockRegistryHandler)(nil).EnsureWriteAccess), arg0...) } diff --git a/internal/layer/testmock/sbom_restorer.go b/testmock/sbom_restorer.go similarity index 100% rename from internal/layer/testmock/sbom_restorer.go rename to testmock/sbom_restorer.go