diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6f54ffb2..06763f68 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -200,7 +200,7 @@ jobs: with: name: lifecycle-bom-cdx-sha256 path: lifecycle-v*-bom.cdx.json.sha256 - - uses: azure/docker-login@v1 + - uses: azure/docker-login@v2 if: github.event_name == 'push' with: username: ${{ secrets.DOCKER_USERNAME }} diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index 797b0b2f..5112a21e 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -22,7 +22,7 @@ jobs: go install github.com/google/go-containerregistry/cmd/crane@latest - name: Install cosign uses: sigstore/cosign-installer@v3 - - uses: azure/docker-login@v1 + - uses: azure/docker-login@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/test-s390x.yml b/.github/workflows/test-s390x.yml index 546d9395..4c45b1f2 100644 --- a/.github/workflows/test-s390x.yml +++ b/.github/workflows/test-s390x.yml @@ -32,11 +32,11 @@ jobs: id: ZVSI run: | #creation of zvsi - ibmcloud is instance-create $ZVSI_INSTANCE_NAME ${{ secrets.ZVSI_VPC }} $ZVSI_ZONE_NAME $ZVSI_PROFILE_NAME ${{ secrets.ZVSI_SUBNET }} --image ${{ secrets.ZVSI_IMAGE }} --keys ${{ secrets.ZVSI_KEY }} --resource-group-id ${{ secrets.ZVSI_RG_ID }} --sgs ${{ secrets.ZVSI_SG }} + ibmcloud is instance-create $ZVSI_INSTANCE_NAME ${{ secrets.ZVSI_VPC }} $ZVSI_ZONE_NAME $ZVSI_PROFILE_NAME ${{ secrets.ZVSI_SUBNET }} --image ${{ secrets.ZVSI_IMAGE }} --keys ${{ secrets.ZVSI_KEY }} --resource-group-id ${{ secrets.ZVSI_RG_ID }} --primary-network-interface "{\"name\":\"eth0\",\"allow_ip_spoofing\":false,\"subnet\": {\"name\":\"${{ secrets.ZVSI_SUBNET }}\"},\"security_groups\":[{\"id\":\"${{ secrets.ZVSI_SG }}\"}]}" #Reserving a floating ip to the ZVSI ibmcloud is floating-ip-reserve $ZVSI_FP_NAME --zone $ZVSI_ZONE_NAME --resource-group-id ${{ secrets.ZVSI_RG_ID }} --in $ZVSI_INSTANCE_NAME #Bouding the Floating ip to the ZVSI - ibmcloud is floating-ip-update $ZVSI_FP_NAME --nic primary --in $ZVSI_INSTANCE_NAME + ibmcloud is floating-ip-update $ZVSI_FP_NAME --nic eth0 --in $ZVSI_INSTANCE_NAME sleep 60 #Saving the Floating IP to login ZVSI ZVSI_HOST=$(ibmcloud is floating-ip $ZVSI_FP_NAME | awk '/Address/{print $2}') diff --git a/acceptance/analyzer_test.go b/acceptance/analyzer_test.go index 37e0345f..92e9d487 100644 --- a/acceptance/analyzer_test.go +++ b/acceptance/analyzer_test.go @@ -450,7 +450,7 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe output, err := cmd.CombinedOutput() h.AssertNotNil(t, err) - expected := "validating registry write access: ensure registry read/write access to " + analyzeRegFixtures.InaccessibleImage + expected := "ensure registry read/write access to " + analyzeRegFixtures.InaccessibleImage h.AssertStringContains(t, string(output), expected) }) }) diff --git a/acceptance/testdata/detector/Dockerfile b/acceptance/testdata/detector/Dockerfile index 07364b9a..695e030d 100644 --- a/acceptance/testdata/detector/Dockerfile +++ b/acceptance/testdata/detector/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:bionic +FROM ubuntu:jammy ARG cnb_uid=1234 ARG cnb_gid=1000 diff --git a/acceptance/testdata/detector/container/cnb/buildpacks/simple_buildpack/simple_buildpack_version/buildpack.toml b/acceptance/testdata/detector/container/cnb/buildpacks/simple_buildpack/simple_buildpack_version/buildpack.toml index b70a985e..6cb40946 100644 --- a/acceptance/testdata/detector/container/cnb/buildpacks/simple_buildpack/simple_buildpack_version/buildpack.toml +++ b/acceptance/testdata/detector/container/cnb/buildpacks/simple_buildpack/simple_buildpack_version/buildpack.toml @@ -1,6 +1,12 @@ -api = "0.10" +api = "0.9" [buildpack] - id = "simple_buildpack" - version = "simple_buildpack_version" - name = "Simple Buildpack" +id = "simple_buildpack" +version = "simple_buildpack_version" +name = "Simple Buildpack" + +[[stacks]] +id = "io.buildpacks.stacks.bionic" + +[[stacks]] +id = "io.buildpacks.stacks.jammy" diff --git a/api/apis.go b/api/apis.go index e46a0470..821df457 100644 --- a/api/apis.go +++ b/api/apis.go @@ -11,7 +11,7 @@ var ( // Platform is a pair of lists of Platform API versions: // 1. All supported versions (including deprecated versions) // 2. The versions that are deprecated - Platform = newApisMustParse([]string{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13"}, []string{}) + Platform = newApisMustParse([]string{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13", "0.14"}, []string{}) // Buildpack is a pair of lists of Buildpack API versions: // 1. All supported versions (including deprecated versions) // 2. The versions that are deprecated diff --git a/buildpack/bp_descriptor.go b/buildpack/bp_descriptor.go index 18fa6c60..57c58258 100644 --- a/buildpack/bp_descriptor.go +++ b/buildpack/bp_descriptor.go @@ -8,6 +8,7 @@ import ( "github.com/BurntSushi/toml" + "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/internal/encoding" ) @@ -17,7 +18,7 @@ type BpDescriptor struct { Order Order `toml:"order"` WithRootDir string `toml:"-"` Targets []TargetMetadata `toml:"targets"` - Stacks []StackMetadata `tome:"stacks"` // just for backwards compat so we can check if it's the bionic stack, which we translate to a target + Stacks []StackMetadata `toml:"stacks"` // just for backwards compat so we can check if it's the bionic stack, which we translate to a target } @@ -69,7 +70,9 @@ func ReadBpDescriptor(path string) (*BpDescriptor, error) { if len(descriptor.Targets) == 0 { for _, stack := range descriptor.Stacks { if stack.ID == "io.buildpacks.stacks.bionic" { - descriptor.Targets = append(descriptor.Targets, TargetMetadata{OS: "linux", Arch: "amd64", Distros: []OSDistro{{Name: "ubuntu", Version: "18.04"}}}) + if api.MustParse(descriptor.API()).AtLeast("0.10") || len(descriptor.Stacks) == 1 { + descriptor.Targets = append(descriptor.Targets, TargetMetadata{OS: "linux", Arch: "amd64", Distros: []OSDistro{{Name: "ubuntu", Version: "18.04"}}}) + } } else if stack.ID == "*" { descriptor.Targets = append(descriptor.Targets, TargetMetadata{}) // matches any } diff --git a/buildpack/bp_descriptor_test.go b/buildpack/bp_descriptor_test.go index 2fff71ac..cd438709 100644 --- a/buildpack/bp_descriptor_test.go +++ b/buildpack/bp_descriptor_test.go @@ -81,44 +81,140 @@ func testBpDescriptor(t *testing.T, when spec.G, it spec.S) { h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "V8.4-2L3") }) - it("does translate one special stack value into target values for older apis", func() { - path := filepath.Join("testdata", "buildpack", "by-id", "B", "v1", "buildpack.toml") - descriptor, err := buildpack.ReadBpDescriptor(path) - h.AssertNil(t, err) - // common sanity checks - h.AssertEq(t, descriptor.WithAPI, "0.7") - h.AssertEq(t, descriptor.Buildpack.ID, "B") - h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B") - h.AssertEq(t, descriptor.Buildpack.Version, "v1") - h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage") - h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"}) - // specific behaviors for this test - h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic") - h.AssertEq(t, len(descriptor.Targets), 1) - h.AssertEq(t, descriptor.Targets[0].Arch, "amd64") - h.AssertEq(t, descriptor.Targets[0].OS, "linux") - h.AssertEq(t, descriptor.Targets[0].Distros[0].Name, "ubuntu") - h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "18.04") - }) + when("translating stacks to targets", func() { + when("older buildpacks", func() { + when("there is only bionic", func() { + it("creates a target", func() { + path := filepath.Join("testdata", "buildpack", "by-id", "B", "v1", "buildpack.toml") + descriptor, err := buildpack.ReadBpDescriptor(path) + h.AssertNil(t, err) + // common sanity checks + h.AssertEq(t, descriptor.WithAPI, "0.7") + h.AssertEq(t, descriptor.Buildpack.ID, "B") + h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B") + h.AssertEq(t, descriptor.Buildpack.Version, "v1") + h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage") + h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"}) + // specific behaviors for this test + h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic") + h.AssertEq(t, len(descriptor.Targets), 1) + h.AssertEq(t, descriptor.Targets[0].Arch, "amd64") + h.AssertEq(t, descriptor.Targets[0].OS, "linux") + h.AssertEq(t, descriptor.Targets[0].Distros[0].Name, "ubuntu") + h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "18.04") + }) + }) - it("translates one special stack value into target values", func() { - path := filepath.Join("testdata", "buildpack", "by-id", "B", "v2", "buildpack.toml") - descriptor, err := buildpack.ReadBpDescriptor(path) - h.AssertNil(t, err) - // common sanity checks - h.AssertEq(t, descriptor.WithAPI, "0.12") - h.AssertEq(t, descriptor.Buildpack.ID, "B") - h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B") - h.AssertEq(t, descriptor.Buildpack.Version, "v1") - h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage") - h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"}) - // specific behaviors for this test - h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic") - h.AssertEq(t, len(descriptor.Targets), 1) - h.AssertEq(t, descriptor.Targets[0].Arch, "amd64") - h.AssertEq(t, descriptor.Targets[0].OS, "linux") - h.AssertEq(t, descriptor.Targets[0].Distros[0].Name, "ubuntu") - h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "18.04") + when("there are multiple stacks", func() { + it("does NOT create a target", func() { + path := filepath.Join("testdata", "buildpack", "by-id", "B", "v1.2", "buildpack.toml") + descriptor, err := buildpack.ReadBpDescriptor(path) + h.AssertNil(t, err) + // common sanity checks + h.AssertEq(t, descriptor.WithAPI, "0.7") + h.AssertEq(t, descriptor.Buildpack.ID, "B") + h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B") + h.AssertEq(t, descriptor.Buildpack.Version, "v1.2") + h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage") + h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"}) + // specific behaviors for this test + h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic") + h.AssertEq(t, len(descriptor.Targets), 0) + }) + }) + + when("there is a wildcard stack", func() { + it("creates a wildcard target", func() { + path := filepath.Join("testdata", "buildpack", "by-id", "B", "v1.star", "buildpack.toml") + descriptor, err := buildpack.ReadBpDescriptor(path) + h.AssertNil(t, err) + // common sanity checks + h.AssertEq(t, descriptor.WithAPI, "0.7") + h.AssertEq(t, descriptor.Buildpack.ID, "B") + h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B") + h.AssertEq(t, descriptor.Buildpack.Version, "v1.star") + h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage") + h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"}) + // specific behaviors for this test + h.AssertEq(t, descriptor.Stacks[0].ID, "*") + h.AssertEq(t, len(descriptor.Targets), 1) + // a target that is completely empty will always match whatever is the base image target + h.AssertEq(t, descriptor.Targets[0].Arch, "") + h.AssertEq(t, descriptor.Targets[0].OS, "") + h.AssertEq(t, descriptor.Targets[0].ArchVariant, "") + h.AssertEq(t, len(descriptor.Targets[0].Distros), 0) + }) + }) + }) + + when("newer buildpacks", func() { + when("there is only bionic", func() { + it("creates a target", func() { + path := filepath.Join("testdata", "buildpack", "by-id", "B", "v2", "buildpack.toml") + descriptor, err := buildpack.ReadBpDescriptor(path) + h.AssertNil(t, err) + // common sanity checks + h.AssertEq(t, descriptor.WithAPI, "0.12") + h.AssertEq(t, descriptor.Buildpack.ID, "B") + h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B") + h.AssertEq(t, descriptor.Buildpack.Version, "v2") + h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage") + h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"}) + // specific behaviors for this test + h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic") + h.AssertEq(t, len(descriptor.Targets), 1) + h.AssertEq(t, descriptor.Targets[0].Arch, "amd64") + h.AssertEq(t, descriptor.Targets[0].OS, "linux") + h.AssertEq(t, descriptor.Targets[0].Distros[0].Name, "ubuntu") + h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "18.04") + }) + }) + + when("there are multiple stacks", func() { + it("creates a target", func() { + path := filepath.Join("testdata", "buildpack", "by-id", "B", "v2.2", "buildpack.toml") + descriptor, err := buildpack.ReadBpDescriptor(path) + h.AssertNil(t, err) + // common sanity checks + h.AssertEq(t, descriptor.WithAPI, "0.12") + h.AssertEq(t, descriptor.Buildpack.ID, "B") + h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B") + h.AssertEq(t, descriptor.Buildpack.Version, "v2.2") + h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage") + h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"}) + // specific behaviors for this test + h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic") + h.AssertEq(t, len(descriptor.Targets), 1) + h.AssertEq(t, descriptor.Targets[0].Arch, "amd64") + h.AssertEq(t, descriptor.Targets[0].OS, "linux") + h.AssertEq(t, descriptor.Targets[0].Distros[0].Name, "ubuntu") + h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "18.04") + }) + }) + + when("there is a wildcard stack", func() { + it("creates a wildcard target", func() { + path := filepath.Join("testdata", "buildpack", "by-id", "B", "v2.star", "buildpack.toml") + descriptor, err := buildpack.ReadBpDescriptor(path) + h.AssertNil(t, err) + // common sanity checks + h.AssertEq(t, descriptor.WithAPI, "0.12") + h.AssertEq(t, descriptor.Buildpack.ID, "B") + h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B") + h.AssertEq(t, descriptor.Buildpack.Version, "v2.star") + h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage") + h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"}) + // specific behaviors for this test + h.AssertEq(t, descriptor.Stacks[0].ID, "*") + h.AssertEq(t, len(descriptor.Targets), 1) + // a target that is completely empty will always match whatever is the base image target + h.AssertEq(t, descriptor.Targets[0].Arch, "") + h.AssertEq(t, descriptor.Targets[0].OS, "") + h.AssertEq(t, descriptor.Targets[0].ArchVariant, "") + h.AssertEq(t, len(descriptor.Targets[0].Distros), 0) + }) + }) + }) }) it("does not translate non-special stack values", func() { diff --git a/buildpack/build_test.go b/buildpack/build_test.go index a28b3101..f336c913 100644 --- a/buildpack/build_test.go +++ b/buildpack/build_test.go @@ -941,7 +941,7 @@ func testBuild(t *testing.T, when spec.G, it spec.S) { filepath.Join(appDir, "launch-A-v1.toml"), ) _, err := executor.Build(descriptor, inputs, logger) - h.AssertError(t, err, "toml: line 2 (last key \"processes.command\"): incompatible types: TOML value has type []interface {}; destination has type string") + h.AssertError(t, err, "toml: line 2 (last key \"processes.command\"): incompatible types: TOML value has type []any; destination has type string") }) }) }) diff --git a/buildpack/testdata/buildpack/by-id/B/v1.2/buildpack.toml b/buildpack/testdata/buildpack/by-id/B/v1.2/buildpack.toml new file mode 100644 index 00000000..7b2df167 --- /dev/null +++ b/buildpack/testdata/buildpack/by-id/B/v1.2/buildpack.toml @@ -0,0 +1,14 @@ +api = "0.7" + +[buildpack] +id = "B" +name = "Buildpack B" +version = "v1.2" +homepage = "Buildpack B Homepage" +sbom-formats = ["application/vnd.cyclonedx+json"] + +[[stacks]] +id = "io.buildpacks.stacks.bionic" + +[[stacks]] +id = "io.buildpacks.stacks.jammy" diff --git a/buildpack/testdata/buildpack/by-id/B/v1.star/buildpack.toml b/buildpack/testdata/buildpack/by-id/B/v1.star/buildpack.toml new file mode 100644 index 00000000..9267b69f --- /dev/null +++ b/buildpack/testdata/buildpack/by-id/B/v1.star/buildpack.toml @@ -0,0 +1,11 @@ +api = "0.7" + +[buildpack] +id = "B" +name = "Buildpack B" +version = "v1.star" +homepage = "Buildpack B Homepage" +sbom-formats = ["application/vnd.cyclonedx+json"] + +[[stacks]] +id = "*" diff --git a/buildpack/testdata/buildpack/by-id/B/v2.2/buildpack.toml b/buildpack/testdata/buildpack/by-id/B/v2.2/buildpack.toml new file mode 100644 index 00000000..87cc955e --- /dev/null +++ b/buildpack/testdata/buildpack/by-id/B/v2.2/buildpack.toml @@ -0,0 +1,14 @@ +api = "0.12" + +[buildpack] +id = "B" +name = "Buildpack B" +version = "v2.2" +homepage = "Buildpack B Homepage" +sbom-formats = ["application/vnd.cyclonedx+json"] + +[[stacks]] +id = "io.buildpacks.stacks.bionic" + +[[stacks]] +id = "io.buildpacks.stacks.jammy" diff --git a/buildpack/testdata/buildpack/by-id/B/v2.star/buildpack.toml b/buildpack/testdata/buildpack/by-id/B/v2.star/buildpack.toml new file mode 100644 index 00000000..018823b0 --- /dev/null +++ b/buildpack/testdata/buildpack/by-id/B/v2.star/buildpack.toml @@ -0,0 +1,11 @@ +api = "0.12" + +[buildpack] +id = "B" +name = "Buildpack B" +version = "v2.star" +homepage = "Buildpack B Homepage" +sbom-formats = ["application/vnd.cyclonedx+json"] + +[[stacks]] +id = "*" diff --git a/buildpack/testdata/buildpack/by-id/B/v2/buildpack.toml b/buildpack/testdata/buildpack/by-id/B/v2/buildpack.toml index ad55d56b..7780b29f 100644 --- a/buildpack/testdata/buildpack/by-id/B/v2/buildpack.toml +++ b/buildpack/testdata/buildpack/by-id/B/v2/buildpack.toml @@ -3,7 +3,7 @@ api = "0.12" [buildpack] id = "B" name = "Buildpack B" -version = "v1" +version = "v2" homepage = "Buildpack B Homepage" sbom-formats = ["application/vnd.cyclonedx+json"] diff --git a/cache/caching_image_test.go b/cache/caching_image_test.go index 345acc2c..31ebf374 100644 --- a/cache/caching_image_test.go +++ b/cache/caching_image_test.go @@ -13,6 +13,8 @@ import ( "github.com/sclevine/spec" "github.com/sclevine/spec/report" + "github.com/buildpacks/lifecycle/cmd" + "github.com/buildpacks/lifecycle/cache" h "github.com/buildpacks/lifecycle/testhelpers" ) @@ -37,7 +39,7 @@ func testCachingImage(t *testing.T, when spec.G, it spec.S) { fakeImage = fakes.NewImage("some-image", "", nil) tmpDir, err = os.MkdirTemp("", "") h.AssertNil(t, err) - volumeCache, err = cache.NewVolumeCache(tmpDir) + volumeCache, err = cache.NewVolumeCache(tmpDir, cmd.DefaultLogger) h.AssertNil(t, err) subject = cache.NewCachingImage(fakeImage, volumeCache) layerPath, layerSHA, layerData = h.RandomLayer(t, tmpDir) diff --git a/cache/common.go b/cache/common.go index 8b06e362..c37d2f20 100644 --- a/cache/common.go +++ b/cache/common.go @@ -5,3 +5,25 @@ import ( ) var errCacheCommitted = errors.New("cache cannot be modified after commit") + +// ReadErr is an error type for filesystem read errors. +type ReadErr struct { + msg string +} + +// NewReadErr creates a new ReadErr. +func NewReadErr(msg string) ReadErr { + return ReadErr{msg: msg} +} + +// Error returns the error message. +func (e ReadErr) Error() string { + return e.msg +} + +// IsReadErr checks if an error is a ReadErr. +func IsReadErr(err error) (bool, *ReadErr) { + var e ReadErr + isReadErr := errors.As(err, &e) + return isReadErr, &e +} diff --git a/cache/image_cache.go b/cache/image_cache.go index efdbdf2e..a029c87d 100644 --- a/cache/image_cache.go +++ b/cache/image_cache.go @@ -99,15 +99,44 @@ func (c *ImageCache) AddLayerFile(tarPath string, diffID string) error { return c.newImage.AddLayerWithDiffID(tarPath, diffID) } +// isLayerNotFound checks if the error is a layer not found error +// +// FIXME: we should not have to rely on trapping ErrUnexpectedEOF. +// If a blob is not present in the registry, we should get imgutil.ErrLayerNotFound, +// but we do not and instead get io.ErrUnexpectedEOF +func isLayerNotFound(err error) bool { + var e imgutil.ErrLayerNotFound + return errors.As(err, &e) || errors.Is(err, io.ErrUnexpectedEOF) +} + func (c *ImageCache) ReuseLayer(diffID string) error { if c.committed { return errCacheCommitted } - return c.newImage.ReuseLayer(diffID) + err := c.newImage.ReuseLayer(diffID) + if err != nil { + // FIXME: this path is not currently executed. + // If a blob is not present in the registry, we should get imgutil.ErrLayerNotFound. + // We should then skip attempting to reuse the layer. + // However, we do not get imgutil.ErrLayerNotFound when the blob is not present. + if isLayerNotFound(err) { + return NewReadErr(fmt.Sprintf("failed to find cache layer with SHA '%s'", diffID)) + } + return fmt.Errorf("failed to reuse cache layer with SHA '%s'", diffID) + } + return nil } +// RetrieveLayer retrieves a layer from the cache func (c *ImageCache) RetrieveLayer(diffID string) (io.ReadCloser, error) { - return c.origImage.GetLayer(diffID) + closer, err := c.origImage.GetLayer(diffID) + if err != nil { + if isLayerNotFound(err) { + return nil, NewReadErr(fmt.Sprintf("failed to find cache layer with SHA '%s'", diffID)) + } + return nil, fmt.Errorf("failed to get cache layer with SHA '%s'", diffID) + } + return closer, nil } func (c *ImageCache) Commit() error { diff --git a/cache/image_cache_test.go b/cache/image_cache_test.go index d3fcb2d1..3a92c45e 100644 --- a/cache/image_cache_test.go +++ b/cache/image_cache_test.go @@ -146,7 +146,7 @@ func testImageCache(t *testing.T, when spec.G, it spec.S) { when("layer does not exist", func() { it("returns an error", func() { _, err := subject.RetrieveLayer("some_nonexistent_sha") - h.AssertError(t, err, "failed to get layer with sha 'some_nonexistent_sha'") + h.AssertError(t, err, "failed to get cache layer with SHA 'some_nonexistent_sha'") }) }) }) @@ -236,7 +236,7 @@ func testImageCache(t *testing.T, when spec.G, it spec.S) { h.AssertNil(t, subject.AddLayerFile(testLayerTarPath, testLayerSHA)) _, err := subject.RetrieveLayer(testLayerSHA) - h.AssertError(t, err, fmt.Sprintf("failed to get layer with sha '%s'", testLayerSHA)) + h.AssertError(t, err, fmt.Sprintf("failed to get cache layer with SHA '%s'", testLayerSHA)) }) }) }) diff --git a/cache/image_deleter.go b/cache/image_deleter.go index 8807715d..f6fc03ac 100644 --- a/cache/image_deleter.go +++ b/cache/image_deleter.go @@ -12,6 +12,7 @@ import ( // ImageDeleter defines the methods available to delete and compare cached images type ImageDeleter interface { DeleteOrigImageIfDifferentFromNewImage(origImage, newImage imgutil.Image) + DeleteImage(image imgutil.Image) } // ImageDeleterImpl is a component to manage cache image deletion @@ -35,13 +36,13 @@ func (c *ImageDeleterImpl) DeleteOrigImageIfDifferentFromNewImage(origImage, new } if !same { - c.deleteImage(origImage) + c.DeleteImage(origImage) } } } -// deleteImage deletes an image -func (c *ImageDeleterImpl) deleteImage(image imgutil.Image) { +// DeleteImage deletes an image +func (c *ImageDeleterImpl) DeleteImage(image imgutil.Image) { if c.deletionEnabled { if err := image.Delete(); err != nil { c.logger.Warnf("Unable to delete cache image: %v", err.Error()) diff --git a/cache/volume_cache.go b/cache/volume_cache.go index 210e1afb..3ec0003c 100644 --- a/cache/volume_cache.go +++ b/cache/volume_cache.go @@ -2,6 +2,7 @@ package cache import ( "encoding/json" + "fmt" "io" "os" "path/filepath" @@ -10,6 +11,8 @@ import ( "github.com/pkg/errors" + "github.com/buildpacks/lifecycle/log" + "github.com/buildpacks/lifecycle/internal/fsutil" "github.com/buildpacks/lifecycle/platform" ) @@ -20,9 +23,11 @@ type VolumeCache struct { backupDir string stagingDir string committedDir string + logger log.Logger } -func NewVolumeCache(dir string) (*VolumeCache, error) { +// NewVolumeCache creates a new VolumeCache +func NewVolumeCache(dir string, logger log.Logger) (*VolumeCache, error) { if _, err := os.Stat(dir); err != nil { return nil, err } @@ -32,6 +37,7 @@ func NewVolumeCache(dir string) (*VolumeCache, error) { backupDir: filepath.Join(dir, "committed-backup"), stagingDir: filepath.Join(dir, "staging"), committedDir: filepath.Join(dir, "committed"), + logger: logger, } if err := c.setupStagingDir(); err != nil { @@ -133,7 +139,20 @@ func (c *VolumeCache) ReuseLayer(diffID string) error { if c.committed { return errCacheCommitted } - if err := os.Link(diffIDPath(c.committedDir, diffID), diffIDPath(c.stagingDir, diffID)); err != nil && !os.IsExist(err) { + committedPath := diffIDPath(c.committedDir, diffID) + stagingPath := diffIDPath(c.stagingDir, diffID) + + if _, err := os.Stat(committedPath); err != nil { + if os.IsNotExist(err) { + return NewReadErr(fmt.Sprintf("failed to find cache layer with SHA '%s'", diffID)) + } + if os.IsPermission(err) { + return NewReadErr(fmt.Sprintf("failed to read cache layer with SHA '%s' due to insufficient permissions", diffID)) + } + return fmt.Errorf("failed to re-use cache layer with SHA '%s': %w", diffID, err) + } + + if err := os.Link(committedPath, stagingPath); err != nil && !os.IsExist(err) { return errors.Wrapf(err, "reusing layer (%s)", diffID) } return nil @@ -146,7 +165,13 @@ func (c *VolumeCache) RetrieveLayer(diffID string) (io.ReadCloser, error) { } file, err := os.Open(path) if err != nil { - return nil, errors.Wrapf(err, "opening layer with SHA '%s'", diffID) + if os.IsPermission(err) { + return nil, NewReadErr(fmt.Sprintf("failed to read cache layer with SHA '%s' due to insufficient permissions", diffID)) + } + if os.IsNotExist(err) { + return nil, NewReadErr(fmt.Sprintf("failed to find cache layer with SHA '%s'", diffID)) + } + return nil, fmt.Errorf("failed to get cache layer with SHA '%s'", diffID) } return file, nil } @@ -165,7 +190,7 @@ func (c *VolumeCache) RetrieveLayerFile(diffID string) (string, error) { path := diffIDPath(c.committedDir, diffID) if _, err := os.Stat(path); err != nil { if os.IsNotExist(err) { - return "", errors.Wrapf(err, "layer with SHA '%s' not found", diffID) + return "", NewReadErr(fmt.Sprintf("failed to find cache layer with SHA '%s'", diffID)) } return "", errors.Wrapf(err, "retrieving layer with SHA '%s'", diffID) } diff --git a/cache/volume_cache_test.go b/cache/volume_cache_test.go index 3ab97ffe..e7df2ef2 100644 --- a/cache/volume_cache_test.go +++ b/cache/volume_cache_test.go @@ -10,6 +10,9 @@ import ( "github.com/sclevine/spec" "github.com/sclevine/spec/report" + "github.com/buildpacks/lifecycle/cmd" + "github.com/buildpacks/lifecycle/log" + "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/cache" "github.com/buildpacks/lifecycle/platform" @@ -28,6 +31,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { backupDir string stagingDir string committedDir string + testLogger log.Logger ) it.Before(func() { @@ -42,6 +46,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { backupDir = filepath.Join(volumeDir, "committed-backup") stagingDir = filepath.Join(volumeDir, "staging") committedDir = filepath.Join(volumeDir, "committed") + testLogger = cmd.DefaultLogger }) it.After(func() { @@ -50,7 +55,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { when("#NewVolumeCache", func() { it("returns an error when the volume path does not exist", func() { - _, err := cache.NewVolumeCache(filepath.Join(tmpDir, "does_not_exist")) + _, err := cache.NewVolumeCache(filepath.Join(tmpDir, "does_not_exist"), testLogger) if err == nil { t.Fatal("expected NewVolumeCache to fail because volume path does not exist") } @@ -66,7 +71,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { it("clears staging", func() { var err error - subject, err = cache.NewVolumeCache(volumeDir) + subject, err = cache.NewVolumeCache(volumeDir, testLogger) h.AssertNil(t, err) _, err = os.Stat(filepath.Join(stagingDir, "some-layer.tar")) @@ -80,7 +85,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { it("creates staging dir", func() { var err error - subject, err = cache.NewVolumeCache(volumeDir) + subject, err = cache.NewVolumeCache(volumeDir, testLogger) h.AssertNil(t, err) _, err = os.Stat(stagingDir) @@ -92,7 +97,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { it("creates committed dir", func() { var err error - subject, err = cache.NewVolumeCache(volumeDir) + subject, err = cache.NewVolumeCache(volumeDir, testLogger) h.AssertNil(t, err) _, err = os.Stat(committedDir) @@ -109,7 +114,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { it("clears the backup dir", func() { var err error - subject, err = cache.NewVolumeCache(volumeDir) + subject, err = cache.NewVolumeCache(volumeDir, testLogger) h.AssertNil(t, err) _, err = os.Stat(filepath.Join(backupDir, "some-layer.tar")) @@ -124,7 +129,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { it.Before(func() { var err error - subject, err = cache.NewVolumeCache(volumeDir) + subject, err = cache.NewVolumeCache(volumeDir, testLogger) h.AssertNil(t, err) }) @@ -206,7 +211,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { when("layer does not exist", func() { it("returns an error", func() { _, err := subject.RetrieveLayer("some_nonexistent_sha") - h.AssertError(t, err, "layer with SHA 'some_nonexistent_sha' not found") + h.AssertError(t, err, "failed to find cache layer with SHA 'some_nonexistent_sha'") }) }) }) @@ -230,7 +235,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { when("layer does not exist", func() { it("returns an error", func() { _, err := subject.RetrieveLayerFile("some_nonexistent_sha") - h.AssertError(t, err, "layer with SHA 'some_nonexistent_sha' not found") + h.AssertError(t, err, "failed to find cache layer with SHA 'some_nonexistent_sha'") }) }) }) @@ -340,7 +345,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { h.AssertNil(t, subject.AddLayerFile(tarPath, "some_sha")) _, err := subject.RetrieveLayer("some_sha") - h.AssertError(t, err, "layer with SHA 'some_sha' not found") + h.AssertError(t, err, "failed to find cache layer with SHA 'some_sha'") }) }) @@ -415,7 +420,7 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { h.AssertNil(t, subject.AddLayer(layerReader, layerSha)) _, err := subject.RetrieveLayer(layerSha) - h.AssertError(t, err, fmt.Sprintf("layer with SHA '%s' not found", layerSha)) + h.AssertError(t, err, fmt.Sprintf("failed to find cache layer with SHA '%s'", layerSha)) }) }) @@ -507,6 +512,21 @@ func testVolumeCache(t *testing.T, when spec.G, it spec.S) { h.AssertEq(t, string(bytes), "existing data") }) }) + + when("the layer does not exist", func() { + it("fails with a read error", func() { + err := subject.ReuseLayer("some_nonexistent_sha") + isReadErr, _ := cache.IsReadErr(err) + h.AssertEq(t, isReadErr, true) + + err = subject.Commit() + h.AssertNil(t, err) + + _, err = subject.RetrieveLayer("some_sha") + isReadErr, _ = cache.IsReadErr(err) + h.AssertEq(t, isReadErr, true) + }) + }) }) when("attempting to commit more than once", func() { diff --git a/cmd/lifecycle/exporter.go b/cmd/lifecycle/exporter.go index 30518661..06929fe5 100644 --- a/cmd/lifecycle/exporter.go +++ b/cmd/lifecycle/exporter.go @@ -19,6 +19,8 @@ import ( "github.com/pkg/errors" "golang.org/x/sync/errgroup" + "github.com/buildpacks/lifecycle/log" + "github.com/buildpacks/lifecycle/auth" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/cache" @@ -200,7 +202,7 @@ func (e *exportCmd) export(group buildpack.Group, cacheStore phase.Cache, analyz case e.UseLayout: appImage, runImageID, err = e.initLayoutAppImage(analyzedMD) case e.UseDaemon: - appImage, runImageID, err = e.initDaemonAppImage(analyzedMD) + appImage, runImageID, err = e.initDaemonAppImage(analyzedMD, cmd.DefaultLogger) default: appImage, runImageID, err = e.initRemoteAppImage(analyzedMD) } @@ -258,7 +260,7 @@ func (e *exportCmd) export(group buildpack.Group, cacheStore phase.Cache, analyz return nil } -func (e *exportCmd) initDaemonAppImage(analyzedMD files.Analyzed) (imgutil.Image, string, error) { +func (e *exportCmd) initDaemonAppImage(analyzedMD files.Analyzed, logger log.Logger) (imgutil.Image, string, error) { var opts = []imgutil.ImageOption{ local.FromBaseImage(e.RunImageRef), } @@ -301,7 +303,7 @@ func (e *exportCmd) initDaemonAppImage(analyzedMD files.Analyzed) (imgutil.Image } if e.LaunchCacheDir != "" { - volumeCache, err := cache.NewVolumeCache(e.LaunchCacheDir) + volumeCache, err := cache.NewVolumeCache(e.LaunchCacheDir, logger) if err != nil { return nil, "", cmd.FailErr(err, "create launch cache") } diff --git a/cmd/lifecycle/main.go b/cmd/lifecycle/main.go index 74b51a01..42fede72 100644 --- a/cmd/lifecycle/main.go +++ b/cmd/lifecycle/main.go @@ -96,14 +96,14 @@ func (ch *DefaultCacheHandler) InitCache(cacheImageRef string, cacheDir string, cacheStore phase.Cache err error ) + logger := cmd.DefaultLogger if cacheImageRef != "" { - logger := cmd.DefaultLogger cacheStore, err = cache.NewImageCacheFromName(cacheImageRef, ch.keychain, logger, cache.NewImageDeleter(cache.NewImageComparer(), logger, deletionEnabled)) if err != nil { return nil, errors.Wrap(err, "creating image cache") } } else if cacheDir != "" { - cacheStore, err = cache.NewVolumeCache(cacheDir) + cacheStore, err = cache.NewVolumeCache(cacheDir, logger) if err != nil { return nil, errors.Wrap(err, "creating volume cache") } @@ -118,14 +118,14 @@ func initCache(cacheImageTag, cacheDir string, keychain authn.Keychain, deletion cacheStore phase.Cache err error ) + logger := cmd.DefaultLogger if cacheImageTag != "" { - logger := cmd.DefaultLogger cacheStore, err = cache.NewImageCacheFromName(cacheImageTag, keychain, logger, cache.NewImageDeleter(cache.NewImageComparer(), logger, deletionEnabled)) if err != nil { return nil, cmd.FailErr(err, "create image cache") } } else if cacheDir != "" { - cacheStore, err = cache.NewVolumeCache(cacheDir) + cacheStore, err = cache.NewVolumeCache(cacheDir, logger) if err != nil { return nil, cmd.FailErr(err, "create volume cache") } diff --git a/cmd/lifecycle/restorer.go b/cmd/lifecycle/restorer.go index dc2bfc65..e0798dbf 100644 --- a/cmd/lifecycle/restorer.go +++ b/cmd/lifecycle/restorer.go @@ -50,6 +50,9 @@ func (r *restoreCmd) DefineFlags() { cli.FlagBuildImage(&r.BuildImageRef) } cli.FlagAnalyzedPath(&r.AnalyzedPath) + if r.PlatformAPI.AtLeast("0.14") { + cli.FlagRunPath(&r.RunPath) + } cli.FlagCacheDir(&r.CacheDir) cli.FlagCacheImage(&r.CacheImageRef) cli.FlagGID(&r.GID) @@ -123,6 +126,15 @@ func (r *restoreCmd) Exec() error { runImage imgutil.Image ) runImageName := analyzedMD.RunImageImage() // FIXME: if we have a digest reference available in `Reference` (e.g., in the non-daemon case) we should use it + accessibleRunImage, err := r.runImageAccessCheck(runImageName) + if err != nil { + return err + } + if runImageName != accessibleRunImage { + analyzedMD.RunImage.Image = accessibleRunImage + analyzedMD.RunImage.Reference = accessibleRunImage + } + if r.supportsRunImageExtension() && needsPulling(analyzedMD.RunImage) { cmd.DefaultLogger.Debugf("Pulling run image metadata for %s...", runImageName) runImage, err = r.pullSparse(runImageName) @@ -192,6 +204,23 @@ func needsPulling(runImage *files.RunImage) bool { return runImage.Extend } +func (r *restoreCmd) runImageAccessCheck(runImageName string) (string, error) { + if r.PlatformAPI.LessThan("0.14") { + return runImageName, nil + } + + runToml, err := files.Handler.ReadRun(r.RunPath, cmd.DefaultLogger) + if err != nil { + return "", err + } + + if !runToml.Contains(runImageName) { + return runImageName, nil + } + + return platform.BestRunImageMirrorFor("", runToml.FindByRef(runImageName), r.AccessChecker()) +} + func (r *restoreCmd) needsUpdating(runImage *files.RunImage, group buildpack.Group) bool { if r.PlatformAPI.LessThan("0.10") { return false @@ -276,7 +305,7 @@ func (r *restoreCmd) restore(layerMetadata files.LayersMetadata, group buildpack Buildpacks: group.Group, Logger: cmd.DefaultLogger, PlatformAPI: r.PlatformAPI, - LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(r.LayersDir, r.SkipLayers, cmd.DefaultLogger), + LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(r.LayersDir, r.SkipLayers, cmd.DefaultLogger, r.PlatformAPI), LayersMetadata: layerMetadata, SBOMRestorer: layer.NewSBOMRestorer(layer.SBOMRestorerOpts{ LayersDir: r.LayersDir, diff --git a/go.mod b/go.mod index 1f91275e..6907c83a 100644 --- a/go.mod +++ b/go.mod @@ -1,29 +1,28 @@ module github.com/buildpacks/lifecycle require ( - github.com/BurntSushi/toml v1.3.2 - github.com/GoogleContainerTools/kaniko v1.21.1 + github.com/BurntSushi/toml v1.4.0 + github.com/GoogleContainerTools/kaniko v1.23.1 github.com/apex/log v1.9.0 - github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231213181459-b0fcec718dc6 - github.com/buildpacks/imgutil v0.0.0-20240416145632-bef4977c8877 + github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240419161514-af205d85bb44 + github.com/buildpacks/imgutil v0.0.0-20240605145725-186f89b2d168 github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 - github.com/containerd/containerd v1.7.14 - github.com/docker/docker v26.0.1+incompatible + github.com/containerd/containerd v1.7.19 + github.com/docker/docker v26.1.4+incompatible github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 - github.com/google/go-containerregistry v0.19.1 + github.com/google/go-containerregistry v0.20.0 github.com/google/uuid v1.6.0 github.com/heroku/color v0.0.6 - github.com/moby/buildkit v0.13.1 + github.com/moby/buildkit v0.13.2 github.com/pkg/errors v0.9.1 github.com/sclevine/spec v1.4.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.18.0 + golang.org/x/sys v0.22.0 ) require ( - cloud.google.com/go/compute v1.24.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect @@ -36,41 +35,44 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/Microsoft/hcsshim v0.11.4 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/Microsoft/hcsshim v0.11.7 // indirect github.com/agext/levenshtein v1.2.3 // indirect - github.com/aws/aws-sdk-go-v2 v1.25.2 // indirect - github.com/aws/aws-sdk-go-v2/config v1.27.4 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.4 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2 // indirect + github.com/aws/aws-sdk-go-v2 v1.27.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.16 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.16 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ecr v1.24.5 // indirect - github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.2 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.20.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.28.1 // indirect - github.com/aws/smithy-go v1.20.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ecr v1.27.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.23.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.20.9 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.28.10 // indirect + github.com/aws/smithy-go v1.20.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cilium/ebpf v0.12.3 // indirect + github.com/cilium/ebpf v0.9.1 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/containerd/cgroups/v3 v3.0.2 // indirect + github.com/containerd/containerd/api v1.7.19 // indirect github.com/containerd/continuity v0.4.3 // indirect + github.com/containerd/errdefs v0.1.0 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect - github.com/containerd/ttrpc v1.2.3 // indirect + github.com/containerd/ttrpc v1.2.5 // indirect github.com/containerd/typeurl/v2 v2.1.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/cli v25.0.3+incompatible // indirect + github.com/docker/cli v26.1.4+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker-credential-helpers v0.8.0 // indirect + github.com/docker/docker-credential-helpers v0.8.1 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-metrics v0.0.1 // indirect @@ -84,7 +86,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-memdb v1.3.4 // indirect @@ -109,7 +111,7 @@ require ( github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/opencontainers/selinux v1.11.0 // indirect github.com/otiai10/copy v1.14.0 // indirect @@ -126,19 +128,15 @@ require ( go.opentelemetry.io/otel v1.25.0 // indirect go.opentelemetry.io/otel/metric v1.25.0 // indirect go.opentelemetry.io/otel/trace v1.25.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/exp v0.0.0-20231219160207-73b9e39aefca // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.17.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.17.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect - google.golang.org/grpc v1.61.1 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e // indirect + google.golang.org/grpc v1.64.1 // indirect + google.golang.org/protobuf v1.34.1 // indirect ) go 1.22 diff --git a/go.sum b/go.sum index 57cad926..2a4b0f5d 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= -cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA= @@ -35,14 +33,14 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/GoogleContainerTools/kaniko v1.21.1 h1:Q77TGiuSRopS1FvZY9Bzu9Wp9VYlpP6zU+/mu08/COs= -github.com/GoogleContainerTools/kaniko v1.21.1/go.mod h1:5kbaXGmhHLNc2Zzi/P1Se0qhFYDvK8R62QJh/O0n8rk= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= -github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/GoogleContainerTools/kaniko v1.23.1 h1:1N6XhVTrUB0CNVIUeruXfZ9CtpJP3TuZkxDZYg2NilA= +github.com/GoogleContainerTools/kaniko v1.23.1/go.mod h1:r3od0LXG7hnM2i/WMIX1e6kmLiKpV1D7skalBZBvsbk= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ= +github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -53,45 +51,45 @@ github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDw github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v1.25.2 h1:/uiG1avJRgLGiQM9X3qJM8+Qa6KRGK5rRPuXE0HUM+w= -github.com/aws/aws-sdk-go-v2 v1.25.2/go.mod h1:Evoc5AsmtveRt1komDwIsjHFyrP5tDuF1D1U+6z6pNo= -github.com/aws/aws-sdk-go-v2/config v1.27.4 h1:AhfWb5ZwimdsYTgP7Od8E9L1u4sKmDW2ZVeLcf2O42M= -github.com/aws/aws-sdk-go-v2/config v1.27.4/go.mod h1:zq2FFXK3A416kiukwpsd+rD4ny6JC7QSkp4QdN1Mp2g= -github.com/aws/aws-sdk-go-v2/credentials v1.17.4 h1:h5Vztbd8qLppiPwX+y0Q6WiwMZgpd9keKe2EAENgAuI= -github.com/aws/aws-sdk-go-v2/credentials v1.17.4/go.mod h1:+30tpwrkOgvkJL1rUZuRLoxcJwtI/OkeBLYnHxJtVe0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 h1:AK0J8iYBFeUk2Ax7O8YpLtFsfhdOByh2QIkHmigpRYk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2/go.mod h1:iRlGzMix0SExQEviAyptRWRGdYNo3+ufW/lCzvKVTUc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2 h1:bNo4LagzUKbjdxE0tIcR9pMzLR2U/Tgie1Hq1HQ3iH8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2/go.mod h1:wRQv0nN6v9wDXuWThpovGQjqF1HFdcgWjporw14lS8k= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2 h1:EtOU5jsPdIQNP+6Q2C5e3d65NKT1PeCiQk+9OdzO12Q= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2/go.mod h1:tyF5sKccmDz0Bv4NrstEr+/9YkSPJHrcO7UsUKf7pWM= +github.com/aws/aws-sdk-go-v2 v1.27.0 h1:7bZWKoXhzI+mMR/HjdMx8ZCC5+6fY0lS5tr0bbgiLlo= +github.com/aws/aws-sdk-go-v2 v1.27.0/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= +github.com/aws/aws-sdk-go-v2/config v1.27.16 h1:knpCuH7laFVGYTNd99Ns5t+8PuRjDn4HnnZK48csipM= +github.com/aws/aws-sdk-go-v2/config v1.27.16/go.mod h1:vutqgRhDUktwSge3hrC3nkuirzkJ4E/mLj5GvI0BQas= +github.com/aws/aws-sdk-go-v2/credentials v1.17.16 h1:7d2QxY83uYl0l58ceyiSpxg9bSbStqBC6BeEeHEchwo= +github.com/aws/aws-sdk-go-v2/credentials v1.17.16/go.mod h1:Ae6li/6Yc6eMzysRL2BXlPYvnrLLBg3D11/AmOjw50k= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 h1:dQLK4TjtnlRGb0czOht2CevZ5l6RSyRWAnKeGd7VAFE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3/go.mod h1:TL79f2P6+8Q7dTsILpiVST+AL9lkF6PPGI167Ny0Cjw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 h1:lf/8VTF2cM+N4SLzaYJERKEWAXq8MOMpZfU6wEPWsPk= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7/go.mod h1:4SjkU7QiqK2M9oozyMzfZ/23LmUY+h3oFqhdeP5OMiI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 h1:4OYVp0705xu8yjdyoWix0r9wPIRXnIzzOoUpQVHIJ/g= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7/go.mod h1:vd7ESTEvI76T2Na050gODNmNU7+OyKrIKroYTu4ABiI= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/service/ecr v1.24.5 h1:wLPDAUFT50NEXGXpywRU3AA74pg35RJjWol/68ruvQQ= -github.com/aws/aws-sdk-go-v2/service/ecr v1.24.5/go.mod h1:AOHmGMoPtSY9Zm2zBuwUJQBisIvYAZeA1n7b6f4e880= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5 h1:PQp21GBlGNaQ+AVJAB8w2KTmLx0DkFS2fDET2Iy3+f0= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5/go.mod h1:WMntdAol8KgeYsa5sDZPsRTXs4jVZIMYu0eQVVIQxnc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 h1:EyBZibRTVAs6ECHZOw5/wlylS9OcTzwyjeQMudmREjE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1/go.mod h1:JKpmtYhhPs7D97NL/ltqz7yCkERFW5dOlHyVl66ZYF8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.2 h1:5ffmXjPtwRExp1zc7gENLgCPyHFbhEPwVTkTiH9niSk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.2/go.mod h1:Ru7vg1iQ7cR4i7SZ/JTLYN9kaXtbL69UdgG0OQWQxW0= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.1 h1:utEGkfdQ4L6YW/ietH7111ZYglLJvS+sLriHJ1NBJEQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.1/go.mod h1:RsYqzYr2F2oPDdpy+PdhephuZxTfjHQe7SOBcZGoAU8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1 h1:9/GylMS45hGGFCcMrUZDVayQE1jYSIN6da9jo7RAYIw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1/go.mod h1:YjAPFn4kGFqKC54VsHs5fn5B6d+PCY2tziEa3U/GB5Y= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.1 h1:3I2cBEYgKhrWlwyZgfpSO2BpaMY1LHPqXYk/QGlu2ew= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.1/go.mod h1:uQ7YYKZt3adCRrdCBREm1CD3efFLOUNH77MrUCvx5oA= -github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw= -github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231213181459-b0fcec718dc6 h1:PlJRmqKlSlEUlwem1c3zdPaEMtJc/ktnV7naD5Qvsx4= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231213181459-b0fcec718dc6/go.mod h1:08sPJIlDHu4HwQ1xScPgsBWezvM6U10ghGKBJu0mowA= +github.com/aws/aws-sdk-go-v2/service/ecr v1.27.4 h1:Qr9W21mzWT3RhfYn9iAux7CeRIdbnTAqmiOlASqQgZI= +github.com/aws/aws-sdk-go-v2/service/ecr v1.27.4/go.mod h1:if7ybzzjOmDB8pat9FE35AHTY6ZxlYSy3YviSmFZv8c= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.23.4 h1:aNuiieMaS2IHxqAsTdM/pjHyY1aoaDLBGLqpNnFMMqk= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.23.4/go.mod h1:8pvvNAklmq+hKmqyvFoMRg0bwg9sdGOvdwximmKiKP0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 h1:Wx0rlZoEJR7JwlSZcHnEa7CNjrSIyVxMFWGAaXy4fJY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9/go.mod h1:aVMHdE0aHO3v+f/iw01fmXV/5DbfQ3Bi9nN7nd9bE9Y= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.9 h1:aD7AGQhvPuAxlSUfo0CWU7s6FpkbyykMhGYMvlqTjVs= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.9/go.mod h1:c1qtZUWtygI6ZdvKppzCSXsDOq5I4luJPZ0Ud3juFCA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.3 h1:Pav5q3cA260Zqez42T9UhIlsd9QeypszRPwC9LdSSsQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.3/go.mod h1:9lmoVDVLz/yUZwLaQ676TK02fhCu4+PgRSmMaKR1ozk= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.10 h1:69tpbPED7jKPyzMcrwSvhWcJ9bPnZsZs18NT40JwM0g= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.10/go.mod h1:0Aqn1MnEuitqfsCNyKsdKLhDUOr4txD/g19EfiUqgws= +github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= +github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240419161514-af205d85bb44 h1:oNDkocd5/+6jUuxyz07jQWnKhgpNtKQoZSXKMb7emqQ= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240419161514-af205d85bb44/go.mod h1:2nlYPkG0rFrODp6R875pk/kOnB8Ivj3+onhzk2mO57g= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/buildpacks/imgutil v0.0.0-20240416145632-bef4977c8877 h1:dUayG/LUjbDZ/sfE0Vne779HlrVNvFCyjYg0LHjIU3U= -github.com/buildpacks/imgutil v0.0.0-20240416145632-bef4977c8877/go.mod h1:n2R6VRuWsAX3cyHCp/u0Z4WJcixny0gYg075J39owrk= +github.com/buildpacks/imgutil v0.0.0-20240605145725-186f89b2d168 h1:yVYVi1V7x1bXklOx9lpbTfteyzQKGZC/wkl+IlaVRlU= +github.com/buildpacks/imgutil v0.0.0-20240605145725-186f89b2d168/go.mod h1:n2R6VRuWsAX3cyHCp/u0Z4WJcixny0gYg075J39owrk= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -99,26 +97,32 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589/go.mod h1:OuDyvmLnMCwa2ep4Jkm6nyA0ocJuZlGyk2gGseVzERM= -github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= -github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= +github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= +github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0= github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE= -github.com/containerd/containerd v1.7.14 h1:H/XLzbnGuenZEGK+v0RkwTdv2u1QFAruMe5N0GNPJwA= -github.com/containerd/containerd v1.7.14/go.mod h1:YMC9Qt5yzNqXx/fO4j/5yYVIHXSRrlB3H7sxkUTvspg= +github.com/containerd/containerd v1.7.19 h1:/xQ4XRJ0tamDkdzrrBAUy/LE5nCcxFKdBm4EcPrSMEE= +github.com/containerd/containerd v1.7.19/go.mod h1:h4FtNYUUMB4Phr6v+xG89RYKj9XccvbNSCKjdufCrkc= +github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA= +github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= +github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU= github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk= -github.com/containerd/ttrpc v1.2.3 h1:4jlhbXIGvijRtNC8F/5CpuJZ7yKOBFGFOOXg1bkISz0= -github.com/containerd/ttrpc v1.2.3/go.mod h1:ieWsXucbb8Mj9PH0rXCw1i8IunRbbAiDkpXkbfflWBM= +github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU= +github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= @@ -132,14 +136,14 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v25.0.3+incompatible h1:KLeNs7zws74oFuVhgZQ5ONGZiXUUdgsdy6/EsX/6284= -github.com/docker/cli v25.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v26.1.4+incompatible h1:I8PHdc0MtxEADqYJZvhBrW9bo8gawKwwenxRM7/rLu8= +github.com/docker/cli v26.1.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v26.0.1+incompatible h1:t39Hm6lpXuXtgkF0dm1t9a5HkbUfdGy6XbWexmGr+hA= -github.com/docker/docker v26.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= -github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= +github.com/docker/docker v26.1.4+incompatible h1:vuTpXDuoga+Z38m1OZHzl7NKisKWaWlhjQk7IDPSLsU= +github.com/docker/docker v26.1.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo= +github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= @@ -159,8 +163,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= -github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= +github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -199,9 +203,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -211,8 +214,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.19.1 h1:yMQ62Al6/V0Z7CqIrrS1iYoA5/oQCm88DeNujc7C1KY= -github.com/google/go-containerregistry v0.19.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= +github.com/google/go-containerregistry v0.20.0 h1:wRqHpOeVh3DnenOrPy9xDOLdnLatiGuuNRVelR2gSbg= +github.com/google/go-containerregistry v0.20.0/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -275,8 +278,8 @@ github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/moby/buildkit v0.13.1 h1:L8afOFhPq2RPJJSr/VyzbufwID7jquZVB7oFHbPRcPE= -github.com/moby/buildkit v0.13.1/go.mod h1:aNmNQKLBFYAOFuzQjR3VA27/FijlvtBD1pjNwTSN37k= +github.com/moby/buildkit v0.13.2 h1:nXNszM4qD9E7QtG7bFWPnDI1teUQFQglBzon/IU3SzI= +github.com/moby/buildkit v0.13.2/go.mod h1:2cyVOv9NoHM7arphK9ZfHIWKn9YVZRFd1wXB8kKmEzY= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= @@ -311,8 +314,8 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= -github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= @@ -418,11 +421,9 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20231219160207-73b9e39aefca h1:+xQfFu/HO/82Wwg4zuJ5xiLp0yaOLJjBGnuafXp85YQ= -golang.org/x/exp v0.0.0-20231219160207-73b9e39aefca/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -430,8 +431,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -449,11 +450,11 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -487,8 +488,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -497,10 +498,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -513,32 +513,30 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= -google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c h1:9g7erC9qu44ks7UK4gDNlnk4kOxZG707xKm4jVniy6o= -google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= +google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw= +google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda/go.mod h1:g2LLCvCeCSir/JJSWosk19BR4NVxGqHUC6rxIRsd7Aw= +google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 h1:W5Xj/70xIA4x60O/IFyXivR5MGqblAb8R3w26pnD6No= +google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e h1:Elxv5MwEkCI9f5SkoL6afed6NTdxaGoAo39eANBwHL8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= -google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -549,10 +547,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/image/registry_handler.go b/image/registry_handler.go index cbfcd563..2bf11056 100644 --- a/image/registry_handler.go +++ b/image/registry_handler.go @@ -1,12 +1,11 @@ package image import ( + "fmt" + "github.com/buildpacks/imgutil" "github.com/buildpacks/imgutil/remote" "github.com/google/go-containerregistry/pkg/authn" - "github.com/pkg/errors" - - "github.com/buildpacks/lifecycle/cmd" ) // RegistryHandler takes care of the registry settings and checks @@ -73,8 +72,7 @@ func verifyReadAccess(imageRef string, keychain authn.Keychain, opts []imgutil.I img, _ := remote.NewImage(imageRef, keychain, opts...) canRead, err := img.CheckReadAccess() if !canRead { - cmd.DefaultLogger.Debugf("Error checking read access: %s", err) - return errors.Errorf("ensure registry read access to %s", imageRef) + return fmt.Errorf("failed to ensure registry read access to %s: %w", imageRef, err) } return nil @@ -88,8 +86,7 @@ func verifyReadWriteAccess(imageRef string, keychain authn.Keychain, opts []imgu img, _ := remote.NewImage(imageRef, keychain, opts...) canReadWrite, err := img.CheckReadWriteAccess() if !canReadWrite { - cmd.DefaultLogger.Debugf("Error checking read/write access: %s", err) - return errors.Errorf("ensure registry read/write access to %s", imageRef) + return fmt.Errorf("failed to ensure registry read/write access to %s: %w", imageRef, err) } return nil } diff --git a/internal/layer/metadata_restorer.go b/internal/layer/metadata_restorer.go index f33e0e17..6660e7a0 100644 --- a/internal/layer/metadata_restorer.go +++ b/internal/layer/metadata_restorer.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" + "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/internal/encoding" "github.com/buildpacks/lifecycle/launch" @@ -24,18 +25,21 @@ type MetadataRestorer interface { Restore(buildpacks []buildpack.GroupElement, appMeta files.LayersMetadata, cacheMeta platform.CacheMetadata, layerSHAStore SHAStore) error } -func NewDefaultMetadataRestorer(layersDir string, skipLayers bool, logger log.Logger) *DefaultMetadataRestorer { +// NewDefaultMetadataRestorer returns an instance of the DefaultMetadataRestorer struct +func NewDefaultMetadataRestorer(layersDir string, skipLayers bool, logger log.Logger, platformAPI *api.Version) *DefaultMetadataRestorer { return &DefaultMetadataRestorer{ - Logger: logger, - LayersDir: layersDir, - SkipLayers: skipLayers, + Logger: logger, + LayersDir: layersDir, + SkipLayers: skipLayers, + PlatformAPI: platformAPI, } } type DefaultMetadataRestorer struct { - LayersDir string - SkipLayers bool - Logger log.Logger + LayersDir string + SkipLayers bool + Logger log.Logger + PlatformAPI *api.Version } func (r *DefaultMetadataRestorer) Restore(buildpacks []buildpack.GroupElement, appMeta files.LayersMetadata, cacheMeta platform.CacheMetadata, layerSHAStore SHAStore) error { @@ -113,10 +117,12 @@ func (r *DefaultMetadataRestorer) restoreLayerMetadata(layerSHAStore SHAStore, a r.Logger.Debugf("Not restoring %q from cache, marked as cache=false", identifier) continue } - // If launch=true, the metadata was restored from the app image or the layer is stale. + // If launch=true, the metadata was restored from the appLayers if present. if layer.Launch { - r.Logger.Debugf("Not restoring %q from cache, marked as launch=true", identifier) - continue + if _, ok := appLayers[layerName]; ok || r.PlatformAPI.LessThan("0.14") { + r.Logger.Debugf("Not restoring %q from cache, marked as launch=true", identifier) + continue + } } r.Logger.Infof("Restoring metadata for %q from cache", identifier) if err := r.writeLayerMetadata(layerSHAStore, buildpackDir, layerName, layer, bp.ID); err != nil { diff --git a/internal/layer/metadata_restorer_test.go b/internal/layer/metadata_restorer_test.go index aec91d37..d1aec921 100644 --- a/internal/layer/metadata_restorer_test.go +++ b/internal/layer/metadata_restorer_test.go @@ -42,7 +42,7 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) { layerDir, err = os.MkdirTemp("", "lifecycle-layer-dir") h.AssertNil(t, err) logger = log.Logger{Handler: &discard.Handler{}} - layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger) + layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger, api.Platform.Latest()) layerSHAStore = layer.NewSHAStore() }) @@ -136,6 +136,8 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) { {"no.cache.buildpack/some-layer.toml", "[metadata]\n some-layer-key = \"some-layer-value\""}, // Cache-image-only layers. {"metadata.buildpack/cache.toml", "[metadata]\n cache-key = \"cache-value\""}, + // Cached launch layers not in app + {"metadata.buildpack/launch-cache-not-in-app.toml", "[metadata]\n cache-only-key = \"cache-only-value\"\n launch-cache-key = \"cache-specific-value\""}, } { got := h.MustReadFile(t, filepath.Join(layerDir, data.name)) h.AssertStringContains(t, string(got), data.want) @@ -143,6 +145,33 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) { } }) + when("platformAPI is less than 0.14", func() { + it.Before(func() { + layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger, api.MustParse("0.13")) + }) + + it("ignores launch-cache-not-in-app", func() { + err := layerMetadataRestorer.Restore(buildpacks, layersMetadata, cacheMetadata, layerSHAStore) + h.AssertNil(t, err) + + h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "metadata.buildpack/launch-cache-not-in-app.toml")) + unsetFlags := "[types]" + for _, data := range []struct{ name, want string }{ + // App layers. + {"metadata.buildpack/launch.toml", "[metadata]\n launch-key = \"launch-value\""}, + {"metadata.buildpack/launch-build-cache.toml", "[metadata]\n launch-build-cache-key = \"launch-build-cache-value\""}, + {"metadata.buildpack/launch-cache.toml", "[metadata]\n launch-cache-key = \"launch-cache-value\""}, + {"no.cache.buildpack/some-layer.toml", "[metadata]\n some-layer-key = \"some-layer-value\""}, + // Cache-image-only layers. + {"metadata.buildpack/cache.toml", "[metadata]\n cache-key = \"cache-value\""}, + } { + got := h.MustReadFile(t, filepath.Join(layerDir, data.name)) + h.AssertStringContains(t, string(got), data.want) + h.AssertStringDoesNotContain(t, string(got), unsetFlags) // The [types] table shouldn't exist. The build, cache and launch flags are set to false. + } + }) + }) + it("restores layer metadata without the launch, build and cache flags", func() { buildpacks = []buildpack.GroupElement{ {ID: "metadata.buildpack", API: api.Buildpack.Latest().String()}, @@ -186,14 +215,6 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) { h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "no.group.buildpack")) }) - it("does not restore launch=true layer metadata", func() { - err := layerMetadataRestorer.Restore(buildpacks, layersMetadata, cacheMetadata, layerSHAStore) - h.AssertNil(t, err) - - h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "metadata.buildpack", "launch-cache-not-in-app.toml")) - h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "metadata.buildpack", "launch-cache-not-in-app.sha")) - }) - it("does not restore cache=false layer metadata", func() { err := layerMetadataRestorer.Restore(buildpacks, layersMetadata, cacheMetadata, layerSHAStore) h.AssertNil(t, err) @@ -299,7 +320,7 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) { when("skip layers is true", func() { it.Before(func() { skipLayers = true - layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger) + layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger, api.Platform.Latest()) }) it("does not write buildpack layer metadata", func() { diff --git a/internal/layer/sbom_restorer_test.go b/internal/layer/sbom_restorer_test.go index 544b3f29..f542f00e 100644 --- a/internal/layer/sbom_restorer_test.go +++ b/internal/layer/sbom_restorer_test.go @@ -164,7 +164,7 @@ func testSBOMRestorer(t *testing.T, when spec.G, it spec.S) { cacheDir, err = os.MkdirTemp("", "lifecycle.cache-dir.") h.AssertNil(t, err) - testCache, err = cache.NewVolumeCache(cacheDir) + testCache, err = cache.NewVolumeCache(cacheDir, &log.Logger{Handler: &discard.Handler{}}) h.AssertNil(t, err) h.AssertNil(t, testCache.AddLayerFile(layer.TarPath, layer.Digest)) h.AssertNil(t, testCache.SetMetadata(platform.CacheMetadata{BOM: files.LayerMetadata{SHA: layer.Digest}})) diff --git a/internal/layer/testdata/cache_metadata.json b/internal/layer/testdata/cache_metadata.json index cc982aa9..a60ee35a 100644 --- a/internal/layer/testdata/cache_metadata.json +++ b/internal/layer/testdata/cache_metadata.json @@ -36,7 +36,7 @@ "launch": true, "sha": "launch-cache-old-sha" }, - "launch-cache-not-in-app": { + "launch-cache-not-in-app": { "cache": true, "data": { "launch-cache-key": "cache-specific-value", diff --git a/phase/analyzer.go b/phase/analyzer.go index 0217411b..256dac7a 100644 --- a/phase/analyzer.go +++ b/phase/analyzer.go @@ -6,7 +6,6 @@ import ( "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/image" - "github.com/buildpacks/lifecycle/internal/fsutil" "github.com/buildpacks/lifecycle/internal/layer" "github.com/buildpacks/lifecycle/log" "github.com/buildpacks/lifecycle/platform" @@ -43,7 +42,7 @@ func (f *ConnectedFactory) NewAnalyzer(inputs platform.LifecycleInputs, logger l } var err error - if analyzer.PreviousImage, err = f.getPreviousImage(inputs.PreviousImageRef, inputs.LaunchCacheDir); err != nil { + if analyzer.PreviousImage, err = f.getPreviousImage(inputs.PreviousImageRef, inputs.LaunchCacheDir, logger); err != nil { return nil, err } if analyzer.RunImage, err = f.getRunImage(inputs.RunImageRef); err != nil { @@ -88,7 +87,7 @@ func (a *Analyzer) Analyze() (files.Analyzed, error) { return files.Analyzed{}, errors.Wrap(err, "unpacking metadata from image") } if atm.OS == "" { - platform.GetTargetOSFromFileSystem(&fsutil.Detect{}, atm, a.Logger) + return files.Analyzed{}, errors.New("failed to find OS in run image config") } } } diff --git a/phase/analyzer_test.go b/phase/analyzer_test.go index 71f965f0..b9224bce 100644 --- a/phase/analyzer_test.go +++ b/phase/analyzer_test.go @@ -332,6 +332,7 @@ func testAnalyzer(platformAPI string) func(t *testing.T, when spec.G, it spec.S) it.Before(func() { var err error + discardLogger := log.Logger{Handler: &discard.Handler{}} tmpDir, err = os.MkdirTemp("", "analyzer-tests") h.AssertNil(t, err) @@ -342,15 +343,13 @@ func testAnalyzer(platformAPI string) func(t *testing.T, when spec.G, it spec.S) cacheDir, err = os.MkdirTemp("", "some-cache-dir") h.AssertNil(t, err) - testCache, err = cache.NewVolumeCache(cacheDir) + testCache, err = cache.NewVolumeCache(cacheDir, &discardLogger) h.AssertNil(t, err) previousImage = fakes.NewImage("image-repo-name", "", local.IDIdentifier{ ImageID: "s0m3D1g3sT", }) - discardLogger := log.Logger{Handler: &discard.Handler{}} - mockCtrl = gomock.NewController(t) sbomRestorer = testmock.NewMockSBOMRestorer(mockCtrl) @@ -484,6 +483,7 @@ func testAnalyzer(platformAPI string) func(t *testing.T, when spec.G, it spec.S) h.AssertEq(t, md.RunImage.Reference, "s0m3D1g3sT") }) + it("populates target metadata from the run image", func() { h.AssertNil(t, previousImage.SetLabel("io.buildpacks.base.id", "id software")) h.AssertNil(t, previousImage.SetOS("windows")) @@ -508,6 +508,18 @@ func testAnalyzer(platformAPI string) func(t *testing.T, when spec.G, it spec.S) h.AssertEq(t, md.RunImage.TargetMetadata.Distro.Version, "Helpful Holstein") } }) + + when("run image is missing OS", func() { + it("errors", func() { + h.AssertNil(t, previousImage.SetOS("")) + _, err := analyzer.Analyze() + if api.MustParse(platformAPI).LessThan("0.12") { + h.AssertNil(t, err) + } else { + h.AssertError(t, err, "failed to find OS") + } + }) + }) }) }) } diff --git a/phase/builder.go b/phase/builder.go index eae12dec..397b0378 100644 --- a/phase/builder.go +++ b/phase/builder.go @@ -149,7 +149,7 @@ func (b *Builder) getBuildInputs() buildpack.BuildInputs { LayersDir: b.LayersDir, PlatformDir: b.PlatformDir, Env: env.NewBuildEnv(os.Environ()), - TargetEnv: platform.EnvVarsFor(b.AnalyzeMD.RunImageTarget(), b.Logger), + TargetEnv: platform.EnvVarsFor(&fsutil.Detect{}, b.AnalyzeMD.RunImageTarget(), b.Logger), Out: b.Out, Err: b.Err, } diff --git a/phase/builder_test.go b/phase/builder_test.go index 04507307..3a197cce 100644 --- a/phase/builder_test.go +++ b/phase/builder_test.go @@ -189,10 +189,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { executor.EXPECT().Build(*bpA, gomock.Any(), gomock.Any()).DoAndReturn( func(_ buildpack.BpDescriptor, inputs buildpack.BuildInputs, logger llog.Logger) (buildpack.BuildOutputs, error) { h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH=amd64") - h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH_VARIANT=") h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_OS=linux") - h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_NAME=") - h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_VERSION=") return buildpack.BuildOutputs{}, nil }, ) @@ -203,10 +200,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { executor.EXPECT().Build(*bpB, gomock.Any(), gomock.Any()).Do( func(_ buildpack.BpDescriptor, inputs buildpack.BuildInputs, _ llog.Logger) (buildpack.BuildOutputs, error) { h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH=amd64") - h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH_VARIANT=") h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_OS=linux") - h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_NAME=") - h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_VERSION=") return buildpack.BuildOutputs{}, nil }) diff --git a/phase/cache.go b/phase/cache.go index 16685d97..4467df0d 100644 --- a/phase/cache.go +++ b/phase/cache.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" "github.com/buildpacks/lifecycle/buildpack" + c "github.com/buildpacks/lifecycle/cache" "github.com/buildpacks/lifecycle/layers" "github.com/buildpacks/lifecycle/log" "github.com/buildpacks/lifecycle/platform" @@ -100,7 +101,14 @@ func (e *Exporter) addOrReuseCacheLayer(cache Cache, layerDir LayerDir, previous if layer.Digest == previousSHA { e.Logger.Infof("Reusing cache layer '%s'\n", layer.ID) e.Logger.Debugf("Layer '%s' SHA: %s\n", layer.ID, layer.Digest) - return layer.Digest, cache.ReuseLayer(previousSHA) + err = cache.ReuseLayer(previousSHA) + if err != nil { + isReadErr, readErr := c.IsReadErr(err) + if !isReadErr { + return "", errors.Wrapf(err, "reusing layer %s", layer.ID) + } + e.Logger.Warnf("Skipping re-use for layer %s: %s", layer.ID, readErr.Error()) + } } e.Logger.Infof("Adding cache layer '%s'\n", layer.ID) e.Logger.Debugf("Layer '%s' SHA: %s\n", layer.ID, layer.Digest) diff --git a/phase/cache_test.go b/phase/cache_test.go index 03971e7c..bf73376a 100644 --- a/phase/cache_test.go +++ b/phase/cache_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/apex/log" + "github.com/apex/log/handlers/discard" "github.com/apex/log/handlers/memory" "github.com/golang/mock/gomock" "github.com/sclevine/spec" @@ -46,6 +47,10 @@ func testCache(t *testing.T, when spec.G, it spec.S) { mockCtrl = gomock.NewController(t) layerFactory = testmock.NewMockLayerFactory(mockCtrl) + logHandler = memory.New() + level, err := log.ParseLevel("info") + h.AssertNil(t, err) + tmpDir, err = os.MkdirTemp("", "lifecycle.cacher.layer") h.AssertNil(t, err) h.AssertNil(t, os.Mkdir(filepath.Join(tmpDir, "artifacts"), 0777)) @@ -53,11 +58,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { cacheDir = filepath.Join(tmpDir, "cache") h.AssertNil(t, os.Mkdir(cacheDir, 0777)) - testCache, err = cache.NewVolumeCache(cacheDir) - h.AssertNil(t, err) - - logHandler = memory.New() - level, err := log.ParseLevel("info") + testCache, err = cache.NewVolumeCache(cacheDir, &log.Logger{Handler: logHandler, Level: level}) h.AssertNil(t, err) exporter = &phase.Exporter{ @@ -300,13 +301,15 @@ func assertCacheHasLayer(t *testing.T, cache phase.Cache, id string) { } func initializeCache(t *testing.T, exporter *phase.Exporter, testCache *phase.Cache, cacheDir, layersDir, metadataTemplate string) { - previousCache, err := cache.NewVolumeCache(cacheDir) + logger := &log.Logger{Handler: &discard.Handler{}} + + previousCache, err := cache.NewVolumeCache(cacheDir, logger) h.AssertNil(t, err) err = exporter.Cache(layersDir, previousCache) h.AssertNil(t, err) - *testCache, err = cache.NewVolumeCache(cacheDir) + *testCache, err = cache.NewVolumeCache(cacheDir, logger) h.AssertNil(t, err) h.AssertNil(t, os.WriteFile( diff --git a/phase/connected_factory.go b/phase/connected_factory.go index 3514938a..083d4ca0 100644 --- a/phase/connected_factory.go +++ b/phase/connected_factory.go @@ -5,6 +5,8 @@ import ( "github.com/buildpacks/imgutil" + "github.com/buildpacks/lifecycle/log" + "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/cache" "github.com/buildpacks/lifecycle/image" @@ -58,7 +60,7 @@ func (f *ConnectedFactory) ensureRegistryAccess(inputs platform.LifecycleInputs) return nil } -func (f *ConnectedFactory) getPreviousImage(imageRef string, launchCacheDir string) (imgutil.Image, error) { +func (f *ConnectedFactory) getPreviousImage(imageRef string, launchCacheDir string, logger log.Logger) (imgutil.Image, error) { if imageRef == "" { return nil, nil } @@ -69,7 +71,7 @@ func (f *ConnectedFactory) getPreviousImage(imageRef string, launchCacheDir stri if launchCacheDir == "" || f.imageHandler.Kind() != image.LocalKind { return previousImage, nil } - volumeCache, err := cache.NewVolumeCache(launchCacheDir) + volumeCache, err := cache.NewVolumeCache(launchCacheDir, logger) if err != nil { return nil, fmt.Errorf("creating launch cache: %w", err) } diff --git a/phase/detector.go b/phase/detector.go index 4324fdc6..a4d3522a 100644 --- a/phase/detector.go +++ b/phase/detector.go @@ -13,6 +13,7 @@ import ( "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/env" "github.com/buildpacks/lifecycle/internal/encoding" + "github.com/buildpacks/lifecycle/internal/fsutil" "github.com/buildpacks/lifecycle/log" "github.com/buildpacks/lifecycle/platform" "github.com/buildpacks/lifecycle/platform/files" @@ -143,26 +144,14 @@ func (d *Detector) detectOrder(order buildpack.Order, done, next []buildpack.Gro return nil, nil, ErrFailedDetection } -// isWildcard returns true IFF the Arch and OS are unspecified, meaning that the target arch/os are "any" -func isWildcard(t files.TargetMetadata) bool { - return t.Arch == "" && t.OS == "" -} - -func hasWildcard(ts []buildpack.TargetMetadata) bool { - for _, tm := range ts { - if tm.OS == "" && tm.Arch == "" { - return true - } - } - return false -} - func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElement, wg *sync.WaitGroup) ([]buildpack.GroupElement, []files.BuildPlanEntry, error) { // used below to mark each item as "done" by appending it to the done list markDone := func(groupEl buildpack.GroupElement, descriptor buildpack.Descriptor) { done = append(done, groupEl.WithAPI(descriptor.API()).WithHomepage(descriptor.Homepage())) } + runImageTargetInfo := d.AnalyzeMD.RunImageTarget() + for i, groupEl := range group.Group { // Continue if element has already been processed. if hasIDForKind(done, groupEl.Kind(), groupEl.ID) { @@ -174,7 +163,7 @@ func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElem return d.detectOrder(groupEl.OrderExtensions, done, group.Group[i+1:], true, wg) } - // Lookup element in store. <-- "the store" is the directory where all the buildpacks are. + // Lookup element in store (the "store" is the directory where all the buildpacks are). var ( descriptor buildpack.Descriptor err error @@ -191,26 +180,31 @@ func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElem // FIXME: cyclical references lead to infinite recursion return d.detectOrder(order, done, group.Group[i+1:], groupEl.Optional, wg) } - descriptor = bpDescriptor // standardize the type so below we don't have to care whether it was an extension + descriptor = bpDescriptor // Standardize the type so below we don't have to care whether it is an extension. } else { descriptor, err = d.DirStore.LookupExt(groupEl.ID, groupEl.Version) if err != nil { return nil, nil, err } } + + // Check target compatibility. if d.PlatformAPI.AtLeast("0.12") { - targetMatch := false - if isWildcard(d.AnalyzeMD.RunImageTarget()) || hasWildcard(descriptor.TargetsList()) { + var targetMatch bool + if len(descriptor.TargetsList()) == 0 { + // This is actually just for tests. In practice, the lifecycle will infer a target with at least an OS + // when targets are missing from buildpack.toml. targetMatch = true } else { - for i := range descriptor.TargetsList() { - d.Logger.Debugf("Checking for match against descriptor: %s", descriptor.TargetsList()[i]) - if platform.TargetSatisfiedForBuild(*d.AnalyzeMD.RunImage.TargetMetadata, descriptor.TargetsList()[i]) { + for _, target := range descriptor.TargetsList() { + d.Logger.Debugf("Checking for match against descriptor: %s", target) + if platform.TargetSatisfiedForBuild(&fsutil.Detect{}, &runImageTargetInfo, target, d.Logger) { targetMatch = true break } } } + if !targetMatch && !groupEl.Optional { markDone(groupEl, descriptor) d.Runs.Store( @@ -219,7 +213,7 @@ func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElem Code: -1, Err: fmt.Errorf( "unable to satisfy target os/arch constraints; run image: %s, buildpack: %s", - encoding.ToJSONMaybe(d.AnalyzeMD.RunImage.TargetMetadata), + encoding.ToJSONMaybe(runImageTargetInfo), encoding.ToJSONMaybe(descriptor.TargetsList()), ), }) @@ -239,7 +233,7 @@ func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElem BuildConfigDir: d.BuildConfigDir, PlatformDir: d.PlatformDir, Env: env.NewBuildEnv(os.Environ()), - TargetEnv: platform.EnvVarsFor(d.AnalyzeMD.RunImageTarget(), d.Logger), + TargetEnv: platform.EnvVarsFor(&fsutil.Detect{}, runImageTargetInfo, d.Logger), } d.Runs.Store(key, d.Executor.Detect(descriptor, inputs, d.Logger)) // this is where we finally invoke bin/detect } diff --git a/phase/detector_test.go b/phase/detector_test.go index 6820ef37..ff429cd5 100644 --- a/phase/detector_test.go +++ b/phase/detector_test.go @@ -221,10 +221,7 @@ func testDetector(t *testing.T, when spec.G, it spec.S) { executor.EXPECT().Detect(bpA1, gomock.Any(), gomock.Any()).Do( func(_ buildpack.Descriptor, inputs buildpack.DetectInputs, _ log.Logger) buildpack.DetectOutputs { h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH=amd64") - h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH_VARIANT=") h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_OS=linux") - h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_NAME=") - h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_VERSION=") return buildpack.DetectOutputs{} }) diff --git a/phase/generator.go b/phase/generator.go index 7f6e5410..8a2106e8 100644 --- a/phase/generator.go +++ b/phase/generator.go @@ -151,7 +151,7 @@ func (g *Generator) getGenerateInputs() buildpack.GenerateInputs { BuildConfigDir: g.BuildConfigDir, PlatformDir: g.PlatformDir, Env: env.NewBuildEnv(os.Environ()), - TargetEnv: platform.EnvVarsFor(g.AnalyzedMD.RunImageTarget(), g.Logger), + TargetEnv: platform.EnvVarsFor(&fsutil.Detect{}, g.AnalyzedMD.RunImageTarget(), g.Logger), Out: g.Out, Err: g.Err, } diff --git a/phase/generator_test.go b/phase/generator_test.go index cd6895bf..df7ecb98 100644 --- a/phase/generator_test.go +++ b/phase/generator_test.go @@ -283,9 +283,6 @@ func testGenerator(t *testing.T, when spec.G, it spec.S) { func(d buildpack.ExtDescriptor, inputs buildpack.GenerateInputs, _ *log.Logger) (buildpack.GenerateOutputs, error) { h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH=amd64", - "CNB_TARGET_ARCH_VARIANT=", - "CNB_TARGET_DISTRO_NAME=", - "CNB_TARGET_DISTRO_VERSION=", "CNB_TARGET_OS=linux", ) return buildpack.GenerateOutputs{Dockerfiles: []buildpack.DockerfileInfo{{ExtensionID: d.Extension.ID, @@ -295,9 +292,6 @@ func testGenerator(t *testing.T, when spec.G, it spec.S) { func(d buildpack.ExtDescriptor, inputs buildpack.GenerateInputs, _ *log.Logger) (buildpack.GenerateOutputs, error) { h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH=amd64", - "CNB_TARGET_ARCH_VARIANT=", - "CNB_TARGET_DISTRO_NAME=", - "CNB_TARGET_DISTRO_VERSION=", "CNB_TARGET_OS=linux", ) return buildpack.GenerateOutputs{Dockerfiles: []buildpack.DockerfileInfo{{ExtensionID: d.Extension.ID, diff --git a/phase/restorer.go b/phase/restorer.go index 48b6da59..901c2812 100644 --- a/phase/restorer.go +++ b/phase/restorer.go @@ -6,6 +6,8 @@ import ( "github.com/pkg/errors" "golang.org/x/sync/errgroup" + c "github.com/buildpacks/lifecycle/cache" + "github.com/buildpacks/lifecycle/api" "github.com/buildpacks/lifecycle/buildpack" "github.com/buildpacks/lifecycle/internal/layer" @@ -36,7 +38,7 @@ func (r *Restorer) Restore(cache Cache) error { } if r.LayerMetadataRestorer == nil { - r.LayerMetadataRestorer = layer.NewDefaultMetadataRestorer(r.LayersDir, false, r.Logger) + r.LayerMetadataRestorer = layer.NewDefaultMetadataRestorer(r.LayersDir, false, r.Logger, r.PlatformAPI) } if r.SBOMRestorer == nil { @@ -98,7 +100,16 @@ func (r *Restorer) Restore(cache Cache) error { } else { r.Logger.Infof("Restoring data for %q from cache", bpLayer.Identifier()) g.Go(func() error { - return r.restoreCacheLayer(cache, cachedLayer.SHA) + err = r.restoreCacheLayer(cache, cachedLayer.SHA) + if err != nil { + isReadErr, readErr := c.IsReadErr(err) + if isReadErr { + r.Logger.Warnf("Skipping restore for layer %s: %s", bpLayer.Identifier(), readErr.Error()) + return nil + } + return errors.Wrapf(err, "restoring layer %s", bpLayer.Identifier()) + } + return nil }) } } diff --git a/phase/restorer_test.go b/phase/restorer_test.go index 61066c88..9f4b4a5e 100644 --- a/phase/restorer_test.go +++ b/phase/restorer_test.go @@ -54,6 +54,8 @@ func testRestorer(buildpackAPI, platformAPI string) func(t *testing.T, when spec it.Before(func() { var err error + logHandler = memory.New() + logger := log.Logger{Handler: logHandler, Level: log.DebugLevel} layersDir, err = os.MkdirTemp("", "lifecycle-layer-dir") h.AssertNil(t, err) @@ -61,13 +63,9 @@ func testRestorer(buildpackAPI, platformAPI string) func(t *testing.T, when spec cacheDir, err = os.MkdirTemp("", "") h.AssertNil(t, err) - testCache, err = cache.NewVolumeCache(cacheDir) + testCache, err = cache.NewVolumeCache(cacheDir, &logger) h.AssertNil(t, err) - logHandler = memory.New() - - logger := log.Logger{Handler: logHandler, Level: log.DebugLevel} - mockCtrl = gomock.NewController(t) sbomRestorer = testmock.NewMockSBOMRestorer(mockCtrl) if api.MustParse(platformAPI).AtLeast("0.8") { @@ -81,7 +79,7 @@ func testRestorer(buildpackAPI, platformAPI string) func(t *testing.T, when spec {ID: "buildpack.id", API: buildpackAPI}, {ID: "escaped/buildpack/id", API: buildpackAPI}, }, - LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(layersDir, skipLayers, &logger), + LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(layersDir, skipLayers, &logger, api.Platform.Latest()), SBOMRestorer: sbomRestorer, PlatformAPI: api.MustParse(platformAPI), } @@ -203,53 +201,7 @@ func testRestorer(buildpackAPI, platformAPI string) func(t *testing.T, when spec h.AssertNil(t, os.RemoveAll(layersDir)) h.AssertNil(t, os.Mkdir(layersDir, 0777)) - contents := fmt.Sprintf(`{ - "buildpacks": [ - { - "key": "buildpack.id", - "layers": { - "cache-false": { - "cache": false, - "sha": "%s" - }, - "cache-launch": { - "cache": true, - "launch": true, - "sha": "%s" - }, - "cache-only": { - "cache": true, - "data": { - "cache-only-key": "cache-only-val" - }, - "sha": "%s" - } - } - }, - { - "key": "nogroup.buildpack.id", - "layers": { - "some-layer": { - "cache": true, - "sha": "%s" - } - } - }, - { - "key": "escaped/buildpack/id", - "layers": { - "escaped-bp-layer": { - "cache": true, - "data": { - "escaped-bp-key": "escaped-bp-val" - }, - "sha": "%s" - } - } - } - ] -} -`, cacheFalseLayerSHA, cacheLaunchLayerSHA, cacheOnlyLayerSHA, noGroupLayerSHA, escapedLayerSHA) + contents := buildMetadata(cacheFalseLayerSHA, cacheLaunchLayerSHA, cacheOnlyLayerSHA, noGroupLayerSHA, escapedLayerSHA) err = os.WriteFile( filepath.Join(cacheDir, "committed", "io.buildpacks.lifecycle.cache.metadata"), @@ -348,6 +300,40 @@ func testRestorer(buildpackAPI, platformAPI string) func(t *testing.T, when spec }) }) + when("there is a cache-only layer referenced in metadata that does not exist", func() { + var nonExistentCacheLaunchLayerSHA string + + it.Before(func() { + nonExistentCacheLaunchLayerSHA = "some-made-up-sha" + contents := buildMetadata(cacheFalseLayerSHA, nonExistentCacheLaunchLayerSHA, cacheOnlyLayerSHA, noGroupLayerSHA, escapedLayerSHA) + + err := os.WriteFile( + filepath.Join(cacheDir, "committed", "io.buildpacks.lifecycle.cache.metadata"), + []byte(contents), + 0600, + ) + h.AssertNil(t, err) + + err = restorer.Restore(testCache) + h.AssertNil(t, err) + }) + + it("restores expected cache-only layer", func() { + got := h.MustReadFile(t, filepath.Join(layersDir, "buildpack.id", "cache-only", "file-from-cache-only-layer")) + want := "echo text from cache-only layer\n" + h.AssertEq(t, string(got), want) + }) + + it("keeps expected layer metadata", func() { + got := h.MustReadFile(t, filepath.Join(layersDir, "buildpack.id", "cache-only.toml")) + h.AssertEq(t, string(got), "[metadata]\n cache-only-key = \"cache-only-val\"\n") + }) + + it("skips restoring non-existent cache-launch layer", func() { + h.AssertPathDoesNotExist(t, filepath.Join(layersDir, "buildpack.id", "cache-launch")) + }) + }) + when("there is a cache=true layer not in cache", func() { it.Before(func() { var meta, sha string @@ -550,3 +536,53 @@ func TestWriteLayer(t *testing.T) { h.AssertPathDoesNotExist(t, filepath.Join(layersDir, "test-buildpack", "test-layer")) } + +func buildMetadata(cacheFalseLayerSHA string, cacheLaunchLayerSHA string, cacheOnlyLayerSHA string, noGroupLayerSHA string, escapedLayerSHA string) string { + return fmt.Sprintf(`{ + "buildpacks": [ + { + "key": "buildpack.id", + "layers": { + "cache-false": { + "cache": false, + "sha": "%s" + }, + "cache-launch": { + "cache": true, + "launch": true, + "sha": "%s" + }, + "cache-only": { + "cache": true, + "data": { + "cache-only-key": "cache-only-val" + }, + "sha": "%s" + } + } + }, + { + "key": "nogroup.buildpack.id", + "layers": { + "some-layer": { + "cache": true, + "sha": "%s" + } + } + }, + { + "key": "escaped/buildpack/id", + "layers": { + "escaped-bp-layer": { + "cache": true, + "data": { + "escaped-bp-key": "escaped-bp-val" + }, + "sha": "%s" + } + } + } + ] +} +`, cacheFalseLayerSHA, cacheLaunchLayerSHA, cacheOnlyLayerSHA, noGroupLayerSHA, escapedLayerSHA) +} diff --git a/phase/testmock/cache/image_deleter.go b/phase/testmock/cache/image_deleter.go index f4a6051e..39d6a4b5 100644 --- a/phase/testmock/cache/image_deleter.go +++ b/phase/testmock/cache/image_deleter.go @@ -34,6 +34,18 @@ func (m *MockImageDeleter) EXPECT() *MockImageDeleterMockRecorder { return m.recorder } +// DeleteImage mocks base method. +func (m *MockImageDeleter) DeleteImage(arg0 imgutil.Image) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DeleteImage", arg0) +} + +// DeleteImage indicates an expected call of DeleteImage. +func (mr *MockImageDeleterMockRecorder) DeleteImage(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteImage", reflect.TypeOf((*MockImageDeleter)(nil).DeleteImage), arg0) +} + // DeleteOrigImageIfDifferentFromNewImage mocks base method. func (m *MockImageDeleter) DeleteOrigImageIfDifferentFromNewImage(arg0, arg1 imgutil.Image) { m.ctrl.T.Helper() diff --git a/platform/files/run.go b/platform/files/run.go index 00fbe038..310448b8 100644 --- a/platform/files/run.go +++ b/platform/files/run.go @@ -19,3 +19,14 @@ func (r *Run) Contains(providedImage string) bool { } return false } + +// FindByRef return the RunImageForExport struct which contains the imageRef. +func (r *Run) FindByRef(imageRef string) RunImageForExport { + for _, i := range r.Images { + if i.Contains(imageRef) { + return i + } + } + + return RunImageForExport{} +} diff --git a/platform/run_image_test.go b/platform/run_image_test.go index 7a14e684..566b00cc 100644 --- a/platform/run_image_test.go +++ b/platform/run_image_test.go @@ -201,32 +201,6 @@ func testRunImage(t *testing.T, when spec.G, it spec.S) { }) }) - when(".EnvVarsFor", func() { - it("returns the right thing", func() { - tm := files.TargetMetadata{Arch: "pentium", ArchVariant: "mmx", ID: "my-id", OS: "linux", Distro: &files.OSDistro{Name: "nix", Version: "22.11"}} - observed := platform.EnvVarsFor(tm, nil) - h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch) - h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT="+tm.ArchVariant) - h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME="+tm.Distro.Name) - h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION="+tm.Distro.Version) - h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS) - h.AssertEq(t, len(observed), 5) - }) - - it("does not return the wrong thing", func() { - tm := files.TargetMetadata{Arch: "pentium", OS: "linux"} - observed := platform.EnvVarsFor(tm, nil) - h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch) - h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS) - // note: per the spec only the ID field is optional, so I guess the others should always be set: https://github.com/buildpacks/rfcs/blob/main/text/0096-remove-stacks-mixins.md#runtime-metadata - // the empty ones: - h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT=") - h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME=") - h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION=") - h.AssertEq(t, len(observed), 5) - }) - }) - when(".BestRunImageMirrorFor", func() { var ( stackMD *files.Stack diff --git a/platform/target_data.go b/platform/target_data.go index 26977d67..85c32f68 100644 --- a/platform/target_data.go +++ b/platform/target_data.go @@ -1,6 +1,9 @@ package platform import ( + "fmt" + "runtime" + "github.com/buildpacks/imgutil" "github.com/buildpacks/lifecycle/buildpack" @@ -9,29 +12,6 @@ import ( "github.com/buildpacks/lifecycle/platform/files" ) -// EnvVarsFor fulfills the prophecy set forth in https://github.com/buildpacks/rfcs/blob/b8abe33f2bdc58792acf0bd094dc4ce3c8a54dbb/text/0096-remove-stacks-mixins.md?plain=1#L97 -// by returning an array of "VARIABLE=value" strings suitable for inclusion in your environment or complete breakfast. -func EnvVarsFor(tm files.TargetMetadata, logger log.Logger) []string { - // we should always have os & arch, - // if they are not populated try to get target information from the build-time base image - if tm.OS == "" { - GetTargetOSFromFileSystem(&fsutil.Detect{}, &tm, logger) - } - ret := []string{ - "CNB_TARGET_OS=" + tm.OS, - "CNB_TARGET_ARCH=" + tm.Arch, - "CNB_TARGET_ARCH_VARIANT=" + tm.ArchVariant, - } - var distName, distVersion string - if tm.Distro != nil { - distName = tm.Distro.Name - distVersion = tm.Distro.Version - } - ret = append(ret, "CNB_TARGET_DISTRO_NAME="+distName) - ret = append(ret, "CNB_TARGET_DISTRO_VERSION="+distVersion) - return ret -} - func GetTargetMetadata(fromImage imgutil.Image) (*files.TargetMetadata, error) { tm := files.TargetMetadata{} var err error @@ -63,25 +43,20 @@ func GetTargetMetadata(fromImage imgutil.Image) (*files.TargetMetadata, error) { return &tm, nil } -// GetTargetOSFromFileSystem populates the target metadata you pass in if the information is available -// returns a boolean indicating whether it populated any data. -func GetTargetOSFromFileSystem(d fsutil.Detector, tm *files.TargetMetadata, logger log.Logger) { - if d.HasSystemdFile() { - contents, err := d.ReadSystemdFile() - if err != nil { - logger.Warnf("Encountered error trying to read /etc/os-release file: %s", err.Error()) - return - } - info := d.GetInfo(contents) - if info.Version != "" || info.Name != "" { - tm.OS = "linux" - tm.Distro = &files.OSDistro{Name: info.Name, Version: info.Version} - } - } -} - -// TargetSatisfiedForBuild treats empty fields as wildcards and returns true if all populated fields match. -func TargetSatisfiedForBuild(base files.TargetMetadata, module buildpack.TargetMetadata) bool { +// TargetSatisfiedForBuild modifies the provided target information for the base (run) image if distribution information is missing, +// by reading distribution information from /etc/os-release. +// OS, arch, and arch variant if not specified by at least one entity (image or module) will be treated as matches. +// If a module specifies distribution information, the image must also specify matching information. +func TargetSatisfiedForBuild(d fsutil.Detector, base *files.TargetMetadata, module buildpack.TargetMetadata, logger log.Logger) bool { + if base == nil { + base = &files.TargetMetadata{} + } + // ensure we have all available data + if base.Distro == nil { + logger.Info("target distro name/version labels not found, reading /etc/os-release file") + GetTargetOSFromFileSystem(d, base, logger) + } + // check matches if !matches(base.OS, module.OS) { return false } @@ -91,9 +66,13 @@ func TargetSatisfiedForBuild(base files.TargetMetadata, module buildpack.TargetM if !matches(base.ArchVariant, module.ArchVariant) { return false } - if base.Distro == nil || len(module.Distros) == 0 { + // check distro + if len(module.Distros) == 0 { return true } + if base.Distro == nil { + return false + } foundMatchingDist := false for _, modDist := range module.Distros { if matches(base.Distro.Name, modDist.Name) && matches(base.Distro.Version, modDist.Version) { @@ -111,6 +90,61 @@ func matches(target1, target2 string) bool { return target1 == target2 } +// GetTargetOSFromFileSystem populates the provided target metadata with information from /etc/os-release +// if it is available. +func GetTargetOSFromFileSystem(d fsutil.Detector, tm *files.TargetMetadata, logger log.Logger) { + if d.HasSystemdFile() { + if tm.OS == "" { + tm.OS = "linux" + } + if tm.Arch == "" { + tm.Arch = runtime.GOARCH // in a future world where we support cross platform builds, this should be removed + } + contents, err := d.ReadSystemdFile() + if err != nil { + logger.Warnf("Encountered error trying to read /etc/os-release file: %s", err.Error()) + return + } + info := d.GetInfo(contents) + if info.Version != "" || info.Name != "" { + tm.Distro = &files.OSDistro{Name: info.Name, Version: info.Version} + } + } +} + +// EnvVarsFor fulfills the prophecy set forth in https://github.com/buildpacks/rfcs/blob/b8abe33f2bdc58792acf0bd094dc4ce3c8a54dbb/text/0096-remove-stacks-mixins.md?plain=1#L97 +// by returning an array of "VARIABLE=value" strings suitable for inclusion in your environment or complete breakfast. +func EnvVarsFor(d fsutil.Detector, tm files.TargetMetadata, logger log.Logger) []string { + // we should always have os & arch, + // if they are not populated try to get target information from the build-time base image + if tm.Distro == nil { + logger.Info("target distro name/version labels not found, reading /etc/os-release file") + GetTargetOSFromFileSystem(d, &tm, logger) + } + // required + ret := []string{ + "CNB_TARGET_OS=" + tm.OS, + "CNB_TARGET_ARCH=" + tm.Arch, + } + // optional + var distName, distVersion string + if tm.Distro != nil { + distName = tm.Distro.Name + distVersion = tm.Distro.Version + } + ret = appendIfNotEmpty(ret, "CNB_TARGET_ARCH_VARIANT", tm.ArchVariant) + ret = appendIfNotEmpty(ret, "CNB_TARGET_DISTRO_NAME", distName) + ret = appendIfNotEmpty(ret, "CNB_TARGET_DISTRO_VERSION", distVersion) + return ret +} + +func appendIfNotEmpty(env []string, key, val string) []string { + if val == "" { + return env + } + return append(env, fmt.Sprintf("%s=%s", key, val)) +} + // TargetSatisfiedForRebase treats optional fields (ArchVariant and Distribution fields) as wildcards if empty, returns true if all populated fields match func TargetSatisfiedForRebase(t files.TargetMetadata, appTargetMetadata files.TargetMetadata) bool { if t.OS != appTargetMetadata.OS || t.Arch != appTargetMetadata.Arch { diff --git a/platform/target_data_test.go b/platform/target_data_test.go index cc9bd5dc..f5aa9ee6 100644 --- a/platform/target_data_test.go +++ b/platform/target_data_test.go @@ -2,6 +2,7 @@ package platform_test import ( "fmt" + "runtime" "testing" "github.com/apex/log" @@ -21,34 +22,61 @@ func TestTargetData(t *testing.T) { func testTargetData(t *testing.T, when spec.G, it spec.S) { when(".TargetSatisfiedForBuild", func() { - var baseTarget files.TargetMetadata + baseTarget := &files.TargetMetadata{OS: "Win95", Arch: "Pentium"} + d := mockDetector{ + contents: "this is just test contents really", + t: t, + HasFile: false, // by default, don't use info from /etc/os-release for these tests + } + when("base image data", func() { when("has os and arch", func() { - baseTarget = files.TargetMetadata{OS: "Win95", Arch: "Pentium"} - when("buildpack data", func() { when("has os and arch", func() { it("must match", func() { - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}), true) - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: "Win98", Arch: baseTarget.Arch}), false) - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: "Pentium MMX"}), false) + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}, &log.Logger{Handler: memory.New()}), true) + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: "Win98", Arch: baseTarget.Arch}, &log.Logger{Handler: memory.New()}), false) + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: "Pentium MMX"}, &log.Logger{Handler: memory.New()}), false) }) }) when("missing os and arch", func() { it("matches", func() { - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: "", Arch: ""}), true) + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: "", Arch: ""}, &log.Logger{Handler: memory.New()}), true) }) }) - when("has extra information", func() { - it("matches", func() { - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, ArchVariant: "MMX"}), true) - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{ + when("has distro information", func() { + it("does not match", func() { + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{ OS: baseTarget.OS, Arch: baseTarget.Arch, Distros: []buildpack.OSDistro{{Name: "a", Version: "2"}}, - }), true) + }, &log.Logger{Handler: memory.New()}), false) + }) + + when("/etc/os-release has information", func() { + it("must match", func() { + d := mockDetector{ + contents: "this is just test contents really", + t: t, + HasFile: true, + } + h.AssertEq( + t, + platform.TargetSatisfiedForBuild( + &d, + baseTarget, + buildpack.TargetMetadata{ + OS: baseTarget.OS, + Arch: baseTarget.Arch, + Distros: []buildpack.OSDistro{ + {Name: "opensesame", Version: "3.14"}, + }, + }, + &log.Logger{Handler: memory.New()}, + ), true) + }) }) }) }) @@ -59,14 +87,14 @@ func testTargetData(t *testing.T, when spec.G, it spec.S) { when("buildpack data", func() { when("has arch variant", func() { it("must match", func() { - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, ArchVariant: "some-arch-variant"}), true) - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, ArchVariant: "some-other-arch-variant"}), false) + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, ArchVariant: "some-arch-variant"}, &log.Logger{Handler: memory.New()}), true) + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, ArchVariant: "some-other-arch-variant"}, &log.Logger{Handler: memory.New()}), false) }) }) when("missing arch variant", func() { it("matches", func() { - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}), true) + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}, &log.Logger{Handler: memory.New()}), true) }) }) }) @@ -78,14 +106,14 @@ func testTargetData(t *testing.T, when spec.G, it spec.S) { when("buildpack data", func() { when("has distro information", func() { it("must match", func() { - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, Distros: []buildpack.OSDistro{{Name: "B", Version: "2"}, {Name: "A", Version: "1"}}}), true) - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, Distros: []buildpack.OSDistro{{Name: "g", Version: "2"}, {Name: "B", Version: "2"}}}), false) + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, Distros: []buildpack.OSDistro{{Name: "B", Version: "2"}, {Name: "A", Version: "1"}}}, &log.Logger{Handler: memory.New()}), true) + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, Distros: []buildpack.OSDistro{{Name: "g", Version: "2"}, {Name: "B", Version: "2"}}}, &log.Logger{Handler: memory.New()}), false) }) }) when("missing distro information", func() { it("matches", func() { - h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}), true) + h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}, &log.Logger{Handler: memory.New()}), true) }) }) }) @@ -94,6 +122,111 @@ func testTargetData(t *testing.T, when spec.G, it spec.S) { }) }) + when(".GetTargetOSFromFileSystem", func() { + it("populates appropriately", func() { + logr := &log.Logger{Handler: memory.New()} + tm := files.TargetMetadata{} + d := mockDetector{contents: "this is just test contents really", + t: t, + HasFile: true} + platform.GetTargetOSFromFileSystem(&d, &tm, logr) + h.AssertEq(t, "linux", tm.OS) + h.AssertEq(t, runtime.GOARCH, tm.Arch) + h.AssertEq(t, "opensesame", tm.Distro.Name) + h.AssertEq(t, "3.14", tm.Distro.Version) + }) + + it("doesn't populate if there's no file", func() { + logr := &log.Logger{Handler: memory.New()} + tm := files.TargetMetadata{} + d := mockDetector{contents: "in unit tests 2.0 the users will generate the content but we'll serve them ads", + t: t, + HasFile: false} + platform.GetTargetOSFromFileSystem(&d, &tm, logr) + h.AssertNil(t, tm.Distro) + }) + + it("doesn't populate if there's an error reading the file", func() { + logr := &log.Logger{Handler: memory.New()} + tm := files.TargetMetadata{} + d := mockDetector{contents: "contentment is the greatest wealth", + t: t, + HasFile: true, + ReadFileErr: fmt.Errorf("I'm sorry Dave, I don't even remember exactly what HAL says"), + } + platform.GetTargetOSFromFileSystem(&d, &tm, logr) + h.AssertNil(t, tm.Distro) + }) + }) + + when(".EnvVarsFor", func() { + it("returns the right thing", func() { + tm := files.TargetMetadata{Arch: "pentium", ArchVariant: "mmx", ID: "my-id", OS: "linux", Distro: &files.OSDistro{Name: "nix", Version: "22.11"}} + d := &mockDetector{ + contents: "this is just test contents really", + t: t, + HasFile: false, + } + observed := platform.EnvVarsFor(d, tm, &log.Logger{Handler: memory.New()}) + h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch) + h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT="+tm.ArchVariant) + h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME="+tm.Distro.Name) + h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION="+tm.Distro.Version) + h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS) + h.AssertEq(t, len(observed), 5) + }) + + it("returns the right thing from /etc/os-release", func() { + d := &mockDetector{ + contents: "this is just test contents really", + t: t, + HasFile: true, + } + tm := files.TargetMetadata{Arch: "pentium", ArchVariant: "mmx", ID: "my-id", OS: "linux", Distro: nil} + observed := platform.EnvVarsFor(d, tm, &log.Logger{Handler: memory.New()}) + h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch) + h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT="+tm.ArchVariant) + h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME=opensesame") + h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION=3.14") + h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS) + h.AssertEq(t, len(observed), 5) + }) + + it("does not return the wrong thing", func() { + tm := files.TargetMetadata{Arch: "pentium", OS: "linux"} + d := &mockDetector{ + contents: "this is just test contents really", + t: t, + HasFile: false, + } + observed := platform.EnvVarsFor(d, tm, &log.Logger{Handler: memory.New()}) + h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch) + h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS) + h.AssertEq(t, len(observed), 2) + }) + + when("optional vars are empty", func() { + it("omits them", func() { + tm := files.TargetMetadata{ + // required + OS: "linux", + Arch: "pentium", + // optional + ArchVariant: "", + Distro: &files.OSDistro{Name: "nix", Version: ""}, + ID: "", + } + d := &mockDetector{ + contents: "this is just test contents really", + t: t, + HasFile: false, + } + observed := platform.EnvVarsFor(d, tm, &log.Logger{Handler: memory.New()}) + h.AssertEq(t, len(observed), 3) + }) + }) + }) + when(".TargetSatisfiedForRebase", func() { var baseTarget files.TargetMetadata when("orig image data", func() { @@ -167,41 +300,6 @@ func testTargetData(t *testing.T, when spec.G, it spec.S) { }) }) }) - - when(".GetTargetOSFromFileSystem", func() { - it("populates appropriately", func() { - logr := &log.Logger{Handler: memory.New()} - tm := files.TargetMetadata{} - d := mockDetector{contents: "this is just test contents really", - t: t, - HasFile: true} - platform.GetTargetOSFromFileSystem(&d, &tm, logr) - h.AssertEq(t, "opensesame", tm.Distro.Name) - h.AssertEq(t, "3.14", tm.Distro.Version) - }) - - it("doesn't populate if there's no file", func() { - logr := &log.Logger{Handler: memory.New()} - tm := files.TargetMetadata{} - d := mockDetector{contents: "in unit tests 2.0 the users will generate the content but we'll serve them ads", - t: t, - HasFile: false} - platform.GetTargetOSFromFileSystem(&d, &tm, logr) - h.AssertNil(t, tm.Distro) - }) - - it("doesn't populate if there's an error reading the file", func() { - logr := &log.Logger{Handler: memory.New()} - tm := files.TargetMetadata{} - d := mockDetector{contents: "contentment is the greatest wealth", - t: t, - HasFile: true, - ReadFileErr: fmt.Errorf("I'm sorry Dave, I don't even remember exactly what HAL says"), - } - platform.GetTargetOSFromFileSystem(&d, &tm, logr) - h.AssertNil(t, tm.Distro) - }) - }) } type mockDetector struct {