mirror of https://github.com/buildpacks/pack.git
1466 lines
55 KiB
Go
1466 lines
55 KiB
Go
package client_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-containerregistry/pkg/name"
|
|
"github.com/google/go-containerregistry/pkg/v1/tarball"
|
|
|
|
"github.com/buildpacks/imgutil/fakes"
|
|
"github.com/buildpacks/lifecycle/api"
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/heroku/color"
|
|
mobysystem "github.com/moby/moby/api/types/system"
|
|
dockerclient "github.com/moby/moby/client"
|
|
"github.com/pkg/errors"
|
|
"github.com/sclevine/spec"
|
|
"github.com/sclevine/spec/report"
|
|
|
|
pubbldr "github.com/buildpacks/pack/builder"
|
|
pubbldpkg "github.com/buildpacks/pack/buildpackage"
|
|
"github.com/buildpacks/pack/internal/builder"
|
|
ifakes "github.com/buildpacks/pack/internal/fakes"
|
|
"github.com/buildpacks/pack/internal/paths"
|
|
"github.com/buildpacks/pack/internal/style"
|
|
"github.com/buildpacks/pack/pkg/archive"
|
|
"github.com/buildpacks/pack/pkg/blob"
|
|
"github.com/buildpacks/pack/pkg/buildpack"
|
|
"github.com/buildpacks/pack/pkg/client"
|
|
"github.com/buildpacks/pack/pkg/dist"
|
|
"github.com/buildpacks/pack/pkg/image"
|
|
"github.com/buildpacks/pack/pkg/logging"
|
|
"github.com/buildpacks/pack/pkg/testmocks"
|
|
h "github.com/buildpacks/pack/testhelpers"
|
|
)
|
|
|
|
func TestCreateBuilder(t *testing.T) {
|
|
color.Disable(true)
|
|
defer color.Disable(false)
|
|
spec.Run(t, "create_builder", testCreateBuilder, spec.Parallel(), spec.Report(report.Terminal{}))
|
|
}
|
|
|
|
func testCreateBuilder(t *testing.T, when spec.G, it spec.S) {
|
|
when("#CreateBuilder", func() {
|
|
var (
|
|
mockController *gomock.Controller
|
|
mockDownloader *testmocks.MockBlobDownloader
|
|
mockBuildpackDownloader *testmocks.MockBuildpackDownloader
|
|
mockImageFactory *testmocks.MockImageFactory
|
|
mockImageFetcher *testmocks.MockImageFetcher
|
|
mockDockerClient *testmocks.MockAPIClient
|
|
fakeBuildImage *fakes.Image
|
|
fakeRunImage *fakes.Image
|
|
fakeRunImageMirror *fakes.Image
|
|
opts client.CreateBuilderOptions
|
|
subject *client.Client
|
|
logger logging.Logger
|
|
out bytes.Buffer
|
|
tmpDir string
|
|
)
|
|
var prepareFetcherWithRunImages = func() {
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/run-image", gomock.Any()).Return(fakeRunImage, nil).AnyTimes()
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "localhost:5000/some/run-image", gomock.Any()).Return(fakeRunImageMirror, nil).AnyTimes()
|
|
}
|
|
|
|
var prepareFetcherWithBuildImage = func() {
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", gomock.Any()).Return(fakeBuildImage, nil)
|
|
}
|
|
|
|
var prepareExtensions = func() {
|
|
// Extensions require Platform API >= 0.13
|
|
opts.Config.Lifecycle.URI = "file:///some-lifecycle-platform-0-13"
|
|
opts.Config.Extensions = []pubbldr.ModuleConfig{
|
|
{
|
|
ModuleInfo: dist.ModuleInfo{ID: "ext.one", Version: "1.2.3", Homepage: "http://one.extension"},
|
|
ImageOrURI: dist.ImageOrURI{
|
|
BuildpackURI: dist.BuildpackURI{
|
|
URI: "https://example.fake/ext-one.tgz",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
opts.Config.OrderExtensions = []dist.OrderEntry{{
|
|
Group: []dist.ModuleRef{
|
|
{ModuleInfo: dist.ModuleInfo{ID: "ext.one", Version: "1.2.3"}, Optional: true},
|
|
}},
|
|
}
|
|
}
|
|
|
|
var createBuildpack = func(descriptor dist.BuildpackDescriptor) buildpack.BuildModule {
|
|
buildpack, err := ifakes.NewFakeBuildpack(descriptor, 0644)
|
|
h.AssertNil(t, err)
|
|
return buildpack
|
|
}
|
|
|
|
var shouldCallBuildpackDownloaderWith = func(uri string, buildpackDownloadOptions buildpack.DownloadOptions) {
|
|
buildpack := createBuildpack(dist.BuildpackDescriptor{
|
|
WithAPI: api.MustParse("0.3"),
|
|
WithInfo: dist.ModuleInfo{ID: "example/foo", Version: "1.1.0"},
|
|
WithStacks: []dist.Stack{{ID: "some.stack.id"}},
|
|
})
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), uri, gomock.Any()).Return(buildpack, nil, nil)
|
|
}
|
|
|
|
it.Before(func() {
|
|
logger = logging.NewLogWithWriters(&out, &out, logging.WithVerbose())
|
|
mockController = gomock.NewController(t)
|
|
mockDownloader = testmocks.NewMockBlobDownloader(mockController)
|
|
mockImageFetcher = testmocks.NewMockImageFetcher(mockController)
|
|
mockImageFactory = testmocks.NewMockImageFactory(mockController)
|
|
mockDockerClient = testmocks.NewMockAPIClient(mockController)
|
|
mockBuildpackDownloader = testmocks.NewMockBuildpackDownloader(mockController)
|
|
|
|
fakeBuildImage = fakes.NewImage("some/build-image", "", nil)
|
|
h.AssertNil(t, fakeBuildImage.SetLabel("io.buildpacks.stack.id", "some.stack.id"))
|
|
h.AssertNil(t, fakeBuildImage.SetLabel("io.buildpacks.stack.mixins", `["mixinX", "build:mixinY"]`))
|
|
h.AssertNil(t, fakeBuildImage.SetEnv("CNB_USER_ID", "1234"))
|
|
h.AssertNil(t, fakeBuildImage.SetEnv("CNB_GROUP_ID", "4321"))
|
|
|
|
fakeRunImage = fakes.NewImage("some/run-image", "", nil)
|
|
h.AssertNil(t, fakeRunImage.SetLabel("io.buildpacks.stack.id", "some.stack.id"))
|
|
|
|
fakeRunImageMirror = fakes.NewImage("localhost:5000/some/run-image", "", nil)
|
|
h.AssertNil(t, fakeRunImageMirror.SetLabel("io.buildpacks.stack.id", "some.stack.id"))
|
|
|
|
exampleBuildpackBlob := blob.NewBlob(filepath.Join("testdata", "buildpack"))
|
|
mockDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/bp-one.tgz").Return(exampleBuildpackBlob, nil).AnyTimes()
|
|
exampleExtensionBlob := blob.NewBlob(filepath.Join("testdata", "extension"))
|
|
mockDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/ext-one.tgz").Return(exampleExtensionBlob, 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", "platform-0.4")), nil).AnyTimes()
|
|
mockDownloader.EXPECT().Download(gomock.Any(), "file:///some-lifecycle-platform-0-1").Return(blob.NewBlob(filepath.Join("testdata", "lifecycle", "platform-0.3")), nil).AnyTimes()
|
|
mockDownloader.EXPECT().Download(gomock.Any(), "file:///some-lifecycle-platform-0-13").Return(blob.NewBlob(filepath.Join("testdata", "lifecycle", "platform-0.13")), nil).AnyTimes()
|
|
|
|
bp, err := buildpack.FromBuildpackRootBlob(exampleBuildpackBlob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/bp-one.tgz", gomock.Any()).Return(bp, nil, nil).AnyTimes()
|
|
ext, err := buildpack.FromExtensionRootBlob(exampleExtensionBlob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/ext-one.tgz", gomock.Any()).Return(ext, nil, nil).AnyTimes()
|
|
|
|
subject, err = client.NewClient(
|
|
client.WithLogger(logger),
|
|
client.WithDownloader(mockDownloader),
|
|
client.WithImageFactory(mockImageFactory),
|
|
client.WithFetcher(mockImageFetcher),
|
|
client.WithDockerClient(mockDockerClient),
|
|
client.WithBuildpackDownloader(mockBuildpackDownloader),
|
|
)
|
|
h.AssertNil(t, err)
|
|
|
|
mockDockerClient.EXPECT().Info(context.TODO(), gomock.Any()).Return(dockerclient.SystemInfoResult{Info: mobysystem.Info{OSType: "linux"}}, nil).AnyTimes()
|
|
|
|
opts = client.CreateBuilderOptions{
|
|
RelativeBaseDir: "/",
|
|
BuilderName: "some/builder",
|
|
Config: pubbldr.Config{
|
|
Description: "Some description",
|
|
Buildpacks: []pubbldr.ModuleConfig{
|
|
{
|
|
ModuleInfo: dist.ModuleInfo{ID: "bp.one", Version: "1.2.3", Homepage: "http://one.buildpack"},
|
|
ImageOrURI: dist.ImageOrURI{
|
|
BuildpackURI: dist.BuildpackURI{
|
|
URI: "https://example.fake/bp-one.tgz",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Order: []dist.OrderEntry{{
|
|
Group: []dist.ModuleRef{
|
|
{ModuleInfo: dist.ModuleInfo{ID: "bp.one", Version: "1.2.3"}, Optional: false},
|
|
}},
|
|
},
|
|
Stack: pubbldr.StackConfig{
|
|
ID: "some.stack.id",
|
|
},
|
|
Run: pubbldr.RunConfig{
|
|
Images: []pubbldr.RunImageConfig{{
|
|
Image: "some/run-image",
|
|
Mirrors: []string{"localhost:5000/some/run-image"},
|
|
}},
|
|
},
|
|
Build: pubbldr.BuildConfig{
|
|
Image: "some/build-image",
|
|
},
|
|
Lifecycle: pubbldr.LifecycleConfig{URI: "file:///some-lifecycle"},
|
|
},
|
|
Publish: false,
|
|
PullPolicy: image.PullAlways,
|
|
}
|
|
|
|
tmpDir, err = os.MkdirTemp("", "create-builder-test")
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
it.After(func() {
|
|
mockController.Finish()
|
|
h.AssertNil(t, os.RemoveAll(tmpDir))
|
|
})
|
|
|
|
var successfullyCreateBuilder = func() *builder.Builder {
|
|
t.Helper()
|
|
|
|
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)
|
|
|
|
return bldr
|
|
}
|
|
|
|
when("validating the builder config", func() {
|
|
it("should not fail when the stack ID is empty", func() {
|
|
opts.Config.Stack.ID = ""
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
it("should fail when the stack ID from the builder config does not match the stack ID from the build image", func() {
|
|
h.AssertNil(t, fakeBuildImage.SetLabel("io.buildpacks.stack.id", "other.stack.id"))
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "stack 'some.stack.id' from builder config is incompatible with stack 'other.stack.id' from build image")
|
|
})
|
|
|
|
it("should not fail when the stack is empty", func() {
|
|
opts.Config.Stack.ID = ""
|
|
opts.Config.Stack.BuildImage = ""
|
|
opts.Config.Stack.RunImage = ""
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
it("should fail when the run images and stack are empty", func() {
|
|
opts.Config.Stack.BuildImage = ""
|
|
opts.Config.Stack.RunImage = ""
|
|
|
|
opts.Config.Run = pubbldr.RunConfig{}
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "run.images are required")
|
|
})
|
|
|
|
it("should fail when the run images image and stack are empty", func() {
|
|
opts.Config.Stack.BuildImage = ""
|
|
opts.Config.Stack.RunImage = ""
|
|
|
|
opts.Config.Run = pubbldr.RunConfig{
|
|
Images: []pubbldr.RunImageConfig{{}},
|
|
}
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "run.images.image is required")
|
|
})
|
|
|
|
it("should fail if stack and run image are different", func() {
|
|
opts.Config.Stack.RunImage = "some-other-stack-run-image"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "run.images and stack.run-image do not match")
|
|
})
|
|
|
|
it("should fail if stack and build image are different", func() {
|
|
opts.Config.Stack.BuildImage = "some-other-stack-build-image"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "build.image and stack.build-image do not match")
|
|
})
|
|
|
|
it("should fail when lifecycle version is not a semver", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = ""
|
|
opts.Config.Lifecycle.Version = "not-semver"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "'lifecycle.version' must be a valid semver")
|
|
})
|
|
|
|
it("should fail when both lifecycle version and uri are present", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = "file://some-lifecycle"
|
|
opts.Config.Lifecycle.Version = "something"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "'lifecycle' can only declare 'version' or 'uri', not both")
|
|
})
|
|
|
|
it("should fail when buildpack ID does not match downloaded buildpack", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Buildpacks[0].ID = "does.not.match"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "buildpack from URI 'https://example.fake/bp-one.tgz' has ID 'bp.one' which does not match ID 'does.not.match' from builder config")
|
|
})
|
|
|
|
it("should fail when buildpack version does not match downloaded buildpack", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Buildpacks[0].Version = "0.0.0"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "buildpack from URI 'https://example.fake/bp-one.tgz' has version '1.2.3' which does not match version '0.0.0' from builder config")
|
|
})
|
|
|
|
it("should fail when extension ID does not match downloaded extension", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
prepareExtensions()
|
|
opts.Config.Extensions[0].ID = "does.not.match"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "extension from URI 'https://example.fake/ext-one.tgz' has ID 'ext.one' which does not match ID 'does.not.match' from builder config")
|
|
})
|
|
|
|
it("should fail when extension version does not match downloaded extension", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
prepareExtensions()
|
|
opts.Config.Extensions[0].Version = "0.0.0"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "extension from URI 'https://example.fake/ext-one.tgz' has version '1.2.3' which does not match version '0.0.0' from builder config")
|
|
})
|
|
})
|
|
|
|
when("validating the run image config", func() {
|
|
it("should fail when the stack ID from the builder config does not match the stack ID from the run image", func() {
|
|
prepareFetcherWithRunImages()
|
|
h.AssertNil(t, fakeRunImage.SetLabel("io.buildpacks.stack.id", "other.stack.id"))
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "stack 'some.stack.id' from builder config is incompatible with stack 'other.stack.id' from run image 'some/run-image'")
|
|
})
|
|
|
|
it("should fail when the stack ID from the builder config does not match the stack ID from the run image mirrors", func() {
|
|
prepareFetcherWithRunImages()
|
|
h.AssertNil(t, fakeRunImageMirror.SetLabel("io.buildpacks.stack.id", "other.stack.id"))
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "stack 'some.stack.id' from builder config is incompatible with stack 'other.stack.id' from run image 'localhost:5000/some/run-image'")
|
|
})
|
|
|
|
it("should warn when the run image cannot be found", func() {
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", image.FetchOptions{Daemon: true, PullPolicy: image.PullAlways}).Return(fakeBuildImage, nil)
|
|
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/run-image", image.FetchOptions{Daemon: false, PullPolicy: image.PullAlways}).Return(nil, errors.Wrap(image.ErrNotFound, "yikes"))
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/run-image", image.FetchOptions{Daemon: true, PullPolicy: image.PullAlways}).Return(nil, errors.Wrap(image.ErrNotFound, "yikes"))
|
|
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "localhost:5000/some/run-image", image.FetchOptions{Daemon: false, PullPolicy: image.PullAlways}).Return(nil, errors.Wrap(image.ErrNotFound, "yikes"))
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "localhost:5000/some/run-image", image.FetchOptions{Daemon: true, PullPolicy: image.PullAlways}).Return(nil, errors.Wrap(image.ErrNotFound, "yikes"))
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
|
|
h.AssertContains(t, out.String(), "Warning: run image 'some/run-image' is not accessible")
|
|
})
|
|
|
|
it("should fail when not publish and the run image cannot be fetched", func() {
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/run-image", image.FetchOptions{Daemon: true, PullPolicy: image.PullAlways}).Return(nil, errors.New("yikes"))
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "failed to fetch image: yikes")
|
|
})
|
|
|
|
it("should fail when publish and the run image cannot be fetched", func() {
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/run-image", image.FetchOptions{Daemon: false, PullPolicy: image.PullAlways}).Return(nil, errors.New("yikes"))
|
|
|
|
opts.Publish = true
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "failed to fetch image: yikes")
|
|
})
|
|
|
|
it("should fail when the run image isn't a valid image", func() {
|
|
fakeImage := fakeBadImageStruct{}
|
|
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/run-image", gomock.Any()).Return(fakeImage, nil).AnyTimes()
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "localhost:5000/some/run-image", gomock.Any()).Return(fakeImage, nil).AnyTimes()
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "failed to label image")
|
|
})
|
|
|
|
when("publish is true", func() {
|
|
it("should only try to validate the remote run image", func() {
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", image.FetchOptions{Daemon: true}).Times(0)
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/run-image", image.FetchOptions{Daemon: true}).Times(0)
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "localhost:5000/some/run-image", image.FetchOptions{Daemon: true}).Times(0)
|
|
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", image.FetchOptions{Daemon: false}).Return(fakeBuildImage, nil)
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/run-image", image.FetchOptions{Daemon: false}).Return(fakeRunImage, nil)
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "localhost:5000/some/run-image", image.FetchOptions{Daemon: false}).Return(fakeRunImageMirror, nil)
|
|
|
|
opts.Publish = true
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
})
|
|
})
|
|
|
|
when("creating the base builder", func() {
|
|
when("build image not found", func() {
|
|
it("should fail", func() {
|
|
prepareFetcherWithRunImages()
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", image.FetchOptions{Daemon: true, PullPolicy: image.PullAlways}).Return(nil, image.ErrNotFound)
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "fetch build image: not found")
|
|
})
|
|
})
|
|
|
|
when("build image isn't a valid image", func() {
|
|
it("should fail", func() {
|
|
fakeImage := fakeBadImageStruct{}
|
|
|
|
prepareFetcherWithRunImages()
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", image.FetchOptions{Daemon: true, PullPolicy: image.PullAlways}).Return(fakeImage, nil)
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "failed to create builder: invalid build-image")
|
|
})
|
|
})
|
|
|
|
when("windows containers", func() {
|
|
when("experimental enabled", func() {
|
|
it("succeeds", func() {
|
|
opts.Config.Extensions = nil // TODO: downloading extensions doesn't work yet; to be implemented in https://github.com/buildpacks/pack/issues/1489
|
|
opts.Config.OrderExtensions = nil // TODO: downloading extensions doesn't work yet; to be implemented in https://github.com/buildpacks/pack/issues/1489
|
|
packClientWithExperimental, err := client.NewClient(
|
|
client.WithLogger(logger),
|
|
client.WithDownloader(mockDownloader),
|
|
client.WithImageFactory(mockImageFactory),
|
|
client.WithFetcher(mockImageFetcher),
|
|
client.WithExperimental(true),
|
|
)
|
|
h.AssertNil(t, err)
|
|
|
|
prepareFetcherWithRunImages()
|
|
|
|
h.AssertNil(t, fakeBuildImage.SetOS("windows"))
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", image.FetchOptions{Daemon: true, PullPolicy: image.PullAlways}).Return(fakeBuildImage, nil)
|
|
|
|
err = packClientWithExperimental.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
})
|
|
|
|
when("experimental disabled", func() {
|
|
it("fails", func() {
|
|
prepareFetcherWithRunImages()
|
|
|
|
h.AssertNil(t, fakeBuildImage.SetOS("windows"))
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", gomock.Any()).Return(fakeBuildImage, nil)
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "failed to create builder: Windows containers support is currently experimental.")
|
|
})
|
|
})
|
|
})
|
|
|
|
when("error downloading lifecycle", func() {
|
|
it("should fail", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = "fake"
|
|
|
|
uri, err := paths.FilePathToURI(opts.Config.Lifecycle.URI, opts.RelativeBaseDir)
|
|
h.AssertNil(t, err)
|
|
|
|
mockDownloader.EXPECT().Download(gomock.Any(), uri).Return(nil, errors.New("error here")).AnyTimes()
|
|
|
|
err = subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "downloading lifecycle")
|
|
})
|
|
})
|
|
|
|
when("lifecycle isn't a valid lifecycle", func() {
|
|
it("should fail", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = "fake"
|
|
|
|
uri, err := paths.FilePathToURI(opts.Config.Lifecycle.URI, opts.RelativeBaseDir)
|
|
h.AssertNil(t, err)
|
|
|
|
mockDownloader.EXPECT().Download(gomock.Any(), uri).Return(blob.NewBlob(filepath.Join("testdata", "empty-file")), nil).AnyTimes()
|
|
|
|
err = subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "invalid lifecycle")
|
|
})
|
|
})
|
|
})
|
|
|
|
when("validating lifecycle Platform API for extensions", func() {
|
|
when("lifecycle supports Platform API >= 0.13", func() {
|
|
it("should allow extensions without experimental flag", func() {
|
|
// Uses default lifecycle which has Platform API 0.13
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = "file:///some-lifecycle-platform-0-13"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
})
|
|
|
|
when("lifecycle supports Platform API < 0.13", func() {
|
|
when("experimental flag is not set", func() {
|
|
it("should fail when builder has extensions", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
prepareExtensions()
|
|
// Override to use lifecycle with Platform API 0.3 (< 0.13) for this test
|
|
opts.Config.Lifecycle.URI = "file:///some-lifecycle"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "support for image extensions with Platform API < 0.13 is currently experimental")
|
|
})
|
|
})
|
|
|
|
when("experimental flag is set", func() {
|
|
it("should succeed when builder has extensions", func() {
|
|
packClientWithExperimental, err := client.NewClient(
|
|
client.WithLogger(logger),
|
|
client.WithDownloader(mockDownloader),
|
|
client.WithImageFactory(mockImageFactory),
|
|
client.WithFetcher(mockImageFetcher),
|
|
client.WithDockerClient(mockDockerClient),
|
|
client.WithBuildpackDownloader(mockBuildpackDownloader),
|
|
client.WithExperimental(true),
|
|
)
|
|
h.AssertNil(t, err)
|
|
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
prepareExtensions()
|
|
// Remove buildpacks to avoid API compatibility issues
|
|
opts.Config.Buildpacks = nil
|
|
opts.Config.Order = nil
|
|
// Override to use lifecycle with Platform API 0.3 (< 0.13) for this test
|
|
opts.Config.Lifecycle.URI = "file:///some-lifecycle"
|
|
|
|
err = packClientWithExperimental.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
})
|
|
})
|
|
|
|
when("builder has no extensions", func() {
|
|
it("should succeed regardless of Platform API version", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
// Remove extensions from config
|
|
opts.Config.Extensions = nil
|
|
opts.Config.OrderExtensions = nil
|
|
// Use lifecycle with Platform API 0.3 (< 0.13)
|
|
opts.Config.Lifecycle.URI = "file:///some-lifecycle"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
})
|
|
})
|
|
|
|
when("only lifecycle version is provided", func() {
|
|
it("should download from predetermined uri", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = ""
|
|
opts.Config.Lifecycle.Version = "3.4.5"
|
|
|
|
mockDownloader.EXPECT().Download(
|
|
gomock.Any(),
|
|
"https://github.com/buildpacks/lifecycle/releases/download/v3.4.5/lifecycle-v3.4.5+linux.x86-64.tgz",
|
|
).Return(
|
|
blob.NewBlob(filepath.Join("testdata", "lifecycle", "platform-0.4")), nil,
|
|
)
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
it("should download from predetermined uri for arm64", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = ""
|
|
opts.Config.Lifecycle.Version = "3.4.5"
|
|
h.AssertNil(t, fakeBuildImage.SetArchitecture("arm64"))
|
|
|
|
mockDownloader.EXPECT().Download(
|
|
gomock.Any(),
|
|
"https://github.com/buildpacks/lifecycle/releases/download/v3.4.5/lifecycle-v3.4.5+linux.arm64.tgz",
|
|
).Return(
|
|
blob.NewBlob(filepath.Join("testdata", "lifecycle", "platform-0.4")), nil,
|
|
)
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
when("windows", func() {
|
|
it("should download from predetermined uri", func() {
|
|
opts.Config.Extensions = nil // TODO: downloading extensions doesn't work yet; to be implemented in https://github.com/buildpacks/pack/issues/1489
|
|
opts.Config.OrderExtensions = nil // TODO: downloading extensions doesn't work yet; to be implemented in https://github.com/buildpacks/pack/issues/1489
|
|
packClientWithExperimental, err := client.NewClient(
|
|
client.WithLogger(logger),
|
|
client.WithDownloader(mockDownloader),
|
|
client.WithImageFactory(mockImageFactory),
|
|
client.WithFetcher(mockImageFetcher),
|
|
client.WithExperimental(true),
|
|
)
|
|
h.AssertNil(t, err)
|
|
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = ""
|
|
opts.Config.Lifecycle.Version = "3.4.5"
|
|
h.AssertNil(t, fakeBuildImage.SetOS("windows"))
|
|
|
|
mockDownloader.EXPECT().Download(
|
|
gomock.Any(),
|
|
"https://github.com/buildpacks/lifecycle/releases/download/v3.4.5/lifecycle-v3.4.5+windows.x86-64.tgz",
|
|
).Return(
|
|
blob.NewBlob(filepath.Join("testdata", "lifecycle", "platform-0.4")), nil,
|
|
)
|
|
|
|
err = packClientWithExperimental.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
})
|
|
})
|
|
|
|
when("no lifecycle version or URI is provided", func() {
|
|
it("should download default lifecycle", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = ""
|
|
opts.Config.Lifecycle.Version = ""
|
|
|
|
mockDownloader.EXPECT().Download(
|
|
gomock.Any(),
|
|
fmt.Sprintf(
|
|
"https://github.com/buildpacks/lifecycle/releases/download/v%s/lifecycle-v%s+linux.x86-64.tgz",
|
|
builder.DefaultLifecycleVersion,
|
|
builder.DefaultLifecycleVersion,
|
|
),
|
|
).Return(
|
|
blob.NewBlob(filepath.Join("testdata", "lifecycle", "platform-0.4")), nil,
|
|
)
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
it("should download default lifecycle on arm64", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = ""
|
|
opts.Config.Lifecycle.Version = ""
|
|
h.AssertNil(t, fakeBuildImage.SetArchitecture("arm64"))
|
|
|
|
mockDownloader.EXPECT().Download(
|
|
gomock.Any(),
|
|
fmt.Sprintf(
|
|
"https://github.com/buildpacks/lifecycle/releases/download/v%s/lifecycle-v%s+linux.arm64.tgz",
|
|
builder.DefaultLifecycleVersion,
|
|
builder.DefaultLifecycleVersion,
|
|
),
|
|
).Return(
|
|
blob.NewBlob(filepath.Join("testdata", "lifecycle", "platform-0.4")), nil,
|
|
)
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
when("windows", func() {
|
|
it("should download default lifecycle", func() {
|
|
opts.Config.Extensions = nil // TODO: downloading extensions doesn't work yet; to be implemented in https://github.com/buildpacks/pack/issues/1489
|
|
opts.Config.OrderExtensions = nil // TODO: downloading extensions doesn't work yet; to be implemented in https://github.com/buildpacks/pack/issues/1489
|
|
packClientWithExperimental, err := client.NewClient(
|
|
client.WithLogger(logger),
|
|
client.WithDownloader(mockDownloader),
|
|
client.WithImageFactory(mockImageFactory),
|
|
client.WithFetcher(mockImageFetcher),
|
|
client.WithExperimental(true),
|
|
)
|
|
h.AssertNil(t, err)
|
|
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.Config.Lifecycle.URI = ""
|
|
opts.Config.Lifecycle.Version = ""
|
|
h.AssertNil(t, fakeBuildImage.SetOS("windows"))
|
|
|
|
mockDownloader.EXPECT().Download(
|
|
gomock.Any(),
|
|
fmt.Sprintf(
|
|
"https://github.com/buildpacks/lifecycle/releases/download/v%s/lifecycle-v%s+windows.x86-64.tgz",
|
|
builder.DefaultLifecycleVersion,
|
|
builder.DefaultLifecycleVersion,
|
|
),
|
|
).Return(
|
|
blob.NewBlob(filepath.Join("testdata", "lifecycle", "platform-0.4")), nil,
|
|
)
|
|
|
|
err = packClientWithExperimental.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
})
|
|
})
|
|
|
|
when("lifecycle URI is a docker image", func() {
|
|
var lifecycleImageName = "buildpacksio/lifecycle:latest"
|
|
|
|
setupFakeLifecycleImage := func() *h.FakeWithUnderlyingImage {
|
|
// Write the tar content to a file in tmpDir
|
|
lifecycleLayerPath := filepath.Join("testdata", "lifecycle", "lifecycle.tar")
|
|
lifecycleTag, err := name.NewTag(lifecycleImageName)
|
|
h.AssertNil(t, err)
|
|
|
|
v1LifecycleImage, err := tarball.ImageFromPath(lifecycleLayerPath, &lifecycleTag)
|
|
h.AssertNil(t, err)
|
|
|
|
return h.NewFakeWithUnderlyingV1Image(lifecycleImageName, nil, v1LifecycleImage)
|
|
}
|
|
|
|
it("should download lifecycle from docker registry", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
fakeLifecycleImage := setupFakeLifecycleImage()
|
|
|
|
opts.Config.Lifecycle.URI = "docker://" + lifecycleImageName
|
|
opts.Config.Lifecycle.Version = ""
|
|
opts.RelativeBaseDir = tmpDir
|
|
|
|
// Expect the image fetcher to fetch the lifecycle image
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), lifecycleImageName, image.FetchOptions{Daemon: false}).Return(fakeLifecycleImage, nil)
|
|
|
|
// Create the expected lifecycle.tar file that will be referenced
|
|
lifecyclePath := filepath.Join(tmpDir, "lifecycle.tar")
|
|
|
|
// The downloader will be called with the extracted lifecycle tar
|
|
mockDownloader.EXPECT().Download(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, uri string) (blob.Blob, error) {
|
|
// The URI should be a file:// URI pointing to lifecycle.tar
|
|
h.AssertTrue(t, strings.Contains(uri, "lifecycle.tar"))
|
|
|
|
// Write a minimal lifecycle tar for the test
|
|
f, err := os.Create(lifecyclePath)
|
|
h.AssertNil(t, err)
|
|
defer f.Close()
|
|
|
|
// Copy the test lifecycle content
|
|
testLifecycle := blob.NewBlob(filepath.Join("testdata", "lifecycle", "platform-0.4"))
|
|
return testLifecycle, nil
|
|
})
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
it("should handle docker URI without docker:// prefix", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
fakeLifecycleImage := setupFakeLifecycleImage()
|
|
|
|
opts.Config.Lifecycle.URI = "docker:/" + lifecycleImageName
|
|
opts.Config.Lifecycle.Version = ""
|
|
opts.RelativeBaseDir = tmpDir
|
|
|
|
// Expect the image fetcher to fetch the lifecycle image
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), lifecycleImageName, image.FetchOptions{Daemon: false}).Return(fakeLifecycleImage, nil)
|
|
|
|
// The downloader will be called with the extracted lifecycle tar
|
|
mockDownloader.EXPECT().Download(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, uri string) (blob.Blob, error) {
|
|
testLifecycle := blob.NewBlob(filepath.Join("testdata", "lifecycle", "platform-0.4"))
|
|
return testLifecycle, nil
|
|
})
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
when("fetching lifecycle image fails", func() {
|
|
it("should return an error", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
opts.Config.Lifecycle.URI = "docker://" + lifecycleImageName
|
|
opts.Config.Lifecycle.Version = ""
|
|
opts.RelativeBaseDir = tmpDir
|
|
|
|
// Expect the image fetcher to fail
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), lifecycleImageName, image.FetchOptions{Daemon: false}).Return(nil, errors.New("failed to fetch image"))
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "Could not parse uri from lifecycle image")
|
|
})
|
|
})
|
|
|
|
when("lifecycle image has no layers", func() {
|
|
it("should return an error", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
opts.Config.Lifecycle.URI = "docker://" + lifecycleImageName
|
|
opts.Config.Lifecycle.Version = ""
|
|
opts.RelativeBaseDir = tmpDir
|
|
|
|
// Create an image with no layers
|
|
emptyLifecycleImage := fakes.NewImage(lifecycleImageName, "", nil)
|
|
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), lifecycleImageName, image.FetchOptions{Daemon: false}).Return(emptyLifecycleImage, nil)
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "Could not parse uri from lifecycle image")
|
|
})
|
|
})
|
|
|
|
when("both lifecycle URI and version are provided", func() {
|
|
it("should return an error", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
opts.Config.Lifecycle.URI = "docker://" + lifecycleImageName
|
|
opts.Config.Lifecycle.Version = "1.2.3"
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "'lifecycle' can only declare 'version' or 'uri', not both")
|
|
})
|
|
})
|
|
})
|
|
|
|
when("buildpack mixins are not satisfied", func() {
|
|
it("should return an error", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
h.AssertNil(t, fakeBuildImage.SetLabel("io.buildpacks.stack.mixins", ""))
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
|
|
h.AssertError(t, err, "validating buildpacks: buildpack 'bp.one@1.2.3' requires missing mixin(s): build:mixinY, mixinX")
|
|
})
|
|
})
|
|
|
|
when("creation succeeds", func() {
|
|
it("should set basic metadata", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
bldr := successfullyCreateBuilder()
|
|
|
|
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() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
bldr := successfullyCreateBuilder()
|
|
|
|
bpInfo := dist.ModuleInfo{
|
|
ID: "bp.one",
|
|
Version: "1.2.3",
|
|
Homepage: "http://one.buildpack",
|
|
}
|
|
h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{bpInfo})
|
|
bpInfo.Homepage = ""
|
|
h.AssertEq(t, bldr.Order(), dist.Order{{
|
|
Group: []dist.ModuleRef{{
|
|
ModuleInfo: bpInfo,
|
|
Optional: false,
|
|
}},
|
|
}})
|
|
})
|
|
|
|
it("should set extensions and order-extensions metadata", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
prepareExtensions()
|
|
|
|
bldr := successfullyCreateBuilder()
|
|
|
|
extInfo := dist.ModuleInfo{
|
|
ID: "ext.one",
|
|
Version: "1.2.3",
|
|
Homepage: "http://one.extension",
|
|
}
|
|
h.AssertEq(t, bldr.Extensions(), []dist.ModuleInfo{extInfo})
|
|
extInfo.Homepage = ""
|
|
h.AssertEq(t, bldr.OrderExtensions(), dist.Order{{
|
|
Group: []dist.ModuleRef{{
|
|
ModuleInfo: extInfo,
|
|
Optional: false, // extensions are always optional
|
|
}},
|
|
}})
|
|
})
|
|
|
|
it("should embed the lifecycle", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
successfullyCreateBuilder()
|
|
|
|
layerTar, err := fakeBuildImage.FindLayerWithPath("/cnb/lifecycle")
|
|
h.AssertNil(t, err)
|
|
h.AssertTarHasFile(t, layerTar, "/cnb/lifecycle/detector")
|
|
h.AssertTarHasFile(t, layerTar, "/cnb/lifecycle/restorer")
|
|
h.AssertTarHasFile(t, layerTar, "/cnb/lifecycle/analyzer")
|
|
h.AssertTarHasFile(t, layerTar, "/cnb/lifecycle/builder")
|
|
h.AssertTarHasFile(t, layerTar, "/cnb/lifecycle/exporter")
|
|
h.AssertTarHasFile(t, layerTar, "/cnb/lifecycle/launcher")
|
|
})
|
|
|
|
it("should set lifecycle descriptor", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
bldr := successfullyCreateBuilder()
|
|
|
|
h.AssertEq(t, bldr.LifecycleDescriptor().Info.Version.String(), "0.0.0")
|
|
//nolint:staticcheck
|
|
h.AssertEq(t, bldr.LifecycleDescriptor().API.BuildpackVersion.String(), "0.2")
|
|
//nolint:staticcheck
|
|
h.AssertEq(t, bldr.LifecycleDescriptor().API.PlatformVersion.String(), "0.2")
|
|
h.AssertEq(t, bldr.LifecycleDescriptor().APIs.Buildpack.Deprecated.AsStrings(), []string{"0.2", "0.3"})
|
|
h.AssertEq(t, bldr.LifecycleDescriptor().APIs.Buildpack.Supported.AsStrings(), []string{"0.2", "0.3", "0.4", "0.9"})
|
|
h.AssertEq(t, bldr.LifecycleDescriptor().APIs.Platform.Deprecated.AsStrings(), []string{"0.2"})
|
|
h.AssertEq(t, bldr.LifecycleDescriptor().APIs.Platform.Supported.AsStrings(), []string{"0.3", "0.4"})
|
|
})
|
|
|
|
it("should warn when deprecated Buildpack API version is used", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
prepareExtensions()
|
|
bldr := successfullyCreateBuilder()
|
|
|
|
h.AssertEq(t, bldr.LifecycleDescriptor().APIs.Buildpack.Deprecated.AsStrings(), []string{"0.2", "0.3"})
|
|
h.AssertContains(t, out.String(), fmt.Sprintf("Buildpack %s is using deprecated Buildpacks API version %s", style.Symbol("bp.one@1.2.3"), style.Symbol("0.3")))
|
|
h.AssertContains(t, out.String(), fmt.Sprintf("Extension %s is using deprecated Buildpacks API version %s", style.Symbol("ext.one@1.2.3"), style.Symbol("0.3")))
|
|
})
|
|
|
|
it("shouldn't warn when Buildpack API version used isn't deprecated", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
prepareExtensions()
|
|
opts.Config.Buildpacks[0].URI = "https://example.fake/bp-one-with-api-4.tgz"
|
|
opts.Config.Extensions[0].URI = "https://example.fake/ext-one-with-api-9.tgz"
|
|
|
|
buildpackBlob := blob.NewBlob(filepath.Join("testdata", "buildpack-api-0.4"))
|
|
bp, err := buildpack.FromBuildpackRootBlob(buildpackBlob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/bp-one-with-api-4.tgz", gomock.Any()).Return(bp, nil, nil)
|
|
|
|
extensionBlob := blob.NewBlob(filepath.Join("testdata", "extension-api-0.9"))
|
|
extension, err := buildpack.FromExtensionRootBlob(extensionBlob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/ext-one-with-api-9.tgz", gomock.Any()).Return(extension, nil, nil)
|
|
|
|
bldr := successfullyCreateBuilder()
|
|
|
|
h.AssertEq(t, bldr.LifecycleDescriptor().APIs.Buildpack.Deprecated.AsStrings(), []string{"0.2", "0.3"})
|
|
h.AssertNotContains(t, out.String(), "is using deprecated Buildpacks API version")
|
|
})
|
|
|
|
it("should set labels", func() {
|
|
opts.Labels = map[string]string{"test.label.one": "1", "test.label.two": "2"}
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
|
|
imageLabels, err := fakeBuildImage.Labels()
|
|
h.AssertNil(t, err)
|
|
h.AssertEq(t, imageLabels["test.label.one"], "1")
|
|
h.AssertEq(t, imageLabels["test.label.two"], "2")
|
|
})
|
|
|
|
when("Buildpack dependencies are provided", func() {
|
|
var (
|
|
bp1v1 buildpack.BuildModule
|
|
bp1v2 buildpack.BuildModule
|
|
bp2v1 buildpack.BuildModule
|
|
bp2v2 buildpack.BuildModule
|
|
fakeLayerImage *h.FakeAddedLayerImage
|
|
err error
|
|
)
|
|
it.Before(func() {
|
|
fakeLayerImage = &h.FakeAddedLayerImage{Image: fakeBuildImage}
|
|
})
|
|
|
|
var prepareBuildpackDependencies = func() []buildpack.BuildModule {
|
|
bp1v1Blob := blob.NewBlob(filepath.Join("testdata", "buildpack-non-deterministic", "buildpack-1-version-1"))
|
|
bp1v2Blob := blob.NewBlob(filepath.Join("testdata", "buildpack-non-deterministic", "buildpack-1-version-2"))
|
|
bp2v1Blob := blob.NewBlob(filepath.Join("testdata", "buildpack-non-deterministic", "buildpack-2-version-1"))
|
|
bp2v2Blob := blob.NewBlob(filepath.Join("testdata", "buildpack-non-deterministic", "buildpack-2-version-2"))
|
|
|
|
bp1v1, err = buildpack.FromBuildpackRootBlob(bp1v1Blob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
|
|
bp1v2, err = buildpack.FromBuildpackRootBlob(bp1v2Blob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
|
|
bp2v1, err = buildpack.FromBuildpackRootBlob(bp2v1Blob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
|
|
bp2v2, err = buildpack.FromBuildpackRootBlob(bp2v2Blob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
|
|
return []buildpack.BuildModule{bp2v2, bp2v1, bp1v1, bp1v2}
|
|
}
|
|
|
|
var successfullyCreateDeterministicBuilder = func() {
|
|
t.Helper()
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
h.AssertEq(t, fakeLayerImage.IsSaved(), true)
|
|
}
|
|
|
|
it("should add dependencies buildpacks layers order by ID and version", func() {
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", gomock.Any()).Return(fakeLayerImage, nil)
|
|
prepareFetcherWithRunImages()
|
|
prepareExtensions()
|
|
opts.Config.Buildpacks[0].URI = "https://example.fake/bp-one-with-api-4.tgz"
|
|
opts.Config.Extensions[0].URI = "https://example.fake/ext-one-with-api-9.tgz"
|
|
bpDependencies := prepareBuildpackDependencies()
|
|
|
|
buildpackBlob := blob.NewBlob(filepath.Join("testdata", "buildpack-api-0.4"))
|
|
bp, err := buildpack.FromBuildpackRootBlob(buildpackBlob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/bp-one-with-api-4.tgz", gomock.Any()).DoAndReturn(
|
|
func(ctx context.Context, buildpackURI string, opts buildpack.DownloadOptions) (buildpack.BuildModule, []buildpack.BuildModule, error) {
|
|
// test options
|
|
h.AssertEq(t, opts.Target.ValuesAsPlatform(), "linux/amd64")
|
|
return bp, bpDependencies, nil
|
|
})
|
|
|
|
extensionBlob := blob.NewBlob(filepath.Join("testdata", "extension-api-0.9"))
|
|
extension, err := buildpack.FromExtensionRootBlob(extensionBlob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/ext-one-with-api-9.tgz", gomock.Any()).DoAndReturn(
|
|
func(ctx context.Context, buildpackURI string, opts buildpack.DownloadOptions) (buildpack.BuildModule, []buildpack.BuildModule, error) {
|
|
// test options
|
|
h.AssertEq(t, opts.Target.ValuesAsPlatform(), "linux/amd64")
|
|
return extension, nil, nil
|
|
})
|
|
|
|
successfullyCreateDeterministicBuilder()
|
|
|
|
layers := fakeLayerImage.AddedLayersOrder()
|
|
// Main buildpack + 4 dependencies + 1 extension
|
|
h.AssertEq(t, len(layers), 6)
|
|
|
|
// [0] bp.one.1.2.3.tar - main buildpack
|
|
h.AssertTrue(t, strings.Contains(layers[1], h.LayerFileName(bp1v1)))
|
|
h.AssertTrue(t, strings.Contains(layers[2], h.LayerFileName(bp1v2)))
|
|
h.AssertTrue(t, strings.Contains(layers[3], h.LayerFileName(bp2v1)))
|
|
h.AssertTrue(t, strings.Contains(layers[4], h.LayerFileName(bp2v2)))
|
|
// [5] ext.one.1.2.3.tar - extension
|
|
})
|
|
})
|
|
})
|
|
|
|
it("supports directory buildpacks", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.RelativeBaseDir = ""
|
|
directoryPath := "testdata/buildpack"
|
|
opts.Config.Buildpacks[0].URI = directoryPath
|
|
|
|
buildpackBlob := blob.NewBlob(directoryPath)
|
|
buildpack, err := buildpack.FromBuildpackRootBlob(buildpackBlob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), directoryPath, gomock.Any()).Return(buildpack, nil, nil)
|
|
|
|
err = subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
it("supports directory extensions", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
prepareExtensions()
|
|
opts.RelativeBaseDir = ""
|
|
directoryPath := "testdata/extension"
|
|
opts.Config.Extensions[0].URI = directoryPath
|
|
|
|
extensionBlob := blob.NewBlob(directoryPath)
|
|
extension, err := buildpack.FromExtensionRootBlob(extensionBlob, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), directoryPath, gomock.Any()).Return(extension, nil, nil)
|
|
|
|
err = subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
})
|
|
|
|
when("package file", func() {
|
|
it.Before(func() {
|
|
fileURI := func(path string) (original, uri string) {
|
|
absPath, err := paths.FilePathToURI(path, "")
|
|
h.AssertNil(t, err)
|
|
return path, absPath
|
|
}
|
|
|
|
cnbFile, _ := fileURI(filepath.Join(tmpDir, "bp_one1.cnb"))
|
|
buildpackPath, buildpackPathURI := fileURI(filepath.Join("testdata", "buildpack"))
|
|
mockDownloader.EXPECT().Download(gomock.Any(), buildpackPathURI).Return(blob.NewBlob(buildpackPath), nil)
|
|
|
|
h.AssertNil(t, subject.PackageBuildpack(context.TODO(), client.PackageBuildpackOptions{
|
|
Name: cnbFile,
|
|
Config: pubbldpkg.Config{
|
|
Platform: dist.Platform{OS: "linux"},
|
|
Buildpack: dist.BuildpackURI{URI: buildpackPath},
|
|
},
|
|
Format: "file",
|
|
}))
|
|
|
|
buildpack, _, err := buildpack.BuildpacksFromOCILayoutBlob(blob.NewBlob(cnbFile))
|
|
h.AssertNil(t, err)
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), cnbFile, gomock.Any()).Return(buildpack, nil, nil).AnyTimes()
|
|
opts.Config.Buildpacks = []pubbldr.ModuleConfig{{
|
|
ImageOrURI: dist.ImageOrURI{BuildpackURI: dist.BuildpackURI{URI: cnbFile}},
|
|
}}
|
|
})
|
|
|
|
it("package file is valid", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
bldr := successfullyCreateBuilder()
|
|
|
|
bpInfo := dist.ModuleInfo{
|
|
ID: "bp.one",
|
|
Version: "1.2.3",
|
|
Homepage: "http://one.buildpack",
|
|
}
|
|
h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{bpInfo})
|
|
bpInfo.Homepage = ""
|
|
h.AssertEq(t, bldr.Order(), dist.Order{{
|
|
Group: []dist.ModuleRef{{
|
|
ModuleInfo: bpInfo,
|
|
Optional: false,
|
|
}},
|
|
}})
|
|
})
|
|
})
|
|
|
|
when("packages", func() {
|
|
when("package image lives in cnb registry", func() {
|
|
when("publish=false and pull-policy=always", func() {
|
|
it("should call BuildpackDownloader with the proper argumentss", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
opts.BuilderName = "some/builder"
|
|
opts.Publish = false
|
|
opts.PullPolicy = image.PullAlways
|
|
opts.Registry = "some-registry"
|
|
opts.Config.Buildpacks = append(
|
|
opts.Config.Buildpacks,
|
|
pubbldr.ModuleConfig{
|
|
ImageOrURI: dist.ImageOrURI{
|
|
BuildpackURI: dist.BuildpackURI{
|
|
URI: "urn:cnb:registry:example/foo@1.1.0",
|
|
},
|
|
},
|
|
},
|
|
)
|
|
|
|
shouldCallBuildpackDownloaderWith("urn:cnb:registry:example/foo@1.1.0", buildpack.DownloadOptions{Daemon: true, PullPolicy: image.PullAlways, RegistryName: "some-"})
|
|
h.AssertNil(t, subject.CreateBuilder(context.TODO(), opts))
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
when("flatten option is set", func() {
|
|
/* 1
|
|
* / \
|
|
* 2 3
|
|
* / \
|
|
* 4 5
|
|
* / \
|
|
* 6 7
|
|
*/
|
|
var (
|
|
fakeLayerImage *h.FakeAddedLayerImage
|
|
err error
|
|
)
|
|
|
|
var successfullyCreateFlattenBuilder = func() {
|
|
t.Helper()
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertNil(t, err)
|
|
h.AssertEq(t, fakeLayerImage.IsSaved(), true)
|
|
}
|
|
|
|
it.Before(func() {
|
|
fakeLayerImage = &h.FakeAddedLayerImage{Image: fakeBuildImage}
|
|
mockImageFetcher.EXPECT().Fetch(gomock.Any(), "some/build-image", gomock.Any()).Return(fakeLayerImage, nil)
|
|
|
|
var depBPs []buildpack.BuildModule
|
|
blob1 := blob.NewBlob(filepath.Join("testdata", "buildpack-flatten", "buildpack-1"))
|
|
for i := 2; i <= 7; i++ {
|
|
b := blob.NewBlob(filepath.Join("testdata", "buildpack-flatten", fmt.Sprintf("buildpack-%d", i)))
|
|
bp, err := buildpack.FromBuildpackRootBlob(b, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
depBPs = append(depBPs, bp)
|
|
}
|
|
mockDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/flatten-bp-1.tgz").Return(blob1, nil).AnyTimes()
|
|
|
|
bp, err := buildpack.FromBuildpackRootBlob(blob1, archive.DefaultTarWriterFactory(), nil)
|
|
h.AssertNil(t, err)
|
|
mockBuildpackDownloader.EXPECT().Download(gomock.Any(), "https://example.fake/flatten-bp-1.tgz", gomock.Any()).Return(bp, depBPs, nil).AnyTimes()
|
|
|
|
opts = client.CreateBuilderOptions{
|
|
RelativeBaseDir: "/",
|
|
BuilderName: "some/builder",
|
|
Config: pubbldr.Config{
|
|
Description: "Some description",
|
|
Buildpacks: []pubbldr.ModuleConfig{
|
|
{
|
|
ModuleInfo: dist.ModuleInfo{ID: "flatten/bp-1", Version: "1", Homepage: "http://buildpack-1"},
|
|
ImageOrURI: dist.ImageOrURI{
|
|
BuildpackURI: dist.BuildpackURI{
|
|
URI: "https://example.fake/flatten-bp-1.tgz",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Order: []dist.OrderEntry{{
|
|
Group: []dist.ModuleRef{
|
|
{ModuleInfo: dist.ModuleInfo{ID: "flatten/bp-2", Version: "2"}, Optional: false},
|
|
{ModuleInfo: dist.ModuleInfo{ID: "flatten/bp-4", Version: "4"}, Optional: false},
|
|
{ModuleInfo: dist.ModuleInfo{ID: "flatten/bp-6", Version: "6"}, Optional: false},
|
|
{ModuleInfo: dist.ModuleInfo{ID: "flatten/bp-7", Version: "7"}, Optional: false},
|
|
}},
|
|
},
|
|
Stack: pubbldr.StackConfig{
|
|
ID: "some.stack.id",
|
|
},
|
|
Run: pubbldr.RunConfig{
|
|
Images: []pubbldr.RunImageConfig{{
|
|
Image: "some/run-image",
|
|
Mirrors: []string{"localhost:5000/some/run-image"},
|
|
}},
|
|
},
|
|
Build: pubbldr.BuildConfig{
|
|
Image: "some/build-image",
|
|
},
|
|
Lifecycle: pubbldr.LifecycleConfig{URI: "file:///some-lifecycle"},
|
|
},
|
|
Publish: false,
|
|
PullPolicy: image.PullAlways,
|
|
}
|
|
})
|
|
|
|
when("flatten all", func() {
|
|
it("creates 1 layer for all buildpacks", func() {
|
|
prepareFetcherWithRunImages()
|
|
opts.Flatten, err = buildpack.ParseFlattenBuildModules([]string{"flatten/bp-1@1,flatten/bp-2@2,flatten/bp-4@4,flatten/bp-6@6,flatten/bp-7@7,flatten/bp-3@3,flatten/bp-5@5"})
|
|
h.AssertNil(t, err)
|
|
|
|
successfullyCreateFlattenBuilder()
|
|
|
|
layers := fakeLayerImage.AddedLayersOrder()
|
|
|
|
h.AssertEq(t, len(layers), 1)
|
|
})
|
|
})
|
|
|
|
when("only some modules are flattened", func() {
|
|
it("creates 1 layer for buildpacks [1,2,3,4,5,6] and 1 layer for buildpack [7]", func() {
|
|
prepareFetcherWithRunImages()
|
|
opts.Flatten, err = buildpack.ParseFlattenBuildModules([]string{"flatten/bp-1@1,flatten/bp-2@2,flatten/bp-4@4,flatten/bp-6@6,flatten/bp-3@3,flatten/bp-5@5"})
|
|
h.AssertNil(t, err)
|
|
|
|
successfullyCreateFlattenBuilder()
|
|
|
|
layers := fakeLayerImage.AddedLayersOrder()
|
|
h.AssertEq(t, len(layers), 2)
|
|
})
|
|
|
|
it("creates 1 layer for buildpacks [1,2,3] and 1 layer for [4,5,6] and 1 layer for [7]", func() {
|
|
prepareFetcherWithRunImages()
|
|
opts.Flatten, err = buildpack.ParseFlattenBuildModules([]string{"flatten/bp-1@1,flatten/bp-2@2,flatten/bp-3@3", "flatten/bp-4@4,flatten/bp-6@6,flatten/bp-5@5"})
|
|
h.AssertNil(t, err)
|
|
|
|
successfullyCreateFlattenBuilder()
|
|
|
|
layers := fakeLayerImage.AddedLayersOrder()
|
|
h.AssertEq(t, len(layers), 3)
|
|
})
|
|
})
|
|
})
|
|
|
|
when("daemon target selection for multi-platform builders", func() {
|
|
when("publish is false", func() {
|
|
when("daemon is linux/amd64", func() {
|
|
it.Before(func() {
|
|
mockDockerClient.EXPECT().ServerVersion(gomock.Any(), gomock.Any()).Return(dockerclient.ServerVersionResult{
|
|
Os: "linux",
|
|
Arch: "amd64",
|
|
}, nil).AnyTimes()
|
|
})
|
|
|
|
when("multiple targets are provided", func() {
|
|
it("selects the matching OS and architecture", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
opts.Targets = []dist.Target{
|
|
{OS: "linux", Arch: "arm64"},
|
|
{OS: "linux", Arch: "amd64"}, // should match
|
|
{OS: "windows", Arch: "amd64"},
|
|
}
|
|
|
|
h.AssertNil(t, subject.CreateBuilder(context.TODO(), opts))
|
|
|
|
// Verify that only one image was created (for the matching target)
|
|
h.AssertEq(t, fakeBuildImage.IsSaved(), true)
|
|
})
|
|
})
|
|
|
|
when("no exact architecture match exists", func() {
|
|
it("returns error", func() {
|
|
opts.Targets = []dist.Target{
|
|
{OS: "linux", Arch: "arm64"},
|
|
{OS: "linux", Arch: "arm"},
|
|
}
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "could not find a target that matches daemon os=linux and architecture=amd64")
|
|
})
|
|
})
|
|
|
|
when("target with empty architecture exists", func() {
|
|
it("selects the OS-only match", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
opts.Targets = []dist.Target{
|
|
{OS: "linux", Arch: "arm64"},
|
|
{OS: "linux", Arch: ""}, // should match
|
|
{OS: "windows", Arch: "amd64"},
|
|
}
|
|
|
|
h.AssertNil(t, subject.CreateBuilder(context.TODO(), opts))
|
|
|
|
// Verify that the builder was created
|
|
h.AssertEq(t, fakeBuildImage.IsSaved(), true)
|
|
})
|
|
})
|
|
})
|
|
|
|
when("daemon is linux/arm64", func() {
|
|
it.Before(func() {
|
|
mockDockerClient.EXPECT().ServerVersion(gomock.Any(), gomock.Any()).Return(dockerclient.ServerVersionResult{
|
|
Os: "linux",
|
|
Arch: "arm64",
|
|
}, nil).AnyTimes()
|
|
})
|
|
|
|
when("targets are ordered with amd64 first", func() {
|
|
it("selects arm64 even when amd64 appears first", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
opts.Targets = []dist.Target{
|
|
{OS: "linux", Arch: "amd64"}, // appears first
|
|
{OS: "linux", Arch: "arm64"}, // should match
|
|
{OS: "windows", Arch: "arm64"},
|
|
}
|
|
|
|
h.AssertNil(t, subject.CreateBuilder(context.TODO(), opts))
|
|
|
|
// Verify that the builder was created
|
|
h.AssertEq(t, fakeBuildImage.IsSaved(), true)
|
|
})
|
|
})
|
|
|
|
when("only amd64 targets available", func() {
|
|
it("returns error", func() {
|
|
opts.Targets = []dist.Target{
|
|
{OS: "linux", Arch: "amd64"},
|
|
{OS: "windows", Arch: "amd64"},
|
|
}
|
|
|
|
err := subject.CreateBuilder(context.TODO(), opts)
|
|
h.AssertError(t, err, "could not find a target that matches daemon os=linux and architecture=arm64")
|
|
})
|
|
})
|
|
})
|
|
|
|
when("empty targets list", func() {
|
|
it("creates builder without calling daemonTarget", func() {
|
|
prepareFetcherWithBuildImage()
|
|
prepareFetcherWithRunImages()
|
|
|
|
// Empty targets should use the default behavior
|
|
opts.Targets = []dist.Target{}
|
|
|
|
// ServerVersion should NOT be called for empty targets
|
|
mockDockerClient.EXPECT().ServerVersion(gomock.Any(), gomock.Any()).Times(0)
|
|
|
|
h.AssertNil(t, subject.CreateBuilder(context.TODO(), opts))
|
|
|
|
// Verify that the builder was created
|
|
h.AssertEq(t, fakeBuildImage.IsSaved(), true)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
type fakeBadImageStruct struct {
|
|
*fakes.Image
|
|
}
|
|
|
|
func (i fakeBadImageStruct) Name() string {
|
|
return "fake image"
|
|
}
|
|
|
|
func (i fakeBadImageStruct) Label(str string) (string, error) {
|
|
return "", errors.New("error here")
|
|
}
|