1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00
sap-jenkins-library/cmd/kanikoExecute_test.go
Jk1484 010aea0edb
feat(config): exporting generateConfig function and applying minor changes (#4605)
* exporting generateConfig function and applying minor changes

* Added setConfigOptions to set configOptions variable.
Added possibility to set format output, json or yaml for now.

* Correcting mistake on cmd/getDefaults.go

Co-authored-by: Jordi van Liempt <35920075+jliempt@users.noreply.github.com>

---------

Co-authored-by: Jordi van Liempt <35920075+jliempt@users.noreply.github.com>
2023-10-04 12:44:48 +02:00

866 lines
38 KiB
Go

//go:build unit
// +build unit
package cmd
import (
"bytes"
"fmt"
"io"
"net/http"
"path/filepath"
"strings"
"testing"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/jarcoal/httpmock"
"github.com/stretchr/testify/assert"
)
type kanikoMockClient struct {
httpMethod string
httpStatusCode int
urlsCalled []string
requestBody io.Reader
responseBody string
errorMessage string
}
func (c *kanikoMockClient) SetOptions(opts piperhttp.ClientOptions) {}
func (c *kanikoMockClient) SendRequest(method, url string, body io.Reader, header http.Header, cookies []*http.Cookie) (*http.Response, error) {
c.httpMethod = method
c.urlsCalled = append(c.urlsCalled, url)
c.requestBody = body
if len(c.errorMessage) > 0 {
return nil, fmt.Errorf(c.errorMessage)
}
return &http.Response{StatusCode: c.httpStatusCode, Body: io.NopCloser(bytes.NewReader([]byte(c.responseBody)))}, nil
}
func (c *kanikoMockClient) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error {
if len(c.errorMessage) > 0 {
return fmt.Errorf(c.errorMessage)
}
return nil
}
func TestRunKanikoExecute(t *testing.T) {
// required due to config resolution during build settings retrieval
// ToDo: proper mocking
openFileBak := configOptions.OpenFile
defer func() {
configOptions.OpenFile = openFileBak
}()
configOptions.OpenFile = configOpenFileMock
t.Run("success case", func(t *testing.T) {
config := &kanikoExecuteOptions{
BuildOptions: []string{"--skip-tls-verify-pull"},
ContainerImage: "myImage:tag",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
DockerConfigJSON: "path/to/docker/config.json",
BuildSettingsInfo: `{"mavenExecuteBuild":[{"dockerImage":"maven"}]}`,
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{
responseBody: "testCert",
}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.NoError(t, err)
assert.Equal(t, "rm", execRunner.Calls[0].Exec)
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
assert.NoError(t, err)
assert.Equal(t, `{"auths":{"custom":"test"}}`, string(c))
assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag"}, execRunner.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"}]`)
assert.Equal(t, "myImage:tag", commonPipelineEnvironment.container.imageNameTag)
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, "", commonPipelineEnvironment.container.imageDigest)
assert.Empty(t, commonPipelineEnvironment.container.imageDigests)
})
t.Run("success case - pass image digest to cpe if activated", func(t *testing.T) {
config := &kanikoExecuteOptions{
BuildOptions: []string{"--skip-tls-verify-pull"},
ContainerImage: "myImage:tag",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
DockerConfigJSON: "path/to/docker/config.json",
BuildSettingsInfo: `{"mavenExecuteBuild":[{"dockerImage":"maven"}]}`,
ReadImageDigest: true,
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{
responseBody: "testCert",
}
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, execRunner, certClient, fileUtils)
assert.NoError(t, err)
assert.Equal(t, "rm", execRunner.Calls[0].Exec)
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
assert.NoError(t, err)
assert.Equal(t, `{"auths":{"custom":"test"}}`, string(c))
assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag", "--digest-file", "/tmp/*-kanikoExecutetest/digest.txt"}, execRunner.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"}]`)
assert.Equal(t, "myImage:tag", commonPipelineEnvironment.container.imageNameTag)
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) {
config := &kanikoExecuteOptions{
BuildOptions: []string{"--skip-tls-verify-pull"},
ContainerImageName: "myImage",
ContainerImageTag: "1.2.3-a+x",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
DockerConfigJSON: "path/to/docker/config.json",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{
responseBody: "testCert",
}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.NoError(t, err)
assert.Equal(t, "rm", execRunner.Calls[0].Exec)
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
assert.NoError(t, err)
assert.Equal(t, `{"auths":{"custom":"test"}}`, string(c))
assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--skip-tls-verify-pull", "--destination", "my.registry.com:50000/myImage:1.2.3-a-x"}, execRunner.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, "", commonPipelineEnvironment.container.imageDigest)
assert.Empty(t, commonPipelineEnvironment.container.imageDigests)
})
t.Run("success case - image params with custom destination", func(t *testing.T) {
config := &kanikoExecuteOptions{
BuildOptions: []string{"--skip-tls-verify-pull", "--destination", "my.other.registry.com:50000/myImage:3.2.1-a-x"},
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
DockerConfigJSON: "path/to/docker/config.json",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{
responseBody: "testCert",
}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.NoError(t, err)
assert.Equal(t, "rm", execRunner.Calls[0].Exec)
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
assert.NoError(t, err)
assert.Equal(t, `{"auths":{"custom":"test"}}`, string(c))
assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--skip-tls-verify-pull", "--destination", "my.other.registry.com:50000/myImage:3.2.1-a-x"}, execRunner.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, "", commonPipelineEnvironment.container.imageDigest)
assert.Empty(t, []string{}, commonPipelineEnvironment.container.imageDigests)
})
t.Run("no error case - when cert update skipped", func(t *testing.T) {
config := &kanikoExecuteOptions{
BuildOptions: []string{"--skip-tls-verify-pull"},
ContainerImageName: "myImage",
ContainerImageTag: "1.2.3-a+x",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{},
DockerfilePath: "Dockerfile",
DockerConfigJSON: "path/to/docker/config.json",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{}
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")}
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.NoErrorf(t, err, "failed to update certificates: failed to load file '/kaniko/ssl/certs/ca-certificates.crt': read error")
})
t.Run("success case - no push, no docker config.json", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerBuildOptions: "--skip-tls-verify-pull",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{
responseBody: "testCert",
}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.NoError(t, err)
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
assert.NoError(t, err)
assert.Equal(t, `{"auths":{}}`, string(c))
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--skip-tls-verify-pull", "--no-push"}, execRunner.Calls[1].Params)
})
t.Run("success case - backward compatibility", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerBuildOptions: "--skip-tls-verify-pull",
ContainerImage: "myImage:tag",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
DockerConfigJSON: "path/to/docker/config.json",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{
responseBody: "testCert",
}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.NoError(t, err)
cwd, _ := fileUtils.Getwd()
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag"}, execRunner.Calls[1].Params)
})
t.Run("success case - createBOM", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerImage: "myImage:tag",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
DockerfilePath: "Dockerfile",
DockerConfigJSON: "path/to/docker/config.json",
CreateBOM: true,
SyftDownloadURL: "http://test-syft-url.io",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
httpmock.Activate()
defer httpmock.DeactivateAndReset()
fakeArchive, err := fileUtils.CreateArchive(map[string][]byte{"syft": []byte("test")})
assert.NoError(t, err)
httpmock.RegisterResponder(http.MethodGet, "http://test-syft-url.io", httpmock.NewBytesResponder(http.StatusOK, fakeArchive))
client := &piperhttp.Client{}
client.SetOptions(piperhttp.ClientOptions{MaxRetries: -1, UseDefaultTransport: true})
err = runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, client, fileUtils)
assert.NoError(t, err)
assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
assert.Equal(t, "myImage:tag", commonPipelineEnvironment.container.imageNameTag)
assert.Equal(t, "https://index.docker.io", commonPipelineEnvironment.container.registryURL)
assert.Equal(t, "/tmp/syfttest/syft", execRunner.Calls[2].Exec)
assert.Equal(t, []string{"packages", "registry:index.docker.io/myImage:tag", "-o", "cyclonedx-xml", "--file", "bom-docker-0.xml", "-q"}, execRunner.Calls[2].Params)
})
t.Run("success case - multi image build with root image", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerImageName: "myImage",
ContainerImageTag: "myTag",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerMultiImageBuild: true,
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("Dockerfile", []byte("some content"))
fileUtils.AddFile("sub1/Dockerfile", []byte("some content"))
fileUtils.AddFile("sub2/Dockerfile", []byte("some content"))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, nil, fileUtils)
assert.NoError(t, err)
assert.Equal(t, 3, len(execRunner.Calls))
assert.Equal(t, "/kaniko/executor", execRunner.Calls[0].Exec)
assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
assert.Equal(t, "/kaniko/executor", execRunner.Calls[2].Exec)
cwd, _ := fileUtils.Getwd()
expectedParams := [][]string{
{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage:myTag"},
{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
}
// need to go this way since we cannot count on the correct order
for _, call := range execRunner.Calls {
found := false
for _, expected := range expectedParams {
if strings.Join(call.Params, " ") == strings.Join(expected, " ") {
found = true
break
}
}
assert.True(t, found, fmt.Sprintf("%v not found", call.Params))
}
assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
assert.Equal(t, "myImage:myTag", commonPipelineEnvironment.container.imageNameTag)
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage")
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub1")
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub2")
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)
assert.Empty(t, commonPipelineEnvironment.container.imageDigests)
})
t.Run("success case - multi image build excluding root image", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerImageName: "myImage",
ContainerImageTag: "myTag",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerMultiImageBuild: true,
ContainerMultiImageBuildExcludes: []string{"Dockerfile"},
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("Dockerfile", []byte("some content"))
fileUtils.AddFile("sub1/Dockerfile", []byte("some content"))
fileUtils.AddFile("sub2/Dockerfile", []byte("some content"))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, nil, fileUtils)
assert.NoError(t, err)
assert.Equal(t, 2, len(execRunner.Calls))
assert.Equal(t, "/kaniko/executor", execRunner.Calls[0].Exec)
assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
cwd, _ := fileUtils.Getwd()
expectedParams := [][]string{
{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
}
// need to go this way since we cannot count on the correct order
for _, call := range execRunner.Calls {
found := false
for _, expected := range expectedParams {
if strings.Join(call.Params, " ") == strings.Join(expected, " ") {
found = true
break
}
}
assert.True(t, found, fmt.Sprintf("%v not found", call.Params))
}
assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
assert.Equal(t, "", commonPipelineEnvironment.container.imageNameTag)
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub1")
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub2")
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub1:myTag")
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub2:myTag")
})
t.Run("success case - multi image build with CreateBOM", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerImageName: "myImage",
ContainerImageTag: "myTag",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerMultiImageBuild: true,
DockerConfigJSON: "path/to/docker/config.json",
CreateBOM: true,
SyftDownloadURL: "http://test-syft-url.io",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.AddFile("Dockerfile", []byte("some content"))
fileUtils.AddFile("sub1/Dockerfile", []byte("some content"))
fileUtils.AddFile("sub2/Dockerfile", []byte("some content"))
httpmock.Activate()
defer httpmock.DeactivateAndReset()
fakeArchive, err := fileUtils.CreateArchive(map[string][]byte{"syft": []byte("test")})
assert.NoError(t, err)
httpmock.RegisterResponder(http.MethodGet, "http://test-syft-url.io", httpmock.NewBytesResponder(http.StatusOK, fakeArchive))
client := &piperhttp.Client{}
client.SetOptions(piperhttp.ClientOptions{MaxRetries: -1, UseDefaultTransport: true})
err = runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, client, fileUtils)
assert.NoError(t, err)
assert.Equal(t, 6, len(execRunner.Calls))
assert.Equal(t, "/kaniko/executor", execRunner.Calls[0].Exec)
assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
assert.Equal(t, "/kaniko/executor", execRunner.Calls[2].Exec)
cwd, _ := fileUtils.Getwd()
expectedParams := [][]string{
{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage:myTag"},
{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
{"packages", "registry:my.registry.com:50000/myImage:myTag", "-o", "cyclonedx-xml", "--file"},
{"packages", "registry:my.registry.com:50000/myImage-sub1:myTag", "-o", "cyclonedx-xml", "--file"},
{"packages", "registry:my.registry.com:50000/myImage-sub2:myTag", "-o", "cyclonedx-xml", "--file"},
}
// need to go this way since we cannot count on the correct order
for index, call := range execRunner.Calls {
found := false
for _, expected := range expectedParams {
if expected[0] == "packages" {
expected = append(expected, fmt.Sprintf("bom-docker-%d.xml", index-3), "-q")
}
if strings.Join(call.Params, " ") == strings.Join(expected, " ") {
found = true
break
}
}
assert.True(t, found, fmt.Sprintf("%v not found", call.Params))
}
assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
assert.Equal(t, "myImage:myTag", commonPipelineEnvironment.container.imageNameTag)
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage")
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub1")
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub2")
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)
assert.Empty(t, commonPipelineEnvironment.container.imageDigests)
})
t.Run("success case - updating an existing docker config json with addtional credentials", func(t *testing.T) {
config := &kanikoExecuteOptions{
BuildOptions: []string{"--skip-tls-verify-pull"},
ContainerImageName: "myImage",
ContainerImageTag: "1.2.3-a+x",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
DockerConfigJSON: "path/to/docker/config.json",
ContainerRegistryUser: "dummyUser",
ContainerRegistryPassword: "dummyPassword",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{
responseBody: "testCert",
}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths": {"dummyUrl": {"auth": "XXXXXXX"}}}`))
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.NoError(t, err)
assert.Equal(t, "rm", execRunner.Calls[0].Exec)
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
assert.NoError(t, err)
assert.Equal(t, `{"auths":{"dummyUrl":{"auth":"XXXXXXX"},"https://my.registry.com:50000":{"auth":"ZHVtbXlVc2VyOmR1bW15UGFzc3dvcmQ="}}}`, string(c))
})
t.Run("success case - creating new docker config json with provided container credentials", func(t *testing.T) {
config := &kanikoExecuteOptions{
BuildOptions: []string{"--skip-tls-verify-pull"},
ContainerImageName: "myImage",
ContainerImageTag: "1.2.3-a+x",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
ContainerRegistryUser: "dummyUser",
ContainerRegistryPassword: "dummyPassword",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{
responseBody: "testCert",
}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.NoError(t, err)
assert.Equal(t, "rm", execRunner.Calls[0].Exec)
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
assert.NoError(t, err)
assert.Equal(t, `{"auths":{"https://my.registry.com:50000":{"auth":"ZHVtbXlVc2VyOmR1bW15UGFzc3dvcmQ="}}}`, string(c))
})
t.Run("success case - multi context build with CreateBOM", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerImageName: "myImage",
ContainerImageTag: "myTag",
ContainerRegistryURL: "https://my.registry.com:50000",
DockerConfigJSON: "path/to/docker/config.json",
DockerfilePath: "Dockerfile",
CreateBOM: true,
SyftDownloadURL: "http://test-syft-url.io",
MultipleImages: []map[string]interface{}{
{
"contextSubPath": "/test1",
"containerImageName": "myImageOne",
},
{
"contextSubPath": "/test2",
"containerImageName": "myImageTwo",
"containerImageTag": "myTagTwo",
},
},
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.AddFile("Dockerfile", []byte("some content"))
fileUtils.AddFile("test1/test", []byte("some content test1"))
fileUtils.AddFile("test2/test", []byte("some content test2"))
httpmock.Activate()
defer httpmock.DeactivateAndReset()
fakeArchive, err := fileUtils.CreateArchive(map[string][]byte{"syft": []byte("test")})
assert.NoError(t, err)
httpmock.RegisterResponder(http.MethodGet, "http://test-syft-url.io", httpmock.NewBytesResponder(http.StatusOK, fakeArchive))
client := &piperhttp.Client{}
client.SetOptions(piperhttp.ClientOptions{MaxRetries: -1, UseDefaultTransport: true})
err = runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, client, fileUtils)
assert.NoError(t, err)
assert.Equal(t, 4, len(execRunner.Calls))
assert.Equal(t, "/kaniko/executor", execRunner.Calls[0].Exec)
assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
cwd, _ := fileUtils.Getwd()
expectedParams := [][]string{
{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--context-sub-path", "/test1", "--destination", "my.registry.com:50000/myImageOne:myTag"},
{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--context-sub-path", "/test2", "--destination", "my.registry.com:50000/myImageTwo:myTagTwo"},
{"packages", "registry:my.registry.com:50000/myImageOne:myTag", "-o", "cyclonedx-xml", "--file"},
{"packages", "registry:my.registry.com:50000/myImageTwo:myTagTwo", "-o", "cyclonedx-xml", "--file"},
}
// need to go this way since we cannot count on the correct order
for index, call := range execRunner.Calls {
found := false
for _, expected := range expectedParams {
if expected[0] == "packages" {
expected = append(expected, fmt.Sprintf("bom-docker-%d.xml", index-2), "-q")
}
if strings.Join(call.Params, " ") == strings.Join(expected, " ") {
found = true
break
}
}
assert.True(t, found, fmt.Sprintf("%v not found", call.Params))
}
assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
assert.Equal(t, "myImage:myTag", commonPipelineEnvironment.container.imageNameTag)
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImageOne")
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImageTwo")
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImageOne:myTag")
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImageTwo:myTagTwo")
assert.Equal(t, "", commonPipelineEnvironment.container.imageDigest)
assert.Empty(t, commonPipelineEnvironment.container.imageDigests)
})
t.Run("error case - multi image build: no docker files", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerImageName: "myImage",
ContainerImageTag: "myTag",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerMultiImageBuild: true,
}
cpe := kanikoExecuteCommonPipelineEnvironment{}
execRunner := &mock.ExecMockRunner{}
fileUtils := &mock.FilesMock{}
err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, execRunner, nil, fileUtils)
assert.Error(t, err)
assert.Contains(t, fmt.Sprint(err), "failed to identify image list for multi image build")
})
t.Run("error case - multi image build: no docker files to process", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerImageName: "myImage",
ContainerImageTag: "myTag",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerMultiImageBuild: true,
ContainerMultiImageBuildExcludes: []string{"Dockerfile"},
}
cpe := kanikoExecuteCommonPipelineEnvironment{}
execRunner := &mock.ExecMockRunner{}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("Dockerfile", []byte("some content"))
err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, execRunner, nil, fileUtils)
assert.Error(t, err)
assert.Contains(t, fmt.Sprint(err), "no docker files to process, please check exclude list")
})
t.Run("error case - multi image build: build failed", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerImageName: "myImage",
ContainerImageTag: "myTag",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerMultiImageBuild: true,
}
cpe := kanikoExecuteCommonPipelineEnvironment{}
execRunner := &mock.ExecMockRunner{}
execRunner.ShouldFailOnCommand = map[string]error{"/kaniko/executor": fmt.Errorf("execution failed")}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("Dockerfile", []byte("some content"))
err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, execRunner, nil, fileUtils)
assert.Error(t, err)
assert.Contains(t, fmt.Sprint(err), "failed to build image")
})
t.Run("error case - Kaniko init failed", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
}
execRunner := &mock.ExecMockRunner{
ShouldFailOnCommand: map[string]error{"rm": fmt.Errorf("rm failed")},
}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{}
fileUtils := &mock.FilesMock{}
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.EqualError(t, err, "failed to initialize Kaniko container: rm failed")
})
t.Run("error case - Kaniko execution failed", func(t *testing.T) {
config := &kanikoExecuteOptions{}
execRunner := &mock.ExecMockRunner{
ShouldFailOnCommand: map[string]error{"/kaniko/executor": fmt.Errorf("kaniko run failed")},
}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{}
fileUtils := &mock.FilesMock{}
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.EqualError(t, err, "execution of '/kaniko/executor' failed: kaniko run failed")
})
t.Run("error case - cert update failed", func(t *testing.T) {
config := &kanikoExecuteOptions{
BuildOptions: []string{"--skip-tls-verify-pull"},
ContainerImageName: "myImage",
ContainerImageTag: "1.2.3-a+x",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
DockerConfigJSON: "path/to/docker/config.json",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{}
fileUtils := &mock.FilesMock{}
fileUtils.FileReadErrors = map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")}
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.EqualError(t, err, "failed to update certificates: failed to load file '/kaniko/ssl/certs/ca-certificates.crt': read error")
})
t.Run("error case - dockerconfig read failed", func(t *testing.T) {
config := &kanikoExecuteOptions{
DockerConfigJSON: "path/to/docker/config.json",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{}
fileUtils := &mock.FilesMock{}
fileUtils.FileReadErrors = map[string]error{"path/to/docker/config.json": fmt.Errorf("read error")}
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.EqualError(t, err, "failed to read existing docker config json at 'path/to/docker/config.json': read error")
})
t.Run("error case - dockerconfig write failed", func(t *testing.T) {
config := &kanikoExecuteOptions{
DockerConfigJSON: "path/to/docker/config.json",
}
execRunner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
fileUtils.FileWriteErrors = map[string]error{"/kaniko/.docker/config.json": fmt.Errorf("write error")}
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
assert.EqualError(t, err, "failed to write file '/kaniko/.docker/config.json': write error")
})
t.Run("error case - multi context build: no subcontext provided", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerImageName: "myImage",
ContainerImageTag: "myTag",
ContainerRegistryURL: "https://my.registry.com:50000",
MultipleImages: []map[string]interface{}{
{"containerImageName": "myImageOne"},
{"containerImageName": "myImageTwo"},
},
}
cpe := kanikoExecuteCommonPipelineEnvironment{}
execRunner := &mock.ExecMockRunner{}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("Dockerfile", []byte("some content"))
err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, execRunner, nil, fileUtils)
assert.Error(t, err)
assert.Contains(t, fmt.Sprint(err), "multipleImages: empty contextSubPath")
})
}