pack/internal/builder/builder_test.go

2101 lines
69 KiB
Go

package builder_test
import (
"bytes"
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"os"
"path"
"path/filepath"
"runtime"
"slices"
"strings"
"testing"
"github.com/buildpacks/imgutil"
"github.com/buildpacks/imgutil/fakes"
"github.com/buildpacks/lifecycle/api"
"github.com/golang/mock/gomock"
"github.com/heroku/color"
"github.com/pkg/errors"
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
pubbldr "github.com/buildpacks/pack/builder"
"github.com/buildpacks/pack/internal/builder"
"github.com/buildpacks/pack/internal/builder/testmocks"
ifakes "github.com/buildpacks/pack/internal/fakes"
"github.com/buildpacks/pack/pkg/archive"
"github.com/buildpacks/pack/pkg/buildpack"
"github.com/buildpacks/pack/pkg/dist"
"github.com/buildpacks/pack/pkg/logging"
h "github.com/buildpacks/pack/testhelpers"
)
func TestBuilder(t *testing.T) {
color.Disable(true)
defer color.Disable(false)
spec.Run(t, "Builder", testBuilder, spec.Parallel(), spec.Report(report.Terminal{}))
}
func testBuilder(t *testing.T, when spec.G, it spec.S) {
var (
baseImage *fakes.Image
subject *builder.Builder
mockController *gomock.Controller
mockLifecycle *testmocks.MockLifecycle
bp1v1 buildpack.BuildModule
bp1v2 buildpack.BuildModule
bp2v1 buildpack.BuildModule
bp2v2 buildpack.BuildModule
ext1v1 buildpack.BuildModule
ext1v2 buildpack.BuildModule
ext2v1 buildpack.BuildModule
bpOrder buildpack.BuildModule
outBuf bytes.Buffer
logger logging.Logger
)
it.Before(func() {
logger = logging.NewLogWithWriters(&outBuf, &outBuf)
baseImage = fakes.NewImage("base/image", "", nil)
mockController = gomock.NewController(t)
lifecycleTarReader := archive.ReadDirAsTar(
filepath.Join("testdata", "lifecycle", "platform-0.4"),
".", 0, 0, 0755, true, false, nil,
)
descriptorContents, err := os.ReadFile(filepath.Join("testdata", "lifecycle", "platform-0.4", "lifecycle.toml"))
h.AssertNil(t, err)
lifecycleDescriptor, err := builder.ParseDescriptor(string(descriptorContents))
h.AssertNil(t, err)
mockLifecycle = testmocks.NewMockLifecycle(mockController)
mockLifecycle.EXPECT().Open().Return(lifecycleTarReader, nil).AnyTimes()
mockLifecycle.EXPECT().Descriptor().Return(builder.CompatDescriptor(lifecycleDescriptor)).AnyTimes()
bp1v1, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.2"),
WithInfo: dist.ModuleInfo{
ID: "buildpack-1-id",
Version: "buildpack-1-version-1",
},
WithStacks: []dist.Stack{{
ID: "some.stack.id",
Mixins: []string{"mixinX", "mixinY"},
}},
}, 0644)
h.AssertNil(t, err)
bp1v2, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.2"),
WithInfo: dist.ModuleInfo{
ID: "buildpack-1-id",
Version: "buildpack-1-version-2",
},
WithStacks: []dist.Stack{{
ID: "some.stack.id",
Mixins: []string{"mixinX", "mixinY"},
}},
}, 0644)
h.AssertNil(t, err)
bp2v1, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.2"),
WithInfo: dist.ModuleInfo{
ID: "buildpack-2-id",
Version: "buildpack-2-version-1",
},
WithStacks: []dist.Stack{{
ID: "some.stack.id",
Mixins: []string{"build:mixinA", "run:mixinB"},
}},
}, 0644)
h.AssertNil(t, err)
ext1v1, err = ifakes.NewFakeExtension(dist.ExtensionDescriptor{
WithAPI: api.MustParse("0.9"),
WithInfo: dist.ModuleInfo{
ID: "extension-1-id",
Version: "extension-1-version-1",
},
}, 0644)
h.AssertNil(t, err)
ext1v2, err = ifakes.NewFakeExtension(dist.ExtensionDescriptor{
WithAPI: api.MustParse("0.9"),
WithInfo: dist.ModuleInfo{
ID: "extension-1-id",
Version: "extension-1-version-2",
},
}, 0644)
h.AssertNil(t, err)
ext2v1, err = ifakes.NewFakeExtension(dist.ExtensionDescriptor{
WithAPI: api.MustParse("0.9"),
WithInfo: dist.ModuleInfo{
ID: "extension-2-id",
Version: "extension-2-version-1",
},
}, 0644)
h.AssertNil(t, err)
bpOrder, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.2"),
WithInfo: dist.ModuleInfo{
ID: "order-buildpack-id",
Version: "order-buildpack-version",
},
WithOrder: []dist.OrderEntry{{
Group: []dist.ModuleRef{
{
ModuleInfo: bp1v1.Descriptor().Info(),
Optional: true,
},
{
ModuleInfo: bp2v1.Descriptor().Info(),
Optional: false,
},
},
}},
}, 0644)
h.AssertNil(t, err)
})
it.After(func() {
h.AssertNilE(t, baseImage.Cleanup())
mockController.Finish()
})
when("the base image is not valid", func() {
when("#FromImage", func() {
when("metadata isn't valid", func() {
it("returns an error", func() {
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.builder.metadata",
`{"something-random": ,}`,
))
_, err := builder.FromImage(baseImage)
h.AssertError(t, err, "getting label")
})
})
})
when("#New", func() {
when("metadata isn't valid", func() {
it("returns an error", func() {
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.builder.metadata",
`{"something-random": ,}`,
))
_, err := builder.FromImage(baseImage)
h.AssertError(t, err, "getting label")
})
})
when("missing CNB_USER_ID", func() {
it("returns an error", func() {
_, err := builder.New(baseImage, "some/builder")
h.AssertError(t, err, "image 'base/image' missing required env var 'CNB_USER_ID'")
})
})
when("missing CNB_GROUP_ID", func() {
it.Before(func() {
h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
})
it("returns an error", func() {
_, err := builder.New(baseImage, "some/builder")
h.AssertError(t, err, "image 'base/image' missing required env var 'CNB_GROUP_ID'")
})
})
when("CNB_USER_ID is not an int", func() {
it.Before(func() {
h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "not an int"))
h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
})
it("returns an error", func() {
_, err := builder.New(baseImage, "some/builder")
h.AssertError(t, err, "failed to parse 'CNB_USER_ID', value 'not an int' should be an integer")
})
})
when("CNB_GROUP_ID is not an int", func() {
it.Before(func() {
h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "not an int"))
})
it("returns an error", func() {
_, err := builder.New(baseImage, "some/builder")
h.AssertError(t, err, "failed to parse 'CNB_GROUP_ID', value 'not an int' should be an integer")
})
})
when("missing stack id label and run image", func() {
it.Before(func() {
h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
})
it("does not return an error", func() {
_, err := builder.New(baseImage, "some/builder")
h.AssertNilE(t, err)
})
})
when("mixins metadata is malformed", func() {
it.Before(func() {
h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.id", "some-id"))
})
it("returns an error", func() {
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.mixins", `{"mixinX", "mixinY", "build:mixinA"}`))
_, err := builder.New(baseImage, "some/builder")
h.AssertError(t, err, "getting label io.buildpacks.stack.mixins")
})
})
when("order metadata is malformed", func() {
it.Before(func() {
h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.id", "some-id"))
})
it("returns an error", func() {
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.buildpack.order", `{"something", }`))
_, err := builder.New(baseImage, "some/builder")
h.AssertError(t, err, "getting label io.buildpacks.buildpack.order")
})
})
})
})
when("the base image is a valid build image", func() {
it.Before(func() {
var err error
h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.id", "some.stack.id"))
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.mixins", `["mixinX", "mixinY", "build:mixinA"]`))
subject, err = builder.New(baseImage, "some/builder")
h.AssertNil(t, err)
subject.SetLifecycle(mockLifecycle)
})
it.After(func() {
h.AssertNilE(t, baseImage.Cleanup())
})
when("#Save", func() {
it("creates a builder from the image and renames it", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
h.AssertEq(t, baseImage.Name(), "some/builder")
})
it("adds creator metadata", func() {
testName := "test-name"
testVersion := "1.2.5"
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{
Name: testName,
Version: testVersion,
}))
h.AssertEq(t, baseImage.IsSaved(), true)
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.CreatedBy.Name, testName)
h.AssertEq(t, metadata.CreatedBy.Version, testVersion)
})
it("adds creator name if not provided", func() {
testVersion := "1.2.5"
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{
Version: testVersion,
}))
h.AssertEq(t, baseImage.IsSaved(), true)
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.CreatedBy.Name, "Pack CLI")
h.AssertEq(t, metadata.CreatedBy.Version, testVersion)
})
it("creates the workspace dir with CNB user and group", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
layerTar, err := baseImage.FindLayerWithPath("/workspace")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/workspace",
h.IsDirectory(),
h.HasFileMode(0755),
h.HasOwnerAndGroup(1234, 4321),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("creates the layers dir with CNB user and group", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
layerTar, err := baseImage.FindLayerWithPath("/layers")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/layers",
h.IsDirectory(),
h.HasOwnerAndGroup(1234, 4321),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("creates the cnb dir", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
layerTar, err := baseImage.FindLayerWithPath("/cnb")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb",
h.IsDirectory(),
h.HasOwnerAndGroup(0, 0),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("creates the build-config dir", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
layerTar, err := baseImage.FindLayerWithPath("/cnb/build-config")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/build-config",
h.IsDirectory(),
h.HasOwnerAndGroup(0, 0),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("creates the buildpacks dir", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
layerTar, err := baseImage.FindLayerWithPath("/cnb/buildpacks")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/buildpacks",
h.IsDirectory(),
h.HasOwnerAndGroup(0, 0),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("creates the platform dir", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
layerTar, err := baseImage.FindLayerWithPath("/platform")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/platform",
h.IsDirectory(),
h.HasOwnerAndGroup(0, 0),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, "/platform/env",
h.IsDirectory(),
h.HasOwnerAndGroup(0, 0),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("sets the working dir to the layers dir", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
workingDir, err := baseImage.WorkingDir()
h.AssertNil(t, err)
h.AssertEq(t, workingDir, "/layers")
})
it("does not overwrite the order layer when SetOrder has not been called", func() {
tmpDir, err := os.MkdirTemp("", "")
h.AssertNil(t, err)
defer os.RemoveAll(tmpDir)
layerFile := filepath.Join(tmpDir, "order.tar")
err = archive.CreateSingleFileTar(layerFile, "/cnb/order.toml", "some content")
h.AssertNil(t, err)
h.AssertNil(t, baseImage.AddLayer(layerFile))
h.AssertNil(t, baseImage.Save())
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
layerTar, err := baseImage.FindLayerWithPath("/cnb/order.toml")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/order.toml", h.ContentEquals("some content"))
})
it("adds additional tags as requested", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}, "additional-tag-one", "additional-tag-two"))
h.AssertEq(t, baseImage.IsSaved(), true)
h.AssertEq(t, baseImage.Name(), "some/builder")
savedNames := baseImage.SavedNames()
slices.Sort(savedNames)
h.AssertEq(t, 3, len(savedNames))
h.AssertEq(t, "additional-tag-one", savedNames[0])
h.AssertEq(t, "additional-tag-two", savedNames[1])
h.AssertEq(t, "some/builder", savedNames[2])
})
when("validating order", func() {
it.Before(func() {
subject.SetLifecycle(mockLifecycle)
})
when("has single buildpack", func() {
it.Before(func() {
subject.AddBuildpack(bp1v1)
})
it("should resolve unset version (to legacy label and order.toml)", func() {
subject.SetOrder(dist.Order{{
Group: []dist.ModuleRef{
{ModuleInfo: dist.ModuleInfo{ID: bp1v1.Descriptor().Info().ID}}},
}})
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertNil(t, err)
layerTar, err := baseImage.FindLayerWithPath("/cnb/order.toml")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/order.toml", h.ContentEquals(`[[order]]
[[order.group]]
id = "buildpack-1-id"
version = "buildpack-1-version-1"
`))
})
when("order points to missing buildpack id", func() {
it("should error", func() {
subject.SetOrder(dist.Order{{
Group: []dist.ModuleRef{
{ModuleInfo: dist.ModuleInfo{ID: "missing-buildpack-id"}}},
}})
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertError(t, err, "no versions of buildpack 'missing-buildpack-id' were found on the builder")
})
})
when("order points to missing buildpack version", func() {
it("should error", func() {
subject.SetOrder(dist.Order{{
Group: []dist.ModuleRef{
{ModuleInfo: dist.ModuleInfo{ID: "buildpack-1-id", Version: "missing-buildpack-version"}}},
}})
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertError(t, err, "buildpack 'buildpack-1-id' with version 'missing-buildpack-version' was not found on the builder")
})
})
})
when("has repeated buildpacks with the same ID and version", func() {
it.Before(func() {
subject.AddBuildpack(bp1v1)
subject.AddBuildpack(bp1v1)
})
when("order omits version", func() {
it("should de-duplicate identical buildpacks", func() {
subject.SetOrder(dist.Order{
{Group: []dist.ModuleRef{{
ModuleInfo: dist.ModuleInfo{
ID: bp1v1.Descriptor().Info().ID,
Homepage: bp1v1.Descriptor().Info().Homepage,
}}},
},
{Group: []dist.ModuleRef{{
ModuleInfo: dist.ModuleInfo{
ID: bp1v1.Descriptor().Info().ID,
Homepage: bp1v1.Descriptor().Info().Homepage,
}}},
},
})
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertNil(t, err)
})
})
})
when("has multiple buildpacks with same ID", func() {
it.Before(func() {
subject.AddBuildpack(bp1v1)
subject.AddBuildpack(bp1v2)
})
when("order explicitly sets version", func() {
it("should keep order version", func() {
subject.SetOrder(dist.Order{{
Group: []dist.ModuleRef{
{ModuleInfo: bp1v1.Descriptor().Info()}},
}})
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertNil(t, err)
layerTar, err := baseImage.FindLayerWithPath("/cnb/order.toml")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/order.toml", h.ContentEquals(`[[order]]
[[order.group]]
id = "buildpack-1-id"
version = "buildpack-1-version-1"
`))
})
})
when("order version is empty", func() {
it("return error", func() {
subject.SetOrder(dist.Order{{
Group: []dist.ModuleRef{
{ModuleInfo: dist.ModuleInfo{ID: "buildpack-1-id"}}},
}})
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertError(t, err, "multiple versions of 'buildpack-1-id' - must specify an explicit version")
})
})
})
})
when("validating buildpacks", func() {
when("nested buildpack does not exist", func() {
when("buildpack by id does not exist", func() {
it("returns an error", func() {
subject.AddBuildpack(bp1v1)
subject.AddBuildpack(bpOrder)
// order buildpack requires bp2v1
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertError(t, err, "buildpack 'buildpack-2-id@buildpack-2-version-1' not found on the builder")
})
})
when("buildpack version does not exist", func() {
it("returns an error", func() {
subject.AddBuildpack(bp1v2)
subject.AddBuildpack(bp2v1)
// order buildpack requires bp1v1 rather than bp1v2
subject.AddBuildpack(bpOrder)
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertError(t, err, "buildpack 'buildpack-1-id@buildpack-1-version-1' not found on the builder")
})
})
})
when("buildpack stack id does not match", func() {
it("returns an error", func() {
bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.2"),
WithInfo: bp1v1.Descriptor().Info(),
WithStacks: []dist.Stack{{ID: "other.stack.id"}},
}, 0644)
h.AssertNil(t, err)
subject.AddBuildpack(bp)
err = subject.Save(logger, builder.CreatorMetadata{})
h.AssertError(t, err, "buildpack 'buildpack-1-id@buildpack-1-version-1' does not support stack 'some.stack.id'")
})
})
when("buildpack is not compatible with lifecycle", func() {
it("returns an error", func() {
bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.1"),
WithInfo: bp1v1.Descriptor().Info(),
WithStacks: []dist.Stack{{ID: "some.stack.id"}},
}, 0644)
h.AssertNil(t, err)
subject.AddBuildpack(bp)
err = subject.Save(logger, builder.CreatorMetadata{})
h.AssertError(t,
err,
"buildpack 'buildpack-1-id@buildpack-1-version-1' (Buildpack API 0.1) is incompatible with lifecycle '0.0.0' (Buildpack API(s) 0.2, 0.3, 0.4, 0.9)")
})
})
when("buildpack mixins are not satisfied", func() {
it("returns an error", func() {
bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.2"),
WithInfo: bp1v1.Descriptor().Info(),
WithStacks: []dist.Stack{{
ID: "some.stack.id",
Mixins: []string{"missing"},
}},
}, 0644)
h.AssertNil(t, err)
subject.AddBuildpack(bp)
err = subject.Save(logger, builder.CreatorMetadata{})
h.AssertError(t, err, "buildpack 'buildpack-1-id@buildpack-1-version-1' requires missing mixin(s): missing")
})
})
})
when("getting layers label", func() {
it("fails if layers label isn't set correctly", func() {
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.buildpack.layers",
`{"something-here: }`,
))
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertError(t, err, "getting label io.buildpacks.buildpack.layers")
})
})
when("saving with duplicated buildpacks", func() {
it("adds a single buildpack to the builder image", func() {
subject.AddBuildpack(bp1v1)
subject.AddBuildpack(bp2v1)
subject.AddBuildpack(bp1v1)
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertNil(t, err)
h.AssertEq(t, baseImage.IsSaved(), true)
// Expect 6 layers from the following locations:
// - 1 from defaultDirsLayer
// - 1 from lifecycleLayer
// - 2 from buildpacks
// - 1 from orderLayer
// - 1 from stackLayer
// - 1 from runImageLayer
h.AssertEq(t, baseImage.NumberOfAddedLayers(), 7)
})
when("duplicated buildpack, has different contents", func() {
var bp1v1Alt buildpack.BuildModule
var bp1v1AltWithNewContent buildpack.BuildModule
it.Before(func() {
var err error
bp1v1Alt, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.2"),
WithInfo: dist.ModuleInfo{
ID: "buildpack-1-id",
Version: "buildpack-1-version-1",
},
WithStacks: []dist.Stack{{
ID: "some.stack.id",
Mixins: []string{"mixinX", "mixinY"},
}},
}, 0644, ifakes.WithExtraBuildpackContents("coolbeans", "a file cool as beans"))
h.AssertNil(t, err)
bp1v1AltWithNewContent, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.2"),
WithInfo: dist.ModuleInfo{
ID: "buildpack-1-id",
Version: "buildpack-1-version-1",
},
WithStacks: []dist.Stack{{
ID: "some.stack.id",
Mixins: []string{"mixinX", "mixinY"},
}},
}, 0644, ifakes.WithExtraBuildpackContents("coolwatermelon", "a file cool as watermelon"))
h.AssertNil(t, err)
})
it("uses the whiteout layers", func() {
logger := logging.NewLogWithWriters(&outBuf, &outBuf, logging.WithVerbose())
subject.AddBuildpack(bp1v1Alt)
subject.AddBuildpack(bp1v1AltWithNewContent)
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertNil(t, err)
h.AssertEq(t, baseImage.IsSaved(), true)
oldPath := filepath.Join("/cnb", "buildpacks", "buildpack-1-id", "buildpack-1-version-1", "coolbeans")
layer, err := baseImage.FindLayerWithPath(oldPath)
h.AssertEq(t, layer, "")
h.AssertError(t, err, fmt.Sprintf("could not find '%s' in any layer", oldPath))
newPath := filepath.Join("/cnb", "buildpacks", "buildpack-1-id", "buildpack-1-version-1", "coolwatermelon")
layer, err = baseImage.FindLayerWithPath(newPath)
h.AssertNotEq(t, layer, "")
h.AssertNil(t, err)
})
it("uses the last buildpack", func() {
logger := logging.NewLogWithWriters(&outBuf, &outBuf, logging.WithVerbose())
subject.AddBuildpack(bp1v1)
subject.AddBuildpack(bp1v1Alt)
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertNil(t, err)
h.AssertEq(t, baseImage.IsSaved(), true)
// Expect 5 layers from the following locations:
// - 1 from defaultDirsLayer
// - 1 from lifecycleLayer
// - 1 from buildpacks
// - 1 from orderLayer
// - 1 from stackLayer
// - 1 from runImageLayer
h.AssertEq(t, baseImage.NumberOfAddedLayers(), 6)
oldSha256 := "2ba2e8563f7f43533ba26047a44f3e8bb7dd009043bd73a0e6aadb02c084955c"
newSha256 := "719faea06424d01bb5788ce63c1167e8d382b2d9df8fcf3a0a54ea9b2e3b4045"
if runtime.GOOS == "windows" {
newSha256 = "d99d31efba72ebf98e8101ada9e89464566e943c05367c561b116c2cb86837c9"
}
h.AssertContains(t, outBuf.String(), fmt.Sprintf(`buildpack 'buildpack-1-id@buildpack-1-version-1' was previously defined with different contents and will be overwritten
- previous diffID: 'sha256:%s'
- using diffID: 'sha256:%s'`, oldSha256, newSha256))
layer, err := baseImage.FindLayerWithPath(filepath.Join("/cnb", "buildpacks", "buildpack-1-id", "buildpack-1-version-1", "coolbeans"))
h.AssertNil(t, err)
bpLayer, err := os.Open(layer)
h.AssertNil(t, err)
defer bpLayer.Close()
hsh := sha256.New()
_, err = io.Copy(hsh, bpLayer)
h.AssertNil(t, err)
h.AssertEq(t, newSha256, fmt.Sprintf("%x", hsh.Sum(nil)))
})
})
when("adding buildpack that already exists on the image", func() {
it("skips adding buildpack that already exists", func() {
logger := logging.NewLogWithWriters(&outBuf, &outBuf, logging.WithVerbose())
diffID := "2ba2e8563f7f43533ba26047a44f3e8bb7dd009043bd73a0e6aadb02c084955c"
bpLayer := dist.ModuleLayers{
"buildpack-1-id": map[string]dist.ModuleLayerInfo{
"buildpack-1-version-1": {
API: api.MustParse("0.2"),
Stacks: nil,
Order: nil,
LayerDiffID: fmt.Sprintf("sha256:%s", diffID),
Homepage: "",
},
},
}
bpLayerString, err := json.Marshal(bpLayer)
h.AssertNil(t, err)
h.AssertNil(t, baseImage.SetLabel( // label builder as already having a buildpack with diffID `diffID`
dist.BuildpackLayersLabel,
string(bpLayerString),
))
subject.AddBuildpack(bp1v1)
err = subject.Save(logger, builder.CreatorMetadata{})
h.AssertNil(t, err)
fmt.Println(outBuf.String())
expectedLog := "Buildpack 'buildpack-1-id@buildpack-1-version-1' already exists on builder with same contents, skipping..."
h.AssertContains(t, outBuf.String(), expectedLog)
})
})
})
when("error adding buildpacks to builder", func() {
when("unable to convert buildpack to layer tar", func() {
var bp1v1Err buildpack.BuildModule
it.Before(func() {
var err error
bp1v1Err, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.2"),
WithInfo: dist.ModuleInfo{
ID: "buildpack-1-id",
Version: "buildpack-1-version-1",
},
WithStacks: []dist.Stack{{
ID: "some.stack.id",
Mixins: []string{"mixinX", "mixinY"},
}},
}, 0644, ifakes.WithBpOpenError(errors.New("unable to open buildpack")))
h.AssertNil(t, err)
})
it("errors", func() {
subject.AddBuildpack(bp1v1Err)
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertError(t, err, "unable to open buildpack")
})
})
})
when("modules are added in random order", func() {
var fakeLayerImage *h.FakeAddedLayerImage
it.Before(func() {
var err error
fakeLayerImage = &h.FakeAddedLayerImage{Image: baseImage}
subject, err = builder.New(fakeLayerImage, "some/builder")
h.AssertNil(t, err)
subject.SetLifecycle(mockLifecycle)
bp2v2, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
WithAPI: api.MustParse("0.2"),
WithInfo: dist.ModuleInfo{
ID: "buildpack-2-id",
Version: "buildpack-2-version-2",
},
WithStacks: []dist.Stack{{
ID: "some.stack.id",
Mixins: []string{"build:mixinA", "run:mixinB"},
}},
}, 0644)
h.AssertNil(t, err)
})
it("layers are written ordered by buildpacks ID & Version", func() {
// add buildpacks in a random order
subject.AddBuildpack(bp2v2)
subject.AddBuildpack(bp1v2)
subject.AddBuildpack(bp1v1)
subject.AddBuildpack(bp2v1)
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
layers := fakeLayerImage.AddedLayersOrder()
h.AssertEq(t, len(layers), 4)
h.AssertTrue(t, strings.Contains(layers[0], h.LayerFileName(bp1v1)))
h.AssertTrue(t, strings.Contains(layers[1], h.LayerFileName(bp1v2)))
h.AssertTrue(t, strings.Contains(layers[2], h.LayerFileName(bp2v1)))
h.AssertTrue(t, strings.Contains(layers[3], h.LayerFileName(bp2v2)))
})
it("extensions are written ordered by buildpacks ID & Version", func() {
// add buildpacks in a random order
subject.AddBuildpack(ext2v1)
subject.AddBuildpack(ext1v2)
subject.AddBuildpack(ext1v1)
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
layers := fakeLayerImage.AddedLayersOrder()
h.AssertEq(t, len(layers), 3)
h.AssertTrue(t, strings.Contains(layers[0], h.LayerFileName(ext1v1)))
h.AssertTrue(t, strings.Contains(layers[1], h.LayerFileName(ext1v2)))
h.AssertTrue(t, strings.Contains(layers[2], h.LayerFileName(ext2v1)))
})
})
when("system buildpacks", func() {
it.Before(func() {
subject.SetLifecycle(mockLifecycle)
subject.AddBuildpack(bp1v1)
subject.SetSystem(dist.System{
Pre: dist.SystemBuildpacks{
Buildpacks: []dist.ModuleRef{
{ModuleInfo: dist.ModuleInfo{ID: bp1v1.Descriptor().Info().ID}}},
},
})
})
it("should write system buildpacks to system.toml)", func() {
err := subject.Save(logger, builder.CreatorMetadata{})
h.AssertNil(t, err)
layerTar, err := baseImage.FindLayerWithPath("/cnb/system.toml")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/system.toml", h.ContentEquals(`[system]
[system.pre]
[[system.pre.buildpacks]]
id = "buildpack-1-id"
version = "buildpack-1-version-1"
`))
})
})
})
when("#SetLifecycle", func() {
it.Before(func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("should set the lifecycle version successfully", func() {
h.AssertEq(t, subject.LifecycleDescriptor().Info.Version.String(), "0.0.0")
})
it("should add the lifecycle binaries as an image layer", func() {
layerTar, err := baseImage.FindLayerWithPath("/cnb/lifecycle")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle",
h.IsDirectory(),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/detector",
h.ContentEquals("detector"),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/restorer",
h.ContentEquals("restorer"),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/analyzer",
h.ContentEquals("analyzer"),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/builder",
h.ContentEquals("builder"),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/exporter",
h.ContentEquals("exporter"),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/launcher",
h.ContentEquals("launcher"),
h.HasFileMode(0755),
h.HasModTime(archive.NormalizedDateTime),
)
it("should add lifecycle symlink", func() {
h.AssertOnTarEntry(t, layerTar, "/lifecycle",
h.SymlinksTo("/cnb/lifecycle"),
h.HasFileMode(0644),
h.HasModTime(archive.NormalizedDateTime),
)
})
})
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(), "0.0.0")
h.AssertEq(t, metadata.Lifecycle.API.BuildpackVersion.String(), "0.2")
h.AssertEq(t, metadata.Lifecycle.API.PlatformVersion.String(), "0.2")
h.AssertNotNil(t, metadata.Lifecycle.APIs)
h.AssertEq(t, metadata.Lifecycle.APIs.Buildpack.Deprecated.AsStrings(), []string{})
h.AssertEq(t, metadata.Lifecycle.APIs.Buildpack.Supported.AsStrings(), []string{"0.2", "0.3", "0.4", "0.9"})
h.AssertEq(t, metadata.Lifecycle.APIs.Platform.Deprecated.AsStrings(), []string{"0.2"})
h.AssertEq(t, metadata.Lifecycle.APIs.Platform.Supported.AsStrings(), []string{"0.3", "0.4"})
})
})
when("#AddBuildpack", func() {
it.Before(func() {
subject.AddBuildpack(bp1v1)
subject.AddBuildpack(bp1v2)
subject.AddBuildpack(bp2v1)
subject.AddBuildpack(bpOrder)
})
it("adds the buildpack as an image layer", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
assertImageHasBPLayer(t, baseImage, bp1v1)
assertImageHasBPLayer(t, baseImage, bp1v2)
assertImageHasBPLayer(t, baseImage, bp2v1)
assertImageHasOrderBpLayer(t, baseImage, bpOrder)
})
it("adds the buildpack metadata", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
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, len(metadata.Buildpacks), 4)
h.AssertEq(t, metadata.Buildpacks[0].ID, "buildpack-1-id")
h.AssertEq(t, metadata.Buildpacks[0].Version, "buildpack-1-version-1")
h.AssertEq(t, metadata.Buildpacks[1].ID, "buildpack-1-id")
h.AssertEq(t, metadata.Buildpacks[1].Version, "buildpack-1-version-2")
h.AssertEq(t, metadata.Buildpacks[2].ID, "buildpack-2-id")
h.AssertEq(t, metadata.Buildpacks[2].Version, "buildpack-2-version-1")
h.AssertEq(t, metadata.Buildpacks[3].ID, "order-buildpack-id")
h.AssertEq(t, metadata.Buildpacks[3].Version, "order-buildpack-version")
})
it("adds the buildpack layers label", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
label, err := baseImage.Label("io.buildpacks.buildpack.layers")
h.AssertNil(t, err)
var layers dist.ModuleLayers
h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
h.AssertEq(t, len(layers), 3)
h.AssertEq(t, len(layers["buildpack-1-id"]), 2)
h.AssertEq(t, len(layers["buildpack-2-id"]), 1)
h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-1"].Order), 0)
h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-1"].Stacks), 1)
h.AssertEq(t, layers["buildpack-1-id"]["buildpack-1-version-1"].Stacks[0].ID, "some.stack.id")
h.AssertSliceContainsOnly(t, layers["buildpack-1-id"]["buildpack-1-version-1"].Stacks[0].Mixins, "mixinX", "mixinY")
h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-2"].Order), 0)
h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-2"].Stacks), 1)
h.AssertEq(t, layers["buildpack-1-id"]["buildpack-1-version-2"].Stacks[0].ID, "some.stack.id")
h.AssertSliceContainsOnly(t, layers["buildpack-1-id"]["buildpack-1-version-2"].Stacks[0].Mixins, "mixinX", "mixinY")
h.AssertEq(t, len(layers["buildpack-2-id"]["buildpack-2-version-1"].Order), 0)
h.AssertEq(t, len(layers["buildpack-2-id"]["buildpack-2-version-1"].Stacks), 1)
h.AssertEq(t, layers["buildpack-2-id"]["buildpack-2-version-1"].Stacks[0].ID, "some.stack.id")
h.AssertSliceContainsOnly(t, layers["buildpack-2-id"]["buildpack-2-version-1"].Stacks[0].Mixins, "build:mixinA", "run:mixinB")
h.AssertEq(t, len(layers["order-buildpack-id"]["order-buildpack-version"].Order), 1)
h.AssertEq(t, len(layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group), 2)
h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[0].ID, "buildpack-1-id")
h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[0].Version, "buildpack-1-version-1")
h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[0].Optional, true)
h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[1].ID, "buildpack-2-id")
h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[1].Version, "buildpack-2-version-1")
h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[1].Optional, false)
})
when("base image already has buildpack layers label", func() {
it.Before(func() {
var mdJSON bytes.Buffer
h.AssertNil(t, json.Compact(
&mdJSON,
[]byte(`{
"buildpack-1-id": {
"buildpack-1-version-1": {
"layerDiffID": "sha256:buildpack-1-version-1-diff-id"
},
"buildpack-1-version-2": {
"layerDiffID": "sha256:buildpack-1-version-2-diff-id"
}
}
}
`)))
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.buildpack.layers",
mdJSON.String(),
))
var err error
subject, err = builder.New(baseImage, "some/builder")
h.AssertNil(t, err)
subject.AddBuildpack(bp1v2)
subject.AddBuildpack(bp2v1)
subject.SetLifecycle(mockLifecycle)
})
it("appends buildpack layer info", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
label, err := baseImage.Label("io.buildpacks.buildpack.layers")
h.AssertNil(t, err)
var layers dist.ModuleLayers
h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
h.AssertEq(t, len(layers), 2)
h.AssertEq(t, len(layers["buildpack-1-id"]), 2)
h.AssertEq(t, len(layers["buildpack-2-id"]), 1)
h.AssertEq(t, layers["buildpack-1-id"]["buildpack-1-version-1"].LayerDiffID, "sha256:buildpack-1-version-1-diff-id")
h.AssertUnique(t,
layers["buildpack-1-id"]["buildpack-1-version-1"].LayerDiffID,
layers["buildpack-1-id"]["buildpack-1-version-2"].LayerDiffID,
layers["buildpack-2-id"]["buildpack-2-version-1"].LayerDiffID,
)
h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-1"].Order), 0)
h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-2"].Order), 0)
h.AssertEq(t, len(layers["buildpack-2-id"]["buildpack-2-version-1"].Order), 0)
})
it("informs when overriding existing buildpack, and log level is DEBUG", func() {
logger := logging.NewLogWithWriters(&outBuf, &outBuf, logging.WithVerbose())
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
label, err := baseImage.Label("io.buildpacks.buildpack.layers")
h.AssertNil(t, err)
var layers dist.ModuleLayers
h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
h.AssertContains(t,
outBuf.String(),
"buildpack 'buildpack-1-id@buildpack-1-version-2' already exists on builder and will be overwritten",
)
h.AssertNotContains(t, layers["buildpack-1-id"]["buildpack-1-version-2"].LayerDiffID, "buildpack-1-version-2-diff-id")
})
it("doesn't message when overriding existing buildpack when log level is INFO", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
label, err := baseImage.Label("io.buildpacks.buildpack.layers")
h.AssertNil(t, err)
var layers dist.ModuleLayers
h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
h.AssertNotContains(t,
outBuf.String(),
"buildpack 'buildpack-1-id@buildpack-1-version-2' already exists on builder and will be overwritten",
)
h.AssertNotContains(t, layers["buildpack-1-id"]["buildpack-1-version-2"].LayerDiffID, "buildpack-1-version-2-diff-id")
})
})
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"]}},
"lifecycle":{"version":"6.6.6","apis":{"buildpack":{"deprecated":["0.1"],"supported":["0.2","0.3"]},"platform":{"deprecated":[],"supported":["2.3","2.4"]}}}
}`,
))
var err error
subject, err = builder.New(baseImage, "some/builder")
h.AssertNil(t, err)
subject.AddBuildpack(bp1v1)
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("appends the buildpack to 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, len(metadata.Buildpacks), 2)
// keeps original metadata
h.AssertEq(t, metadata.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.LifecycleDescriptor().Info.Version.String(), "6.6.6")
// adds new buildpack
h.AssertEq(t, metadata.Buildpacks[1].ID, "buildpack-1-id")
h.AssertEq(t, metadata.Buildpacks[1].Version, "buildpack-1-version-1")
})
})
})
when("#AddExtension", func() {
it.Before(func() {
subject.AddExtension(ext1v1)
subject.AddExtension(ext1v2)
subject.AddExtension(ext2v1)
})
it("adds the extension as an image layer", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
assertImageHasExtLayer(t, baseImage, ext1v1)
assertImageHasExtLayer(t, baseImage, ext1v2)
assertImageHasExtLayer(t, baseImage, ext2v1)
})
it("adds the extension metadata", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
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, len(metadata.Extensions), 3)
h.AssertEq(t, metadata.Extensions[0].ID, "extension-1-id")
h.AssertEq(t, metadata.Extensions[0].Version, "extension-1-version-1")
h.AssertEq(t, metadata.Extensions[1].ID, "extension-1-id")
h.AssertEq(t, metadata.Extensions[1].Version, "extension-1-version-2")
h.AssertEq(t, metadata.Extensions[2].ID, "extension-2-id")
h.AssertEq(t, metadata.Extensions[2].Version, "extension-2-version-1")
})
it("adds the extension layers label", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
label, err := baseImage.Label("io.buildpacks.extension.layers")
h.AssertNil(t, err)
var layers dist.ModuleLayers
h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
h.AssertEq(t, len(layers), 2)
h.AssertEq(t, len(layers["extension-1-id"]), 2)
h.AssertEq(t, len(layers["extension-2-id"]), 1)
h.AssertEq(t, layers["extension-1-id"]["extension-1-version-1"].API.String(), "0.9")
h.AssertEq(t, layers["extension-1-id"]["extension-1-version-2"].API.String(), "0.9")
h.AssertEq(t, layers["extension-2-id"]["extension-2-version-1"].API.String(), "0.9")
})
when("base image already has extension layers label", func() {
it.Before(func() {
var mdJSON bytes.Buffer
h.AssertNil(t, json.Compact(
&mdJSON,
[]byte(`{
"extension-1-id": {
"extension-1-version-1": {
"layerDiffID": "sha256:extension-1-version-1-diff-id"
},
"extension-1-version-2": {
"layerDiffID": "sha256:extension-1-version-2-diff-id"
}
}
}
`)))
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.extension.layers",
mdJSON.String(),
))
var err error
subject, err = builder.New(baseImage, "some/builder")
h.AssertNil(t, err)
subject.AddExtension(ext1v2)
subject.AddExtension(ext2v1)
subject.SetLifecycle(mockLifecycle)
})
it("appends extension layer info", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
label, err := baseImage.Label("io.buildpacks.extension.layers")
h.AssertNil(t, err)
var layers dist.ModuleLayers
h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
h.AssertEq(t, len(layers), 2)
h.AssertEq(t, len(layers["extension-1-id"]), 2)
h.AssertEq(t, len(layers["extension-2-id"]), 1)
h.AssertEq(t, layers["extension-1-id"]["extension-1-version-1"].LayerDiffID, "sha256:extension-1-version-1-diff-id")
h.AssertUnique(t,
layers["extension-1-id"]["extension-1-version-1"].LayerDiffID,
layers["extension-1-id"]["extension-1-version-2"].LayerDiffID,
layers["extension-2-id"]["extension-2-version-1"].LayerDiffID,
)
})
it("informs when overriding existing extension, and log level is DEBUG", func() {
logger := logging.NewLogWithWriters(&outBuf, &outBuf, logging.WithVerbose())
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
label, err := baseImage.Label("io.buildpacks.extension.layers")
h.AssertNil(t, err)
var layers dist.ModuleLayers
h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
h.AssertContains(t,
outBuf.String(),
"extension 'extension-1-id@extension-1-version-2' already exists on builder and will be overwritten",
)
h.AssertNotContains(t, layers["extension-1-id"]["extension-1-version-2"].LayerDiffID, "extension-1-version-2-diff-id")
})
it("doesn't message when overriding existing extension when log level is INFO", func() {
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
label, err := baseImage.Label("io.buildpacks.extension.layers")
h.AssertNil(t, err)
var layers dist.ModuleLayers
h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
h.AssertNotContains(t,
outBuf.String(),
"extension 'extension-1-id@extension-1-version-2' already exists on builder and will be overwritten",
)
h.AssertNotContains(t, layers["extension-1-id"]["extension-1-version-2"].LayerDiffID, "extension-1-version-2-diff-id")
})
})
when("base image already has metadata", func() {
it.Before(func() {
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.builder.metadata",
`{
"extensions":[{"id":"prev.id"}],
"lifecycle":{"version":"6.6.6","apis":{"buildpack":{"deprecated":["0.1"],"supported":["0.2","0.3","0.9"]},"platform":{"deprecated":[],"supported":["2.3","2.4"]}}}
}`,
))
var err error
subject, err = builder.New(baseImage, "some/builder")
h.AssertNil(t, err)
subject.AddExtension(ext1v1)
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("appends the extensions to 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, len(metadata.Extensions), 2)
// keeps original metadata
h.AssertEq(t, metadata.Extensions[0].ID, "prev.id")
h.AssertEq(t, subject.LifecycleDescriptor().Info.Version.String(), "6.6.6")
// adds new extension
h.AssertEq(t, metadata.Extensions[1].ID, "extension-1-id")
h.AssertEq(t, metadata.Extensions[1].Version, "extension-1-version-1")
})
})
})
when("#SetOrder", func() {
when("the buildpacks exist in the image", func() {
it.Before(func() {
subject.AddBuildpack(bp1v1)
subject.AddBuildpack(bp2v1)
subject.SetOrder(dist.Order{
{Group: []dist.ModuleRef{
{
ModuleInfo: dist.ModuleInfo{
ID: bp1v1.Descriptor().Info().ID,
// Version excluded intentionally
},
},
{
ModuleInfo: bp2v1.Descriptor().Info(),
Optional: true,
},
}},
})
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("adds the order.toml to the image", func() {
layerTar, err := baseImage.FindLayerWithPath("/cnb/order.toml")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/order.toml",
h.ContentEquals(`[[order]]
[[order.group]]
id = "buildpack-1-id"
version = "buildpack-1-version-1"
[[order.group]]
id = "buildpack-2-id"
version = "buildpack-2-version-1"
optional = true
`),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("adds the order to the order label", func() {
label, err := baseImage.Label("io.buildpacks.buildpack.order")
h.AssertNil(t, err)
var order dist.Order
h.AssertNil(t, json.Unmarshal([]byte(label), &order))
h.AssertEq(t, len(order), 1)
h.AssertEq(t, len(order[0].Group), 2)
h.AssertEq(t, order[0].Group[0].ID, "buildpack-1-id")
h.AssertEq(t, order[0].Group[0].Version, "")
h.AssertEq(t, order[0].Group[0].Optional, false)
h.AssertEq(t, order[0].Group[1].ID, "buildpack-2-id")
h.AssertEq(t, order[0].Group[1].Version, "buildpack-2-version-1")
h.AssertEq(t, order[0].Group[1].Optional, true)
})
})
})
when("#SetOrderExtensions", func() {
when("the extensions exist in the image", func() {
it.Before(func() {
subject.AddExtension(ext1v1)
subject.AddExtension(ext2v1)
subject.SetOrderExtensions(dist.Order{
{Group: []dist.ModuleRef{
{
ModuleInfo: dist.ModuleInfo{
ID: ext1v1.Descriptor().Info().ID,
// Version excluded intentionally
},
},
{
ModuleInfo: ext2v1.Descriptor().Info(),
Optional: true, // extensions are always optional; this shouldn't be redundantly printed
},
}},
})
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("adds the order.toml to the image", func() {
layerTar, err := baseImage.FindLayerWithPath("/cnb/order.toml")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/order.toml",
h.ContentEquals(`[[order-extensions]]
[[order-extensions.group]]
id = "extension-1-id"
version = "extension-1-version-1"
[[order-extensions.group]]
id = "extension-2-id"
version = "extension-2-version-1"
`),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("adds the order for extensions to the order-extensions label", func() {
label, err := baseImage.Label("io.buildpacks.buildpack.order-extensions")
h.AssertNil(t, err)
var orderExt dist.Order
h.AssertNil(t, json.Unmarshal([]byte(label), &orderExt))
h.AssertEq(t, len(orderExt), 1)
h.AssertEq(t, len(orderExt[0].Group), 2)
h.AssertEq(t, orderExt[0].Group[0].ID, "extension-1-id")
h.AssertEq(t, orderExt[0].Group[0].Version, "")
h.AssertEq(t, orderExt[0].Group[0].Optional, false)
h.AssertEq(t, orderExt[0].Group[1].ID, "extension-2-id")
h.AssertEq(t, orderExt[0].Group[1].Version, "extension-2-version-1")
h.AssertEq(t, orderExt[0].Group[1].Optional, false)
})
})
})
when("#SetDescription", func() {
it.Before(func() {
subject.SetDescription("Some description")
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("sets the description 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.Description, "Some description")
})
})
when("#SetStack", func() {
it.Before(func() {
subject.SetStack(pubbldr.StackConfig{
RunImage: "some/run",
RunImageMirrors: []string{"some/mirror", "other/mirror"},
})
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("adds the stack.toml to the image", func() {
layerTar, err := baseImage.FindLayerWithPath("/cnb/stack.toml")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/stack.toml",
h.ContentEquals(`[run-image]
image = "some/run"
mirrors = ["some/mirror", "other/mirror"]
`),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("adds the stack to 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.Stack.RunImage.Image, "some/run")
h.AssertEq(t, metadata.Stack.RunImage.Mirrors[0], "some/mirror")
h.AssertEq(t, metadata.Stack.RunImage.Mirrors[1], "other/mirror")
})
})
when("#SetRunImage", func() {
it.Before(func() {
subject.SetRunImage(pubbldr.RunConfig{Images: []pubbldr.RunImageConfig{{
Image: "some/run",
Mirrors: []string{"some/mirror", "other/mirror"},
}}})
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("adds the run.toml to the image", func() {
layerTar, err := baseImage.FindLayerWithPath("/cnb/run.toml")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/run.toml",
h.ContentEquals(`[[images]]
image = "some/run"
mirrors = ["some/mirror", "other/mirror"]
`),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("adds the stack.toml to the image", func() {
layerTar, err := baseImage.FindLayerWithPath("/cnb/stack.toml")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/stack.toml",
h.ContentEquals(`[run-image]
image = "some/run"
mirrors = ["some/mirror", "other/mirror"]
`),
h.HasModTime(archive.NormalizedDateTime),
)
})
it("adds the run image to 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.RunImages[0].Image, "some/run")
h.AssertEq(t, metadata.RunImages[0].Mirrors[0], "some/mirror")
h.AssertEq(t, metadata.RunImages[0].Mirrors[1], "other/mirror")
})
})
when("when CNB_BUILD_CONFIG_DIR is defined", func() {
var buildConfigEnvName = "CNB_BUILD_CONFIG_DIR"
var buildConfigEnvValue = "/cnb/dup-build-config-dir"
it.Before(func() {
os.Setenv(buildConfigEnvName, buildConfigEnvValue)
subject.SetBuildConfigEnv(map[string]string{
"SOME_KEY": "some-val",
"OTHER_KEY.append": "other-val",
"OTHER_KEY.delim": ":",
})
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it.After(func() {
os.Unsetenv(buildConfigEnvName)
})
it("adds the env vars as files to the image", func() {
layerTar, err := baseImage.FindLayerWithPath(buildConfigEnvValue + "/env/SOME_KEY")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, buildConfigEnvValue+"/env/SOME_KEY",
h.ContentEquals(`some-val`),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, buildConfigEnvValue+"/env/OTHER_KEY.append",
h.ContentEquals(`other-val`),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, buildConfigEnvValue+"/env/OTHER_KEY.delim",
h.ContentEquals(`:`),
h.HasModTime(archive.NormalizedDateTime),
)
})
})
when("#SetBuildConfigEnv", func() {
it.Before(func() {
os.Unsetenv("CNB_BUILD_CONFIG_DIR")
subject.SetBuildConfigEnv(map[string]string{
"SOME_KEY": "some-val",
"OTHER_KEY.append": "other-val",
"OTHER_KEY.delim": ":",
})
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("adds the env vars as files to the image", func() {
layerTar, err := baseImage.FindLayerWithPath("/cnb/build-config/env/SOME_KEY")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/cnb/build-config/env/SOME_KEY",
h.ContentEquals(`some-val`),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, "/cnb/build-config/env/OTHER_KEY.append",
h.ContentEquals(`other-val`),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, "/cnb/build-config/env/OTHER_KEY.delim",
h.ContentEquals(`:`),
h.HasModTime(archive.NormalizedDateTime),
)
})
})
when("#SetEnv", func() {
it.Before(func() {
subject.SetEnv(map[string]string{
"SOME_KEY": "some-val",
"OTHER_KEY": "other-val",
})
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("adds the env vars as files to the image", func() {
layerTar, err := baseImage.FindLayerWithPath("/platform/env/SOME_KEY")
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, "/platform/env/SOME_KEY",
h.ContentEquals(`some-val`),
h.HasModTime(archive.NormalizedDateTime),
)
h.AssertOnTarEntry(t, layerTar, "/platform/env/OTHER_KEY",
h.ContentEquals(`other-val`),
h.HasModTime(archive.NormalizedDateTime),
)
})
})
when("#DefaultRunImage", func() {
it.Before(func() {
subject.SetRunImage(pubbldr.RunConfig{Images: []pubbldr.RunImageConfig{{
Image: "some/run",
Mirrors: []string{"some/mirror", "other/mirror"},
}}})
h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
h.AssertEq(t, baseImage.IsSaved(), true)
})
it("adds the run.toml to the image", func() {
actual := subject.DefaultRunImage()
h.AssertEq(t, actual.Image, "some/run")
h.AssertEq(t, actual.Mirrors, []string{"some/mirror", "other/mirror"})
})
})
})
when("builder exists", func() {
var builderImage imgutil.Image
it.Before(func() {
h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.id", "some.stack.id"))
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.mixins", `["mixinX", "mixinY", "build:mixinA"]`))
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.builder.metadata",
`{"description": "some-description", "createdBy": {"name": "some-name", "version": "1.2.3"}, "buildpacks": [{"id": "buildpack-1-id"}, {"id": "buildpack-2-id"}], "groups": [{"buildpacks": [{"id": "buildpack-1-id", "version": "buildpack-1-version", "optional": false}, {"id": "buildpack-2-id", "version": "buildpack-2-version-1", "optional": true}]}], "stack": {"runImage": {"image": "prev/run", "mirrors": ["prev/mirror"]}}, "lifecycle": {"version": "6.6.6"}}`,
))
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.buildpack.order",
`[{"group": [{"id": "buildpack-1-id", "optional": false}, {"id": "buildpack-2-id", "version": "buildpack-2-version-1", "optional": true}]}]`,
))
builderImage = baseImage
})
when("#FromImage", func() {
var bldr *builder.Builder
it.Before(func() {
var err error
bldr, err = builder.FromImage(builderImage)
h.AssertNil(t, err)
})
it("gets builder from image", func() {
h.AssertEq(t, bldr.Buildpacks()[0].ID, "buildpack-1-id")
h.AssertEq(t, bldr.Buildpacks()[1].ID, "buildpack-2-id")
order := bldr.Order()
h.AssertEq(t, len(order), 1)
h.AssertEq(t, len(order[0].Group), 2)
h.AssertEq(t, order[0].Group[0].ID, "buildpack-1-id")
h.AssertEq(t, order[0].Group[0].Version, "")
h.AssertEq(t, order[0].Group[0].Optional, false)
h.AssertEq(t, order[0].Group[1].ID, "buildpack-2-id")
h.AssertEq(t, order[0].Group[1].Version, "buildpack-2-version-1")
h.AssertEq(t, order[0].Group[1].Optional, true)
})
it("gets mixins from image", func() {
h.AssertSliceContainsOnly(t, bldr.Mixins(), "mixinX", "mixinY", "build:mixinA")
})
when("metadata is missing", func() {
it.Before(func() {
h.AssertNil(t, builderImage.SetLabel(
"io.buildpacks.builder.metadata",
"",
))
})
it("should error", func() {
_, err := builder.FromImage(builderImage)
h.AssertError(t, err, "missing label 'io.buildpacks.builder.metadata'")
})
})
when("#Description", func() {
it("return description", func() {
h.AssertEq(t, bldr.Description(), "some-description")
})
})
when("#CreatedBy", func() {
it("return CreatedBy", func() {
expectedCreatorMetadata := builder.CreatorMetadata{
Name: "some-name",
Version: "1.2.3",
}
h.AssertEq(t, bldr.CreatedBy(), expectedCreatorMetadata)
})
})
when("#Name", func() {
it("return Name", func() {
h.AssertEq(t, bldr.Name(), "base/image")
})
})
when("#Image", func() {
it("return Image", func() {
h.AssertSameInstance(t, bldr.Image(), baseImage)
})
})
when("#Stack", func() {
it("return Stack", func() {
expectedStack := builder.StackMetadata{
RunImage: builder.RunImageMetadata{
Image: "prev/run",
Mirrors: []string{"prev/mirror"}}}
h.AssertEq(t, bldr.Stack(), expectedStack)
})
})
when("#UID", func() {
it("return UID", func() {
h.AssertEq(t, bldr.UID(), 1234)
})
})
when("#GID", func() {
it("return GID", func() {
h.AssertEq(t, bldr.GID(), 4321)
})
})
when("#BaseImageName", func() {
it("return name of base image", func() {
h.AssertEq(t, bldr.BaseImageName(), "base/image")
})
})
})
when("#New", func() {
when("#WithRunImage", func() {
// Current runImage information in builder image:
// "stack": {"runImage": {"image": "prev/run", "mirrors": ["prev/mirror"]}}
var newBuilder *builder.Builder
newRunImage := "another/run"
it.Before(func() {
var err error
newBuilder, err = builder.New(builderImage, "newBuilder/image", builder.WithRunImage(newRunImage))
h.AssertNil(t, err)
})
it("overrides the run image metadata (which becomes run.toml)", func() {
// RunImages() returns Stacks + RunImages metadata.
metadata := newBuilder.RunImages()
h.AssertTrue(t, len(metadata) == 2)
for _, m := range metadata {
// Both images must be equal to the expected run-image
h.AssertEq(t, m.Image, newRunImage)
h.AssertEq(t, len(m.Mirrors), 0)
}
})
})
})
})
when("flatten", func() {
var (
bldr *builder.Builder
builderImage imgutil.Image
deps []buildpack.BuildModule
)
it.Before(func() {
h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.id", "some.stack.id"))
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.mixins", `["mixinX", "mixinY", "build:mixinA"]`))
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.builder.metadata",
`{"description": "some-description", "createdBy": {"name": "some-name", "version": "1.2.3"}, "buildpacks": [{"id": "buildpack-1-id"}, {"id": "buildpack-2-id"}], "groups": [{"buildpacks": [{"id": "buildpack-1-id", "version": "buildpack-1-version", "optional": false}, {"id": "buildpack-2-id", "version": "buildpack-2-version-1", "optional": true}]}], "stack": {"runImage": {"image": "prev/run", "mirrors": ["prev/mirror"]}}, "lifecycle": {"version": "6.6.6"}}`,
))
h.AssertNil(t, baseImage.SetLabel(
"io.buildpacks.buildpack.order",
`[{"group": [{"id": "buildpack-1-id", "optional": false}, {"id": "buildpack-2-id", "version": "buildpack-2-version-1", "optional": true}]}]`,
))
builderImage = baseImage
deps = []buildpack.BuildModule{bp2v1, bp1v2}
})
when("buildpacks to be flattened are NOT defined", func() {
it.Before(func() {
var err error
bldr, err = builder.New(builderImage, "some-builder")
h.AssertNil(t, err)
// Let's add the buildpacks
bldr.AddBuildpacks(bp1v1, deps)
})
when("#FlattenedModules", func() {
it("it return an empty array", func() {
h.AssertEq(t, len(bldr.FlattenedModules(buildpack.KindBuildpack)), 0)
})
})
when("#AllModules", func() {
it("it returns each buildpack individually", func() {
h.AssertEq(t, len(bldr.AllModules(buildpack.KindBuildpack)), 3)
})
})
when("#ShouldFlatten", func() {
it("it returns false for each buildpack", func() {
h.AssertFalse(t, bldr.ShouldFlatten(bp1v1))
h.AssertFalse(t, bldr.ShouldFlatten(bp2v1))
h.AssertFalse(t, bldr.ShouldFlatten(bp1v2))
})
})
})
when("buildpacks to be flattened are defined", func() {
it.Before(func() {
var err error
flattenModules, err := buildpack.ParseFlattenBuildModules([]string{"buildpack-1-id@buildpack-1-version-1,buildpack-1-id@buildpack-1-version-2,buildpack-2-id@buildpack-2-version-1"})
h.AssertNil(t, err)
bldr, err = builder.New(builderImage, "some-builder", builder.WithFlattened(flattenModules))
h.AssertNil(t, err)
// Let's add the buildpacks
bldr.AddBuildpacks(bp1v1, deps)
})
when("#FlattenedModules", func() {
it("it return one array with all buildpacks on it", func() {
h.AssertEq(t, len(bldr.FlattenedModules(buildpack.KindBuildpack)), 1)
h.AssertEq(t, len(bldr.FlattenedModules(buildpack.KindBuildpack)[0]), 3)
})
})
when("#AllModules", func() {
it("it returns each buildpack individually", func() {
h.AssertEq(t, len(bldr.AllModules(buildpack.KindBuildpack)), 3)
})
})
when("#ShouldFlatten", func() {
it("it returns true for each buildpack", func() {
h.AssertTrue(t, bldr.ShouldFlatten(bp1v1))
h.AssertTrue(t, bldr.ShouldFlatten(bp2v1))
h.AssertTrue(t, bldr.ShouldFlatten(bp1v2))
})
})
})
})
when("labels", func() {
var (
customLabels, imageLabels map[string]string
err error
)
it.Before(func() {
h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.id", "some.stack.id"))
h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.mixins", `["mixinX", "mixinY", "build:mixinA"]`))
})
it.After(func() {
h.AssertNilE(t, baseImage.Cleanup())
})
it("should set labels to the image", func() {
customLabels = map[string]string{"test.label.one": "1", "test.label.two": "2"}
subject, err = builder.New(baseImage, "some/builder", builder.WithLabels(customLabels))
h.AssertNil(t, err)
imageLabels, err = baseImage.Labels()
h.AssertNil(t, err)
h.AssertEq(t, imageLabels["test.label.one"], "1")
h.AssertEq(t, imageLabels["test.label.two"], "2")
})
})
}
func assertImageHasBPLayer(t *testing.T, image *fakes.Image, bp buildpack.BuildModule) {
t.Helper()
dirPath := fmt.Sprintf("/cnb/buildpacks/%s/%s", bp.Descriptor().Info().ID, bp.Descriptor().Info().Version)
layerTar, err := image.FindLayerWithPath(dirPath)
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, dirPath,
h.IsDirectory(),
)
h.AssertOnTarEntry(t, layerTar, path.Dir(dirPath),
h.IsDirectory(),
)
h.AssertOnTarEntry(t, layerTar, dirPath+"/bin/build",
h.ContentEquals("build-contents"),
)
h.AssertOnTarEntry(t, layerTar, dirPath+"/bin/detect",
h.ContentEquals("detect-contents"),
)
}
func assertImageHasExtLayer(t *testing.T, image *fakes.Image, ext buildpack.BuildModule) {
t.Helper()
dirPath := fmt.Sprintf("/cnb/extensions/%s/%s", ext.Descriptor().Info().ID, ext.Descriptor().Info().Version)
layerTar, err := image.FindLayerWithPath(dirPath)
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, dirPath,
h.IsDirectory(),
)
h.AssertOnTarEntry(t, layerTar, path.Dir(dirPath),
h.IsDirectory(),
)
h.AssertOnTarEntry(t, layerTar, dirPath+"/bin/generate",
h.ContentEquals("generate-contents"),
)
h.AssertOnTarEntry(t, layerTar, dirPath+"/bin/detect",
h.ContentEquals("detect-contents"),
)
}
func assertImageHasOrderBpLayer(t *testing.T, image *fakes.Image, bp buildpack.BuildModule) {
t.Helper()
dirPath := fmt.Sprintf("/cnb/buildpacks/%s/%s", bp.Descriptor().Info().ID, bp.Descriptor().Info().Version)
layerTar, err := image.FindLayerWithPath(dirPath)
h.AssertNil(t, err)
h.AssertOnTarEntry(t, layerTar, dirPath,
h.IsDirectory(),
)
h.AssertOnTarEntry(t, layerTar, path.Dir(dirPath),
h.IsDirectory(),
)
}