2020-07-10 08:07:59 +02:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"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/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: ioutil.NopCloser(bytes.NewReader([]byte(c.responseBody)))}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type kanikoFileMock struct {
|
|
|
|
fileReadContent map[string]string
|
|
|
|
fileReadErr map[string]error
|
|
|
|
fileWriteContent map[string]string
|
|
|
|
fileWriteErr map[string]error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *kanikoFileMock) FileExists(path string) (bool, error) {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *kanikoFileMock) Copy(src, dest string) (int64, error) {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *kanikoFileMock) FileRead(path string) ([]byte, error) {
|
|
|
|
if f.fileReadErr[path] != nil {
|
|
|
|
return []byte{}, f.fileReadErr[path]
|
|
|
|
}
|
|
|
|
return []byte(f.fileReadContent[path]), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *kanikoFileMock) FileWrite(path string, content []byte, perm os.FileMode) error {
|
|
|
|
if f.fileWriteErr[path] != nil {
|
|
|
|
return f.fileWriteErr[path]
|
|
|
|
}
|
|
|
|
f.fileWriteContent[path] = string(content)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *kanikoFileMock) MkdirAll(path string, perm os.FileMode) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-16 14:25:01 +02:00
|
|
|
func (f *kanikoFileMock) Chmod(path string, mode os.FileMode) error {
|
|
|
|
return fmt.Errorf("not implemented. func is only present in order to fullfil the interface contract. Needs to be ajusted in case it gets used.")
|
|
|
|
}
|
|
|
|
|
2020-07-28 17:06:17 +02:00
|
|
|
func (f *kanikoFileMock) Abs(path string) (string, error) {
|
|
|
|
return "", fmt.Errorf("not implemented. func is only present in order to fullfil the interface contract. Needs to be ajusted in case it gets used.")
|
|
|
|
}
|
|
|
|
|
2020-07-30 10:35:46 +02:00
|
|
|
func (f *kanikoFileMock) Glob(pattern string) (matches []string, err error) {
|
|
|
|
return nil, fmt.Errorf("not implemented. func is only present in order to fullfil the interface contract. Needs to be ajusted in case it gets used.")
|
|
|
|
}
|
|
|
|
|
2021-07-22 11:06:46 +02:00
|
|
|
func (f *kanikoFileMock) Chdir(pattern string) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-10 08:07:59 +02:00
|
|
|
func TestRunKanikoExecute(t *testing.T) {
|
|
|
|
|
2020-10-14 11:13:08 +02:00
|
|
|
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
|
|
|
|
2020-07-10 08:07:59 +02:00
|
|
|
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",
|
|
|
|
}
|
|
|
|
|
|
|
|
runner := &mock.ExecMockRunner{}
|
|
|
|
|
|
|
|
certClient := &kanikoMockClient{
|
|
|
|
responseBody: "testCert",
|
|
|
|
}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileReadContent: map[string]string{"path/to/docker/config.json": `{"auths":{"custom":"test"}}`},
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
}
|
|
|
|
|
2020-10-14 11:13:08 +02:00
|
|
|
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
2020-07-10 08:07:59 +02:00
|
|
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, "rm", runner.Calls[0].Exec)
|
|
|
|
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, runner.Calls[0].Params)
|
|
|
|
|
|
|
|
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
|
|
|
|
assert.Equal(t, `{"auths":{"custom":"test"}}`, fileUtils.fileWriteContent["/kaniko/.docker/config.json"])
|
|
|
|
|
|
|
|
assert.Equal(t, "/kaniko/executor", runner.Calls[1].Exec)
|
|
|
|
cwd, _ := os.Getwd()
|
|
|
|
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag"}, runner.Calls[1].Params)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
2020-10-14 11:13:08 +02:00
|
|
|
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",
|
|
|
|
}
|
|
|
|
|
|
|
|
runner := &mock.ExecMockRunner{}
|
|
|
|
|
|
|
|
certClient := &kanikoMockClient{
|
|
|
|
responseBody: "testCert",
|
|
|
|
}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileReadContent: map[string]string{"path/to/docker/config.json": `{"auths":{"custom":"test"}}`},
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
|
|
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, "rm", runner.Calls[0].Exec)
|
|
|
|
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, runner.Calls[0].Params)
|
|
|
|
|
|
|
|
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
|
|
|
|
assert.Equal(t, `{"auths":{"custom":"test"}}`, fileUtils.fileWriteContent["/kaniko/.docker/config.json"])
|
|
|
|
|
|
|
|
assert.Equal(t, "/kaniko/executor", runner.Calls[1].Exec)
|
|
|
|
cwd, _ := os.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)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
2021-03-25 16:32:10 +02:00
|
|
|
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",
|
|
|
|
}
|
|
|
|
|
|
|
|
runner := &mock.ExecMockRunner{}
|
|
|
|
|
|
|
|
certClient := &kanikoMockClient{}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
fileReadErr: map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
|
|
|
|
|
|
|
assert.NoErrorf(t, err, "failed to update certificates: failed to load file '/kaniko/ssl/certs/ca-certificates.crt': read error")
|
|
|
|
})
|
|
|
|
|
2020-07-10 08:07:59 +02:00
|
|
|
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",
|
|
|
|
}
|
|
|
|
|
|
|
|
runner := &mock.ExecMockRunner{}
|
|
|
|
|
|
|
|
certClient := &kanikoMockClient{
|
|
|
|
responseBody: "testCert",
|
|
|
|
}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
}
|
|
|
|
|
2020-10-14 11:13:08 +02:00
|
|
|
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
2020-07-10 08:07:59 +02:00
|
|
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, `{"auths":{}}`, fileUtils.fileWriteContent["/kaniko/.docker/config.json"])
|
|
|
|
|
|
|
|
cwd, _ := os.Getwd()
|
|
|
|
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--no-push"}, runner.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",
|
|
|
|
}
|
|
|
|
|
|
|
|
runner := &mock.ExecMockRunner{}
|
|
|
|
|
|
|
|
certClient := &kanikoMockClient{
|
|
|
|
responseBody: "testCert",
|
|
|
|
}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileReadContent: map[string]string{"path/to/docker/config.json": `{"auths":{"custom":"test"}}`},
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
}
|
|
|
|
|
2020-10-14 11:13:08 +02:00
|
|
|
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
2020-07-10 08:07:59 +02:00
|
|
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
cwd, _ := os.Getwd()
|
|
|
|
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag"}, runner.Calls[1].Params)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("error case - Kaniko init failed", func(t *testing.T) {
|
|
|
|
config := &kanikoExecuteOptions{
|
|
|
|
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
|
|
|
|
}
|
|
|
|
|
|
|
|
runner := &mock.ExecMockRunner{
|
|
|
|
ShouldFailOnCommand: map[string]error{"rm": fmt.Errorf("rm failed")},
|
|
|
|
}
|
|
|
|
|
|
|
|
certClient := &kanikoMockClient{}
|
|
|
|
fileUtils := &kanikoFileMock{}
|
|
|
|
|
2020-10-14 11:13:08 +02:00
|
|
|
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
2020-07-10 08:07:59 +02:00
|
|
|
|
|
|
|
assert.EqualError(t, err, "failed to initialize Kaniko container: rm failed")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("error case - Kaniko execution failed", func(t *testing.T) {
|
|
|
|
config := &kanikoExecuteOptions{}
|
|
|
|
|
|
|
|
runner := &mock.ExecMockRunner{
|
|
|
|
ShouldFailOnCommand: map[string]error{"/kaniko/executor": fmt.Errorf("kaniko run failed")},
|
|
|
|
}
|
|
|
|
|
|
|
|
certClient := &kanikoMockClient{}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
}
|
|
|
|
|
2020-10-14 11:13:08 +02:00
|
|
|
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
2020-07-10 08:07:59 +02:00
|
|
|
|
|
|
|
assert.EqualError(t, err, "execution of '/kaniko/executor' failed: kaniko run failed")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("error case - cert update failed", func(t *testing.T) {
|
2021-03-25 16:32:10 +02:00
|
|
|
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",
|
|
|
|
}
|
2020-07-10 08:07:59 +02:00
|
|
|
|
|
|
|
runner := &mock.ExecMockRunner{}
|
|
|
|
|
|
|
|
certClient := &kanikoMockClient{}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
fileReadErr: map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")},
|
|
|
|
}
|
|
|
|
|
2020-10-14 11:13:08 +02:00
|
|
|
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
2020-07-10 08:07:59 +02:00
|
|
|
|
|
|
|
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",
|
|
|
|
}
|
|
|
|
|
|
|
|
runner := &mock.ExecMockRunner{}
|
|
|
|
|
|
|
|
certClient := &kanikoMockClient{}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
fileReadErr: map[string]error{"path/to/docker/config.json": fmt.Errorf("read error")},
|
|
|
|
}
|
|
|
|
|
2020-10-14 11:13:08 +02:00
|
|
|
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
2020-07-10 08:07:59 +02:00
|
|
|
|
|
|
|
assert.EqualError(t, err, "failed to read file '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",
|
|
|
|
}
|
|
|
|
|
|
|
|
runner := &mock.ExecMockRunner{}
|
|
|
|
|
|
|
|
certClient := &kanikoMockClient{}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
fileWriteErr: map[string]error{"/kaniko/.docker/config.json": fmt.Errorf("write error")},
|
|
|
|
}
|
|
|
|
|
2020-10-14 11:13:08 +02:00
|
|
|
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
2020-07-10 08:07:59 +02:00
|
|
|
|
|
|
|
assert.EqualError(t, err, "failed to write file '/kaniko/.docker/config.json': write error")
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCertificateUpdate(t *testing.T) {
|
|
|
|
certLinks := []string{"https://my.first/cert.crt", "https://my.second/cert.crt"}
|
|
|
|
|
|
|
|
t.Run("success case", func(t *testing.T) {
|
|
|
|
certClient := &kanikoMockClient{
|
|
|
|
responseBody: "testCert",
|
|
|
|
}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileReadContent: map[string]string{"/kaniko/ssl/certs/ca-certificates.crt": "initial cert\n"},
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := certificateUpdate(certLinks, certClient, fileUtils)
|
|
|
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, certLinks, certClient.urlsCalled)
|
|
|
|
assert.Equal(t, "initial cert\ntestCert\ntestCert\n", fileUtils.fileWriteContent["/kaniko/ssl/certs/ca-certificates.crt"])
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("error case - read certs", func(t *testing.T) {
|
|
|
|
certClient := &kanikoMockClient{
|
|
|
|
responseBody: "testCert",
|
|
|
|
}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileReadErr: map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := certificateUpdate(certLinks, certClient, fileUtils)
|
|
|
|
assert.EqualError(t, err, "failed to load file '/kaniko/ssl/certs/ca-certificates.crt': read error")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("error case - write certs", func(t *testing.T) {
|
|
|
|
certClient := &kanikoMockClient{
|
|
|
|
responseBody: "testCert",
|
|
|
|
}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileReadContent: map[string]string{"/kaniko/ssl/certs/ca-certificates.crt": "initial cert\n"},
|
|
|
|
fileWriteErr: map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("write error")},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := certificateUpdate(certLinks, certClient, fileUtils)
|
|
|
|
assert.EqualError(t, err, "failed to update file '/kaniko/ssl/certs/ca-certificates.crt': write error")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("error case - get cert via http", func(t *testing.T) {
|
|
|
|
certClient := &kanikoMockClient{
|
|
|
|
responseBody: "testCert",
|
|
|
|
errorMessage: "http error",
|
|
|
|
}
|
|
|
|
fileUtils := &kanikoFileMock{
|
|
|
|
fileReadContent: map[string]string{"/kaniko/ssl/certs/ca-certificates.crt": "initial cert\n"},
|
|
|
|
fileWriteContent: map[string]string{},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := certificateUpdate(certLinks, certClient, fileUtils)
|
|
|
|
assert.EqualError(t, err, "failed to load certificate from url: http error")
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|