Skip to content

Commit

Permalink
Support Buildpack API 0.10 (#291)
Browse files Browse the repository at this point in the history
* Add Deprecated comment to all stack related identifiers

Co-authored-by: Ralf Pannemans <[email protected]>
Signed-off-by: Nicolas Bender <[email protected]>

* Add target structs

Co-authored-by: Ralf Pannemans <[email protected]>
Signed-off-by: Nicolas Bender <[email protected]>

* Adapt extension related files

Co-authored-by: Ralf Pannemans <[email protected]>
Signed-off-by: Nicolas Bender <[email protected]>

* add support for generated dockerfiles and extend config

Signed-off-by: Pavel Busko <[email protected]>

---------

Signed-off-by: Nicolas Bender <[email protected]>
Signed-off-by: Pavel Busko <[email protected]>
Co-authored-by: Nicolas Bender <[email protected]>
Co-authored-by: Pavel Busko <[email protected]>
  • Loading branch information
3 people authored Jun 18, 2024
1 parent b32eb02 commit b9fc3fe
Show file tree
Hide file tree
Showing 7 changed files with 338 additions and 14 deletions.
23 changes: 21 additions & 2 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,14 @@ type BuildContext struct {
// Platform is the contents of the platform.
Platform Platform

// StackID is the ID of the stack.
// Deprecated: StackID is the ID of the stack.
StackID string

// TargetInfo contains info of the target (os, arch, ...).
TargetInfo TargetInfo

// TargetDistro is the target distribution (name, version).
TargetDistro TargetDistro
}

// BuildResult contains the results of detection.
Expand Down Expand Up @@ -87,7 +93,7 @@ const (
MinSupportedBPVersion = "0.8"

// MaxSupportedBPVersion indicates the maximum supported version of the Buildpacks API
MaxSupportedBPVersion = "0.9"
MaxSupportedBPVersion = "0.10"
)

// NewBuildResult creates a new BuildResult instance, initializing empty fields.
Expand Down Expand Up @@ -231,6 +237,19 @@ func Build(build BuildFunc, config Config) {
config.logger.Debugf("Stack: %s", ctx.StackID)
}

if API.GreaterThan(semver.MustParse("0.9")) {
ctx.TargetInfo = TargetInfo{}
ctx.TargetInfo.OS, _ = os.LookupEnv(EnvTargetOS)
ctx.TargetInfo.Arch, _ = os.LookupEnv(EnvTargetArch)
ctx.TargetInfo.Variant, _ = os.LookupEnv(EnvTargetArchVariant)
config.logger.Debugf("System: %+v", ctx.TargetInfo)

ctx.TargetDistro = TargetDistro{}
ctx.TargetDistro.Name, _ = os.LookupEnv(EnvTargetDistroName)
ctx.TargetDistro.Version, _ = os.LookupEnv(EnvTargetDistroVersion)
config.logger.Debugf("Distro: %+v", ctx.TargetDistro)
}

result, err := build(ctx)
if err != nil {
config.exitHandler.Error(err)
Expand Down
75 changes: 75 additions & 0 deletions build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ test-key = "test-value"
Expect(os.Setenv("CNB_PLATFORM_DIR", platformPath)).To(Succeed())
Expect(os.Setenv("CNB_BP_PLAN_PATH", buildpackPlanPath)).To(Succeed())

Expect(os.Setenv("CNB_TARGET_OS", "linux")).To(Succeed())
Expect(os.Setenv("CNB_TARGET_ARCH", "arm")).To(Succeed())
Expect(os.Setenv("CNB_TARGET_ARCH_VARIANT", "v6")).To(Succeed())
Expect(os.Setenv("CNB_TARGET_DISTRO_NAME", "ubuntu")).To(Succeed())
Expect(os.Setenv("CNB_TARGET_DISTRO_VERSION", "24.04")).To(Succeed())

workingDir, err = os.Getwd()
Expect(err).NotTo(HaveOccurred())
Expect(os.Chdir(applicationPath)).To(Succeed())
Expand All @@ -178,6 +184,12 @@ test-key = "test-value"
Expect(os.Unsetenv("CNB_BP_PLAN_PATH")).To(Succeed())
Expect(os.Unsetenv("CNB_LAYERS_DIR")).To(Succeed())

Expect(os.Unsetenv("CNB_TARGET_OS"))
Expect(os.Unsetenv("CNB_TARGET_ARCH"))
Expect(os.Unsetenv("CNB_TARGET_ARCH_VARIANT"))
Expect(os.Unsetenv("CNB_TARGET_DISTRO_NAME"))
Expect(os.Unsetenv("CNB_TARGET_DISTRO_VERSION"))

Expect(os.RemoveAll(applicationPath)).To(Succeed())
Expect(os.RemoveAll(buildpackPath)).To(Succeed())
Expect(os.RemoveAll(buildpackPlanPath)).To(Succeed())
Expand Down Expand Up @@ -320,6 +332,69 @@ version = "1.1.1"
})
})

