Skip to content

Commit

Permalink
WIP added logic to add buildConfigEnvs to layer of image
Browse files Browse the repository at this point in the history
Signed-off-by: WYGIN <[email protected]>
  • Loading branch information
WYGIN committed Oct 8, 2023
1 parent 7fa8e4d commit 6e085ae
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 311 deletions.
64 changes: 60 additions & 4 deletions internal/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ import (
lifecycleplatform "github.com/buildpacks/lifecycle/platform"
)

var buildConfigDir = cnbBuildConfigDir()

const (
packName = "Pack CLI"

cnbDir = "/cnb"
buildConfigDir = "/cnb/build-config"
buildpacksDir = "/cnb/buildpacks"
cnbDir = "/cnb"
buildpacksDir = "/cnb/buildpacks"

orderPath = "/cnb/order.toml"
stackPath = "/cnb/stack.toml"
Expand Down Expand Up @@ -68,6 +69,7 @@ const (
// Builder represents a pack builder, used to build images
type Builder struct {
baseImageName string
buildConfigEnv map[string]string
image imgutil.Image
layerWriterFactory archive.TarWriterFactory
lifecycle Lifecycle
Expand Down Expand Up @@ -147,6 +149,7 @@ func constructBuilder(img imgutil.Image, newName string, errOnMissingLabel bool,
metadata: metadata,
lifecycleDescriptor: constructLifecycleDescriptor(metadata),
env: map[string]string{},
buildConfigEnv: map[string]string{},
validateMixins: true,
additionalBuildpacks: *buildpack.NewModuleManager(opts.flatten, opts.depth),
additionalExtensions: *buildpack.NewModuleManager(opts.flatten, opts.depth),
Expand Down Expand Up @@ -350,6 +353,11 @@ func (b *Builder) SetEnv(env map[string]string) {
b.env = env
}

// SetBuildConfigEnv sets an environment variable to a value that will take action on platform environment variables basedon filename suffix
func (b *Builder) SetBuildConfigEnv(env map[string]string) {
b.buildConfigEnv = env
}

// SetOrder sets the order of the builder
func (b *Builder) SetOrder(order dist.Order) {
b.order = order
Expand Down Expand Up @@ -526,6 +534,19 @@ func (b *Builder) Save(logger logging.Logger, creatorMetadata CreatorMetadata) e
return errors.Wrap(err, "adding run.tar layer")
}

if len(b.buildConfigEnv) > 0 {
logger.Debugf("Provided Build Config Environment Variables\n %s", style.Map(b.env, " ", "\n"))
}

buildConfigEnvTar, err := b.buildConfigEnvLayer(tmpDir, b.buildConfigEnv)
if err != nil {
return errors.Wrap(err, "retrieving build-config-env layer")
}

if err := b.image.AddLayer(buildConfigEnvTar); err != nil {
return errors.Wrap(err, "adding build-config-env layer")
}

if len(b.env) > 0 {
logger.Debugf("Provided Environment Variables\n %s", style.Map(b.env, " ", "\n"))
}
Expand Down Expand Up @@ -904,7 +925,7 @@ func (b *Builder) defaultDirsLayer(dest string) (string, error) {
}

// can't use filepath.Join(), to ensure Windows doesn't transform it to Windows join
for _, path := range []string{cnbDir, dist.BuildpacksDir, dist.ExtensionsDir, platformDir, platformDir + "/env"} {
for _, path := range []string{cnbDir, dist.BuildpacksDir, dist.ExtensionsDir, platformDir, platformDir + "/env", buildConfigDir, buildConfigDir + "/env"} {
if err := lw.WriteHeader(b.rootOwnedDir(path, ts)); err != nil {
return "", errors.Wrapf(err, "creating %s dir in layer", style.Symbol(path))
}
Expand Down Expand Up @@ -1103,6 +1124,33 @@ func (b *Builder) envLayer(dest string, env map[string]string) (string, error) {
return fh.Name(), nil
}

func (b *Builder) buildConfigEnvLayer(dest string, env map[string]string) (string, error) {
fh, err := os.Create(filepath.Join(dest, "build-config-env.tar"))
if err != nil {
return "", err
}
defer fh.Close()

lw := b.layerWriterFactory.NewWriter(fh)
defer lw.Close()

for k, v := range env {
if err := lw.WriteHeader(&tar.Header{
Name: path.Join(buildConfigDir, "env", k),
Size: int64(len(v)),
Mode: 0644,
ModTime: archive.NormalizedDateTime,
}); err != nil {
return "", err
}
if _, err := lw.Write([]byte(v)); err != nil {
return "", err
}
}

return fh.Name(), nil
}

func (b *Builder) whiteoutLayer(tmpDir string, i int, bpInfo dist.ModuleInfo) (string, error) {
bpWhiteoutsTmpDir := filepath.Join(tmpDir, strconv.Itoa(i)+"_whiteouts")
if err := os.MkdirAll(bpWhiteoutsTmpDir, os.ModePerm); err != nil {
Expand Down Expand Up @@ -1258,3 +1306,11 @@ func (e errModuleTar) Info() dist.ModuleInfo {
func (e errModuleTar) Path() string {
return ""
}

func cnbBuildConfigDir() string {
if env, ok := os.LookupEnv("CNB_BUILD_CONFIG_DIR"); !ok {
return "/cnb/build-config"
} else {
return env
}
}
43 changes: 43 additions & 0 deletions internal/builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,20 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
)
})

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)
Expand Down Expand Up @@ -1607,6 +1621,35 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
})
})

when("#SetBuildConfigEnv", func() {
it.Before(func() {
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{
Expand Down
81 changes: 34 additions & 47 deletions internal/commands/builder_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package commands

import (
"fmt"
"os"
"path/filepath"
"strings"

Expand Down Expand Up @@ -76,20 +75,18 @@ Creating a custom builder allows you to control what buildpacks are used and wha
return errors.Wrap(err, "getting absolute path for config")
}

envMap, warnings, err := parseBuildConfigEnv(builderConfig.Build.Env, flags.BuilderTomlPath)
envMap, warnings, err := ParseBuildConfigEnv(builderConfig.Build.Env, flags.BuilderTomlPath)
for _, v := range warnings {
logger.Warn(v)
}
if err != nil {
return err
}
if err := generateBuildConfigEnvFiles(envMap); err != nil {
return err
}

imageName := args[0]
if err := pack.CreateBuilder(cmd.Context(), client.CreateBuilderOptions{
RelativeBaseDir: relativeBaseDir,
BuildConfigEnv: envMap,
BuilderName: imageName,
Config: builderConfig,
Publish: flags.Publish,
Expand Down Expand Up @@ -150,45 +147,6 @@ func validateCreateFlags(flags *BuilderCreateFlags, cfg config.Config) error {
return nil
}

func generateBuildConfigEnvFiles(envMap map[string]string) error {
dir, err := createBuildConfigEnvDir()
if err != nil {
return err
}
for k, v := range envMap {
f, err := os.Create(filepath.Join(dir, k))
if err != nil {
return err
}
f.WriteString(v)
if e := f.Close(); e != nil {
return e
}
}
return nil
}

func CnbBuildConfigDir() string {
if v := os.Getenv("CNB_BUILD_CONFIG_DIR"); v == "" || len(v) == 0 {
return "/cnb/build-config"
} else {
return v
}
}

func createBuildConfigEnvDir() (dir string, err error) {
dir = filepath.Join(CnbBuildConfigDir(), "env")
_, err = os.Stat(dir)
if os.IsNotExist(err) {
err := os.MkdirAll(dir, os.ModePerm)
if err != nil {
return dir, err
}
return dir, nil
}
return dir, nil
}

func getActionType(suffix builder.Suffix) (suffixString string, err error) {
const delim = "."
switch suffix {
Expand All @@ -206,7 +164,21 @@ func getActionType(suffix builder.Suffix) (suffixString string, err error) {
return suffixString, errors.Errorf("unknown action type %s", style.Symbol(string(suffix)))
}
}
func GetBuildConfigEnvFileName(env builder.BuildConfigEnv) (suffixName, delimName string, err error) {

func getFilePrefixSuffix(filename string) (prefix, suffix string, err error) {
val := strings.Split(filename, ".")
if len(val) <= 1 {
return val[0], suffix, errors.Errorf("Suffix might be null")
}
if len(val) == 2 {
suffix = val[1]
} else {
strings.Join(val[1:], ".")
}
return val[0], suffix, err
}

func getBuildConfigEnvFileName(env builder.BuildConfigEnv) (suffixName, delimName string, err error) {
suffix, err := getActionType(env.Suffix)
if err != nil {
return suffixName, delimName, err
Expand All @@ -222,16 +194,17 @@ func GetBuildConfigEnvFileName(env builder.BuildConfigEnv) (suffixName, delimNam
return suffixName, delimName, err
}

func parseBuildConfigEnv(env []builder.BuildConfigEnv, path string) (envMap map[string]string, warnings []string, err error) {
func ParseBuildConfigEnv(env []builder.BuildConfigEnv, path string) (envMap map[string]string, warnings []string, err error) {
envMap = map[string]string{}
var appendOrPrependWithoutDelim = 0
for _, v := range env {
if name := v.Name; name == "" || len(name) == 0 {
return nil, nil, errors.Wrapf(errors.Errorf("env name should not be empty"), "parse contents of '%s'", path)
}
if val := v.Value; val == "" || len(val) == 0 {
warnings = append(warnings, fmt.Sprintf("empty value for key/name %s", style.Symbol(v.Name)))
}
suffixName, delimName, err := GetBuildConfigEnvFileName(v)
suffixName, delimName, err := getBuildConfigEnvFileName(v)
if err != nil {
return envMap, warnings, err
}
Expand All @@ -246,5 +219,19 @@ func parseBuildConfigEnv(env []builder.BuildConfigEnv, path string) (envMap map[
}
envMap[suffixName] = v.Value
}

for k := range envMap {
name, suffix, err := getFilePrefixSuffix(k)
if err != nil {
continue
}
if _, ok := envMap[name+".delim"]; (suffix == "append" || suffix == "prepend") && !ok {
warnings = append(warnings, fmt.Sprintf(errors.Errorf("env with name/key %s with suffix %s must to have a %s value", style.Symbol(name), style.Symbol(suffix), style.Symbol("delim")).Error(), "parse contents of '%s'", path))
appendOrPrependWithoutDelim++
}
}
if appendOrPrependWithoutDelim > 0 {
return envMap, warnings, errors.Errorf("error parsing [[build.env]] in file '%s'", path)
}
return envMap, warnings, err
}
Loading

0 comments on commit 6e085ae

Please sign in to comment.