Skip to content

Commit

Permalink
Allow rebase to use run image mirrors
Browse files Browse the repository at this point in the history
* create-builder writes stack.toml with run image and mirrors metadata

[buildpacks/roadmap#48]

Signed-off-by: Danny Joyce <djoyce@pivotal.io>
Signed-off-by: Andrew Meyer <ameyer@pivotal.io>
Signed-off-by: Emily Casey <ecasey@pivotal.io>
  • Loading branch information
Danny Joyce authored and ameyer-pivotal committed Mar 27, 2019
1 parent b265540 commit 078292e
Show file tree
Hide file tree
Showing 15 changed files with 287 additions and 150 deletions.
18 changes: 11 additions & 7 deletions acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"testing"
"time"

"github.com/buildpack/lifecycle"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/go-connections/nat"
Expand Down Expand Up @@ -158,9 +159,9 @@ func testAcceptance(t *testing.T, when spec.G, it spec.S) {
t.Log("it uses the default run image as a base image")
assertHasBase(t, repoName, h.DefaultRunImage(t, registryConfig.RunRegistryPort))

t.Log("sets the run image label")
runImageLabel := imageLabel(t, dockerCli, repoName, "io.buildpacks.run-image")
h.AssertEq(t, runImageLabel, h.DefaultRunImage(t, registryConfig.RunRegistryPort))
t.Log("sets the run image metadata")
runImageLabel := imageLabel(t, dockerCli, repoName, lifecycle.MetadataLabel)
h.AssertContains(t, runImageLabel, fmt.Sprintf(`"stack":{"runImage":{"image":"%s"}}}`,h.DefaultRunImage(t, registryConfig.RunRegistryPort)))

t.Log("registry is empty")
contents, err := registryConfig.RegistryCatalog()
Expand Down Expand Up @@ -665,8 +666,11 @@ func testAcceptance(t *testing.T, when spec.G, it spec.S) {

h.CreateImageOnLocal(t, dockerCli, builderName, fmt.Sprintf(`
FROM %s
LABEL %s="{\"runImage\": {\"image\": \"%s\"}}"
`, h.DefaultBuilderImage(t, registryConfig.RunRegistryPort), builder.MetadataLabel, runImage))
LABEL %s="{\"stack\":{\"runImage\": {\"image\": \"%s\"}}}"
USER root
RUN echo "[run-image]\n image=\"%s\"" > /buildpacks/stack.toml
USER pack
`, h.DefaultBuilderImage(t, registryConfig.RunRegistryPort), builder.MetadataLabel, runImage, runImage))

cmd := packCmd(
"build", repoName,
Expand Down Expand Up @@ -837,14 +841,14 @@ func testAcceptance(t *testing.T, when spec.G, it spec.S) {
builderImageName := h.CreateImageOnRemote(t, dockerCli, registryConfig, "some/builder",
fmt.Sprintf(`
FROM scratch
LABEL %s="{\"runImage\":{\"image\":\"some/run1\",\"mirrors\":[\"gcr.io/some/run1\"]},\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\",\"latest\":false},{\"id\":\"test.bp.two\",\"version\":\"0.0.2\",\"latest\":true}],\"groups\":[{\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\"},{\"id\":\"test.bp.two\",\"version\":\"0.0.2\"}]},{\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\"}]}]}"
LABEL %s="{\"stack\":{\"runImage\":{\"image\":\"some/run1\",\"mirrors\":[\"gcr.io/some/run1\"]}},\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\",\"latest\":false},{\"id\":\"test.bp.two\",\"version\":\"0.0.2\",\"latest\":true}],\"groups\":[{\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\"},{\"id\":\"test.bp.two\",\"version\":\"0.0.2\"}]},{\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\"}]}]}"
LABEL io.buildpacks.stack.id=some.test.stack
`, builder.MetadataLabel))

h.CreateImageOnLocal(t, dockerCli, builderImageName,
fmt.Sprintf(`
FROM scratch
LABEL %s="{\"runImage\":{\"image\":\"some/run1\",\"mirrors\":[\"gcr.io/some/run2\"]},\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\",\"latest\":false},{\"id\":\"test.bp.two\",\"version\":\"0.0.2\",\"latest\":true}],\"groups\":[{\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\"},{\"id\":\"test.bp.two\",\"version\":\"0.0.2\"}]},{\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\"}]}]}"
LABEL %s="{\"stack\":{\"runImage\":{\"image\":\"some/run1\",\"mirrors\":[\"gcr.io/some/run2\"]}},\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\",\"latest\":false},{\"id\":\"test.bp.two\",\"version\":\"0.0.2\",\"latest\":true}],\"groups\":[{\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\"},{\"id\":\"test.bp.two\",\"version\":\"0.0.2\"}]},{\"buildpacks\":[{\"id\":\"test.bp.one\",\"version\":\"0.0.1\"}]}]}"
LABEL io.buildpacks.stack.id=some.test.stack
`, builder.MetadataLabel))
defer h.DockerRmi(dockerCli, builderImageName)
Expand Down
44 changes: 18 additions & 26 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,11 @@ type BuildFlags struct {
}

type BuildConfig struct {
Builder string
RunImage string
RepoName string
Publish bool
ClearCache bool
LocallyConfiguredRunImage bool
Builder string
RunImage string
RepoName string
Publish bool
ClearCache bool
// Above are copied from BuildFlags are set by init
Cli Docker
Logger *logging.Logger
Expand Down Expand Up @@ -187,9 +186,8 @@ func (bf *BuildFactory) BuildConfigFromFlags(ctx context.Context, f *BuildFlags)
if f.RunImage != "" {
bf.Logger.Verbose("Using user-provided run image %s", style.Symbol(f.RunImage))
b.RunImage = f.RunImage
b.LocallyConfiguredRunImage = true
} else {
b.RunImage, b.LocallyConfiguredRunImage, err = builderImage.GetRunImageByRepoName(f.RepoName)
b.RunImage, err = builderImage.GetRunImageByRepoName(f.RepoName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -426,10 +424,6 @@ type exporterArgs struct {
repoName string
}

func (e *exporterArgs) label(s string) {
e.args = append(e.args, "-label", s)
}

func (e *exporterArgs) add(args ...string) {
e.args = append(e.args, args...)
}
Expand All @@ -444,18 +438,17 @@ func (e *exporterArgs) list() []string {
}

func (b *BuildConfig) export(ctx context.Context, lifecycle *build.Lifecycle) error {
var export *build.Phase
var err error
var (
export *build.Phase
err error
)

args := &exporterArgs{repoName: b.RepoName}

args.add("-image", b.RunImage,
args.add(
"-image", b.RunImage,
"-layers", launchDir,
"-group", groupPath)

if !b.LocallyConfiguredRunImage {
args.label("io.buildpacks.run-image=" + b.RunImage)
}
"-group", groupPath,
)

if b.Publish {
export, err = lifecycle.NewPhase(
Expand All @@ -465,25 +458,24 @@ func (b *BuildConfig) export(ctx context.Context, lifecycle *build.Lifecycle) er
)
} else {
args.daemon()

export, err = lifecycle.NewPhase(
"exporter",
build.WithDaemonAccess(),
build.WithArgs(args.list()...),
)
}
defer export.Cleanup()

uid, gid, err := b.packUidGid(ctx, b.Builder)
if err != nil {
return errors.Wrap(err, "get pack uid and gid")
}

if err := b.chownDir(ctx, lifecycle, launchDir, uid, gid); err != nil {
return errors.Wrap(err, "chown launch dir")
}
if err = export.Run(ctx); err != nil {
return err
}
return nil

return export.Run(ctx)
}

func (b *BuildConfig) cache(ctx context.Context, lifecycle *build.Lifecycle) error {
Expand Down
29 changes: 10 additions & 19 deletions build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {

it("defaults to daemon, default-builder, pulls builder and run images, selects run-image from builder", func() {
mockBuilderImage := mocks.NewMockImage(mockController)
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"runImage": {"image": "some/run"}}`, nil).AnyTimes()
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"stack":{"runImage": {"image": "some/run"}}}`, nil).AnyTimes()
mockFetcher.EXPECT().FetchUpdatedLocalImage(gomock.Any(), "some/builder", gomock.Any()).Return(mockBuilderImage, nil)

mockRunImage := mocks.NewMockImage(mockController)
Expand All @@ -80,13 +80,12 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {
})
h.AssertNil(t, err)
h.AssertEq(t, config.RunImage, "some/run")
h.AssertEq(t, config.LocallyConfiguredRunImage, false)
h.AssertEq(t, config.Builder, "some/builder")
})

it("respects builder from flags", func() {
mockBuilderImage := mocks.NewMockImage(mockController)
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"runImage": {"image": "some/run"}}`, nil).AnyTimes()
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"stack":{"runImage": {"image": "some/run"}}}`, nil).AnyTimes()
mockFetcher.EXPECT().FetchUpdatedLocalImage(gomock.Any(), "custom/builder", gomock.Any()).Return(mockBuilderImage, nil)

mockRunImage := mocks.NewMockImage(mockController)
Expand All @@ -99,13 +98,12 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {
})
h.AssertNil(t, err)
h.AssertEq(t, config.RunImage, "some/run")
h.AssertEq(t, config.LocallyConfiguredRunImage, false)
h.AssertEq(t, config.Builder, "custom/builder")
})

it("doesn't pull builder or run images when --no-pull is passed", func() {
mockBuilderImage := mocks.NewMockImage(mockController)
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"runImage": {"image": "some/run"}}`, nil).AnyTimes()
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"stack":{"runImage": {"image": "some/run"}}}`, nil).AnyTimes()
mockFetcher.EXPECT().FetchLocalImage("custom/builder").Return(mockBuilderImage, nil)

mockRunImage := mocks.NewMockImage(mockController)
Expand All @@ -119,14 +117,13 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {
})
h.AssertNil(t, err)
h.AssertEq(t, config.RunImage, "some/run")
h.AssertEq(t, config.LocallyConfiguredRunImage, false)
h.AssertEq(t, config.Builder, "custom/builder")
})

it("selects run images with matching registry", func() {
mockBuilderImage := mocks.NewMockImage(mockController)
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").
Return(`{"runImage": {"image": "some/run", "mirrors": ["registry.com/some/run"]}}`, nil).AnyTimes()
Return(`{"stack":{"runImage": {"image": "some/run", "mirrors": ["registry.com/some/run"]}}}`, nil).AnyTimes()
mockFetcher.EXPECT().FetchUpdatedLocalImage(gomock.Any(), "some/builder", gomock.Any()).Return(mockBuilderImage, nil)

mockRunImage := mocks.NewMockImage(mockController)
Expand All @@ -139,7 +136,6 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {
})
h.AssertNil(t, err)
h.AssertEq(t, config.RunImage, "registry.com/some/run")
h.AssertEq(t, config.LocallyConfiguredRunImage, false)
h.AssertEq(t, config.Builder, "some/builder")
})

Expand All @@ -156,7 +152,7 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {

mockBuilderImage := mocks.NewMockImage(mockController)
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").
Return(`{"runImage": {"image": "default/run", "mirrors": ["registry.com/default/run"]}}`, nil).AnyTimes()
Return(`{"stack":{"runImage": {"image": "default/run", "mirrors": ["registry.com/default/run"]}}}`, nil).AnyTimes()
mockFetcher.EXPECT().FetchUpdatedLocalImage(gomock.Any(), "some/builder", gomock.Any()).Return(mockBuilderImage, nil)

mockRunImage = mocks.NewMockImage(mockController)
Expand All @@ -172,7 +168,6 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {
})
h.AssertNil(t, err)
h.AssertEq(t, config.RunImage, "registry.com/override/run")
h.AssertEq(t, config.LocallyConfiguredRunImage, true)
h.AssertEq(t, config.Builder, "some/builder")
})

Expand All @@ -185,14 +180,13 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {
})
h.AssertNil(t, err)
h.AssertEq(t, config.RunImage, "registry.com/override/run")
h.AssertEq(t, config.LocallyConfiguredRunImage, true)
h.AssertEq(t, config.Builder, "some/builder")
})
})

it("uses a remote run image when --publish is passed", func() {
mockBuilderImage := mocks.NewMockImage(mockController)
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"runImage": {"image": "some/run"}}`, nil).AnyTimes()
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"stack":{"runImage": {"image": "some/run"}}}`, nil).AnyTimes()
mockFetcher.EXPECT().FetchUpdatedLocalImage(gomock.Any(), "some/builder", gomock.Any()).Return(mockBuilderImage, nil)

mockRunImage := mocks.NewMockImage(mockController)
Expand All @@ -206,7 +200,6 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {
})
h.AssertNil(t, err)
h.AssertEq(t, config.RunImage, "some/run")
h.AssertEq(t, config.LocallyConfiguredRunImage, false)
h.AssertEq(t, config.Builder, "some/builder")
})

Expand All @@ -226,13 +219,12 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {
})
h.AssertNil(t, err)
h.AssertEq(t, config.RunImage, "override/run")
h.AssertEq(t, config.LocallyConfiguredRunImage, true)
h.AssertEq(t, config.Builder, "some/builder")
})

it("uses working dir if appDir is set to placeholder value", func() {
mockBuilderImage := mocks.NewMockImage(mockController)
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"runImage": {"image": "some/run"}}`, nil).AnyTimes()
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"stack":{"runImage": {"image": "some/run"}}}`, nil).AnyTimes()
mockFetcher.EXPECT().FetchUpdatedLocalImage(gomock.Any(), "some/builder", gomock.Any()).Return(mockBuilderImage, nil)

mockRunImage := mocks.NewMockImage(mockController)
Expand All @@ -247,7 +239,6 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {
})
h.AssertNil(t, err)
h.AssertEq(t, config.RunImage, "some/run")
h.AssertEq(t, config.LocallyConfiguredRunImage, false)
h.AssertEq(t, config.Builder, "some/builder")
h.AssertEq(t, config.LifecycleConfig.AppDir, os.Getenv("PWD"))
})
Expand Down Expand Up @@ -314,7 +305,7 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {

it("sets Env", func() {
mockBuilderImage := mocks.NewMockImage(mockController)
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"runImage": {"image": "some/run"}}`, nil).AnyTimes()
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"stack":{"runImage": {"image": "some/run"}}}`, nil).AnyTimes()
mockFetcher.EXPECT().FetchUpdatedLocalImage(gomock.Any(), "some/builder", gomock.Any()).Return(mockBuilderImage, nil)

mockRunImage := mocks.NewMockImage(mockController)
Expand All @@ -341,7 +332,7 @@ func testBuildFactory(t *testing.T, when spec.G, it spec.S) {

it("sets EnvFile", func() {
mockBuilderImage := mocks.NewMockImage(mockController)
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"runImage": {"image": "some/run"}}`, nil).AnyTimes()
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"stack":{"runImage": {"image": "some/run"}}}`, nil).AnyTimes()
mockFetcher.EXPECT().FetchUpdatedLocalImage(gomock.Any(), "some/builder", gomock.Any()).Return(mockBuilderImage, nil)

mockRunImage := mocks.NewMockImage(mockController)
Expand Down Expand Up @@ -376,7 +367,7 @@ PATH

it("sets EnvFile with Env overrides", func() {
mockBuilderImage := mocks.NewMockImage(mockController)
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"runImage": {"image": "some/run"}}`, nil).AnyTimes()
mockBuilderImage.EXPECT().Label("io.buildpacks.builder.metadata").Return(`{"stack":{"runImage": {"image": "some/run"}}}`, nil).AnyTimes()
mockFetcher.EXPECT().FetchUpdatedLocalImage(gomock.Any(), "some/builder", gomock.Any()).Return(mockBuilderImage, nil)