context("has a build environment specifying target metadata", func() {
var ctx libcnb.BuildContext

it.Before(func() {
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
[]byte(`
api = "0.10"
[buildpack]
id = "test-id"
name = "test-name"
version = "1.1.1"
[[targets]]
os = "linux"
arch = "amd64"
[[targets.distros]]
name = "ubuntu"
version = "18.04"
[[targets.distros]]
name = "debian"
[[targets]]
os = "linux"
arch = "arm"
variant = "v6"
`), 0600),
).To(Succeed())

buildFunc = func(context libcnb.BuildContext) (libcnb.BuildResult, error) {
ctx = context
return libcnb.NewBuildResult(), nil
}
})

it("provides target information", func() {
libcnb.Build(buildFunc,
libcnb.NewConfig(
libcnb.WithArguments([]string{commandPath}),
libcnb.WithLogger(log.New(os.Stdout)),
),
)

Expect(ctx.Buildpack.Targets).To(HaveLen(2))
Expect(ctx.Buildpack.Targets[0].OS).To(Equal("linux"))
Expect(ctx.Buildpack.Targets[0].Arch).To(Equal("amd64"))
Expect(ctx.Buildpack.Targets[0].Distros).To(HaveLen(2))
Expect(ctx.Buildpack.Targets[0].Distros[0].Name).To(Equal("ubuntu"))
Expect(ctx.Buildpack.Targets[0].Distros[0].Version).To(Equal("18.04"))
Expect(ctx.Buildpack.Targets[0].Distros[1].Name).To(Equal("debian"))

Expect(ctx.Buildpack.Targets[1].Variant).To(Equal("v6"))

Expect(ctx.TargetInfo.OS).To(Equal("linux"))
Expect(ctx.TargetInfo.Arch).To(Equal("arm"))
Expect(ctx.TargetInfo.Variant).To(Equal("v6"))
Expect(ctx.TargetDistro.Name).To(Equal("ubuntu"))
Expect(ctx.TargetDistro.Version).To(Equal("24.04"))
})
})

