Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,17 @@ func (c *Client) Build(ctx context.Context, opts BuildOptions) error {
defer c.docker.ImageRemove(context.Background(), ephemeralBuilder.Name(), types.ImageRemoveOptions{Force: true})

lcPlatformAPIVersion := ephemeralBuilder.LifecycleDescriptor().API.PlatformVersion
if !api.MustParse(build.PlatformAPIVersion).SupportsVersion(lcPlatformAPIVersion) {
return errors.Errorf(
"pack %s (Platform API version %s) is incompatible with builder %s (Platform API version %s)",
cmd.Version,
build.PlatformAPIVersion,
style.Symbol(opts.Builder),
lcPlatformAPIVersion,
)
supportsPlatform := false
for _, v := range build.SupportedPlatformAPIVersions {
if api.MustParse(v).SupportsVersion(lcPlatformAPIVersion) {
supportsPlatform = true
break
}
}
if !supportsPlatform {
c.logger.Debugf("pack %s supports Platform API version(s): %s", cmd.Version, strings.Join(build.SupportedPlatformAPIVersions, ", "))
c.logger.Debugf("Builder %s has Platform API version: %s", style.Symbol(opts.Builder), lcPlatformAPIVersion)
return errors.Errorf("Builder %s is incompatible with this version of pack", style.Symbol(opts.Builder))
}

return c.lifecycle.Execute(ctx, build.LifecycleOptions{
Expand Down
16 changes: 3 additions & 13 deletions build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ import (
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"

"github.com/buildpack/pack/cmd"
"github.com/buildpack/pack/internal/api"
"github.com/buildpack/pack/internal/blob"
"github.com/buildpack/pack/internal/build"
"github.com/buildpack/pack/internal/builder"
"github.com/buildpack/pack/internal/dist"
ifakes "github.com/buildpack/pack/internal/fakes"
Expand Down Expand Up @@ -97,7 +95,7 @@ func testBuild(t *testing.T, when spec.G, it spec.S) {
},
API: builder.LifecycleAPI{
BuildpackVersion: api.MustParse("0.3"),
PlatformVersion: api.MustParse(build.PlatformAPIVersion),
PlatformVersion: api.MustParse("0.2"),
},
},
},
Expand Down Expand Up @@ -360,7 +358,7 @@ func testBuild(t *testing.T, when spec.G, it spec.S) {
},
Lifecycle: builder.LifecycleMetadata{
API: builder.LifecycleAPI{
PlatformVersion: api.MustParse(build.PlatformAPIVersion),
PlatformVersion: api.MustParse("0.2"),
},
},
},
Expand Down Expand Up @@ -1037,15 +1035,7 @@ func testBuild(t *testing.T, when spec.G, it spec.S) {
Builder: builderName,
})

h.AssertError(t,
err,
fmt.Sprintf(
"pack %s (Platform API version %s) is incompatible with builder %s (Platform API version %s)",
cmd.Version,
build.PlatformAPIVersion,
style.Symbol(builderName),
"0.9",
))
h.AssertError(t, err, fmt.Sprintf("Builder %s is incompatible with this version of pack", style.Symbol(builderName)))
})
})
})
Expand Down
55 changes: 55 additions & 0 deletions create_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
mockDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/bp-one.tgz").Return(blob.NewBlob(filepath.Join("testdata", "buildpack")), nil).AnyTimes()
mockDownloader.EXPECT().Download(gomock.Any(), "some/buildpack/dir").Return(blob.NewBlob(filepath.Join("testdata", "buildpack")), nil).AnyTimes()
mockDownloader.EXPECT().Download(gomock.Any(), "file:///some-lifecycle").Return(blob.NewBlob(filepath.Join("testdata", "lifecycle")), nil).AnyTimes()
mockDownloader.EXPECT().Download(gomock.Any(), "file:///some-lifecycle-platform-0-1").Return(blob.NewBlob(filepath.Join("testdata", "lifecycle-platform-0.1")), nil).AnyTimes()