mockRunImage := mocks.NewMockImage(mockController)
Expand Down
25 changes: 10 additions & 15 deletions builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,45 +60,40 @@ func (b *Builder) GetLocalRunImageMirrors() ([]string, error) {
if err != nil {
return nil, err
}
if runImage := b.config.GetRunImage(metadata.RunImage.Image); runImage != nil {
if runImage := b.config.GetRunImage(metadata.Stack.RunImage.Image); runImage != nil {
return runImage.Mirrors, nil
}
return []string{}, nil
}

func (b *Builder) GetRunImageByRepoName(repoName string) (runImage string, locallyConfigured bool, err error) {
func (b *Builder) GetRunImageByRepoName(repoName string) (runImage string, err error) {
desiredRegistry, err := registry(repoName)
if err != nil {
return "", false, err
return "", err
}

metadata, err := b.GetMetadata()
if err != nil {
return "", false, err
return "", err
}

localRunImageMirrors, err := b.GetLocalRunImageMirrors()
if err != nil {
return "", false, err
}

for _, img := range localRunImageMirrors {
if reg, err := registry(img); err == nil && reg == desiredRegistry {
return img, true, nil
}
return "", err
}

for _, img := range append([]string{metadata.RunImage.Image}, metadata.RunImage.Mirrors...) {
runImageList := append(localRunImageMirrors, append([]string{metadata.Stack.RunImage.Image}, metadata.Stack.RunImage.Mirrors...)...)
for _, img := range runImageList {
if reg, err := registry(img); err == nil && reg == desiredRegistry {
return img, false, nil
return img, nil
}
}

if len(localRunImageMirrors) > 0 {
return localRunImageMirrors[0], true, nil
return localRunImageMirrors[0], nil
}

return metadata.RunImage.Image, false, nil
return metadata.Stack.RunImage.Image, nil
}

func registry(imageName string) (string, error) {
Expand Down
Loading

0 comments on commit 078292e

Please sign in to comment.