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

"Generate phase" fetching uids and gids according to the build context #26

Merged
merged 9 commits into from
Nov 14, 2023
77 changes: 57 additions & 20 deletions generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,27 @@ import (
_ "embed"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"text/template"

"github.com/Masterminds/semver/v3"
"github.com/paketo-buildpacks/libnodejs"
"github.com/paketo-buildpacks/packit/v2"
"github.com/paketo-buildpacks/packit/v2/draft"
"github.com/paketo-buildpacks/libnodejs"
postal "github.com/paketo-buildpacks/packit/v2/postal"
"github.com/paketo-buildpacks/packit/v2/scribe"
)

var PACKAGES = "make gcc gcc-c++ libatomic_ops git openssl-devel nodejs npm nodejs-nodemon nss_wrapper which"

// Should be externalized
var CNB_USER_ID = 1000
var CNB_GROUP_ID = 1000
var DEFAULT_USER_ID = 1002
var DEFAULT_GROUP_ID = 1000

type DuringBuildPermissions struct {
CNB_USER_ID, CNB_GROUP_ID int
}

//go:embed templates/build.Dockerfile
var buildDockerfileTemplate string
Expand All @@ -45,13 +50,13 @@ type DependencyManager interface {
GenerateBillOfMaterials(dependencies ...postal.Dependency) []packit.BOMEntry
}

