-
Notifications
You must be signed in to change notification settings - Fork 4
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
Installing node from images.json #203
Conversation
bdccd09
to
8210c1f
Compare
8210c1f
to
1031862
Compare
type DuringBuildPermissions struct { | ||
CNB_USER_ID, CNB_GROUP_ID int | ||
} | ||
|
||
//go:embed templates/build.Dockerfile | ||
var buildDockerfileTemplate string | ||
|
||
type BuildDockerfileProps struct { | ||
NODEJS_VERSION uint64 | ||
CNB_USER_ID, CNB_GROUP_ID int | ||
CNB_STACK_ID, PACKAGES string | ||
} | ||
|
||
//go:embed templates/run.Dockerfile | ||
var runDockerfileTemplate string | ||
|
||
type RunDockerfileProps struct { | ||
Source string | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has been moved to utils
func FillPropsToTemplate(properties interface{}, templateString string) (result string, Error error) { | ||
|
||
templ, err := template.New("template").Parse(templateString) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
var buf bytes.Buffer | ||
err = templ.Execute(&buf, properties) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
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, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has been moved untouched to utils
type RunDockerfileProps struct { | ||
Source string | ||
} | ||
|
||
//go:embed templates/run.Dockerfile | ||
var runDockerfileTemplate string | ||
|
||
type BuildDockerfileProps struct { | ||
NODEJS_VERSION uint64 | ||
CNB_USER_ID, CNB_GROUP_ID int | ||
CNB_STACK_ID, PACKAGES string | ||
} | ||
|
||
//go:embed templates/build.Dockerfile | ||
var buildDockerfileTemplate string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has been moved to utils untouched
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}, | ||
)) | ||
}) | ||
}) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has been moved to utils_test untouched
it("Should error in case there are no entries in the buildpack plan.", func() { | ||
|
||
extensionToml, _ := readExtensionTomlTemplateFile() | ||
|
||
cnbDir, err = os.MkdirTemp("", "cnb") | ||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(os.WriteFile(cnbDir+"/extension.toml", []byte(extensionToml), 0600)).To(Succeed()) | ||
|
||
entriesTests := []struct { | ||
Entries []packit.BuildpackPlanEntry | ||
}{ | ||
{ | ||
Entries: []packit.BuildpackPlanEntry{}, | ||
}, | ||
} | ||
|
||
for _, tt := range entriesTests { | ||
|
||
generateResult, err = generate(packit.GenerateContext{ | ||
WorkingDir: workingDir, | ||
CNBPath: cnbDir, | ||
Plan: packit.BuildpackPlan{ | ||
Entries: tt.Entries, | ||
}, | ||
Stack: "io.buildpacks.stacks.ubi8", | ||
}) | ||
|
||
Expect(err).To(HaveOccurred()) | ||
|
||
} | ||
|
||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed as is exactly the same with this test
ubi-nodejs-extension/generate_test.go
Lines 236 to 247 in ac00b6c
it("Node no longer requested in buildplan", func() { | |
generateResult, err = generate(packit.GenerateContext{ | |
WorkingDir: workingDir, | |
Plan: packit.BuildpackPlan{ | |
Entries: []packit.BuildpackPlanEntry{}, | |
}, | |
}) | |
Expect(err).To(HaveOccurred()) | |
Expect(generateResult.BuildDockerfile).To(BeNil()) | |
}) | |
}, spec.Sequential()) |
func fillPropsToTemplate(properties interface{}, templateString string) (result string, Error error) { | ||
|
||
templ, err := template.New("template").Parse(templateString) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
var buf bytes.Buffer | ||
err = templ.Execute(&buf, properties) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return buf.String(), nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to utils from generate untouched
package structs | ||
|
||
type DuringBuildPermissions struct { | ||
CNB_USER_ID, CNB_GROUP_ID int | ||
} | ||
|
||
type BuildDockerfileProps struct { | ||
NODEJS_VERSION uint64 | ||
CNB_USER_ID, CNB_GROUP_ID int | ||
CNB_STACK_ID, PACKAGES string | ||
} | ||
|
||
type RunDockerfileProps struct { | ||
Source string | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to structs from generate, untouched
internal/utils/utils_test.go
Outdated
func testGetDuringBuildPermissions(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 := utils.GetDuringBuildPermissions(path) | ||
|
||
Expect(duringBuilderPermissions).To(Equal( | ||
structs.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()) | ||
|
||
duringBuildPermissions := utils.GetDuringBuildPermissions(path) | ||
|
||
Expect(duringBuildPermissions).To(Equal( | ||
structs.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 := utils.GetDuringBuildPermissions(tmpDir) | ||
|
||
Expect(duringBuilderPermissions).To(Equal( | ||
structs.DuringBuildPermissions{ | ||
CNB_USER_ID: ubinodejsextension.DEFAULT_USER_ID, | ||
CNB_GROUP_ID: ubinodejsextension.DEFAULT_GROUP_ID}, | ||
)) | ||
}) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to utils_test from generate_test untouched.
func testGenerateBuildDockerfile(t *testing.T, context spec.G, it spec.S) { | ||
|
||
var ( | ||
Expect = NewWithT(t).Expect | ||
) | ||
|
||
context("Adding props on build.dockerfile template", func() { | ||
|
||
it("Should fill with properties the template/build.Dockerfile", func() { | ||
|
||
output, err := utils.GenerateBuildDockerfile(structs.BuildDockerfileProps{ | ||
NODEJS_VERSION: 16, | ||
CNB_USER_ID: 1000, | ||
CNB_GROUP_ID: 1000, | ||
CNB_STACK_ID: "io.buildpacks.stacks.ubi8", | ||
PACKAGES: ubinodejsextension.PACKAGES, | ||
}) | ||
|
||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(output).To(Equal(fmt.Sprintf(`ARG base_image | ||
FROM ${base_image} | ||
|
||
USER root | ||
|
||
ARG build_id=0 | ||
RUN echo ${build_id} | ||
|
||
RUN microdnf -y module enable nodejs:16 | ||
RUN microdnf --setopt=install_weak_deps=0 --setopt=tsflags=nodocs install -y %s && microdnf clean all | ||
|
||
RUN echo uid:gid "1000:1000" | ||
USER 1000:1000 | ||
|
||
RUN echo "CNB_STACK_ID: io.buildpacks.stacks.ubi8"`, ubinodejsextension.PACKAGES))) | ||
|
||
}) | ||
|
||
}) | ||
} | ||
|
||
func testGenerateRunDockerfile(t *testing.T, context spec.G, it spec.S) { | ||
|
||
var ( | ||
Expect = NewWithT(t).Expect | ||
) | ||
|
||
context("Adding props on build.dockerfile template", func() { | ||
|
||
it("Should fill with properties the template/run.Dockerfile", func() { | ||
|
||
RunDockerfileProps := structs.RunDockerfileProps{ | ||
Source: "paketocommunity/run-nodejs-18-ubi-base", | ||
} | ||
|
||
output, err := utils.GenerateRunDockerfile(RunDockerfileProps) | ||
|
||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(output).To(Equal(`FROM paketocommunity/run-nodejs-18-ubi-base`)) | ||
|
||
}) | ||
}) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is the same with the old testfillPropstotemplate
ubi-nodejs-extension/generate_test.go
Lines 41 to 91 in ac00b6c
func testFillPropsToTemplate(t *testing.T, context spec.G, it spec.S) { | |
var ( | |
Expect = NewWithT(t).Expect | |
) | |
context("Adding props on templates with FillPropsToTemplate", func() { | |
it("Should fill with properties the template/build.Dockerfile", func() { | |
output, err := ubinodejsextension.FillPropsToTemplate(BuildDockerfileProps{ | |
NODEJS_VERSION: 16, | |
CNB_USER_ID: 1000, | |
CNB_GROUP_ID: 1000, | |
CNB_STACK_ID: "", | |
PACKAGES: ubinodejsextension.PACKAGES, | |
}, buildDockerfileTemplate) | |
Expect(err).NotTo(HaveOccurred()) | |
Expect(output).To(Equal(fmt.Sprintf(`ARG base_image | |
FROM ${base_image} | |
USER root | |
ARG build_id=0 | |
RUN echo ${build_id} | |
RUN microdnf -y module enable nodejs:16 | |
RUN microdnf --setopt=install_weak_deps=0 --setopt=tsflags=nodocs install -y %s && microdnf clean all | |
RUN echo uid:gid "1000:1000" | |
USER 1000:1000 | |
RUN echo "CNB_STACK_ID: "`, ubinodejsextension.PACKAGES))) | |
}) | |
it("Should fill with properties the template/run.Dockerfile", func() { | |
RunDockerfileProps := RunDockerfileProps{ | |
Source: "paketocommunity/run-nodejs-18-ubi-base", | |
} | |
output, err := ubinodejsextension.FillPropsToTemplate(RunDockerfileProps, runDockerfileTemplate) | |
Expect(err).NotTo(HaveOccurred()) | |
Expect(output).To(Equal(`FROM paketocommunity/run-nodejs-18-ubi-base`)) | |
}) | |
}) | |
} |
workingDir = t.TempDir() | ||
Expect(err).NotTo(HaveOccurred()) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error check here was wrong, the t.TempDir() does not return any err object, therefore has been deleted. The WorkingDir variable has been moved a little bit down, next to the buildplan creation.
func GenerateConfigTomlContentFromImagesJson(imagesJsonPath string, stackId string) ([]byte, error) { | ||
imagesJsonData, err := ParseImagesJsonFile(imagesJsonPath) | ||
if err != nil { | ||
return []byte{}, err | ||
} | ||
|
||
nodejsStacks, err := GetNodejsStackImages(imagesJsonData) | ||
if err != nil { | ||
return []byte{}, err | ||
} | ||
|
||
defaultNodeVersion, err := GetDefaultNodeVersion(nodejsStacks) | ||
if err != nil { | ||
return []byte{}, err | ||
} | ||
|
||
configTomlContent, err := CreateConfigTomlFileContent(defaultNodeVersion, nodejsStacks, stackId) | ||
if err != nil { | ||
return []byte{}, err | ||
} | ||
|
||
configTomlContentString := configTomlContent.Bytes() | ||
return configTomlContentString, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a wrapper, and for that wrapper on the utils tests we only test that it manages to return the result and that it manages to propagate an error. The type of the error, the type of the result and the edge cases are covered from the tests of the functions that are wrapped.
WithPullPolicy("always"). | ||
Execute(name, source) | ||
|
||
Expect(err).NotTo(HaveOccurred(), logs.String()) | ||
Expect(err).To(HaveOccurred()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function errors after the extension has run due to it tries to download the fake image nodeRunImage := "this-is-a-run-image"
. Therefore the build fails, so I changed it to only check the logs and that the build failed instead of checking the logs and checking and that the build that succeeded. If we can find an image that will always be available, we can have also the build to succeed. In any case the extension is tested properly so to me seems like there is no issue of test coverage on this scenario.
@mhdawson The tests do not pass due to there is no new ubi-base image with the images.json file in it. If you force the release on the ubi-base-stack, it will generate one, which will update the builder and the tests of this PR will pass. |
internal/utils/utils.go
Outdated
) | ||
|
||
var DEFAULT_USER_ID = 1002 | ||
var DEFAULT_GROUP_ID = 1000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if these could be in a common place as we define them in multiple files?
Just one tiny question, otherwise looks good. Pushed through a release of the stack, will re-run the tests. |
12023c1
to
4ccc558
Compare
1b69ee2
to
15c9400
Compare
Yes, you are right. On the generate.go these constants are not being used anymore as the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Summary
This PR selects which node-engine to install based on the node versions that exist on
images.json
file.Use Cases
Checklist