1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-16 05:16:08 +02:00

feat(cnbBuild): support builders with different CNB user ids (#4625)

Co-authored-by: Ralf Pannemans <ralf.pannemans@sap.com>
This commit is contained in:
Pavel Busko 2023-11-02 16:03:11 +01:00 committed by GitHub
parent df0c9c7b3f
commit 26bfec19b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 121 additions and 29 deletions

View File

@ -6,6 +6,7 @@ import (
"os"
"path"
"path/filepath"
"syscall"
"github.com/SAP/jenkins-library/pkg/buildpacks"
"github.com/SAP/jenkins-library/pkg/buildsettings"
@ -285,8 +286,14 @@ func (config *cnbBuildOptions) resolvePath(utils cnbutils.BuildUtils) (buildpack
func callCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, utils cnbutils.BuildUtils, commonPipelineEnvironment *cnbBuildCommonPipelineEnvironment, httpClient piperhttp.Sender) error {
stepName := "cnbBuild"
telemetry := buildpacks.NewTelemetry(telemetryData)
err := isBuilder(utils)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return errors.Wrap(err, "the provided dockerImage is not a valid builder")
}
telemetry := buildpacks.NewTelemetry(telemetryData)
dockerImage, err := GetDockerImageValue(stepName)
if err != nil {
log.Entry().Warnf("failed to retrieve dockerImage configuration: '%v'", err)
@ -362,11 +369,22 @@ func runCnbBuild(config *cnbBuildOptions, telemetry *buildpacks.Telemetry, image
return errors.Wrap(err, fmt.Sprintf("failed to clean up platform folder %s", platformPath))
}
tempdir, err := os.MkdirTemp("", "cnbBuild-")
tempdir, err := utils.TempDir("", "cnbBuild-")
if err != nil {
return errors.Wrap(err, "failed to create tempdir")
}
defer os.RemoveAll(tempdir)
defer utils.RemoveAll(tempdir)
uid, gid, err := cnbutils.CnbUserInfo()
if err != nil {
return errors.Wrap(err, "failed to get user information")
}
err = utils.Chown(tempdir, uid, gid)
if err != nil {
return errors.Wrap(err, "failed to change tempdir ownership")
}
if config.BuildEnvVars == nil {
config.BuildEnvVars = map[string]interface{}{}
}
@ -374,12 +392,6 @@ func runCnbBuild(config *cnbBuildOptions, telemetry *buildpacks.Telemetry, image
telemetrySegment := createInitialTelemetrySegment(config, utils)
err = isBuilder(utils)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return errors.Wrap(err, "the provided dockerImage is not a valid builder")
}
include := ignore.CompileIgnoreLines("**/*")
exclude := ignore.CompileIgnoreLines("piper", ".pipeline", ".git")
@ -486,6 +498,10 @@ func runCnbBuild(config *cnbBuildOptions, telemetry *buildpacks.Telemetry, image
}
}
if err := utils.Chown(target, uid, gid); err != nil {
return err
}
if ok, _ := utils.FileExists(filepath.Join(target, "pom.xml")); ok {
err = linkTargetFolder(utils, source, target)
if err != nil {
@ -565,7 +581,14 @@ func runCnbBuild(config *cnbBuildOptions, telemetry *buildpacks.Telemetry, image
}
creatorArgs = append(creatorArgs, fmt.Sprintf("%s:%s", containerImage, targetImage.ContainerImageTag))
err = utils.RunExecutable(creatorPath, creatorArgs...)
attr := &syscall.SysProcAttr{
Credential: &syscall.Credential{
Uid: uint32(uid),
Gid: uint32(gid),
},
}
err = utils.RunExecutableWithAttrs(creatorPath, attr, creatorArgs...)
if err != nil {
log.SetErrorCategory(log.ErrorBuild)
return errors.Wrapf(err, "execution of '%s' failed", creatorArgs)

View File

@ -519,7 +519,7 @@ func cnbBuildMetadata() config.StepData {
},
},
Containers: []config.Container{
{Image: "paketobuildpacks/builder:base"},
{Image: "paketobuildpacks/builder:base", Options: []config.Option{{Name: "-u", Value: "0"}}},
},
Outputs: config.StepOutputs{
Resources: []config.StepResources{

View File

@ -107,6 +107,9 @@ func assetBuildEnv(t *testing.T, utils cnbutils.MockUtils, key, value string) bo
func TestRunCnbBuild(t *testing.T) {
configOptions.OpenFile = piperconf.OpenPiperFile
t.Setenv("CNB_USER_ID", "1000")
t.Setenv("CNB_GROUP_ID", "1000")
t.Run("prefers direct configuration", func(t *testing.T) {
t.Parallel()
commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}

View File

@ -43,7 +43,7 @@ func TestCNBIntegrationNPMProject(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestCnbIntegration"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
Environment: map[string]string{
@ -53,7 +53,7 @@ func TestCNBIntegrationNPMProject(t *testing.T) {
container2 := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestCnbIntegration"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
Environment: map[string]string{
@ -93,7 +93,7 @@ func TestCNBIntegrationProjectDescriptor(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestCnbIntegration", "project"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
})
@ -123,7 +123,7 @@ func TestCNBIntegrationBuildSummary(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestCnbIntegration", "project"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
})
@ -148,7 +148,7 @@ func TestCNBIntegrationZipPath(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestCnbIntegration", "zip"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
})
@ -177,7 +177,7 @@ func TestCNBIntegrationNonZipPath(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestMtaIntegration", "npm"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
})
@ -197,7 +197,7 @@ func TestCNBIntegrationNPMCustomBuildpacksFullProject(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestMtaIntegration", "npm"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
})
@ -225,7 +225,7 @@ func TestCNBIntegrationNPMCustomBuildpacksBuildpacklessProject(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: "paketobuildpacks/builder:buildpackless-full",
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestMtaIntegration", "npm"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
})
@ -266,7 +266,7 @@ func TestCNBIntegrationBindings(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestCnbIntegration"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
Environment: map[string]string{
@ -294,7 +294,7 @@ func TestCNBIntegrationMultiImage(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestCnbIntegration"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
})
@ -328,7 +328,7 @@ func TestCNBIntegrationPreserveFiles(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestCnbIntegration"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
})
@ -348,7 +348,7 @@ func TestCNBIntegrationPreserveFilesIgnored(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestCnbIntegration"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
})
@ -367,7 +367,7 @@ func TestCNBIntegrationPrePostBuildpacks(t *testing.T) {
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
Image: baseBuilder,
User: "cnb",
User: "0",
TestDir: []string{"testdata", "TestCnbIntegration"},
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
Environment: map[string]string{

32
pkg/cnbutils/user.go Normal file
View File

@ -0,0 +1,32 @@
package cnbutils
import (
"os"
"strconv"
"github.com/pkg/errors"
)
func CnbUserInfo() (int, int, error) {
uidStr, ok := os.LookupEnv("CNB_USER_ID")
if !ok {
return 0, 0, errors.New("environment variable CNB_USER_ID not found")
}
gidStr, ok := os.LookupEnv("CNB_GROUP_ID")
if !ok {
return 0, 0, errors.New("environment variable CNB_GROUP_ID not found")
}
uid, err := strconv.Atoi(uidStr)
if err != nil {
return 0, 0, err
}
gid, err := strconv.Atoi(gidStr)
if err != nil {
return 0, 0, err
}
return uid, gid, nil
}

View File

@ -42,6 +42,7 @@ type runner interface {
type ExecRunner interface {
runner
RunExecutable(executable string, params ...string) error
RunExecutableWithAttrs(executable string, sysProcAttr *syscall.SysProcAttr, params ...string) error
RunExecutableInBackground(executable string, params ...string) (Execution, error)
}
@ -127,9 +128,18 @@ func (c *Command) RunShell(shell, script string) error {
//
// Thus the executable needs to be on the PATH of the current process and it is not sufficient to alter the PATH on cmd.Env.
func (c *Command) RunExecutable(executable string, params ...string) error {
return c.RunExecutableWithAttrs(executable, nil, params...)
}
// RunExecutableWithAttrs runs the specified executable with parameters and as a specified UID and GID
// !! While the cmd.Env is applied during command execution, it is NOT involved when the actual executable is resolved.
//
// Thus the executable needs to be on the PATH of the current process and it is not sufficient to alter the PATH on cmd.Env.
func (c *Command) RunExecutableWithAttrs(executable string, sysProcAttr *syscall.SysProcAttr, params ...string) error {
c.prepareOut()
cmd := ExecCommand(executable, params...)
cmd.SysProcAttr = sysProcAttr
if len(c.dir) > 0 {
cmd.Dir = c.dir

View File

@ -511,6 +511,10 @@ func (f *FilesMock) Chmod(path string, mode os.FileMode) error {
return nil
}
func (f *FilesMock) Chown(path string, uid, gid int) error {
return nil
}
func (f *FilesMock) Abs(path string) (string, error) {
f.init()
return f.toAbsPath(path), nil

View File

@ -7,6 +7,7 @@ import (
"io"
"regexp"
"strings"
"syscall"
"github.com/SAP/jenkins-library/pkg/command"
)
@ -25,10 +26,11 @@ type ExecMockRunner struct {
}
type ExecCall struct {
Execution *Execution
Async bool
Exec string
Params []string
Execution *Execution
SysProcAttrs *syscall.SysProcAttr
Async bool
Exec string
Params []string
}
type Execution struct {
@ -61,8 +63,11 @@ func (m *ExecMockRunner) AppendEnv(e []string) {
}
func (m *ExecMockRunner) RunExecutable(e string, p ...string) error {
return m.RunExecutableWithAttrs(e, nil, p...)
}
exec := ExecCall{Exec: e, Params: p}
func (m *ExecMockRunner) RunExecutableWithAttrs(e string, attrs *syscall.SysProcAttr, p ...string) error {
exec := ExecCall{Exec: e, SysProcAttrs: attrs, Params: p}
m.Calls = append(m.Calls, exec)
c := strings.Join(append([]string{e}, p...), " ")

View File

@ -31,6 +31,7 @@ type FileUtils interface {
FileRemove(path string) error
MkdirAll(path string, perm os.FileMode) error
Chmod(path string, mode os.FileMode) error
Chown(path string, uid, gid int) error
Glob(pattern string) (matches []string, err error)
Chdir(path string) error
TempDir(string, string) (string, error)
@ -144,6 +145,17 @@ func (f Files) Chmod(path string, mode os.FileMode) error {
return os.Chmod(path, mode)
}
// Chown is a recursive wrapper for os.Chown().
func (f Files) Chown(path string, uid, gid int) error {
return filepath.WalkDir(path, func(name string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
return os.Chown(name, uid, gid)
})
}
// Unzip will decompress a zip archive, moving all files and folders
// within the zip file (parameter 1) to an output directory (parameter 2).
// from https://golangcode.com/unzip-files-in-go/ with the following license:

View File

@ -363,3 +363,6 @@ spec:
type: sbom
containers:
- image: "paketobuildpacks/builder:base"
options:
- name: -u
value: "0"