Skip to content

Commit

Permalink
Merge pull request #40 from paketo-buildpacks/api/0.5
Browse files Browse the repository at this point in the history
Buildpack API 0.5
  • Loading branch information
ekcasey authored Feb 1, 2021
2 parents 6551384 + 9092c61 commit df0d2b2
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 133 deletions.
5 changes: 4 additions & 1 deletion build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
})

it("handles error from Builder", func() {
Expect(ioutil.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"), []byte(`[buildpack]
Expect(ioutil.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"), []byte(`
api = "0.5"
[buildpack]
name = "test-name"
version = "test-version"`),
0644)).To(Succeed())
Expand Down
6 changes: 3 additions & 3 deletions buildpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ type BuildpackDependency struct {
Licenses []BuildpackDependencyLicense `toml:"licenses"`
}

// AsBuildpackPlanEntry renders the dependency as a BuildpackPlanEntry.
func (b BuildpackDependency) AsBuildpackPlanEntry() libcnb.BuildpackPlanEntry {
return libcnb.BuildpackPlanEntry{
// AsBOMEntry renders a bill of materials entry describing the dependency.
func (b BuildpackDependency) AsBOMEntry() libcnb.BOMEntry {
return libcnb.BOMEntry{
Name: b.ID,
Metadata: map[string]interface{}{
"name": b.Name,
Expand Down
4 changes: 2 additions & 2 deletions buildpack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func testBuildpack(t *testing.T, context spec.G, it spec.S) {
Expect = NewWithT(t).Expect
)

it("renders dependency as an BuildpackPlanEntry", func() {
it("renders dependency as a BOMEntry", func() {
dependency := libpak.BuildpackDependency{
ID: "test-id",
Name: "test-name",
Expand All @@ -49,7 +49,7 @@ func testBuildpack(t *testing.T, context spec.G, it spec.S) {
},
}

Expect(dependency.AsBuildpackPlanEntry()).To(Equal(libcnb.BuildpackPlanEntry{
Expect(dependency.AsBOMEntry()).To(Equal(libcnb.BOMEntry{
Name: dependency.ID,
Metadata: map[string]interface{}{
"name": dependency.Name,
Expand Down
5 changes: 4 additions & 1 deletion detect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ func testDetect(t *testing.T, context spec.G, it spec.S) {
})

it("handles error from Detector", func() {
Expect(ioutil.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"), []byte(`[buildpack]
Expect(ioutil.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"), []byte(`
api = "0.5"
[buildpack]
name = "test-name"
version = "test-version"`),
0644)).To(Succeed())
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.15

require (
github.com/Masterminds/semver/v3 v3.1.1
github.com/buildpacks/libcnb v1.18.1
github.com/buildpacks/libcnb v1.19.0
github.com/creack/pty v1.1.11
github.com/heroku/color v0.0.6
github.com/imdario/mergo v0.3.11
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030I
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/buildpacks/libcnb v1.18.1 h1:NcAqtyLmSkpcgIOl83GOHY5Mk2ltBFiAI8mmAvavvEs=
github.com/buildpacks/libcnb v1.18.1/go.mod h1:ozKZYold67tlck+1cobg11YhGmJqkQr8bMAH5gSvEvw=
github.com/buildpacks/libcnb v1.19.0 h1:7Frn3qErlVmQydk95YXmHtpmf5bZ1l7h4rK/C+SKAmY=
github.com/buildpacks/libcnb v1.19.0/go.mod h1:slnOsywO4mNdkShkwX1GYR+kQF1tH2F9Urfr66lWGQs=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
2 changes: 1 addition & 1 deletion internal/toml_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (t TOMLWriter) Write(path string, value interface{}) error {
}

switch v := value.(type) {
case libcnb.Launch:
case libcnb.LaunchTOML:
if len(v.Slices) > 0 {
t.logger.Headerf("%d application slices", len(v.Slices))
}
Expand Down
10 changes: 5 additions & 5 deletions internal/toml_writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ other-field = "other-value"`))
})

it("logs []libcnb.Slice", func() {
err := tomlWriter.Write(path, libcnb.Launch{
err := tomlWriter.Write(path, libcnb.LaunchTOML{
Slices: []libcnb.Slice{
{},
{},
Expand All @@ -100,7 +100,7 @@ other-field = "other-value"`))
})

it("logs []libcnb.Label", func() {
err := tomlWriter.Write(path, libcnb.Launch{
err := tomlWriter.Write(path, libcnb.LaunchTOML{
Labels: []libcnb.Label{
{Key: "test-key-1"},
{Key: "test-key-2"},
Expand All @@ -117,7 +117,7 @@ other-field = "other-value"`))
context("[]libcnb.Process", func() {

it("aligns process types", func() {
err := tomlWriter.Write(path, libcnb.Launch{
err := tomlWriter.Write(path, libcnb.LaunchTOML{
Processes: []libcnb.Process{
{Type: "short", Command: "test-command-1"},
{Type: "a-very-long-type", Command: "test-command-2"},
Expand All @@ -133,7 +133,7 @@ other-field = "other-value"`))
})

it("appends arguments", func() {
err := tomlWriter.Write(path, libcnb.Launch{
err := tomlWriter.Write(path, libcnb.LaunchTOML{
Processes: []libcnb.Process{
{Type: "test-type", Command: "test-command", Arguments: []string{"test-arg-1", "test-arg-2"}},
},
Expand All @@ -147,7 +147,7 @@ other-field = "other-value"`))
})

it("indicates direct", func() {
err := tomlWriter.Write(path, libcnb.Launch{
err := tomlWriter.Write(path, libcnb.LaunchTOML{
Processes: []libcnb.Process{
{Type: "test-type", Command: "test-command", Direct: true},
},
Expand Down
122 changes: 59 additions & 63 deletions layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"os"
"path/filepath"
"reflect"
"strings"

"github.com/heroku/color"
"github.com/pelletier/go-toml"
Expand All @@ -45,29 +44,26 @@ type LayerContributor struct {

// Name is the user readable name of the contribution.
Name string

// ExpectedTypes indicates the types that should be set on the layer.
ExpectedTypes libcnb.LayerTypes
}

// NewLayerContributor creates a new instance.
func NewLayerContributor(name string, expectedMetadata interface{}) LayerContributor {
func NewLayerContributor(name string, expectedMetadata interface{}, expectedTypes libcnb.LayerTypes) LayerContributor {
return LayerContributor{
ExpectedMetadata: expectedMetadata,
Name: name,
ExpectedTypes: expectedTypes,
}
}

// LayerFunc is a callback function that is invoked when a layer needs to be contributed.
type LayerFunc func() (libcnb.Layer, error)

type LayerFlag uint8

const (
BuildLayer LayerFlag = iota
CacheLayer
LaunchLayer
)

// Contribute is the function to call when implementing your libcnb.LayerContributor.
func (l *LayerContributor) Contribute(layer libcnb.Layer, f LayerFunc, flags ...LayerFlag) (libcnb.Layer, error) {
func (l *LayerContributor) Contribute(layer libcnb.Layer, f LayerFunc) (libcnb.Layer, error) {
raw, err := toml.Marshal(l.ExpectedMetadata)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to encode metadata\n%w", err)
Expand All @@ -81,6 +77,7 @@ func (l *LayerContributor) Contribute(layer libcnb.Layer, f LayerFunc, flags ...
l.Logger.Debugf("Expected metadata: %+v", expected)
l.Logger.Debugf("Actual metadata: %+v", layer.Metadata)

// TODO: compare entire layer not just metadata (in case build, launch, or cache have changed)
if reflect.DeepEqual(expected, layer.Metadata) {
l.Logger.Headerf("%s: %s cached layer", color.BlueString(l.Name), color.GreenString("Reusing"))
return layer, nil
Expand All @@ -101,17 +98,7 @@ func (l *LayerContributor) Contribute(layer libcnb.Layer, f LayerFunc, flags ...
return libcnb.Layer{}, err
}

for _, f := range flags {
switch f {
case BuildLayer:
layer.Build = true
case CacheLayer:
layer.Cache = true
case LaunchLayer:
layer.Launch = true
}
}

layer.LayerTypes = l.ExpectedTypes
layer.Metadata = expected

return layer, nil
Expand All @@ -127,8 +114,11 @@ type DependencyLayerContributor struct {
// DependencyCache is the cache to use to get the dependency.
DependencyCache DependencyCache

// LayerContributor is the contained LayerContributor used for the actual contribution.
LayerContributor LayerContributor
// ExpectedTypes indicates the types that should be set on the layer.
ExpectedTypes libcnb.LayerTypes

// ExpectedMetadata contains metadata describing the expected layer
ExpectedMetadata interface{}

// Logger is the logger to use.
Logger bard.Logger
Expand All @@ -137,53 +127,67 @@ type DependencyLayerContributor struct {
RequestModifierFuncs []RequestModifierFunc
}

// NewDependencyLayerContributor creates a new instance and adds the dependency to the Buildpack Plan.
func NewDependencyLayerContributor(dependency BuildpackDependency, cache DependencyCache, plan *libcnb.BuildpackPlan) DependencyLayerContributor {
// NewDependencyLayer returns a new DependencyLayerContributor for the given BuildpackDependency and a BOMEntry describing the layer contents.
func NewDependencyLayer(dependency BuildpackDependency, cache DependencyCache, types libcnb.LayerTypes) (DependencyLayerContributor, libcnb.BOMEntry) {
c := DependencyLayerContributor{
Dependency: dependency,
ExpectedMetadata: dependency,
DependencyCache: cache,
LayerContributor: NewLayerContributor(fmt.Sprintf("%s %s", dependency.Name, dependency.Version), dependency),
ExpectedTypes: types,
}

entry := dependency.AsBuildpackPlanEntry()
entry := dependency.AsBOMEntry()
entry.Metadata["layer"] = c.LayerName()
plan.Entries = append(plan.Entries, entry)

return c
if types.Launch {
entry.Launch = true
}
if !(types.Launch && !types.Cache && !types.Build) {
// launch-only layers are the only layers NOT guaranteed to be present in the build environment
entry.Build = true
}

return c, entry
}

// DependencyLayerFunc is a callback function that is invoked when a dependency needs to be contributed.
type DependencyLayerFunc func(artifact *os.File) (libcnb.Layer, error)

// Contribute is the function to call whe implementing your libcnb.LayerContributor.
func (d *DependencyLayerContributor) Contribute(layer libcnb.Layer, f DependencyLayerFunc, flags ...LayerFlag) (libcnb.Layer, error) {
d.LayerContributor.Logger = d.Logger
func (d *DependencyLayerContributor) Contribute(layer libcnb.Layer, f DependencyLayerFunc) (libcnb.Layer, error) {
lc := NewLayerContributor(d.Name(), d.ExpectedMetadata, d.ExpectedTypes)
lc.Logger = d.Logger

return d.LayerContributor.Contribute(layer, func() (libcnb.Layer, error) {
return lc.Contribute(layer, func() (libcnb.Layer, error) {
artifact, err := d.DependencyCache.Artifact(d.Dependency, d.RequestModifierFuncs...)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to get dependency %s\n%w", d.Dependency.ID, err)
}
defer artifact.Close()

return f(artifact)
}, flags...)
})
}

// LayerName returns the conventional name of the layer for this contributor
func (d *DependencyLayerContributor) LayerName() string {
return d.Dependency.ID
}

// Name returns the human readable name of the layer
func (d *DependencyLayerContributor) Name() string {
return fmt.Sprintf("%s %s", d.Dependency.Name, d.Dependency.Version)
}

// HelperLayerContributor is a helper for implementing a libcnb.LayerContributor for a buildpack helper application in
// order to get consistent logging and avoidance.
type HelperLayerContributor struct {

// Path is the path to the helper application.
Path string

// LayerContributor is the contained LayerContributor used for the actual contribution.
LayerContributor LayerContributor
// BuildpackInfo describes the buildpack that provides the helper
BuildpackInfo libcnb.BuildpackInfo

// Logger is the logger to use.
Logger bard.Logger
Expand All @@ -192,31 +196,40 @@ type HelperLayerContributor struct {
Names []string
}

// NewHelperLayerContributor creates a new instance and adds the helper to the Buildpack Plan.
func NewHelperLayerContributor(buildpack libcnb.Buildpack, plan *libcnb.BuildpackPlan, names ...string) HelperLayerContributor {
// NewHelperLayer returns a new HelperLayerContributor and a BOMEntry describing the layer contents.
func NewHelperLayer(buildpack libcnb.Buildpack, names ...string) (HelperLayerContributor, libcnb.BOMEntry) {
c := HelperLayerContributor{
Path: filepath.Join(buildpack.Path, "bin", "helper"),
LayerContributor: NewLayerContributor("Launch Helper", buildpack.Info),
Names: names,
Path: filepath.Join(buildpack.Path, "bin", "helper"),
Names: names,
BuildpackInfo: buildpack.Info,
}

plan.Entries = append(plan.Entries, libcnb.BuildpackPlanEntry{
entry := libcnb.BOMEntry{
Name: "helper",
Metadata: map[string]interface{}{
"layer": c.Name(),
"names": names,
"version": buildpack.Info.Version,
},
})
Launch: true,
}

return c, entry
}

return c
// Name returns the conventional name of the layer for this contributor
func (h HelperLayerContributor) Name() string {
return filepath.Base(h.Path)
}

// Contribute is the function to call whe implementing your libcnb.LayerContributor.
func (h HelperLayerContributor) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
h.LayerContributor.Logger = h.Logger
lc := NewLayerContributor("Launch Helper", h.BuildpackInfo, libcnb.LayerTypes{
Launch: true,
})
lc.Logger = h.Logger

return h.LayerContributor.Contribute(layer, func() (libcnb.Layer, error) {
return lc.Contribute(layer, func() (libcnb.Layer, error) {
in, err := os.Open(h.Path)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to open %s\n%w", h.Path, err)
Expand All @@ -228,7 +241,6 @@ func (h HelperLayerContributor) Contribute(layer libcnb.Layer) (libcnb.Layer, er
return libcnb.Layer{}, fmt.Errorf("unable to copy %s to %s", h.Path, out)
}

var p []string
for _, n := range h.Names {
link := layer.Exec.FilePath(n)
h.Logger.Bodyf("Creating %s", link)
Expand All @@ -241,24 +253,8 @@ func (h HelperLayerContributor) Contribute(layer libcnb.Layer) (libcnb.Layer, er
if err := os.Symlink(out, link); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to link %s to %s\n%w", out, link, err)
}

p = append(p,
"exec 4<&1",
fmt.Sprintf(`for_export=$(%s 3>&1 >&4) || exit $?`, link),
"exec 4<&-",
"set -a",
`eval "$for_export"`,
"set +a",
)
}

layer.Profile.Add("helper", strings.Join(p, "\n"))

return layer, nil
}, LaunchLayer)
}

// Name returns the conventional name of the layer for this contributor
func (h HelperLayerContributor) Name() string {
return filepath.Base(h.Path)
})
}
Loading

0 comments on commit df0d2b2

Please sign in to comment.