1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-03-03 15:02:35 +02:00

feat(kanikoExecute): image digests (#3566)

* feat(init stage): artifact version

* feat(kaniko): expose image digests to cpe

* fix

* handle tmp folder creation differently

* fix

* fix

* fix tests

* set ignore-path to /
This commit is contained in:
Christian Volk 2022-02-23 11:41:26 +01:00 committed by GitHub
parent 9fa8390ef2
commit d428cfcbd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 91 additions and 20 deletions

View File

@ -134,7 +134,7 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus
containerImageNameAndTag := fmt.Sprintf("%v:%v", image, containerImageTag)
dest = []string{"--destination", fmt.Sprintf("%v/%v", containerRegistry, containerImageNameAndTag)}
buildOpts := append(config.BuildOptions, dest...)
err = runKaniko(file, buildOpts, execRunner, fileUtils)
err = runKaniko(file, buildOpts, execRunner, fileUtils, commonPipelineEnvironment)
if err != nil {
return fmt.Errorf("failed to build image '%v' using '%v': %w", image, file, err)
}
@ -206,15 +206,23 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus
}
// no support for building multiple containers
return runKaniko(config.DockerfilePath, config.BuildOptions, execRunner, fileUtils)
return runKaniko(config.DockerfilePath, config.BuildOptions, execRunner, fileUtils, commonPipelineEnvironment)
}
func runKaniko(dockerFilepath string, buildOptions []string, execRunner command.ExecRunner, fileUtils piperutils.FileUtils) error {
func runKaniko(dockerFilepath string, buildOptions []string, execRunner command.ExecRunner, fileUtils piperutils.FileUtils, commonPipelineEnvironment *kanikoExecuteCommonPipelineEnvironment) error {
cwd, err := fileUtils.Getwd()
if err != nil {
return fmt.Errorf("failed to get current working directory: %w", err)
}
kanikoOpts := []string{"--dockerfile", dockerFilepath, "--context", cwd}
tmpDir, err := fileUtils.TempDir("", "*-kanikoExecute")
if err != nil {
return fmt.Errorf("failed to create tmp dir for kanikoExecute: %w", err)
}
digestFilePath := fmt.Sprintf("%s/digest.txt", tmpDir)
kanikoOpts := []string{"--dockerfile", dockerFilepath, "--context", cwd, "--reproducible", "--digest-file", digestFilePath}
kanikoOpts = append(kanikoOpts, buildOptions...)
err = execRunner.RunExecutable("/kaniko/executor", kanikoOpts...)
@ -222,5 +230,23 @@ func runKaniko(dockerFilepath string, buildOptions []string, execRunner command.
log.SetErrorCategory(log.ErrorBuild)
return errors.Wrap(err, "execution of '/kaniko/executor' failed")
}
if b, err := fileUtils.FileExists(digestFilePath); err == nil && b {
digest, err := fileUtils.FileRead(digestFilePath)
if err != nil {
return errors.Wrap(err, "error while reading image digest")
}
digestStr := string(digest)
log.Entry().Debugf("image digest: %s", digestStr)
commonPipelineEnvironment.container.imageDigest = string(digestStr)
commonPipelineEnvironment.container.imageDigests = append(commonPipelineEnvironment.container.imageDigests, digestStr)
} else {
log.Entry().Warn("couldn't resolve image digest")
}
return nil
}

View File

@ -38,8 +38,10 @@ type kanikoExecuteCommonPipelineEnvironment struct {
container struct {
registryURL string
imageNameTag string
imageDigest string
imageNames []string
imageNameTags []string
imageDigests []string
}
custom struct {
buildSettingsInfo string
@ -54,8 +56,10 @@ func (p *kanikoExecuteCommonPipelineEnvironment) persist(path, resourceName stri
}{
{category: "container", name: "registryUrl", value: p.container.registryURL},
{category: "container", name: "imageNameTag", value: p.container.imageNameTag},
{category: "container", name: "imageDigest", value: p.container.imageDigest},
{category: "container", name: "imageNames", value: p.container.imageNames},
{category: "container", name: "imageNameTags", value: p.container.imageNameTags},
{category: "container", name: "imageDigests", value: p.container.imageDigests},
{category: "custom", name: "buildSettingsInfo", value: p.custom.buildSettingsInfo},
}
@ -369,7 +373,7 @@ func kanikoExecuteMetadata() config.StepData {
},
},
Containers: []config.Container{
{Image: "gcr.io/kaniko-project/executor:debug", EnvVars: []config.EnvVar{{Name: "container", Value: "docker"}, {Name: "TMPDIR", Value: "/"}}, Options: []config.Option{{Name: "-u", Value: "0"}, {Name: "--entrypoint", Value: ""}}},
{Image: "gcr.io/kaniko-project/executor:debug", EnvVars: []config.EnvVar{{Name: "container", Value: "docker"}}, Options: []config.Option{{Name: "-u", Value: "0"}, {Name: "--entrypoint", Value: ""}}},
},
Outputs: config.StepOutputs{
Resources: []config.StepResources{
@ -379,8 +383,10 @@ func kanikoExecuteMetadata() config.StepData {
Parameters: []map[string]interface{}{
{"name": "container/registryUrl"},
{"name": "container/imageNameTag"},
{"name": "container/imageDigest"},
{"name": "container/imageNames", "type": "[]string"},
{"name": "container/imageNameTags", "type": "[]string"},
{"name": "container/imageDigests", "type": "[]string"},
{"name": "custom/buildSettingsInfo"},
},
},

View File

@ -68,6 +68,7 @@ func TestRunKanikoExecute(t *testing.T) {
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
fileUtils.AddFile("/tmp/*-kanikoExecutetest/digest.txt", []byte(`sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0`))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
@ -83,7 +84,7 @@ func TestRunKanikoExecute(t *testing.T) {
assert.Equal(t, "/kaniko/executor", runner.Calls[1].Exec)
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag"}, runner.Calls[1].Params)
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--reproducible", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt", "--skip-tls-verify-pull", "--destination", "myImage:tag"}, runner.Calls[1].Params)
assert.Contains(t, commonPipelineEnvironment.custom.buildSettingsInfo, `"mavenExecuteBuild":[{"dockerImage":"maven"}]`)
assert.Contains(t, commonPipelineEnvironment.custom.buildSettingsInfo, `"kanikoExecute":[{"dockerImage":"gcr.io/kaniko-project/executor:debug"}]`)
@ -92,6 +93,9 @@ func TestRunKanikoExecute(t *testing.T) {
assert.Equal(t, "https://index.docker.io", commonPipelineEnvironment.container.registryURL)
assert.Equal(t, []string{"myImage"}, commonPipelineEnvironment.container.imageNames)
assert.Equal(t, []string{"myImage:tag"}, commonPipelineEnvironment.container.imageNameTags)
assert.Equal(t, "sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0", commonPipelineEnvironment.container.imageDigest)
assert.Equal(t, []string{"sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0"}, commonPipelineEnvironment.container.imageDigests)
})
t.Run("success case - image params", func(t *testing.T) {
@ -115,6 +119,7 @@ func TestRunKanikoExecute(t *testing.T) {
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
fileUtils.AddFile("/tmp/*-kanikoExecutetest/digest.txt", []byte(`sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0`))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
@ -130,12 +135,15 @@ func TestRunKanikoExecute(t *testing.T) {
assert.Equal(t, "/kaniko/executor", runner.Calls[1].Exec)
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--destination", "my.registry.com:50000/myImage:1.2.3-a-x"}, runner.Calls[1].Params)
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--reproducible", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt", "--skip-tls-verify-pull", "--destination", "my.registry.com:50000/myImage:1.2.3-a-x"}, runner.Calls[1].Params)
assert.Equal(t, "myImage:1.2.3-a-x", commonPipelineEnvironment.container.imageNameTag)
assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
assert.Equal(t, []string{"myImage"}, commonPipelineEnvironment.container.imageNames)
assert.Equal(t, []string{"myImage:1.2.3-a-x"}, commonPipelineEnvironment.container.imageNameTags)
assert.Equal(t, "sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0", commonPipelineEnvironment.container.imageDigest)
assert.Equal(t, []string{"sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0"}, commonPipelineEnvironment.container.imageDigests)
})
t.Run("success case - image params with custom destination", func(t *testing.T) {
@ -156,6 +164,7 @@ func TestRunKanikoExecute(t *testing.T) {
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
fileUtils.AddFile("/tmp/*-kanikoExecutetest/digest.txt", []byte(`sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0`))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
@ -171,12 +180,15 @@ func TestRunKanikoExecute(t *testing.T) {
assert.Equal(t, "/kaniko/executor", runner.Calls[1].Exec)
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--destination", "my.other.registry.com:50000/myImage:3.2.1-a-x"}, runner.Calls[1].Params)
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--reproducible", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt", "--skip-tls-verify-pull", "--destination", "my.other.registry.com:50000/myImage:3.2.1-a-x"}, runner.Calls[1].Params)
assert.Equal(t, "myImage:3.2.1-a-x", commonPipelineEnvironment.container.imageNameTag)
assert.Equal(t, "https://my.other.registry.com:50000", commonPipelineEnvironment.container.registryURL)
assert.Equal(t, []string{"myImage"}, commonPipelineEnvironment.container.imageNames)
assert.Equal(t, []string{"myImage:3.2.1-a-x"}, commonPipelineEnvironment.container.imageNameTags)
assert.Equal(t, "sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0", commonPipelineEnvironment.container.imageDigest)
assert.Equal(t, []string{"sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0"}, commonPipelineEnvironment.container.imageDigests)
})
t.Run("no error case - when cert update skipped", func(t *testing.T) {
@ -198,6 +210,7 @@ func TestRunKanikoExecute(t *testing.T) {
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(``))
fileUtils.FileReadErrors = map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")}
fileUtils.AddFile("/tmp/*-kanikoExecutetest/digest.txt", []byte(`sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0`))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
@ -220,6 +233,7 @@ func TestRunKanikoExecute(t *testing.T) {
}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
fileUtils.AddFile("/tmp/*-kanikoExecutetest/digest.txt", []byte(`sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0`))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
@ -230,7 +244,7 @@ func TestRunKanikoExecute(t *testing.T) {
assert.Equal(t, `{"auths":{}}`, string(c))
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--no-push"}, runner.Calls[1].Params)
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--reproducible", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt", "--skip-tls-verify-pull", "--no-push"}, runner.Calls[1].Params)
})
t.Run("success case - backward compatibility", func(t *testing.T) {
@ -252,12 +266,13 @@ func TestRunKanikoExecute(t *testing.T) {
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
fileUtils.AddFile("/tmp/*-kanikoExecutetest/digest.txt", []byte(`sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0`))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
assert.NoError(t, err)
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag"}, runner.Calls[1].Params)
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--reproducible", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt", "--skip-tls-verify-pull", "--destination", "myImage:tag"}, runner.Calls[1].Params)
})
t.Run("success case - multi image build with root image", func(t *testing.T) {
@ -275,6 +290,7 @@ func TestRunKanikoExecute(t *testing.T) {
fileUtils.AddFile("Dockerfile", []byte("some content"))
fileUtils.AddFile("sub1/Dockerfile", []byte("some content"))
fileUtils.AddFile("sub2/Dockerfile", []byte("some content"))
fileUtils.AddFile("/tmp/*-kanikoExecutetest/digest.txt", []byte(`sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0`))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, nil, fileUtils)
@ -287,9 +303,9 @@ func TestRunKanikoExecute(t *testing.T) {
cwd, _ := fileUtils.Getwd()
expectedParams := [][]string{
{"--dockerfile", "Dockerfile", "--context", cwd, "--destination", "my.registry.com:50000/myImage:myTag"},
{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", cwd, "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", cwd, "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
{"--dockerfile", "Dockerfile", "--context", cwd, "--reproducible", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt", "--destination", "my.registry.com:50000/myImage:myTag"},
{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", cwd, "--reproducible", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt", "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", cwd, "--reproducible", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt", "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
}
// need to go this way since we cannot count on the correct order
for _, call := range runner.Calls {
@ -311,6 +327,9 @@ func TestRunKanikoExecute(t *testing.T) {
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage:myTag")
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub1:myTag")
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub2:myTag")
assert.Equal(t, commonPipelineEnvironment.container.imageDigest, "sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0")
assert.Equal(t, commonPipelineEnvironment.container.imageDigests, []string{"sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0", "sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0", "sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0"})
})
t.Run("success case - multi image build excluding root image", func(t *testing.T) {
@ -329,6 +348,7 @@ func TestRunKanikoExecute(t *testing.T) {
fileUtils.AddFile("Dockerfile", []byte("some content"))
fileUtils.AddFile("sub1/Dockerfile", []byte("some content"))
fileUtils.AddFile("sub2/Dockerfile", []byte("some content"))
fileUtils.AddFile("/tmp/*-kanikoExecutetest/digest.txt", []byte(`sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0`))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, nil, fileUtils)
@ -340,8 +360,8 @@ func TestRunKanikoExecute(t *testing.T) {
cwd, _ := fileUtils.Getwd()
expectedParams := [][]string{
{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", cwd, "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", cwd, "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", cwd, "--reproducible", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt", "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", cwd, "--reproducible", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt", "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
}
// need to go this way since we cannot count on the correct order
for _, call := range runner.Calls {

View File

@ -42,6 +42,13 @@ type Files struct {
// TempDir creates a temporary directory
func (f Files) TempDir(dir, pattern string) (name string, err error) {
if len(dir) == 0 {
// lazy init system temp dir in case it doesn't exist
if exists, _ := f.DirExists(os.TempDir()); !exists {
f.MkdirAll(os.TempDir(), 0666)
}
}
return ioutil.TempDir(dir, pattern)
}

View File

@ -34,8 +34,8 @@ spec:
- STEPS
default:
- --skip-tls-verify-pull
# fixing Kaniko issue with multistage builds and missing busybox
# suggestion from https://github.com/GoogleContainerTools/kaniko/issues/1586#issuecomment-945718536 did not solve the problem
# fixing Kaniko issue https://github.com/GoogleContainerTools/kaniko/issues/1586
# as per comment https://github.com/GoogleContainerTools/kaniko/issues/1586#issuecomment-945718536
- --ignore-path=/
- name: buildSettingsInfo
type: string
@ -173,10 +173,13 @@ spec:
params:
- name: container/registryUrl
- name: container/imageNameTag
- name: container/imageDigest
- name: container/imageNames
type: "[]string"
- name: container/imageNameTags
type: "[]string"
- name: container/imageDigests
type: "[]string"
- name: custom/buildSettingsInfo
containers:
- image: gcr.io/kaniko-project/executor:debug
@ -191,5 +194,3 @@ spec:
env:
- name: container
value: docker
- name: TMPDIR
value: /

View File

@ -65,7 +65,13 @@ import static com.sap.piper.Prerequisites.checkScript
*/
'verbose'
]
@Field STAGE_STEP_KEYS = []
@Field STAGE_STEP_KEYS = [
/**
* Sets the build version.
* @possibleValues `true`, `false`
*/
'artifactPrepareVersion'
]
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus(STAGE_STEP_KEYS)
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus([
/**
@ -220,6 +226,11 @@ void call(Map parameters = [:]) {
if (parameters.script.commonPipelineEnvironment.configuration.runStep?.get('Init')?.slackSendNotification) {
slackSendNotification script: script, message: "STARTED: Job <${env.BUILD_URL}|${URLDecoder.decode(env.JOB_NAME, java.nio.charset.StandardCharsets.UTF_8.name())} ${env.BUILD_DISPLAY_NAME}>", color: 'WARNING'
}
config.artifactPrepareVersion = true
}
if (config.artifactPrepareVersion) {
Map prepareVersionParams = [script: script]
if (config.inferBuildTool) {
prepareVersionParams.buildTool = buildTool