Skip to content

Commit

Permalink
Adding a profile utility buildpack
Browse files Browse the repository at this point in the history
This commit adds detect and build business logic and basic tests

Signed-off-by: gcemaj <gcemaj@bloomberg.net>
  • Loading branch information
gcemaj authored and gcemaj committed Jul 19, 2022
1 parent d2f2bcf commit 45e0c11
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 32 deletions.
34 changes: 17 additions & 17 deletions profile/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,43 @@
package profile

import (
_ "embed"
"fmt"
"os"
"path/filepath"

"github.com/buildpacks/libcnb"
)

//go:embed execd_wrapper.sh
var execDScript string

func Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
// NOTE: the logger is not passed into this function, that will likely be a change in libcnbv2
result := libcnb.NewBuildResult()

if len(context.Plan.Entries) == 0 {
return result, nil
layer, err := context.Layers.Layer(profileName)

if err != nil {
return result, err
}

layer := libcnb.Layer{}
execPath := layer.Exec.FilePath(execDScriptName)

err = os.MkdirAll(layer.Exec.Path, os.ModePerm)
if err != nil {
return result, err
}

profilePath := filepath.Join(context.ApplicationPath, ".profile")
execPath := layer.Exec.FilePath("dotprofile.sh")
f, err := os.Create(execPath)
fmt.Println(err)

if err != nil {
return result, err
}

defer f.Close()

_, err = f.WriteString(
fmt.Sprintf(`
#!/bin/bash
set -eo
source %s
env | sed 's/=/ = "/' | sed 's/$/"/' >&3
`,
profilePath,
),
)
_, err = f.WriteString(execDScript)

if err != nil {
return result, err
Expand All @@ -66,9 +66,9 @@ env | sed 's/=/ = "/' | sed 's/$/"/' >&3
}

layer.Launch = true
layer.Cache = true

result.Layers = append(result.Layers, layer)

// TODO: implement build actions
return result, nil
}
59 changes: 46 additions & 13 deletions profile/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package profile_test

import (
"os"
"path/filepath"
"testing"

. "github.com/onsi/gomega"
Expand All @@ -42,6 +43,23 @@ func (b BuildTest) SetupWorkspace() BuildTest {
var err error
b.context.ApplicationPath, err = os.MkdirTemp("", "profile")
b.expect(err).NotTo(HaveOccurred())
profilePath := filepath.Join(b.context.ApplicationPath, ".profile")

f, err := os.Create(profilePath)
b.expect(err).NotTo(HaveOccurred())
defer f.Close()

b.expect(err).To(BeNil())

_, err = f.WriteString(
`
echo "Hello world"
HELLO="world"
export HELLO
`,
)
b.expect(err).NotTo(HaveOccurred())

return b
}

Expand All @@ -51,22 +69,37 @@ func (b BuildTest) RemoveWorkspace() BuildTest {
}

func (b BuildTest) Build() (libcnb.BuildContext, ExpectFunc, AfterFunc) {
return b.context, b.expect, func() { b.RemoveWorkspace() }
outputPath, _ := os.MkdirTemp("/tmp", "dotprofile_out")
os.MkdirAll(outputPath, os.ModePerm)
b.context.Layers.Path = outputPath
return b.context, b.expect, func() {
b.RemoveWorkspace()
os.RemoveAll(outputPath)
}
}

func TestBuildDoesNothingWithoutPlanEntry(t *testing.T) {
func TestBuildExecutes(t *testing.T) {
ctx, Expect, After := BuildTest{}.SetupGomega(t).SetupWorkspace().Build()
defer After()
Expect(profile.Build(ctx)).To(Equal(libcnb.BuildResult{
Layers: []libcnb.Layer{{
LayerTypes: libcnb.LayerTypes{
Build: false,
Launch: true,
Cache: true,
},
BuildEnvironment: libcnb.Environment{},
LaunchEnvironment: libcnb.Environment{},
SharedEnvironment: libcnb.Environment{},
Name: "profile",
Path: filepath.Join(ctx.Layers.Path, "profile"),
Profile: libcnb.Profile{},
Exec: libcnb.Exec{
Path: filepath.Join(ctx.Layers.Path, "profile", "exec.d"),
},
}},
PersistentMetadata: map[string]interface{}{},
}))

Expect(profile.Build(ctx)).To(Equal(libcnb.NewBuildResult()))
}

func TestBuildExecutesWithPlanEntry(t *testing.T) {
ctx, Expect, After := BuildTest{}.SetupGomega(t).SetupWorkspace().Build()
defer After()

// TODO: test setup for a working build

// TODO: test validation for a working build
Expect(profile.Build(ctx)).To(Equal(libcnb.NewBuildResult()))
Expect(filepath.Join(ctx.Layers.Path, "profile", "exec.d", "dotprofile.sh")).To(BeAnExistingFile())
}
6 changes: 6 additions & 0 deletions profile/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package profile

// Constants
const profileName = "profile"
const scriptName = ".profile"
const execDScriptName = "dotprofile.sh"
13 changes: 11 additions & 2 deletions profile/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,18 @@ func Detect(context libcnb.DetectContext) (libcnb.DetectResult, error) {

_, shErr := exec.LookPath("sh")

profilePath := filepath.Join(context.ApplicationPath, ".profile")
profilePath := filepath.Join(context.ApplicationPath, scriptName)
if _, err := os.Stat(profilePath); shErr == nil && !os.IsNotExist(err) {
return libcnb.DetectResult{Pass: true}, nil
return libcnb.DetectResult{Pass: true, Plans: []libcnb.BuildPlan{
{
Provides: []libcnb.BuildPlanProvide{{
Name: profileName,
}},
Requires: []libcnb.BuildPlanRequire{{
Name: profileName,
}},
},
}}, nil
}

return libcnb.DetectResult{Pass: false}, nil
Expand Down
8 changes: 8 additions & 0 deletions profile/detect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,13 @@ func TestDetectPassesWithProfileScript(t *testing.T) {

Expect(profile.Detect(ctx)).To(Equal(libcnb.DetectResult{
Pass: true,
Plans: []libcnb.BuildPlan{{
Provides: []libcnb.BuildPlanProvide{{
Name: "profile",
}},
Requires: []libcnb.BuildPlanRequire{{
Name: "profile",
}},
}},
}))
}
11 changes: 11 additions & 0 deletions profile/execd_wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

# Source the .profile script
source .profile
# Iterate through the list of exported env vars
for var in $(compgen -e); do
# quote the output values replacing double quotes with an escaped version
value=$(echo ${!var} | sed 's/"/\\"/g')
# output the variables to FD3
echo "${var} = \"${value}\"" 1>&3
done

0 comments on commit 45e0c11

Please sign in to comment.