subject = &Client{
logger: logger,
Expand Down Expand Up @@ -302,6 +303,60 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {

it("should embed the lifecycle", func() {
h.AssertEq(t, bldr.LifecycleDescriptor().Info.Version.String(), "3.4.5")
h.AssertEq(t, bldr.LifecycleDescriptor().API.PlatformVersion.String(), "0.2")

layerTar, err := fakeBuildImage.FindLayerWithPath("/cnb/lifecycle")
h.AssertNil(t, err)
assertTarHasFile(t, layerTar, "/cnb/lifecycle/detector")
assertTarHasFile(t, layerTar, "/cnb/lifecycle/restorer")
assertTarHasFile(t, layerTar, "/cnb/lifecycle/analyzer")
assertTarHasFile(t, layerTar, "/cnb/lifecycle/builder")
assertTarHasFile(t, layerTar, "/cnb/lifecycle/exporter")
assertTarHasFile(t, layerTar, "/cnb/lifecycle/launcher")
})
})

when("creation succeeds for platform API < 0.2", func() {
var bldr *builder.Builder

it.Before(func() {
opts.BuilderConfig.Lifecycle = pubbldr.LifecycleConfig{URI: "file:///some-lifecycle-platform-0-1"}
err := subject.CreateBuilder(context.TODO(), opts)
h.AssertNil(t, err)
h.AssertEq(t, fakeBuildImage.IsSaved(), true)

bldr, err = builder.FromImage(fakeBuildImage)
h.AssertNil(t, err)
})

it("should set basic metadata", func() {
h.AssertEq(t, bldr.Name(), "some/builder")
h.AssertEq(t, bldr.Description(), "Some description")
h.AssertEq(t, bldr.UID, 1234)
h.AssertEq(t, bldr.GID, 4321)
h.AssertEq(t, bldr.StackID, "some.stack.id")
})

it("should set buildpack and order metadata", func() {
bpInfo := dist.BuildpackInfo{
ID: "bp.one",
Version: "1.2.3",
}
h.AssertEq(t, bldr.Buildpacks(), []builder.BuildpackMetadata{{
BuildpackInfo: bpInfo,
Latest: true,
}})
h.AssertEq(t, bldr.Order(), dist.Order{{
Group: []dist.BuildpackRef{{
BuildpackInfo: bpInfo,
Optional: false,
}},
}})
})

it("should embed the lifecycle", func() {
h.AssertEq(t, bldr.LifecycleDescriptor().Info.Version.String(), "3.4.5")
h.AssertEq(t, bldr.LifecycleDescriptor().API.PlatformVersion.String(), "0.1")

layerTar, err := fakeBuildImage.FindLayerWithPath("/cnb/lifecycle")
h.AssertNil(t, err)
Expand Down
80 changes: 53 additions & 27 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,31 @@ import (
"github.com/google/go-containerregistry/pkg/name"
"github.com/pkg/errors"

"github.com/buildpack/pack/internal/api"
"github.com/buildpack/pack/internal/builder"
"github.com/buildpack/pack/internal/cache"
"github.com/buildpack/pack/internal/style"
"github.com/buildpack/pack/logging"
)

// PlatformAPIVersion is the current Platform API Version supported by this version of pack.
const PlatformAPIVersion = "0.1"
var (
// SupportedPlatformAPIVersions lists the Platform API versions pack supports.
SupportedPlatformAPIVersions = []string{"0.1", "0.2"}
)

type Lifecycle struct {
builder *builder.Builder
logger logging.Logger
docker *client.Client
appPath string
appOnce *sync.Once
httpProxy string
httpsProxy string
noProxy string
version string
LayersVolume string
AppVolume string
builder *builder.Builder
logger logging.Logger
docker *client.Client
appPath string
appOnce *sync.Once
httpProxy string
httpsProxy string
noProxy string
version string
platformAPIVersion string
LayersVolume string
AppVolume string
}

type Cache interface {
Expand Down Expand Up @@ -59,6 +63,11 @@ type LifecycleOptions struct {
Network string
}

// CombinedExporterCacher returns true if the lifecycle contains combined exporter/cacher phases and reversed analyzer/restorer phases.
func (l *Lifecycle) CombinedExporterCacher() bool {
return api.MustParse(l.platformAPIVersion).Compare(api.MustParse("0.2")) >= 0
}

func (l *Lifecycle) Execute(ctx context.Context, opts LifecycleOptions) error {
l.Setup(opts)
defer l.Cleanup()
Expand All @@ -79,16 +88,30 @@ func (l *Lifecycle) Execute(ctx context.Context, opts LifecycleOptions) error {
return err
}

l.logger.Info(style.Step("RESTORING"))
if opts.ClearCache {
l.logger.Info("Skipping 'restore' due to clearing cache")
} else if err := l.Restore(ctx, buildCache.Name()); err != nil {
return err
}
if l.CombinedExporterCacher() {
l.logger.Info(style.Step("ANALYZING"))
if err := l.Analyze(ctx, opts.Image.Name(), buildCache.Name(), opts.Publish, opts.ClearCache); err != nil {
return err
}

l.logger.Info(style.Step("ANALYZING"))
if err := l.Analyze(ctx, opts.Image.Name(), opts.Publish, opts.ClearCache); err != nil {
return err
l.logger.Info(style.Step("RESTORING"))
if opts.ClearCache {
l.logger.Info("Skipping 'restore' due to clearing cache")
} else if err := l.Restore(ctx, buildCache.Name()); err != nil {
return err
}
} else {
l.logger.Info(style.Step("RESTORING"))
if opts.ClearCache {
l.logger.Info("Skipping 'restore' due to clearing cache")
} else if err := l.Restore(ctx, buildCache.Name()); err != nil {
return err
}

l.logger.Info(style.Step("ANALYZING"))
if err := l.Analyze(ctx, opts.Image.Name(), buildCache.Name(), opts.Publish, opts.ClearCache); err != nil {
return err
}
}

l.logger.Info(style.Step("BUILDING"))
Expand All @@ -97,15 +120,17 @@ func (l *Lifecycle) Execute(ctx context.Context, opts LifecycleOptions) error {
}

l.logger.Info(style.Step("EXPORTING"))
launchCacheName := launchCache.Name()
if err := l.Export(ctx, opts.Image.Name(), opts.RunImage, opts.Publish, launchCacheName); err != nil {
if err := l.Export(ctx, opts.Image.Name(), opts.RunImage, opts.Publish, launchCache.Name(), buildCache.Name()); err != nil {
return err
}

l.logger.Info(style.Step("CACHING"))
if err := l.Cache(ctx, buildCache.Name()); err != nil {
return err
if !l.CombinedExporterCacher() {
l.logger.Info(style.Step("CACHING"))
if err := l.Cache(ctx, buildCache.Name()); err != nil {
return err
}
}

return nil
}

Expand All @@ -119,6 +144,7 @@ func (l *Lifecycle) Setup(opts LifecycleOptions) {
l.httpsProxy = opts.HTTPSProxy
l.noProxy = opts.NoProxy
l.version = opts.Builder.LifecycleDescriptor().Info.Version.String()
l.platformAPIVersion = opts.Builder.LifecycleDescriptor().API.PlatformVersion.String()
}

func (l *Lifecycle) Cleanup() error {
Expand Down
46 changes: 31 additions & 15 deletions internal/build/phases.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,17 @@ func (l *Lifecycle) Detect(ctx context.Context, networkMode string) error {
}

func (l *Lifecycle) Restore(ctx context.Context, cacheName string) error {
cacheFlag := "-path"
if l.CombinedExporterCacher() {
cacheFlag = "-cache-dir"
}

restore, err := l.NewPhase(
"restorer",
WithDaemonAccess(),
WithArgs(
l.withLogLevel(
"-path", cacheDir,
cacheFlag, cacheDir,
"-layers", layersDir,
)...,
),
Expand All @@ -52,29 +57,32 @@ func (l *Lifecycle) Restore(ctx context.Context, cacheName string) error {
return restore.Run(ctx)
}

func (l *Lifecycle) Analyze(ctx context.Context, repoName string, publish, clearCache bool) error {
analyze, err := l.newAnalyze(repoName, publish, clearCache)
func (l *Lifecycle) Analyze(ctx context.Context, repoName, cacheName string, publish, clearCache bool) error {
analyze, err := l.newAnalyze(repoName, cacheName, publish, clearCache)
if err != nil {
return err
}
defer analyze.Cleanup()
return analyze.Run(ctx)
}

func (l *Lifecycle) newAnalyze(repoName string, publish, clearCache bool) (*Phase, error) {
func (l *Lifecycle) newAnalyze(repoName, cacheName string, publish, clearCache bool) (*Phase, error) {
args := []string{
"-layers", layersDir,
repoName,
}
if clearCache {
args = prependArg("-skip-layers", args)
} else if l.CombinedExporterCacher() {
args = append([]string{"-cache-dir", cacheDir}, args...)
}

if publish {
return l.NewPhase(
"analyzer",
WithRegistryAccess(repoName),
WithArgs(args...),
WithBinds(fmt.Sprintf("%s:%s", cacheName, cacheDir)),
)
}
return l.NewPhase(
Expand All @@ -88,6 +96,7 @@ func (l *Lifecycle) newAnalyze(repoName string, publish, clearCache bool) (*Phas
)...,
)...,
),
WithBinds(fmt.Sprintf("%s:%s", cacheName, cacheDir)),
)
}

Expand All @@ -112,16 +121,16 @@ func (l *Lifecycle) Build(ctx context.Context, networkMode string) error {
return build.Run(ctx)
}

func (l *Lifecycle) Export(ctx context.Context, repoName string, runImage string, publish bool, launchCacheName string) error {
export, err := l.newExport(repoName, runImage, publish, launchCacheName)
func (l *Lifecycle) Export(ctx context.Context, repoName string, runImage string, publish bool, launchCacheName, cacheName string) error {
export, err := l.newExport(repoName, runImage, publish, launchCacheName, cacheName)
if err != nil {
return err
}
defer export.Cleanup()
return export.Run(ctx)
}

func (l *Lifecycle) newExport(repoName, runImage string, publish bool, launchCacheName string) (*Phase, error) {
func (l *Lifecycle) newExport(repoName, runImage string, publish bool, launchCacheName, cacheName string) (*Phase, error) {
if publish {
return l.NewPhase(
"exporter",
Expand All @@ -137,23 +146,30 @@ func (l *Lifecycle) newExport(repoName, runImage string, publish bool, launchCac
)
}

args := []string{
"-image", runImage,
"-layers", layersDir,
"-app", appDir,
"-daemon",
"-launch-cache", launchCacheDir,
repoName,
}
if l.CombinedExporterCacher() {
args = append([]string{"-cache-dir", cacheDir}, args...)
}

return l.NewPhase(
"exporter",
WithDaemonAccess(),
WithArgs(
l.withLogLevel(
"-image", runImage,
"-layers", layersDir,
"-app", appDir,
"-daemon",
"-launch-cache", launchCacheDir,
repoName,
)...,
l.withLogLevel(args...)...,
),
WithBinds(fmt.Sprintf("%s:%s", launchCacheName, launchCacheDir)),
WithBinds(fmt.Sprintf("%s:%s", cacheName, cacheDir)),
)
}

// The cache phase is obsolete with Platform API 0.2 and will be removed in the future.
func (l *Lifecycle) Cache(ctx context.Context, cacheName string) error {
cache, err := l.NewPhase(
"cacher",
Expand Down
Loading