it("fails if CNB_BUILDPACK_DIR is not set", func() {
Expect(os.Unsetenv("CNB_BUILDPACK_DIR")).To(Succeed())

Expand Down
36 changes: 34 additions & 2 deletions buildpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ type BuildpackOrder struct {
Groups []BuildpackOrderBuildpack `toml:"group"`
}

// BuildpackStack is a stack supported by the buildpack.
// Deprecated: BuildpackStack is a stack supported by the buildpack.
type BuildpackStack struct {
// ID is the id of the stack.
ID string `toml:"id"`
Expand All @@ -85,6 +85,35 @@ type BuildpackStack struct {
Mixins []string `toml:"mixins"`
}

// TargetDistro is the supported target distro
type TargetDistro struct {
// Name is the name of the supported distro.
Name string `toml:"name"`

// Version is the version of the supported distro.
Version string `toml:"version"`
}

// TargetInfo is the supported target
type TargetInfo struct {
// OS is the supported os.
OS string `toml:"os"`

// Arch is the supported architecture.
Arch string `toml:"arch"`

// Variant is the supported variant of the architecture.
Variant string `toml:"variant"`
}

// Target is a target supported by the buildpack.
type Target struct {
TargetInfo

// Distros is the collection of distros associated with the target.
Distros []TargetDistro `toml:"distros"`
}

// Buildpack is the contents of the buildpack.toml file.
type Buildpack struct {
// API is the api version expected by the buildpack.
Expand All @@ -96,9 +125,12 @@ type Buildpack struct {
// Path is the path to the buildpack.
Path string `toml:"-"`

// Stacks is the collection of stacks supported by the buildpack.
// Deprecated: Stacks is the collection of stacks supported by the buildpack.
Stacks []BuildpackStack `toml:"stacks"`

// Targets is the collection of targets supported by the buildpack.
Targets []Target `toml:"targets"`

// Metadata is arbitrary metadata attached to the buildpack.
Metadata map[string]interface{} `toml:"metadata"`
}
3 changes: 3 additions & 0 deletions extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ type Extension struct {
// Path is the path to the extension.
Path string `toml:"-"`

// Targets is the collection of targets supported by the buildpack.
Targets []Target `toml:"targets"`

// Metadata is arbitrary metadata attached to the extension.
Metadata map[string]interface{} `toml:"metadata"`
}
76 changes: 72 additions & 4 deletions generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,44 @@ type GenerateContext struct {
// Platform is the contents of the platform.
Platform Platform

// StackID is the ID of the stack.
// TargetInfo contains info of the target (os, arch, ...).
TargetInfo TargetInfo

// TargetDistro is the target distribution (name, version).
TargetDistro TargetDistro

// Deprecated: StackID is the ID of the stack.
StackID string
}

// GenerateResult contains the results of detection.
type GenerateResult struct {
// Unmet contains buildpack plan entries that were not satisfied by the buildpack and therefore should be
// passed to subsequent providers.
Unmet []UnmetPlanEntry
Unmet []UnmetPlanEntry
RunDockerfile []byte
BuildDockerfile []byte
Config *ExtendConfig
}

// DockerfileArg is a Dockerfile argument
type DockerfileArg struct {
Name string `toml:"name"`
Value string `toml:"value"`
}

// NewBuildResult creates a new BuildResult instance, initializing empty fields.
// BuildConfig contains additional arguments passed to the generated Dockerfiles
type BuildConfig struct {
Args []DockerfileArg `toml:"args"`
}

// ExtendConfig contains additional configuration for the Dockerfiles
type ExtendConfig struct {
Build BuildConfig `toml:"build"`
Run BuildConfig `toml:"run"`
}

// NewGenerateResult creates a new BuildResult instance, initializing empty fields.
func NewGenerateResult() GenerateResult {
return GenerateResult{}
}
Expand All @@ -73,7 +99,7 @@ func (b GenerateResult) String() string {
)
}

// BuildFunc takes a context and returns a result, performing extension generate behaviors.
// GenerateFunc takes a context and returns a result, performing extension generate behaviors.
type GenerateFunc func(context GenerateContext) (GenerateResult, error)

// Generate is called by the main function of a extension, for generate phase
Expand Down Expand Up @@ -184,10 +210,52 @@ func Generate(generate GenerateFunc, config Config) {
config.logger.Debugf("Stack: %s", ctx.StackID)
}

if API.GreaterThan(semver.MustParse("0.9")) {
ctx.TargetInfo = TargetInfo{}
ctx.TargetInfo.OS, _ = os.LookupEnv(EnvTargetOS)
ctx.TargetInfo.Arch, _ = os.LookupEnv(EnvTargetArch)
ctx.TargetInfo.Variant, _ = os.LookupEnv(EnvTargetArchVariant)
config.logger.Debugf("System: %+v", ctx.TargetInfo)

ctx.TargetDistro = TargetDistro{}
ctx.TargetDistro.Name, _ = os.LookupEnv(EnvTargetDistroName)
ctx.TargetDistro.Version, _ = os.LookupEnv(EnvTargetDistroVersion)
config.logger.Debugf("Distro: %+v", ctx.TargetDistro)
}

result, err := generate(ctx)
if err != nil {
config.exitHandler.Error(err)
return
}
config.logger.Debugf("Result: %+v", result)

if len(result.RunDockerfile) > 0 {
// #nosec
if err := os.WriteFile(filepath.Join(ctx.OutputDirectory, "run.Dockerfile"), result.RunDockerfile, 0644); err != nil {
config.exitHandler.Error(err)
return
}
}

if len(result.BuildDockerfile) > 0 {
// #nosec
if err := os.WriteFile(filepath.Join(ctx.OutputDirectory, "build.Dockerfile"), result.BuildDockerfile, 0644); err != nil {
config.exitHandler.Error(err)
return
}
}

if result.Config != nil {
configFile, err := os.Create(filepath.Join(ctx.OutputDirectory, "extend-config.toml"))
if err != nil {
config.exitHandler.Error(err)
return
}

if err := toml.NewEncoder(configFile).Encode(result.Config); err != nil {
config.exitHandler.Error(err)
return
}
}
}
Loading

0 comments on commit b9fc3fe

Please sign in to comment.