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:
parent
df0c9c7b3f
commit
26bfec19b3
@ -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)
|
||||
|
@ -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{
|
||||
|
@ -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{}
|
||||
|
@ -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
32
pkg/cnbutils/user.go
Normal 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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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...), " ")
|
||||
|
@ -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:
|
||||
|
@ -363,3 +363,6 @@ spec:
|
||||
type: sbom
|
||||
containers:
|
||||
- image: "paketobuildpacks/builder:base"
|
||||
options:
|
||||
- name: -u
|
||||
value: "0"
|
||||
|
Loading…
Reference in New Issue
Block a user