Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EnsureTargetSupport to BuildpackDescriptor #1713

Merged
merged 12 commits into from
Apr 12, 2023
60 changes: 47 additions & 13 deletions internal/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"github.com/buildpacks/pack/pkg/buildpack"
"github.com/buildpacks/pack/pkg/dist"
"github.com/buildpacks/pack/pkg/logging"

lifecycleplatform "github.com/buildpacks/lifecycle/platform"
)

const (
Expand Down Expand Up @@ -234,9 +236,16 @@ func (b *Builder) Stack() StackMetadata {
return b.metadata.Stack
}

// RunImages returns the run image metadata
// RunImages returns all run image metadata
func (b *Builder) RunImages() []RunImageMetadata {
return b.metadata.RunImages
return append(b.metadata.RunImages, b.Stack().RunImage)
}

// DefaultRunImage returns the default run image metadata
func (b *Builder) DefaultRunImage() RunImageMetadata {
// run.images are ensured in builder.ValidateConfig()
// per the spec, we use the first one as the default
return b.RunImages()[0]
}

// Mixins returns the mixins of the builder
Expand Down Expand Up @@ -360,7 +369,7 @@ func (b *Builder) Save(logger logging.Logger, creatorMetadata CreatorMetadata) e
}
}

if err := validateBuildpacks(b.StackID, b.Mixins(), b.LifecycleDescriptor(), b.Buildpacks(), b.additionalBuildpacks); err != nil {
if err := b.validateBuildpacks(); err != nil {
return errors.Wrap(err, "validating buildpacks")
}

Expand Down Expand Up @@ -633,24 +642,20 @@ func hasElementWithVersion(moduleList []dist.ModuleInfo, version string) bool {
return false
}

func validateBuildpacks(stackID string, mixins []string, lifecycleDescriptor LifecycleDescriptor, allBuildpacks []dist.ModuleInfo, bpsToValidate []buildpack.BuildModule) error {
func (b *Builder) validateBuildpacks() error {
bpLookup := map[string]interface{}{}

for _, bp := range allBuildpacks {
for _, bp := range b.Buildpacks() {
bpLookup[bp.FullName()] = nil
}

for _, bp := range bpsToValidate {
for _, bp := range b.additionalBuildpacks {
bpd := bp.Descriptor()
if err := validateLifecycleCompat(bpd, lifecycleDescriptor); err != nil {
if err := validateLifecycleCompat(bpd, b.LifecycleDescriptor()); err != nil {
return err
}

if len(bpd.Stacks()) >= 1 { // standard buildpack
if err := bpd.EnsureStackSupport(stackID, mixins, false); err != nil {
return err
}
} else { // order buildpack
if len(bpd.Order()) > 0 { // order buildpack
for _, g := range bpd.Order() {
for _, r := range g.Group {
if _, ok := bpLookup[r.FullName()]; !ok {
Expand All @@ -661,6 +666,30 @@ func validateBuildpacks(stackID string, mixins []string, lifecycleDescriptor Lif
}
}
}
} else if err := bpd.EnsureStackSupport(b.StackID, b.Mixins(), false); err != nil {
return err
} else {
buildOS, err := b.Image().OS()
if err != nil {
return err
}
buildArch, err := b.Image().Architecture()
if err != nil {
return err
}
buildDistroName, err := b.Image().Label(lifecycleplatform.OSDistributionNameLabel)
if err != nil {
return err
}
buildDistroVersion, err := b.Image().Label(lifecycleplatform.OSDistributionVersionLabel)
if err != nil {
return err
}
if err := bpd.EnsureTargetSupport(buildOS, buildArch, buildDistroName, buildDistroVersion); err != nil {
return err
}

// TODO ensure at least one run-image
}
}

Expand Down Expand Up @@ -903,7 +932,12 @@ func orderFileContents(order dist.Order, orderExt dist.Order) (string, error) {

func (b *Builder) stackLayer(dest string) (string, error) {
buf := &bytes.Buffer{}
err := toml.NewEncoder(buf).Encode(b.metadata.Stack)
var err error
if b.metadata.Stack.RunImage.Image != "" {
err = toml.NewEncoder(buf).Encode(b.metadata.Stack)
} else if len(b.metadata.RunImages) > 0 {
err = toml.NewEncoder(buf).Encode(b.metadata.RunImages[0])
}
if err != nil {
return "", errors.Wrapf(err, "failed to marshal stack.toml")
}
Expand Down
25 changes: 21 additions & 4 deletions internal/builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -766,10 +766,10 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
// - 1 from stackLayer
// - 1 from runImageLayer
h.AssertEq(t, baseImage.NumberOfAddedLayers(), 6)
oldSha256 := "4dc0072c61fc2bd7118bbc93a432eae0012082de094455cf0a9fed20e3c44789"
newSha256 := "29cb2bce4c2350f0e86f3dd30fa3810beb409b910126a18651de750f457fedfb"
oldSha256 := "2ba2e8563f7f43533ba26047a44f3e8bb7dd009043bd73a0e6aadb02c084955c"
newSha256 := "719faea06424d01bb5788ce63c1167e8d382b2d9df8fcf3a0a54ea9b2e3b4045"
if runtime.GOOS == "windows" {
newSha256 = "eaed4a1617bba5738ae5672f6aefda8add7abb2f8630c75dc97a6232879d4ae4"
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
Expand All @@ -794,7 +794,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
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 := "4dc0072c61fc2bd7118bbc93a432eae0012082de094455cf0a9fed20e3c44789"
diffID := "2ba2e8563f7f43533ba26047a44f3e8bb7dd009043bd73a0e6aadb02c084955c"
bpLayer := dist.ModuleLayers{
"buildpack-1-id": map[string]dist.ModuleLayerInfo{
"buildpack-1-version-1": {
Expand Down Expand Up @@ -1618,6 +1618,23 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
)
})
})

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() {
Expand Down
28 changes: 23 additions & 5 deletions internal/builder/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ type Info struct {
Description string
StackID string
Mixins []string
RunImage string
RunImageMirrors []string
RunImages []pubbldr.RunImageConfig
Buildpacks []dist.ModuleInfo
Order pubbldr.DetectionOrder
BuildpackLayers dist.ModuleLayers
Expand Down Expand Up @@ -80,7 +79,7 @@ func (i *Inspector) Inspect(name string, daemon bool, orderDetectionDepth int) (

stackID, err := labelManager.StackID()
if err != nil {
return Info{}, fmt.Errorf("reading image stack id: %w", err)
// TODO log warn
}

mixins, err := labelManager.Mixins()
Expand Down Expand Up @@ -126,12 +125,31 @@ func (i *Inspector) Inspect(name string, daemon bool, orderDetectionDepth int) (
APIs: metadata.Lifecycle.APIs,
})

var runImages []pubbldr.RunImageConfig
for _, ri := range metadata.RunImages {
runImages = append(runImages, pubbldr.RunImageConfig{
Image: ri.Image,
Mirrors: ri.Mirrors,
})
}
addStackRunImage := true
for _, ri := range runImages {
if ri.Image == metadata.Stack.RunImage.Image {
addStackRunImage = false
}
}
if addStackRunImage && metadata.Stack.RunImage.Image != "" {
runImages = append(runImages, pubbldr.RunImageConfig{
Image: metadata.Stack.RunImage.Image,
Mirrors: metadata.Stack.RunImage.Mirrors,
})
}

return Info{
Description: metadata.Description,
StackID: stackID,
Mixins: append(commonMixins, buildMixins...),
RunImage: metadata.Stack.RunImage.Image,
RunImageMirrors: metadata.Stack.RunImage.Mirrors,
RunImages: runImages,
Buildpacks: sortBuildPacksByID(uniqueBuildpacks(metadata.Buildpacks)),
Order: detectionOrder,
BuildpackLayers: layers,
Expand Down
20 changes: 15 additions & 5 deletions internal/builder/inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
testBuilderDescription = "Test Builder Description"
testStackID = "test-builder-stack-id"
testRunImage = "test/run-image"
testStackRunImage = "test/stack-run-image"
)

var (
Expand Down Expand Up @@ -75,13 +76,19 @@ var (
Stack: testStack,
Lifecycle: inspectTestLifecycle,
CreatedBy: testCreatorData,
RunImages: []builder.RunImageMetadata{
{
Image: testRunImage,
Mirrors: testRunImageMirrors,
},
},
}
testMixins = []string{"build:mixinA", "mixinX", "mixinY"}
expectedTestMixins = []string{"mixinX", "mixinY", "build:mixinA"}
testRunImageMirrors = []string{"test/first-run-image-mirror", "test/second-run-image-mirror"}
testStack = builder.StackMetadata{
RunImage: builder.RunImageMetadata{
Image: testRunImage,
Image: testStackRunImage,
Mirrors: testRunImageMirrors,
},
}
Expand Down Expand Up @@ -218,8 +225,11 @@ func testInspect(t *testing.T, when spec.G, it spec.S) {
assert.Equal(info.Description, testBuilderDescription)
assert.Equal(info.StackID, testStackID)
assert.Equal(info.Mixins, expectedTestMixins)
assert.Equal(info.RunImage, testRunImage)
assert.Equal(info.RunImageMirrors, testRunImageMirrors)
assert.Equal(len(info.RunImages), 2)
assert.Equal(info.RunImages[0].Image, testRunImage)
assert.Equal(info.RunImages[1].Image, testStackRunImage)
assert.Equal(info.RunImages[0].Mirrors, testRunImageMirrors)
assert.Equal(info.RunImages[1].Mirrors, testRunImageMirrors)
assert.Equal(info.Buildpacks, testBuildpacks)
assert.Equal(info.Order, expectedDetectionTestOrder)
assert.Equal(info.BuildpackLayers, testLayers)
Expand Down Expand Up @@ -291,7 +301,7 @@ func testInspect(t *testing.T, when spec.G, it spec.S) {
})
})

when("label manager returns an error for `StackID`", func() {
when("label manager does not return an error for `StackID`", func() {
it("returns the wrapped error", func() {
expectedBaseError := errors.New("label not found")

Expand All @@ -304,7 +314,7 @@ func testInspect(t *testing.T, when spec.G, it spec.S) {
)
_, err := inspector.Inspect(testBuilderName, true, pubbldr.OrderDetectionNone)

assert.ErrorWithMessage(err, "reading image stack id: label not found")
assert.Nil(err)
})
})

Expand Down
60 changes: 32 additions & 28 deletions internal/builder/writer/human_readable.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ Created By:

Trusted: {{.Trusted}}

Stack:
ID: {{ .Info.Stack }}
{{ if ne .Info.Stack "" -}}Stack:
ID: {{ .Info.Stack }}{{ end -}}
{{- if .Verbose}}
{{- if ne (len .Info.Mixins) 0 }}
Mixins:
Expand Down Expand Up @@ -125,7 +125,7 @@ func writeBuilderInfo(

var warnings []string

runImagesString, runImagesWarnings, err := runImagesOutput(info.RunImage, localRunImages, info.RunImageMirrors, sharedInfo.Name)
runImagesString, runImagesWarnings, err := runImagesOutput(info.RunImages, localRunImages, sharedInfo.Name)
if err != nil {
return fmt.Errorf("compiling run images output: %w", err)
}
Expand Down Expand Up @@ -243,46 +243,48 @@ func stringFromBool(subject bool) string {
}

func runImagesOutput(
runImage string,
runImages []pubbldr.RunImageConfig,
localRunImages []config.RunImage,
buildRunImages []string,
builderName string,
) (string, []string, error) {
output := "Run Images:\n"

tabWriterBuf := bytes.Buffer{}

localMirrorTabWriter := tabwriter.NewWriter(&tabWriterBuf, writerMinWidth, writerTabWidth, defaultTabWidth, writerPadChar, writerFlags)
err := writeLocalMirrors(localMirrorTabWriter, runImage, localRunImages)
err := writeLocalMirrors(localMirrorTabWriter, runImages, localRunImages)
if err != nil {
return "", []string{}, fmt.Errorf("writing local mirrors: %w", err)
}

var warnings []string

if runImage != "" {
_, err = fmt.Fprintf(localMirrorTabWriter, " %s\n", runImage)
if err != nil {
return "", []string{}, fmt.Errorf("writing to tabwriter: %w", err)
}
} else {
if len(runImages) == 0 {
warnings = append(
warnings,
fmt.Sprintf("%s does not specify a run image", builderName),
"Users must build with an explicitly specified run image",
)
}
for _, m := range buildRunImages {
_, err = fmt.Fprintf(localMirrorTabWriter, " %s\n", m)
if err != nil {
return "", []string{}, fmt.Errorf("writing to tab writer: %w", err)
} else {
for _, runImage := range runImages {
if runImage.Image != "" {
_, err = fmt.Fprintf(localMirrorTabWriter, " %s\n", runImage.Image)
if err != nil {
return "", []string{}, fmt.Errorf("writing to tabwriter: %w", err)
}
}
for _, m := range runImage.Mirrors {
_, err = fmt.Fprintf(localMirrorTabWriter, " %s\n", m)
if err != nil {
return "", []string{}, fmt.Errorf("writing to tab writer: %w", err)
}
}
err = localMirrorTabWriter.Flush()
if err != nil {
return "", []string{}, fmt.Errorf("flushing tab writer: %w", err)
}
}
}
err = localMirrorTabWriter.Flush()
if err != nil {
return "", []string{}, fmt.Errorf("flushing tab writer: %w", err)
}

runImageOutput := tabWriterBuf.String()
if runImageOutput == "" {
runImageOutput = fmt.Sprintf(" %s\n", none)
Expand All @@ -293,13 +295,15 @@ func runImagesOutput(
return output, warnings, nil
}

func writeLocalMirrors(logWriter io.Writer, runImage string, localRunImages []config.RunImage) error {
func writeLocalMirrors(logWriter io.Writer, runImages []pubbldr.RunImageConfig, localRunImages []config.RunImage) error {
for _, i := range localRunImages {
if i.Image == runImage {
for _, m := range i.Mirrors {
_, err := fmt.Fprintf(logWriter, " %s\t(user-configured)\n", m)
if err != nil {
return fmt.Errorf("writing local mirror: %s: %w", m, err)
for _, ri := range runImages {
if i.Image == ri.Image {
for _, m := range i.Mirrors {
_, err := fmt.Fprintf(logWriter, " %s\t(user-configured)\n", m)
if err != nil {
return fmt.Errorf("writing local mirror: %s: %w", m, err)
}
}
}
}
Expand Down
Loading