Skip to content

Commit

Permalink
Dynamically determine valid lifecycle
Browse files Browse the repository at this point in the history
Signed-off-by: Johannes Dillmann <[email protected]>
  • Loading branch information
modulo11 committed Jan 31, 2025
1 parent 5cca9c5 commit bc08e37
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 14 deletions.
38 changes: 24 additions & 14 deletions pkg/client/create_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

pubbldr "github.com/buildpacks/pack/builder"
"github.com/buildpacks/pack/internal/builder"
"github.com/buildpacks/pack/internal/config"
"github.com/buildpacks/pack/internal/paths"
"github.com/buildpacks/pack/internal/style"
"github.com/buildpacks/pack/pkg/buildpack"
Expand Down Expand Up @@ -284,14 +285,20 @@ func (c *Client) fetchLifecycle(ctx context.Context, config pubbldr.LifecycleCon
return nil, errors.Wrapf(err, "%s must be a valid semver", style.Symbol("lifecycle.version"))
}

uri = c.uriFromLifecycleVersion(*v, os, architecture)
uri, err = c.uriFromLifecycleVersion(*v, os, architecture)
if err != nil {
return nil, errors.Wrap(err, "determine lifecycle")
}
case config.URI != "":
uri, err = paths.FilePathToURI(config.URI, relativeBaseDir)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "determine lifecycle")
}
default:
uri = c.uriFromLifecycleVersion(*semver.MustParse(builder.DefaultLifecycleVersion), os, architecture)
uri, err = c.uriFromLifecycleVersion(*semver.MustParse(builder.DefaultLifecycleVersion), os, architecture)
if err != nil {
return nil, errors.Wrap(err, "determine lifecycle")
}
}

blob, err := c.downloader.Download(ctx, uri)
Expand Down Expand Up @@ -434,19 +441,22 @@ func validateModule(kind string, module buildpack.BuildModule, source, expectedI
return nil
}

func (c *Client) uriFromLifecycleVersion(version semver.Version, os string, architecture string) string {
arch := "x86-64"
func (c *Client) uriFromLifecycleVersion(version semver.Version, os string, architecture string) (string, error) {
image, _ := c.indexFactory.FetchIndex(config.DefaultLifecycleImageRepo, imgutil.FromBaseIndex(config.DefaultLifecycleImageRepo))
manifest, _ := image.IndexManifest()

Check failure on line 446 in pkg/client/create_builder.go

View workflow job for this annotation

GitHub Actions / test (linux)

image.IndexManifest undefined (type imgutil.ImageIndex has no field or method IndexManifest)) (typecheck)

Check failure on line 446 in pkg/client/create_builder.go

View workflow job for this annotation

GitHub Actions / test (linux)

image.IndexManifest undefined (type imgutil.ImageIndex has no field or method IndexManifest)) (typecheck)

Check failure on line 446 in pkg/client/create_builder.go

View workflow job for this annotation

GitHub Actions / test (linux)

image.IndexManifest undefined (type imgutil.ImageIndex has no field or method IndexManifest)) (typecheck)

Check failure on line 446 in pkg/client/create_builder.go

View workflow job for this annotation

GitHub Actions / test (linux)

image.IndexManifest undefined (type imgutil.ImageIndex has no field or method IndexManifest) (typecheck)

Check failure on line 446 in pkg/client/create_builder.go

View workflow job for this annotation

GitHub Actions / test (linux)

image.IndexManifest undefined (type imgutil.ImageIndex has no field or method IndexManifest)) (typecheck)

Check failure on line 446 in pkg/client/create_builder.go

View workflow job for this annotation

GitHub Actions / test (macos)

image.IndexManifest undefined (type imgutil.ImageIndex has no field or method IndexManifest)) (typecheck)

Check failure on line 446 in pkg/client/create_builder.go

View workflow job for this annotation

GitHub Actions / test (macos)

image.IndexManifest undefined (type imgutil.ImageIndex has no field or method IndexManifest)) (typecheck)

Check failure on line 446 in pkg/client/create_builder.go

View workflow job for this annotation

GitHub Actions / test (macos)

image.IndexManifest undefined (type imgutil.ImageIndex has no field or method IndexManifest)) (typecheck)