func Generate(dependencyManager DependencyManager, logger scribe.Emitter) packit.GenerateFunc {
func Generate(dependencyManager DependencyManager, logger scribe.Emitter, duringBuildPermissions DuringBuildPermissions) packit.GenerateFunc {
return func(context packit.GenerateContext) (packit.GenerateResult, error) {
logger.Title("%s %s", context.Info.Name, context.Info.Version)
logger.Process("Resolving Node Engine version")

entryResolver := draft.NewPlanner()
entry, allEntries := libnodejs.ResolveNodeVersion(entryResolver.Resolve, context.Plan);
entry, allEntries := libnodejs.ResolveNodeVersion(entryResolver.Resolve, context.Plan)
if entry.Name == "" {
return packit.GenerateResult{}, packit.Fail.WithMessage("Node.js no longer requested by build plan")
}
Expand All @@ -73,29 +78,23 @@ func Generate(dependencyManager DependencyManager, logger scribe.Emitter) packit
// These variables have to be fetched from the env
CNB_STACK_ID := os.Getenv("CNB_STACK_ID")

/* Creating build.Dockerfile */

buildDockerfileProps := BuildDockerfileProps{
// Generating build.Dockerfile
buildDockerfileContent, err := FillPropsToTemplate(BuildDockerfileProps{
NODEJS_VERSION: NODEJS_VERSION,
CNB_USER_ID: CNB_USER_ID,
CNB_GROUP_ID: CNB_GROUP_ID,
CNB_USER_ID: duringBuildPermissions.CNB_USER_ID,
CNB_GROUP_ID: duringBuildPermissions.CNB_GROUP_ID,
CNB_STACK_ID: CNB_STACK_ID,
PACKAGES: "make gcc gcc-c++ libatomic_ops git openssl-devel nodejs npm nodejs-nodemon nss_wrapper which",
}

buildDockerfileContent, err := FillPropsToTemplate(buildDockerfileProps, buildDockerfileTemplate)
}, buildDockerfileTemplate)

if err != nil {
return packit.GenerateResult{}, err
}

/* Creating run.Dockerfile */

RunDockerfileProps := RunDockerfileProps{
// Generating run.Dockerfile
runDockerfileContent, err := FillPropsToTemplate(RunDockerfileProps{
Source: dependency.Source,
}

runDockerfileContent, err := FillPropsToTemplate(RunDockerfileProps, runDockerfileTemplate)
}, runDockerfileTemplate)

if err != nil {
return packit.GenerateResult{}, err
Expand Down Expand Up @@ -123,5 +122,43 @@ func FillPropsToTemplate(properties interface{}, templateString string) (result
}

return buf.String(), nil
}

func GetDuringBuildPermissions(filepath string) DuringBuildPermissions {

defaultPermissions := DuringBuildPermissions{
CNB_USER_ID: DEFAULT_USER_ID,
CNB_GROUP_ID: DEFAULT_GROUP_ID,
}
re := regexp.MustCompile(`cnb:x:(\d+):(\d+)::`)

etcPasswdFile, err := os.ReadFile(filepath)

if err != nil {
return defaultPermissions
}
etcPasswdContent := string(etcPasswdFile)

matches := re.FindStringSubmatch(etcPasswdContent)

if len(matches) != 3 {
return defaultPermissions
}

CNB_USER_ID, err := strconv.Atoi(matches[1])

if err != nil {
return defaultPermissions
}

CNB_GROUP_ID, err := strconv.Atoi(matches[2])

if err != nil {
return defaultPermissions
}

return DuringBuildPermissions{
CNB_USER_ID: CNB_USER_ID,
CNB_GROUP_ID: CNB_GROUP_ID,
}
}
122 changes: 109 additions & 13 deletions generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,13 @@ func testFillPropsToTemplate(t *testing.T, context spec.G, it spec.S) {

it("Should fill with properties the template/build.Dockerfile", func() {

buildDockerfileProps := BuildDockerfileProps{
output, err := ubinodejsextension.FillPropsToTemplate(BuildDockerfileProps{
NODEJS_VERSION: 16,
CNB_USER_ID: 1000,
CNB_GROUP_ID: 1000,
CNB_STACK_ID: "",
PACKAGES: "make gcc gcc-c++ libatomic_ops git openssl-devel nodejs npm nodejs-nodemon nss_wrapper which",
}

output, err := ubinodejsextension.FillPropsToTemplate(buildDockerfileProps, buildDockerfileTemplate)
}, buildDockerfileTemplate)

Expect(err).NotTo(HaveOccurred())
Expect(output).To(Equal(`ARG base_image
Expand Down Expand Up @@ -92,6 +90,99 @@ RUN echo "CNB_STACK_ID: "`))
})
}

func testFetchingPermissionsFromEtchPasswdFile(t *testing.T, context spec.G, it spec.S) {

var (
Expect = NewWithT(t).Expect
tmpDir string
path string
err error
)

context("/etc/passwd exists and has the cnb user", func() {

it("It should return the permissions specified for the cnb user", func() {
tmpDir, err = os.MkdirTemp("", "")
Expect(err).NotTo(HaveOccurred())

path = filepath.Join(tmpDir, "/passwd")

Expect(os.WriteFile(path, []byte(`root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
cnb:x:1234:2345::/home/cnb:/bin/bash
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
`), 0600)).To(Succeed())

duringBuilderPermissions := ubinodejsextension.GetDuringBuildPermissions(path)

Expect(duringBuilderPermissions).To(Equal(
ubinodejsextension.DuringBuildPermissions{
CNB_USER_ID: 1234,
CNB_GROUP_ID: 2345},
))
})
})

context("/etc/passwd exists and does NOT have the cnb user", func() {

it("It should return the default permissions", func() {
tmpDir, err = os.MkdirTemp("", "")
Expect(err).NotTo(HaveOccurred())

path = filepath.Join(tmpDir, "/passwd")

Expect(os.WriteFile(path, []byte(`root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
`), 0600)).To(Succeed())

duringBuilderPermissions := ubinodejsextension.GetDuringBuildPermissions(path)

Expect(duringBuilderPermissions).To(Equal(
ubinodejsextension.DuringBuildPermissions{
CNB_USER_ID: ubinodejsextension.DEFAULT_USER_ID,
CNB_GROUP_ID: ubinodejsextension.DEFAULT_GROUP_ID},
))
})
})

context("/etc/passwd does NOT exist", func() {

it("It should return the default permissions", func() {
tmpDir, err = os.MkdirTemp("", "")
Expect(err).NotTo(HaveOccurred())

duringBuilderPermissions := ubinodejsextension.GetDuringBuildPermissions(tmpDir)

Expect(duringBuilderPermissions).To(Equal(
ubinodejsextension.DuringBuildPermissions{
CNB_USER_ID: ubinodejsextension.DEFAULT_USER_ID,
CNB_GROUP_ID: ubinodejsextension.DEFAULT_GROUP_ID},
))
})
})
}

func testGenerate(t *testing.T, context spec.G, it spec.S) {

var (
Expand All @@ -104,21 +195,21 @@ func testGenerate(t *testing.T, context spec.G, it spec.S) {
err error
cnbDir string
BuildDockerfileProps = ubinodejsextension.BuildDockerfileProps{
CNB_USER_ID: ubinodejsextension.CNB_USER_ID,
CNB_GROUP_ID: ubinodejsextension.CNB_GROUP_ID,
CNB_USER_ID: 1002,
CNB_GROUP_ID: 1000,
CNB_STACK_ID: "",
PACKAGES: ubinodejsextension.PACKAGES,
}
generate packit.GenerateFunc
buffer *bytes.Buffer
generate packit.GenerateFunc
buffer *bytes.Buffer
logger scribe.Emitter
dependencyManager postal.Service
)

it.Before(func() {
buffer = bytes.NewBuffer(nil)
logger := scribe.NewEmitter(buffer)
dependencyManager := postal.NewService(cargo.NewTransport())
generate = ubinodejsextension.Generate(dependencyManager, logger)

logger = scribe.NewEmitter(buffer)
dependencyManager = postal.NewService(cargo.NewTransport())
})

context("Generate called with NO node in buildplan", func() {
Expand All @@ -127,6 +218,8 @@ func testGenerate(t *testing.T, context spec.G, it spec.S) {
workingDir = t.TempDir()
Expect(err).NotTo(HaveOccurred())

generate = ubinodejsextension.Generate(dependencyManager, logger, ubinodejsextension.DuringBuildPermissions{CNB_USER_ID: 1002, CNB_GROUP_ID: 1000})

err = toml.NewEncoder(buf).Encode(testBuildPlan)
Expect(err).NotTo(HaveOccurred())

Expand Down Expand Up @@ -159,6 +252,8 @@ func testGenerate(t *testing.T, context spec.G, it spec.S) {
workingDir = t.TempDir()
cnbDir, err = os.MkdirTemp("", "cnb")

generate = ubinodejsextension.Generate(dependencyManager, logger, ubinodejsextension.DuringBuildPermissions{CNB_USER_ID: 1002, CNB_GROUP_ID: 1000})

err = toml.NewEncoder(buf).Encode(testBuildPlan)
Expect(err).NotTo(HaveOccurred())

Expand All @@ -169,7 +264,6 @@ func testGenerate(t *testing.T, context spec.G, it spec.S) {

err = os.Chdir(workingDir)
Expect(err).NotTo(HaveOccurred())

})

it.After(func() {
Expand Down Expand Up @@ -610,6 +704,8 @@ func testGenerate(t *testing.T, context spec.G, it spec.S) {
workingDir = t.TempDir()
cnbDir, err = os.MkdirTemp("", "cnb")

generate = ubinodejsextension.Generate(dependencyManager, logger, ubinodejsextension.DuringBuildPermissions{CNB_USER_ID: 1002, CNB_GROUP_ID: 1000})

err = toml.NewEncoder(buf).Encode(testBuildPlan)
Expect(err).NotTo(HaveOccurred())

Expand Down
1 change: 1 addition & 0 deletions init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ func TestUnitNode(t *testing.T) {
suite("Detect", testDetect)
suite("Generate", testGenerate)
suite("Dockerfile Creation", testFillPropsToTemplate)
suite("Fetching during build permissions", testFetchingPermissionsFromEtchPasswdFile)
suite.Run(t)
}
2 changes: 0 additions & 2 deletions integration/openssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ func testOpenSSL(t *testing.T, context spec.G, it spec.S) {
WithEnv(map[string]string{
"BP_NODE_VERSION": "16.*.*",
}).
WithNetwork("host").
WithPullPolicy("always").
Execute(name, source)
Expect(err).ToNot(HaveOccurred(), logs.String)
Expand Down Expand Up @@ -113,7 +112,6 @@ func testOpenSSL(t *testing.T, context spec.G, it spec.S) {
WithEnv(map[string]string{
"BP_NODE_VERSION": "18.*.*",
}).
WithNetwork("host").
WithPullPolicy("always").
Execute(name, source)
Expect(err).ToNot(HaveOccurred(), logs.String)
Expand Down
1 change: 0 additions & 1 deletion integration/optimize_memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ func testOptimizeMemory(t *testing.T, context spec.G, it spec.S) {
settings.Buildpacks.Processes.Online,
).
WithEnv(map[string]string{"BP_NODE_OPTIMIZE_MEMORY": "true"}).
WithNetwork("host").
pacostas marked this conversation as resolved.
Show resolved Hide resolved
WithPullPolicy("always").
Execute(name, source)
Expect(err).NotTo(HaveOccurred(), logs.String())
Expand Down
1 change: 0 additions & 1 deletion integration/project_path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ func testProjectPath(t *testing.T, context spec.G, it spec.S) {
WithEnv(map[string]string{
"BP_NODE_PROJECT_PATH": "hello_world_server",
}).
WithNetwork("host").
WithPullPolicy("always").
Execute(name, source)
Expect(err).ToNot(HaveOccurred(), logs.String)
Expand Down
1 change: 0 additions & 1 deletion integration/provides_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ func testProvides(t *testing.T, context spec.G, it spec.S) {
settings.Buildpacks.NodeEngine.Online,
settings.Buildpacks.BuildPlan.Online,
).
WithNetwork("host").
WithPullPolicy("always").
Execute(name, source)
Expect(err).ToNot(HaveOccurred(), logs.String)
Expand Down
5 changes: 0 additions & 5 deletions integration/simple_app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ func testSimple(t *testing.T, context spec.G, it spec.S) {
settings.Buildpacks.BuildPlan.Online,
).
WithSBOMOutputDir(sbomDir).
WithNetwork("host").
WithPullPolicy("always").
Execute(name, source)
Expect(err).ToNot(HaveOccurred(), logs.String)
Expand Down Expand Up @@ -170,7 +169,6 @@ func testSimple(t *testing.T, context spec.G, it spec.S) {
settings.Buildpacks.NodeEngine.Online,
settings.Buildpacks.BuildPlan.Online,
).
WithNetwork("host").
WithPullPolicy("always").
Execute(name, source)
Expect(err).ToNot(HaveOccurred(), logs.String)
Expand Down Expand Up @@ -249,7 +247,6 @@ func testSimple(t *testing.T, context spec.G, it spec.S) {
settings.Buildpacks.BuildPlan.Online,
).
WithSBOMOutputDir(sbomDir).
WithNetwork("host").
WithPullPolicy("always").
Execute(name, source)
Expect(err).ToNot(HaveOccurred(), logs.String)
Expand Down Expand Up @@ -341,7 +338,6 @@ func testSimple(t *testing.T, context spec.G, it spec.S) {
settings.Buildpacks.BuildPlan.Online,
).
WithSBOMOutputDir(sbomDir).
WithNetwork("host").
WithPullPolicy("always").
Execute(name, source)
Expect(err).ToNot(HaveOccurred(), logs.String)
Expand Down Expand Up @@ -444,7 +440,6 @@ func testSimple(t *testing.T, context spec.G, it spec.S) {
).
WithEnv(map[string]string{"BP_NODE_VERSION": "~14"}).
WithSBOMOutputDir(sbomDir).
WithNetwork("host").
WithPullPolicy("always").
Execute(name, source)

Expand Down
Loading