Skip to content

Commit

Permalink
Parse lifecycle version as semver
Browse files Browse the repository at this point in the history
* this allows for versions in either x.x.x or vx.x.x format
* allows for version comparisons

Signed-off-by: Emily Casey <[email protected]>
Signed-off-by: Danny Joyce <[email protected]>
  • Loading branch information
ekcasey authored and Danny Joyce committed May 15, 2019
1 parent eea7a39 commit 14491be
Show file tree
Hide file tree
Showing 26 changed files with 1,465 additions and 35 deletions.
1 change: 1 addition & 0 deletions acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,7 @@ func isCommandRunning(cmd *exec.Cmd) bool {
return true
}

// FIXME : buf needs a mutex
func terminateAtStep(t *testing.T, cmd *exec.Cmd, buf *bytes.Buffer, pattern string) {
t.Helper()
var interruptSignal os.Signal
Expand Down
10 changes: 7 additions & 3 deletions build/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"sync"
"time"

"github.com/Masterminds/semver"
"github.com/docker/docker/client"
"github.com/google/go-containerregistry/pkg/name"
"github.com/pkg/errors"
Expand Down Expand Up @@ -75,10 +76,10 @@ func (l *Lifecycle) Execute(ctx context.Context, opts LifecycleOptions) error {
l.logger.Verbose("Build cache %s cleared", style.Symbol(buildCache.Name()))
}

if lifecycleVersion := l.builder.GetLifecycleVersion(); lifecycleVersion == "" {
if lifecycleVersion := l.builder.GetLifecycleVersion(); lifecycleVersion == nil {
l.logger.Verbose("Warning: lifecycle version unknown")
} else {
l.logger.Verbose("Executing lifecycle version %s", style.Symbol(lifecycleVersion))
l.logger.Verbose("Executing lifecycle version %s", style.Symbol(lifecycleVersion.String()))
}

l.logger.Verbose(style.Step("DETECTING"))
Expand Down Expand Up @@ -154,5 +155,8 @@ func randString(n int) string {
}

func (l *Lifecycle) supportsVolumeCache() bool {
return l.builder.GetLifecycleVersion() >= "0.2.0"
if l.builder.GetLifecycleVersion() == nil {
return false
}
return l.builder.GetLifecycleVersion().Compare(semver.MustParse("0.2.0")) >= 0
}
3 changes: 2 additions & 1 deletion builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/BurntSushi/toml"
"github.com/Masterminds/semver"
"github.com/buildpack/imgutil"
"github.com/pkg/errors"

Expand Down Expand Up @@ -82,7 +83,7 @@ func (b *Builder) Description() string {
return b.metadata.Description
}

func (b *Builder) GetLifecycleVersion() string {
func (b *Builder) GetLifecycleVersion() *semver.Version {
return b.metadata.Lifecycle.Version
}

Expand Down
20 changes: 17 additions & 3 deletions builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"path/filepath"
"testing"

"github.com/Masterminds/semver"
"github.com/buildpack/imgutil/fakes"
"github.com/fatih/color"
"github.com/sclevine/spec"
Expand Down Expand Up @@ -193,15 +194,15 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
when("#SetLifecycle", func() {
it.Before(func() {
h.AssertNil(t, subject.SetLifecycle(lifecycle.Metadata{
Version: "1.2.3",
Version: semver.MustParse("1.2.3"),
Dir: filepath.Join("testdata", "lifecycle"),
}))
h.AssertNil(t, subject.Save())
h.AssertEq(t, baseImage.IsSaved(), true)
})

it("should set the lifecycle version successfully", func() {
h.AssertEq(t, subject.GetLifecycleVersion(), "1.2.3")
h.AssertEq(t, subject.GetLifecycleVersion().String(), "1.2.3")
})

it("should add the lifecycle binaries as an image layer", func() {
Expand Down Expand Up @@ -244,6 +245,15 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
h.HasFileMode(0755),
)
})

it("sets the lifecycle version on the metadata", func() {
label, err := baseImage.Label("io.buildpacks.builder.metadata")
h.AssertNil(t, err)

var metadata builder.Metadata
h.AssertNil(t, json.Unmarshal([]byte(label), &metadata))
h.AssertEq(t, metadata.Lifecycle.Version.String(), "1.2.3")
})
})

when("#AddBuildpack", func() {
Expand Down Expand Up @@ -348,7 +358,10 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {

when("base image already has metadata", func() {
it.Before(func() {
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.builder.metadata", `{"buildpacks": [{"id": "prev.id"}], "groups": [{"buildpacks": [{"id": "prev.id"}]}], "stack": {"runImage": {"image": "prev/run", "mirrors": ["prev/mirror"]}}}`))
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.builder.metadata",
`{"buildpacks": [{"id": "prev.id"}], "groups": [{"buildpacks": [{"id": "prev.id"}]}], "stack": {"runImage": {"image": "prev/run", "mirrors": ["prev/mirror"]}}, "lifecycle": {"version": "6.6.6"}}`,
))

var err error
subject, err = builder.New(baseImage, "some/builder")
Expand Down Expand Up @@ -377,6 +390,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
h.AssertEq(t, metadata.Groups[0].Buildpacks[0].ID, "prev.id")
h.AssertEq(t, metadata.Stack.RunImage.Image, "prev/run")
h.AssertEq(t, metadata.Stack.RunImage.Mirrors[0], "prev/mirror")
h.AssertEq(t, subject.GetLifecycleVersion().String(), "6.6.6")

// adds new buildpack
h.AssertEq(t, metadata.Buildpacks[1].ID, "some-buildpack-id")
Expand Down
19 changes: 18 additions & 1 deletion create_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"

"github.com/Masterminds/semver"
"github.com/buildpack/imgutil"
"github.com/pkg/errors"

Expand All @@ -24,6 +25,11 @@ func (c *Client) CreateBuilder(ctx context.Context, opts CreateBuilderOptions) e
return errors.Wrap(err, "invalid builder config")
}

lifecycleVersion, err := processLifecycleVersion(opts.BuilderConfig.Lifecycle.Version)
if err != nil {
return errors.Wrap(err, "invalid builder config")
}

if err := c.validateRunImageConfig(ctx, opts); err != nil {
return err
}
Expand Down Expand Up @@ -70,7 +76,7 @@ func (c *Client) CreateBuilder(ctx context.Context, opts CreateBuilderOptions) e

builderImage.SetStackInfo(opts.BuilderConfig.Stack)

lifecycleMd, err := c.lifecycleFetcher.Fetch(opts.BuilderConfig.Lifecycle.Version, opts.BuilderConfig.Lifecycle.URI)
lifecycleMd, err := c.lifecycleFetcher.Fetch(lifecycleVersion, opts.BuilderConfig.Lifecycle.URI)
if err != nil {
return errors.Wrap(err, "fetching lifecycle")
}
Expand All @@ -82,6 +88,17 @@ func (c *Client) CreateBuilder(ctx context.Context, opts CreateBuilderOptions) e
return builderImage.Save()
}

func processLifecycleVersion(version string) (*semver.Version, error) {
if version == "" {
return nil, nil
}
v, err := semver.NewVersion(version)
if err != nil {
return nil, errors.Wrap(err, "lifecycle.version must be a valid semver")
}
return v, nil
}

func validateBuilderConfig(conf builder.Config) error {
if conf.Stack.ID == "" {
return errors.New("stack.id is required")
Expand Down
15 changes: 13 additions & 2 deletions create_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"runtime"
"testing"

"github.com/Masterminds/semver"
"github.com/buildpack/imgutil/fakes"
"github.com/fatih/color"
"github.com/golang/mock/gomock"
Expand Down Expand Up @@ -82,7 +83,11 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {

mockBPFetcher.EXPECT().FetchBuildpack(gomock.Any()).Return(bp, nil).AnyTimes()

mockLifecycleFetcher.EXPECT().Fetch(gomock.Any(), gomock.Any()).Return(lifecycle.Metadata{Dir: filepath.Join("testdata", "lifecycle"), Version: "3.4.5"}, nil).AnyTimes()
mockLifecycleFetcher.EXPECT().Fetch(gomock.Any(), gomock.Any()).
Return(lifecycle.Metadata{
Dir: filepath.Join("testdata", "lifecycle"),
Version: semver.MustParse("3.4.5"),
}, nil).AnyTimes()

logOut, logErr = &bytes.Buffer{}, &bytes.Buffer{}

Expand Down Expand Up @@ -150,6 +155,12 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
err := subject.CreateBuilder(context.TODO(), opts)
h.AssertError(t, err, "stack.run-image is required")
})

it("should fail when lifecycle version is not a semver", func() {
opts.BuilderConfig.Lifecycle.Version = "not-semver"
err := subject.CreateBuilder(context.TODO(), opts)
h.AssertError(t, err, "lifecycle.version must be a valid semver")
})
})

when("validating the run image config", func() {
Expand Down Expand Up @@ -201,7 +212,7 @@ func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
Optional: false,
}},
}})
h.AssertEq(t, builderImage.GetLifecycleVersion(), "3.4.5")
h.AssertEq(t, builderImage.GetLifecycleVersion().String(), "3.4.5")

layerTar, err := fakeBuildImage.FindLayerWithPath("/lifecycle")
h.AssertNil(t, err)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module github.com/buildpack/pack

require (
github.com/BurntSushi/toml v0.3.1
github.com/Masterminds/semver v1.4.2
github.com/buildpack/imgutil v0.0.0-20190509214933-76de939dfb34
github.com/buildpack/lifecycle v0.1.1-0.20190510142604-8612386b1cea
github.com/dgodd/dockerdial v1.0.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7O
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
Expand Down
7 changes: 6 additions & 1 deletion inspect_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ func (c *Client) InspectBuilder(name string, daemon bool) (*BuilderInfo, error)
localMirrors = runImageConfig.Mirrors
}

var lifecycleVersion string
if ver := bldr.GetLifecycleVersion(); ver != nil {
lifecycleVersion = ver.String()
}

return &BuilderInfo{
Description: bldr.Description(),
Stack: bldr.StackID,
Expand All @@ -56,6 +61,6 @@ func (c *Client) InspectBuilder(name string, daemon bool) (*BuilderInfo, error)
LocalRunImageMirrors: localMirrors,
Buildpacks: bldr.GetBuildpacks(),
Groups: bldr.GetOrder(),
LifecycleVersion: bldr.GetLifecycleVersion(),
LifecycleVersion: lifecycleVersion,
}, nil
}
4 changes: 2 additions & 2 deletions inspect_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func testInspectBuilder(t *testing.T, when spec.G, it spec.S) {
]
}
],
"lifecycle": {"version": "some-lifecycle-version"}
"lifecycle": {"version": "1.2.3"}
}`))
})

Expand Down Expand Up @@ -159,7 +159,7 @@ func testInspectBuilder(t *testing.T, when spec.G, it spec.S) {
it("sets the lifecycle version", func() {
builderInfo, err := client.InspectBuilder("some/builder", useDaemon)
h.AssertNil(t, err)
h.AssertEq(t, builderInfo.LifecycleVersion, "some-lifecycle-version")
h.AssertEq(t, builderInfo.LifecycleVersion, "1.2.3")
})
})
})
Expand Down
3 changes: 2 additions & 1 deletion interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pack
import (
"context"

"github.com/Masterminds/semver"
"github.com/buildpack/imgutil"

"github.com/buildpack/pack/buildpack"
Expand All @@ -24,5 +25,5 @@ type BuildpackFetcher interface {
//go:generate mockgen -package mocks -destination mocks/lifecycle_fetcher.go github.com/buildpack/pack LifecycleFetcher

type LifecycleFetcher interface {
Fetch(version, uri string) (lifecycle.Metadata, error)
Fetch(version *semver.Version, uri string) (lifecycle.Metadata, error)
}
11 changes: 6 additions & 5 deletions lifecycle/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import (
"io/ioutil"
"path/filepath"

"github.com/Masterminds/semver"
"github.com/pkg/errors"

"github.com/buildpack/pack/style"
)

const (
DefaultLifecycleVersion = "0.1.0"
DefaultLifecycleVersion = "0.2.0"
)

//go:generate mockgen -package mocks -destination mocks/downloader.go github.com/buildpack/pack/lifecycle Downloader
Expand All @@ -28,13 +29,13 @@ func NewFetcher(downloader Downloader) *Fetcher {
return &Fetcher{downloader: downloader}
}

func (f *Fetcher) Fetch(version, uri string) (Metadata, error) {
if version == "" && uri == "" {
version = DefaultLifecycleVersion
func (f *Fetcher) Fetch(version *semver.Version, uri string) (Metadata, error) {
if version == nil && uri == "" {
version = semver.MustParse(DefaultLifecycleVersion)
}

if uri == "" {
uri = fmt.Sprintf("https://github.com/buildpack/lifecycle/releases/download/v%s/lifecycle-v%s+linux.x86-64.tgz", version, version)
uri = fmt.Sprintf("https://github.com/buildpack/lifecycle/releases/download/v%s/lifecycle-v%s+linux.x86-64.tgz", version.String(), version.String())
}

downloadDir, err := f.downloader.Download(uri)
Expand Down
27 changes: 14 additions & 13 deletions lifecycle/fetcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"path/filepath"
"testing"

"github.com/Masterminds/semver"
"github.com/golang/mock/gomock"
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
Expand Down Expand Up @@ -43,9 +44,9 @@ func testFetcher(t *testing.T, when spec.G, it spec.S) {
Download("https://github.com/buildpack/lifecycle/releases/download/v1.2.3/lifecycle-v1.2.3+linux.x86-64.tgz").
Return(filepath.Join("testdata", "download-dir"), nil)

md, err := subject.Fetch("1.2.3", "")
md, err := subject.Fetch(semver.MustParse("1.2.3"), "")
h.AssertNil(t, err)
h.AssertEq(t, md.Version, "1.2.3")
h.AssertEq(t, md.Version.String(), "1.2.3")
h.AssertEq(t, md.Dir, filepath.Join("testdata", "download-dir", "fake-lifecycle"))
})
})
Expand All @@ -56,9 +57,9 @@ func testFetcher(t *testing.T, when spec.G, it spec.S) {
Download("https://lifecycle.example.com").
Return(filepath.Join("testdata", "download-dir"), nil)

md, err := subject.Fetch("", "https://lifecycle.example.com")
md, err := subject.Fetch(nil, "https://lifecycle.example.com")
h.AssertNil(t, err)
h.AssertEq(t, md.Version, "")
h.AssertNil(t, md.Version)
h.AssertEq(t, md.Dir, filepath.Join("testdata", "download-dir", "fake-lifecycle"))
})
})
Expand All @@ -69,22 +70,22 @@ func testFetcher(t *testing.T, when spec.G, it spec.S) {
Download("https://lifecycle.example.com").
Return(filepath.Join("testdata", "download-dir"), nil)

md, err := subject.Fetch("1.2.3", "https://lifecycle.example.com")
md, err := subject.Fetch(semver.MustParse("1.2.3"), "https://lifecycle.example.com")
h.AssertNil(t, err)
h.AssertEq(t, md.Version, "1.2.3")
h.AssertEq(t, md.Version.String(), "1.2.3")
h.AssertEq(t, md.Dir, filepath.Join("testdata", "download-dir", "fake-lifecycle"))
})
})

when("neither is uri nor version is provided", func() {
it("returns the default lifecycle", func() {
mockDownloader.EXPECT().
Download("https://github.com/buildpack/lifecycle/releases/download/v0.1.0/lifecycle-v0.1.0+linux.x86-64.tgz").
Download("https://github.com/buildpack/lifecycle/releases/download/v0.2.0/lifecycle-v0.2.0+linux.x86-64.tgz").
Return(filepath.Join("testdata", "download-dir"), nil)

md, err := subject.Fetch("", "")
md, err := subject.Fetch(nil, "")
h.AssertNil(t, err)
h.AssertEq(t, md.Version, "0.1.0")
h.AssertEq(t, md.Version.String(), "0.2.0")
h.AssertEq(t, md.Dir, filepath.Join("testdata", "download-dir", "fake-lifecycle"))
})
})
Expand All @@ -96,10 +97,10 @@ func testFetcher(t *testing.T, when spec.G, it spec.S) {
defer os.RemoveAll(tmp)

mockDownloader.EXPECT().
Download("https://github.com/buildpack/lifecycle/releases/download/v0.1.0/lifecycle-v0.1.0+linux.x86-64.tgz").
Download("https://github.com/buildpack/lifecycle/releases/download/v0.2.0/lifecycle-v0.2.0+linux.x86-64.tgz").
Return(tmp, nil)

_, err = subject.Fetch("", "")
_, err = subject.Fetch(nil, "")
h.AssertError(t, err, "invalid lifecycle")
})
})
Expand All @@ -115,10 +116,10 @@ func testFetcher(t *testing.T, when spec.G, it spec.S) {
h.AssertNil(t, ioutil.WriteFile(filepath.Join(tmp, "builder"), []byte("content"), os.ModePerm))

mockDownloader.EXPECT().
Download("https://github.com/buildpack/lifecycle/releases/download/v0.1.0/lifecycle-v0.1.0+linux.x86-64.tgz").
Download("https://github.com/buildpack/lifecycle/releases/download/v0.2.0/lifecycle-v0.2.0+linux.x86-64.tgz").
Return(tmp, nil)

_, err = subject.Fetch("", "")
_, err = subject.Fetch(nil, "")
h.AssertError(t, err, "invalid lifecycle")
})
})
Expand Down
Loading

0 comments on commit 14491be

Please sign in to comment.