Check failure on line 446 in pkg/client/create_builder.go

View workflow job for this annotation

GitHub Actions / test (macos)

image.IndexManifest undefined (type imgutil.ImageIndex has no field or method IndexManifest) (typecheck)

Check failure on line 446 in pkg/client/create_builder.go

View workflow job for this annotation

GitHub Actions / test (macos)

image.IndexManifest undefined (type imgutil.ImageIndex has no field or method IndexManifest)) (typecheck)

if os == "windows" {
return fmt.Sprintf("https://github.com/buildpacks/lifecycle/releases/download/v%s/lifecycle-v%s+windows.%s.tgz", version.String(), version.String(), arch)
for _, m := range manifest.Manifests {
if m.Platform.OS == os && m.Platform.Architecture == architecture {
return lifecycleDownloadURL(version, os, architecture), nil
}
}

if builder.SupportedLinuxArchitecture(architecture) {
arch = architecture
} else {
// FIXME: this should probably be an error case in the future, see https://github.com/buildpacks/pack/issues/2163
c.logger.Warnf("failed to find a lifecycle binary for requested architecture %s, defaulting to %s", style.Symbol(architecture), style.Symbol(arch))
}
return "", fmt.Errorf("could not determine lifecyle, unsupported os/arch: %s/%s", os, architecture)
}

return fmt.Sprintf("https://github.com/buildpacks/lifecycle/releases/download/v%s/lifecycle-v%s+linux.%s.tgz", version.String(), version.String(), arch)
func lifecycleDownloadURL(version semver.Version, os, architecture string) string {
if architecture == "amd64" {
architecture = "x86-64"
}
return fmt.Sprintf("https://github.com/buildpacks/lifecycle/releases/download/v%s/lifecycle-v%s+%s.%s.tgz", version.String(), version.String(), os, architecture)
}
41 changes: 41 additions & 0 deletions pkg/client/create_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/buildpacks/lifecycle/api"
"github.com/docker/docker/api/types/system"
"github.com/golang/mock/gomock"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/heroku/color"
"github.com/pkg/errors"
"github.com/sclevine/spec"
Expand Down Expand Up @@ -49,10 +50,12 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
mockBuildpackDownloader *testmocks.MockBuildpackDownloader
mockImageFactory *testmocks.MockImageFactory
mockImageFetcher *testmocks.MockImageFetcher
mockIndexFactory *testmocks.MockIndexFactory
mockDockerClient *testmocks.MockCommonAPIClient
fakeBuildImage *fakes.Image
fakeRunImage *fakes.Image
fakeRunImageMirror *fakes.Image
fakeLifecycleImage *fakes.ImageIndex

Check failure on line 58 in pkg/client/create_builder_test.go

View workflow job for this annotation

GitHub Actions / test (linux)

undefined: fakes.ImageIndex (typecheck)

Check failure on line 58 in pkg/client/create_builder_test.go

View workflow job for this annotation

GitHub Actions / test (macos)

undefined: fakes.ImageIndex (typecheck)
opts client.CreateBuilderOptions
subject *client.Client
logger logging.Logger
Expand All @@ -68,6 +71,10 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", gomock.Any()).Return(fakeBuildImage, nil)
}

var prepareIndexFetcherWithLifecycleImage = func() {
mockIndexFactory.EXPECT().FetchIndex(gomock.Any(), gomock.Any()).Return(fakeLifecycleImage, nil)
}

var createBuildpack = func(descriptor dist.BuildpackDescriptor) buildpack.BuildModule {
buildpack, err := ifakes.NewFakeBuildpack(descriptor, 0644)
h.AssertNil(t, err)
Expand All @@ -89,6 +96,7 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
mockDownloader = testmocks.NewMockBlobDownloader(mockController)
mockImageFetcher = testmocks.NewMockImageFetcher(mockController)
mockImageFactory = testmocks.NewMockImageFactory(mockController)
mockIndexFactory = testmocks.NewMockIndexFactory(mockController)
mockDockerClient = testmocks.NewMockCommonAPIClient(mockController)
mockBuildpackDownloader = testmocks.NewMockBuildpackDownloader(mockController)

Expand All @@ -101,6 +109,29 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
fakeRunImage = fakes.NewImage("some/run-image", "", nil)
h.AssertNil(t, fakeRunImage.SetLabel("io.buildpacks.stack.id", "some.stack.id"))

fakeLifecycleImage = &fakes.ImageIndex{
Manifests: []v1.Descriptor{
{
Platform: &v1.Platform{
OS: "linux",
Architecture: "amd64",
},
},
{
Platform: &v1.Platform{
OS: "linux",
Architecture: "arm64",
},
},
{
Platform: &v1.Platform{
OS: "windows",
Architecture: "amd64",
},
},
},
}

fakeRunImageMirror = fakes.NewImage("localhost:5000/some/run-image", "", nil)
h.AssertNil(t, fakeRunImageMirror.SetLabel("io.buildpacks.stack.id", "some.stack.id"))

Expand All @@ -124,6 +155,7 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
client.WithDownloader(mockDownloader),
client.WithImageFactory(mockImageFactory),
client.WithFetcher(mockImageFetcher),
client.WithIndexFactory(mockIndexFactory),
client.WithDockerClient(mockDockerClient),
client.WithBuildpackDownloader(mockBuildpackDownloader),
)
Expand Down Expand Up @@ -452,6 +484,7 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
client.WithDownloader(mockDownloader),
client.WithImageFactory(mockImageFactory),
client.WithFetcher(mockImageFetcher),
client.WithIndexFactory(mockIndexFactory),
client.WithExperimental(true),
)
h.AssertNil(t, err)
Expand Down Expand Up @@ -516,6 +549,7 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
it("should download from predetermined uri", func() {
prepareFetcherWithBuildImage()
prepareFetcherWithRunImages()
prepareIndexFetcherWithLifecycleImage()
opts.Config.Lifecycle.URI = ""
opts.Config.Lifecycle.Version = "3.4.5"

Expand All @@ -533,6 +567,7 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
it("should download from predetermined uri for arm64", func() {
prepareFetcherWithBuildImage()
prepareFetcherWithRunImages()
prepareIndexFetcherWithLifecycleImage()
opts.Config.Lifecycle.URI = ""
opts.Config.Lifecycle.Version = "3.4.5"
h.AssertNil(t, fakeBuildImage.SetArchitecture("arm64"))
Expand All @@ -557,12 +592,14 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
client.WithDownloader(mockDownloader),
client.WithImageFactory(mockImageFactory),
client.WithFetcher(mockImageFetcher),
client.WithIndexFactory(mockIndexFactory),
client.WithExperimental(true),
)
h.AssertNil(t, err)

prepareFetcherWithBuildImage()
prepareFetcherWithRunImages()
prepareIndexFetcherWithLifecycleImage()
opts.Config.Lifecycle.URI = ""
opts.Config.Lifecycle.Version = "3.4.5"
h.AssertNil(t, fakeBuildImage.SetOS("windows"))
Expand All @@ -584,6 +621,7 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
it("should download default lifecycle", func() {
prepareFetcherWithBuildImage()
prepareFetcherWithRunImages()
prepareIndexFetcherWithLifecycleImage()
opts.Config.Lifecycle.URI = ""
opts.Config.Lifecycle.Version = ""

Expand All @@ -605,6 +643,7 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
it("should download default lifecycle on arm64", func() {
prepareFetcherWithBuildImage()
prepareFetcherWithRunImages()
prepareIndexFetcherWithLifecycleImage()
opts.Config.Lifecycle.URI = ""
opts.Config.Lifecycle.Version = ""
h.AssertNil(t, fakeBuildImage.SetArchitecture("arm64"))
Expand Down Expand Up @@ -633,12 +672,14 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
client.WithDownloader(mockDownloader),
client.WithImageFactory(mockImageFactory),
client.WithFetcher(mockImageFetcher),
client.WithIndexFactory(mockIndexFactory),
client.WithExperimental(true),
)
h.AssertNil(t, err)

prepareFetcherWithBuildImage()
prepareFetcherWithRunImages()
prepareIndexFetcherWithLifecycleImage()
opts.Config.Lifecycle.URI = ""
opts.Config.Lifecycle.Version = ""
h.AssertNil(t, fakeBuildImage.SetOS("windows"))
Expand Down

0 comments on commit bc08e37

Please sign in to comment.