github.com/SAP/jenkins-library@v1.362.0/cmd/kanikoExecute_test.go (about)

     1  //go:build unit
     2  // +build unit
     3  
     4  package cmd
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"path/filepath"
    12  	"strings"
    13  	"testing"
    14  
    15  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    16  	"github.com/SAP/jenkins-library/pkg/mock"
    17  	"github.com/SAP/jenkins-library/pkg/telemetry"
    18  	"github.com/jarcoal/httpmock"
    19  	"github.com/stretchr/testify/assert"
    20  )
    21  
    22  type kanikoMockClient struct {
    23  	httpMethod     string
    24  	httpStatusCode int
    25  	urlsCalled     []string
    26  	requestBody    io.Reader
    27  	responseBody   string
    28  	errorMessage   string
    29  }
    30  
    31  func (c *kanikoMockClient) SetOptions(opts piperhttp.ClientOptions) {}
    32  
    33  func (c *kanikoMockClient) SendRequest(method, url string, body io.Reader, header http.Header, cookies []*http.Cookie) (*http.Response, error) {
    34  	c.httpMethod = method
    35  	c.urlsCalled = append(c.urlsCalled, url)
    36  	c.requestBody = body
    37  	if len(c.errorMessage) > 0 {
    38  		return nil, fmt.Errorf(c.errorMessage)
    39  	}
    40  	return &http.Response{StatusCode: c.httpStatusCode, Body: io.NopCloser(bytes.NewReader([]byte(c.responseBody)))}, nil
    41  }
    42  func (c *kanikoMockClient) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error {
    43  	if len(c.errorMessage) > 0 {
    44  		return fmt.Errorf(c.errorMessage)
    45  	}
    46  	return nil
    47  }
    48  
    49  func TestRunKanikoExecute(t *testing.T) {
    50  
    51  	// required due to config resolution during build settings retrieval
    52  	// ToDo: proper mocking
    53  	openFileBak := configOptions.OpenFile
    54  	defer func() {
    55  		configOptions.OpenFile = openFileBak
    56  	}()
    57  
    58  	configOptions.OpenFile = configOpenFileMock
    59  
    60  	t.Run("success case", func(t *testing.T) {
    61  		config := &kanikoExecuteOptions{
    62  			BuildOptions:                []string{"--skip-tls-verify-pull"},
    63  			ContainerImage:              "myImage:tag",
    64  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
    65  			CustomTLSCertificateLinks:   []string{"https://test.url/cert.crt"},
    66  			DockerfilePath:              "Dockerfile",
    67  			DockerConfigJSON:            "path/to/docker/config.json",
    68  			BuildSettingsInfo:           `{"mavenExecuteBuild":[{"dockerImage":"maven"}]}`,
    69  		}
    70  
    71  		execRunner := &mock.ExecMockRunner{}
    72  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
    73  
    74  		certClient := &kanikoMockClient{
    75  			responseBody: "testCert",
    76  		}
    77  		fileUtils := &mock.FilesMock{}
    78  		fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
    79  		fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
    80  
    81  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
    82  
    83  		assert.NoError(t, err)
    84  
    85  		assert.Equal(t, "rm", execRunner.Calls[0].Exec)
    86  		assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
    87  
    88  		assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
    89  		c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
    90  		assert.NoError(t, err)
    91  		assert.Equal(t, `{"auths":{"custom":"test"}}`, string(c))
    92  
    93  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
    94  		cwd, _ := fileUtils.Getwd()
    95  		assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag"}, execRunner.Calls[1].Params)
    96  
    97  		assert.Contains(t, commonPipelineEnvironment.custom.buildSettingsInfo, `"mavenExecuteBuild":[{"dockerImage":"maven"}]`)
    98  		assert.Contains(t, commonPipelineEnvironment.custom.buildSettingsInfo, `"kanikoExecute":[{"dockerImage":"gcr.io/kaniko-project/executor:debug"}]`)
    99  
   100  		assert.Equal(t, "myImage:tag", commonPipelineEnvironment.container.imageNameTag)
   101  		assert.Equal(t, "https://index.docker.io", commonPipelineEnvironment.container.registryURL)
   102  		assert.Equal(t, []string{"myImage"}, commonPipelineEnvironment.container.imageNames)
   103  		assert.Equal(t, []string{"myImage:tag"}, commonPipelineEnvironment.container.imageNameTags)
   104  
   105  		assert.Equal(t, "", commonPipelineEnvironment.container.imageDigest)
   106  		assert.Empty(t, commonPipelineEnvironment.container.imageDigests)
   107  	})
   108  
   109  	t.Run("success case - pass image digest to cpe if activated", func(t *testing.T) {
   110  		config := &kanikoExecuteOptions{
   111  			BuildOptions:                []string{"--skip-tls-verify-pull"},
   112  			ContainerImage:              "myImage:tag",
   113  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   114  			CustomTLSCertificateLinks:   []string{"https://test.url/cert.crt"},
   115  			DockerfilePath:              "Dockerfile",
   116  			DockerConfigJSON:            "path/to/docker/config.json",
   117  			BuildSettingsInfo:           `{"mavenExecuteBuild":[{"dockerImage":"maven"}]}`,
   118  			ReadImageDigest:             true,
   119  		}
   120  
   121  		execRunner := &mock.ExecMockRunner{}
   122  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   123  
   124  		certClient := &kanikoMockClient{
   125  			responseBody: "testCert",
   126  		}
   127  		fileUtils := &mock.FilesMock{}
   128  		fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
   129  		fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
   130  		fileUtils.AddFile("/tmp/*-kanikoExecutetest/digest.txt", []byte(`sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0`))
   131  
   132  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   133  
   134  		assert.NoError(t, err)
   135  
   136  		assert.Equal(t, "rm", execRunner.Calls[0].Exec)
   137  		assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
   138  
   139  		assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
   140  		c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
   141  		assert.NoError(t, err)
   142  		assert.Equal(t, `{"auths":{"custom":"test"}}`, string(c))
   143  
   144  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
   145  		cwd, _ := fileUtils.Getwd()
   146  		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)
   147  
   148  		assert.Contains(t, commonPipelineEnvironment.custom.buildSettingsInfo, `"mavenExecuteBuild":[{"dockerImage":"maven"}]`)
   149  		assert.Contains(t, commonPipelineEnvironment.custom.buildSettingsInfo, `"kanikoExecute":[{"dockerImage":"gcr.io/kaniko-project/executor:debug"}]`)
   150  
   151  		assert.Equal(t, "myImage:tag", commonPipelineEnvironment.container.imageNameTag)
   152  		assert.Equal(t, "https://index.docker.io", commonPipelineEnvironment.container.registryURL)
   153  		assert.Equal(t, []string{"myImage"}, commonPipelineEnvironment.container.imageNames)
   154  		assert.Equal(t, []string{"myImage:tag"}, commonPipelineEnvironment.container.imageNameTags)
   155  
   156  		assert.Equal(t, "sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0", commonPipelineEnvironment.container.imageDigest)
   157  		assert.Equal(t, []string{"sha256:468dd1253cc9f498fc600454bb8af96d880fec3f9f737e7057692adfe9f7d5b0"}, commonPipelineEnvironment.container.imageDigests)
   158  	})
   159  
   160  	t.Run("success case - image params", func(t *testing.T) {
   161  		config := &kanikoExecuteOptions{
   162  			BuildOptions:                []string{"--skip-tls-verify-pull"},
   163  			ContainerImageName:          "myImage",
   164  			ContainerImageTag:           "1.2.3-a+x",
   165  			ContainerRegistryURL:        "https://my.registry.com:50000",
   166  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   167  			CustomTLSCertificateLinks:   []string{"https://test.url/cert.crt"},
   168  			DockerfilePath:              "Dockerfile",
   169  			DockerConfigJSON:            "path/to/docker/config.json",
   170  		}
   171  
   172  		execRunner := &mock.ExecMockRunner{}
   173  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   174  
   175  		certClient := &kanikoMockClient{
   176  			responseBody: "testCert",
   177  		}
   178  		fileUtils := &mock.FilesMock{}
   179  		fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
   180  		fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
   181  
   182  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   183  
   184  		assert.NoError(t, err)
   185  
   186  		assert.Equal(t, "rm", execRunner.Calls[0].Exec)
   187  		assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
   188  
   189  		assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
   190  		c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
   191  		assert.NoError(t, err)
   192  		assert.Equal(t, `{"auths":{"custom":"test"}}`, string(c))
   193  
   194  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
   195  		cwd, _ := fileUtils.Getwd()
   196  		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)
   197  
   198  		assert.Equal(t, "myImage:1.2.3-a-x", commonPipelineEnvironment.container.imageNameTag)
   199  		assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
   200  		assert.Equal(t, []string{"myImage"}, commonPipelineEnvironment.container.imageNames)
   201  		assert.Equal(t, []string{"myImage:1.2.3-a-x"}, commonPipelineEnvironment.container.imageNameTags)
   202  
   203  		assert.Equal(t, "", commonPipelineEnvironment.container.imageDigest)
   204  		assert.Empty(t, commonPipelineEnvironment.container.imageDigests)
   205  	})
   206  
   207  	t.Run("success case - image params with custom destination", func(t *testing.T) {
   208  		config := &kanikoExecuteOptions{
   209  			BuildOptions:                []string{"--skip-tls-verify-pull", "--destination", "my.other.registry.com:50000/myImage:3.2.1-a-x"},
   210  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   211  			CustomTLSCertificateLinks:   []string{"https://test.url/cert.crt"},
   212  			DockerfilePath:              "Dockerfile",
   213  			DockerConfigJSON:            "path/to/docker/config.json",
   214  		}
   215  
   216  		execRunner := &mock.ExecMockRunner{}
   217  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   218  
   219  		certClient := &kanikoMockClient{
   220  			responseBody: "testCert",
   221  		}
   222  		fileUtils := &mock.FilesMock{}
   223  		fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
   224  		fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
   225  
   226  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   227  
   228  		assert.NoError(t, err)
   229  
   230  		assert.Equal(t, "rm", execRunner.Calls[0].Exec)
   231  		assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
   232  
   233  		assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
   234  		c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
   235  		assert.NoError(t, err)
   236  		assert.Equal(t, `{"auths":{"custom":"test"}}`, string(c))
   237  
   238  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
   239  		cwd, _ := fileUtils.Getwd()
   240  		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)
   241  
   242  		assert.Equal(t, "myImage:3.2.1-a-x", commonPipelineEnvironment.container.imageNameTag)
   243  		assert.Equal(t, "https://my.other.registry.com:50000", commonPipelineEnvironment.container.registryURL)
   244  		assert.Equal(t, []string{"myImage"}, commonPipelineEnvironment.container.imageNames)
   245  		assert.Equal(t, []string{"myImage:3.2.1-a-x"}, commonPipelineEnvironment.container.imageNameTags)
   246  
   247  		assert.Equal(t, "", commonPipelineEnvironment.container.imageDigest)
   248  		assert.Empty(t, []string{}, commonPipelineEnvironment.container.imageDigests)
   249  	})
   250  
   251  	t.Run("no error case - when cert update skipped", func(t *testing.T) {
   252  		config := &kanikoExecuteOptions{
   253  			BuildOptions:                []string{"--skip-tls-verify-pull"},
   254  			ContainerImageName:          "myImage",
   255  			ContainerImageTag:           "1.2.3-a+x",
   256  			ContainerRegistryURL:        "https://my.registry.com:50000",
   257  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   258  			CustomTLSCertificateLinks:   []string{},
   259  			DockerfilePath:              "Dockerfile",
   260  			DockerConfigJSON:            "path/to/docker/config.json",
   261  		}
   262  
   263  		execRunner := &mock.ExecMockRunner{}
   264  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   265  
   266  		certClient := &kanikoMockClient{}
   267  		fileUtils := &mock.FilesMock{}
   268  		fileUtils.AddFile("path/to/docker/config.json", []byte(``))
   269  		fileUtils.FileReadErrors = map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")}
   270  
   271  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   272  
   273  		assert.NoErrorf(t, err, "failed to update certificates: failed to load file '/kaniko/ssl/certs/ca-certificates.crt': read error")
   274  	})
   275  
   276  	t.Run("success case - no push, no docker config.json", func(t *testing.T) {
   277  		config := &kanikoExecuteOptions{
   278  			ContainerBuildOptions:       "--skip-tls-verify-pull",
   279  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   280  			CustomTLSCertificateLinks:   []string{"https://test.url/cert.crt"},
   281  			DockerfilePath:              "Dockerfile",
   282  		}
   283  
   284  		execRunner := &mock.ExecMockRunner{}
   285  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   286  
   287  		certClient := &kanikoMockClient{
   288  			responseBody: "testCert",
   289  		}
   290  		fileUtils := &mock.FilesMock{}
   291  		fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
   292  
   293  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   294  
   295  		assert.NoError(t, err)
   296  
   297  		c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
   298  		assert.NoError(t, err)
   299  		assert.Equal(t, `{"auths":{}}`, string(c))
   300  
   301  		cwd, _ := fileUtils.Getwd()
   302  		assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--skip-tls-verify-pull", "--no-push"}, execRunner.Calls[1].Params)
   303  	})
   304  
   305  	t.Run("success case - backward compatibility", func(t *testing.T) {
   306  		config := &kanikoExecuteOptions{
   307  			ContainerBuildOptions:       "--skip-tls-verify-pull",
   308  			ContainerImage:              "myImage:tag",
   309  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   310  			CustomTLSCertificateLinks:   []string{"https://test.url/cert.crt"},
   311  			DockerfilePath:              "Dockerfile",
   312  			DockerConfigJSON:            "path/to/docker/config.json",
   313  		}
   314  
   315  		execRunner := &mock.ExecMockRunner{}
   316  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   317  
   318  		certClient := &kanikoMockClient{
   319  			responseBody: "testCert",
   320  		}
   321  		fileUtils := &mock.FilesMock{}
   322  		fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
   323  		fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
   324  
   325  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   326  
   327  		assert.NoError(t, err)
   328  		cwd, _ := fileUtils.Getwd()
   329  		assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag"}, execRunner.Calls[1].Params)
   330  	})
   331  
   332  	t.Run("success case - createBOM", func(t *testing.T) {
   333  		config := &kanikoExecuteOptions{
   334  			ContainerImage:              "myImage:tag",
   335  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   336  			DockerfilePath:              "Dockerfile",
   337  			DockerConfigJSON:            "path/to/docker/config.json",
   338  			CreateBOM:                   true,
   339  			SyftDownloadURL:             "http://test-syft-url.io",
   340  		}
   341  
   342  		execRunner := &mock.ExecMockRunner{}
   343  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   344  		fileUtils := &mock.FilesMock{}
   345  		fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
   346  
   347  		httpmock.Activate()
   348  		defer httpmock.DeactivateAndReset()
   349  		fakeArchive, err := fileUtils.CreateArchive(map[string][]byte{"syft": []byte("test")})
   350  		assert.NoError(t, err)
   351  
   352  		httpmock.RegisterResponder(http.MethodGet, "http://test-syft-url.io", httpmock.NewBytesResponder(http.StatusOK, fakeArchive))
   353  		client := &piperhttp.Client{}
   354  		client.SetOptions(piperhttp.ClientOptions{MaxRetries: -1, UseDefaultTransport: true})
   355  
   356  		err = runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, client, fileUtils)
   357  		assert.NoError(t, err)
   358  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
   359  		assert.Equal(t, "myImage:tag", commonPipelineEnvironment.container.imageNameTag)
   360  		assert.Equal(t, "https://index.docker.io", commonPipelineEnvironment.container.registryURL)
   361  
   362  		assert.Equal(t, "/tmp/syfttest/syft", execRunner.Calls[2].Exec)
   363  		assert.Equal(t, []string{"packages", "registry:index.docker.io/myImage:tag", "-o", "cyclonedx-xml", "--file", "bom-docker-0.xml", "-q"}, execRunner.Calls[2].Params)
   364  	})
   365  
   366  	t.Run("success case - multi image build with root image", func(t *testing.T) {
   367  		config := &kanikoExecuteOptions{
   368  			ContainerImageName:       "myImage",
   369  			ContainerImageTag:        "myTag",
   370  			ContainerRegistryURL:     "https://my.registry.com:50000",
   371  			ContainerMultiImageBuild: true,
   372  		}
   373  
   374  		execRunner := &mock.ExecMockRunner{}
   375  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   376  
   377  		fileUtils := &mock.FilesMock{}
   378  		fileUtils.AddFile("Dockerfile", []byte("some content"))
   379  		fileUtils.AddFile("sub1/Dockerfile", []byte("some content"))
   380  		fileUtils.AddFile("sub2/Dockerfile", []byte("some content"))
   381  
   382  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, nil, fileUtils)
   383  
   384  		assert.NoError(t, err)
   385  
   386  		assert.Equal(t, 3, len(execRunner.Calls))
   387  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[0].Exec)
   388  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
   389  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[2].Exec)
   390  
   391  		cwd, _ := fileUtils.Getwd()
   392  		expectedParams := [][]string{
   393  			{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage:myTag"},
   394  			{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
   395  			{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
   396  		}
   397  		// need to go this way since we cannot count on the correct order
   398  		for _, call := range execRunner.Calls {
   399  			found := false
   400  			for _, expected := range expectedParams {
   401  				if strings.Join(call.Params, " ") == strings.Join(expected, " ") {
   402  					found = true
   403  					break
   404  				}
   405  			}
   406  			assert.True(t, found, fmt.Sprintf("%v not found", call.Params))
   407  		}
   408  
   409  		assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
   410  		assert.Equal(t, "myImage:myTag", commonPipelineEnvironment.container.imageNameTag)
   411  		assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage")
   412  		assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub1")
   413  		assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub2")
   414  		assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage:myTag")
   415  		assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub1:myTag")
   416  		assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub2:myTag")
   417  
   418  		assert.Equal(t, "", commonPipelineEnvironment.container.imageDigest)
   419  		assert.Empty(t, commonPipelineEnvironment.container.imageDigests)
   420  	})
   421  
   422  	t.Run("success case - multi image build excluding root image", func(t *testing.T) {
   423  		config := &kanikoExecuteOptions{
   424  			ContainerImageName:               "myImage",
   425  			ContainerImageTag:                "myTag",
   426  			ContainerRegistryURL:             "https://my.registry.com:50000",
   427  			ContainerMultiImageBuild:         true,
   428  			ContainerMultiImageBuildExcludes: []string{"Dockerfile"},
   429  		}
   430  
   431  		execRunner := &mock.ExecMockRunner{}
   432  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   433  
   434  		fileUtils := &mock.FilesMock{}
   435  		fileUtils.AddFile("Dockerfile", []byte("some content"))
   436  		fileUtils.AddFile("sub1/Dockerfile", []byte("some content"))
   437  		fileUtils.AddFile("sub2/Dockerfile", []byte("some content"))
   438  
   439  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, nil, fileUtils)
   440  
   441  		assert.NoError(t, err)
   442  
   443  		assert.Equal(t, 2, len(execRunner.Calls))
   444  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[0].Exec)
   445  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
   446  
   447  		cwd, _ := fileUtils.Getwd()
   448  		expectedParams := [][]string{
   449  			{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
   450  			{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
   451  		}
   452  		// need to go this way since we cannot count on the correct order
   453  		for _, call := range execRunner.Calls {
   454  			found := false
   455  			for _, expected := range expectedParams {
   456  				if strings.Join(call.Params, " ") == strings.Join(expected, " ") {
   457  					found = true
   458  					break
   459  				}
   460  			}
   461  			assert.True(t, found, fmt.Sprintf("%v not found", call.Params))
   462  		}
   463  
   464  		assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
   465  		assert.Equal(t, "", commonPipelineEnvironment.container.imageNameTag)
   466  		assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub1")
   467  		assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub2")
   468  		assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub1:myTag")
   469  		assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub2:myTag")
   470  	})
   471  
   472  	t.Run("success case - multi image build with CreateBOM", func(t *testing.T) {
   473  		config := &kanikoExecuteOptions{
   474  			ContainerImageName:       "myImage",
   475  			ContainerImageTag:        "myTag",
   476  			ContainerRegistryURL:     "https://my.registry.com:50000",
   477  			ContainerMultiImageBuild: true,
   478  			DockerConfigJSON:         "path/to/docker/config.json",
   479  			CreateBOM:                true,
   480  			SyftDownloadURL:          "http://test-syft-url.io",
   481  		}
   482  		execRunner := &mock.ExecMockRunner{}
   483  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   484  		fileUtils := &mock.FilesMock{}
   485  		fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
   486  		fileUtils.AddFile("Dockerfile", []byte("some content"))
   487  		fileUtils.AddFile("sub1/Dockerfile", []byte("some content"))
   488  		fileUtils.AddFile("sub2/Dockerfile", []byte("some content"))
   489  
   490  		httpmock.Activate()
   491  		defer httpmock.DeactivateAndReset()
   492  		fakeArchive, err := fileUtils.CreateArchive(map[string][]byte{"syft": []byte("test")})
   493  		assert.NoError(t, err)
   494  
   495  		httpmock.RegisterResponder(http.MethodGet, "http://test-syft-url.io", httpmock.NewBytesResponder(http.StatusOK, fakeArchive))
   496  		client := &piperhttp.Client{}
   497  		client.SetOptions(piperhttp.ClientOptions{MaxRetries: -1, UseDefaultTransport: true})
   498  
   499  		err = runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, client, fileUtils)
   500  		assert.NoError(t, err)
   501  
   502  		assert.Equal(t, 6, len(execRunner.Calls))
   503  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[0].Exec)
   504  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
   505  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[2].Exec)
   506  
   507  		cwd, _ := fileUtils.Getwd()
   508  		expectedParams := [][]string{
   509  			{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage:myTag"},
   510  			{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
   511  			{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", "dir://" + cwd, "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
   512  			{"packages", "registry:my.registry.com:50000/myImage:myTag", "-o", "cyclonedx-xml", "--file"},
   513  			{"packages", "registry:my.registry.com:50000/myImage-sub1:myTag", "-o", "cyclonedx-xml", "--file"},
   514  			{"packages", "registry:my.registry.com:50000/myImage-sub2:myTag", "-o", "cyclonedx-xml", "--file"},
   515  		}
   516  		// need to go this way since we cannot count on the correct order
   517  		for index, call := range execRunner.Calls {
   518  			found := false
   519  			for _, expected := range expectedParams {
   520  				if expected[0] == "packages" {
   521  					expected = append(expected, fmt.Sprintf("bom-docker-%d.xml", index-3), "-q")
   522  				}
   523  				if strings.Join(call.Params, " ") == strings.Join(expected, " ") {
   524  					found = true
   525  					break
   526  				}
   527  			}
   528  			assert.True(t, found, fmt.Sprintf("%v not found", call.Params))
   529  		}
   530  
   531  		assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
   532  		assert.Equal(t, "myImage:myTag", commonPipelineEnvironment.container.imageNameTag)
   533  		assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage")
   534  		assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub1")
   535  		assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub2")
   536  		assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage:myTag")
   537  		assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub1:myTag")
   538  		assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub2:myTag")
   539  
   540  		assert.Equal(t, "", commonPipelineEnvironment.container.imageDigest)
   541  		assert.Empty(t, commonPipelineEnvironment.container.imageDigests)
   542  	})
   543  
   544  	t.Run("success case - updating an existing docker config json with addtional credentials", func(t *testing.T) {
   545  		config := &kanikoExecuteOptions{
   546  			BuildOptions:                []string{"--skip-tls-verify-pull"},
   547  			ContainerImageName:          "myImage",
   548  			ContainerImageTag:           "1.2.3-a+x",
   549  			ContainerRegistryURL:        "https://my.registry.com:50000",
   550  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   551  			CustomTLSCertificateLinks:   []string{"https://test.url/cert.crt"},
   552  			DockerfilePath:              "Dockerfile",
   553  			DockerConfigJSON:            "path/to/docker/config.json",
   554  			ContainerRegistryUser:       "dummyUser",
   555  			ContainerRegistryPassword:   "dummyPassword",
   556  		}
   557  
   558  		execRunner := &mock.ExecMockRunner{}
   559  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   560  
   561  		certClient := &kanikoMockClient{
   562  			responseBody: "testCert",
   563  		}
   564  		fileUtils := &mock.FilesMock{}
   565  		fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths": {"dummyUrl": {"auth": "XXXXXXX"}}}`))
   566  		fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
   567  
   568  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   569  
   570  		assert.NoError(t, err)
   571  
   572  		assert.Equal(t, "rm", execRunner.Calls[0].Exec)
   573  		assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
   574  
   575  		assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
   576  		c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
   577  		assert.NoError(t, err)
   578  		assert.Equal(t, `{"auths":{"dummyUrl":{"auth":"XXXXXXX"},"https://my.registry.com:50000":{"auth":"ZHVtbXlVc2VyOmR1bW15UGFzc3dvcmQ="}}}`, string(c))
   579  	})
   580  
   581  	t.Run("success case - creating new docker config json with provided container credentials", func(t *testing.T) {
   582  		config := &kanikoExecuteOptions{
   583  			BuildOptions:                []string{"--skip-tls-verify-pull"},
   584  			ContainerImageName:          "myImage",
   585  			ContainerImageTag:           "1.2.3-a+x",
   586  			ContainerRegistryURL:        "https://my.registry.com:50000",
   587  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   588  			CustomTLSCertificateLinks:   []string{"https://test.url/cert.crt"},
   589  			DockerfilePath:              "Dockerfile",
   590  			ContainerRegistryUser:       "dummyUser",
   591  			ContainerRegistryPassword:   "dummyPassword",
   592  		}
   593  
   594  		execRunner := &mock.ExecMockRunner{}
   595  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   596  
   597  		certClient := &kanikoMockClient{
   598  			responseBody: "testCert",
   599  		}
   600  		fileUtils := &mock.FilesMock{}
   601  		fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
   602  
   603  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   604  
   605  		assert.NoError(t, err)
   606  
   607  		assert.Equal(t, "rm", execRunner.Calls[0].Exec)
   608  		assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, execRunner.Calls[0].Params)
   609  
   610  		assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
   611  		c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
   612  		assert.NoError(t, err)
   613  		assert.Equal(t, `{"auths":{"https://my.registry.com:50000":{"auth":"ZHVtbXlVc2VyOmR1bW15UGFzc3dvcmQ="}}}`, string(c))
   614  	})
   615  
   616  	t.Run("success case - multi context build with CreateBOM", func(t *testing.T) {
   617  		config := &kanikoExecuteOptions{
   618  			ContainerImageName:   "myImage",
   619  			ContainerImageTag:    "myTag",
   620  			ContainerRegistryURL: "https://my.registry.com:50000",
   621  			DockerConfigJSON:     "path/to/docker/config.json",
   622  			DockerfilePath:       "Dockerfile",
   623  			CreateBOM:            true,
   624  			SyftDownloadURL:      "http://test-syft-url.io",
   625  			MultipleImages: []map[string]interface{}{
   626  				{
   627  					"contextSubPath":     "/test1",
   628  					"containerImageName": "myImageOne",
   629  				},
   630  				{
   631  					"contextSubPath":     "/test2",
   632  					"containerImageName": "myImageTwo",
   633  					"containerImageTag":  "myTagTwo",
   634  				},
   635  			},
   636  		}
   637  		execRunner := &mock.ExecMockRunner{}
   638  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   639  		fileUtils := &mock.FilesMock{}
   640  		fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
   641  		fileUtils.AddFile("Dockerfile", []byte("some content"))
   642  		fileUtils.AddFile("test1/test", []byte("some content test1"))
   643  		fileUtils.AddFile("test2/test", []byte("some content test2"))
   644  
   645  		httpmock.Activate()
   646  		defer httpmock.DeactivateAndReset()
   647  		fakeArchive, err := fileUtils.CreateArchive(map[string][]byte{"syft": []byte("test")})
   648  		assert.NoError(t, err)
   649  
   650  		httpmock.RegisterResponder(http.MethodGet, "http://test-syft-url.io", httpmock.NewBytesResponder(http.StatusOK, fakeArchive))
   651  		client := &piperhttp.Client{}
   652  		client.SetOptions(piperhttp.ClientOptions{MaxRetries: -1, UseDefaultTransport: true})
   653  
   654  		err = runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, client, fileUtils)
   655  		assert.NoError(t, err)
   656  
   657  		assert.Equal(t, 4, len(execRunner.Calls))
   658  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[0].Exec)
   659  		assert.Equal(t, "/kaniko/executor", execRunner.Calls[1].Exec)
   660  
   661  		cwd, _ := fileUtils.Getwd()
   662  		expectedParams := [][]string{
   663  			{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--context-sub-path", "/test1", "--destination", "my.registry.com:50000/myImageOne:myTag"},
   664  			{"--dockerfile", "Dockerfile", "--context", "dir://" + cwd, "--context-sub-path", "/test2", "--destination", "my.registry.com:50000/myImageTwo:myTagTwo"},
   665  			{"packages", "registry:my.registry.com:50000/myImageOne:myTag", "-o", "cyclonedx-xml", "--file"},
   666  			{"packages", "registry:my.registry.com:50000/myImageTwo:myTagTwo", "-o", "cyclonedx-xml", "--file"},
   667  		}
   668  		// need to go this way since we cannot count on the correct order
   669  		for index, call := range execRunner.Calls {
   670  			found := false
   671  			for _, expected := range expectedParams {
   672  				if expected[0] == "packages" {
   673  					expected = append(expected, fmt.Sprintf("bom-docker-%d.xml", index-2), "-q")
   674  				}
   675  				if strings.Join(call.Params, " ") == strings.Join(expected, " ") {
   676  					found = true
   677  					break
   678  				}
   679  			}
   680  			assert.True(t, found, fmt.Sprintf("%v not found", call.Params))
   681  		}
   682  
   683  		assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
   684  		assert.Equal(t, "myImage:myTag", commonPipelineEnvironment.container.imageNameTag)
   685  		assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImageOne")
   686  		assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImageTwo")
   687  		assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImageOne:myTag")
   688  		assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImageTwo:myTagTwo")
   689  
   690  		assert.Equal(t, "", commonPipelineEnvironment.container.imageDigest)
   691  		assert.Empty(t, commonPipelineEnvironment.container.imageDigests)
   692  	})
   693  
   694  	t.Run("error case - multi image build: no docker files", func(t *testing.T) {
   695  		config := &kanikoExecuteOptions{
   696  			ContainerImageName:       "myImage",
   697  			ContainerImageTag:        "myTag",
   698  			ContainerRegistryURL:     "https://my.registry.com:50000",
   699  			ContainerMultiImageBuild: true,
   700  		}
   701  
   702  		cpe := kanikoExecuteCommonPipelineEnvironment{}
   703  		execRunner := &mock.ExecMockRunner{}
   704  		fileUtils := &mock.FilesMock{}
   705  
   706  		err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, execRunner, nil, fileUtils)
   707  
   708  		assert.Error(t, err)
   709  		assert.Contains(t, fmt.Sprint(err), "failed to identify image list for multi image build")
   710  	})
   711  
   712  	t.Run("error case - multi image build: no docker files to process", func(t *testing.T) {
   713  		config := &kanikoExecuteOptions{
   714  			ContainerImageName:               "myImage",
   715  			ContainerImageTag:                "myTag",
   716  			ContainerRegistryURL:             "https://my.registry.com:50000",
   717  			ContainerMultiImageBuild:         true,
   718  			ContainerMultiImageBuildExcludes: []string{"Dockerfile"},
   719  		}
   720  
   721  		cpe := kanikoExecuteCommonPipelineEnvironment{}
   722  		execRunner := &mock.ExecMockRunner{}
   723  
   724  		fileUtils := &mock.FilesMock{}
   725  		fileUtils.AddFile("Dockerfile", []byte("some content"))
   726  
   727  		err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, execRunner, nil, fileUtils)
   728  
   729  		assert.Error(t, err)
   730  		assert.Contains(t, fmt.Sprint(err), "no docker files to process, please check exclude list")
   731  	})
   732  
   733  	t.Run("error case - multi image build: build failed", func(t *testing.T) {
   734  		config := &kanikoExecuteOptions{
   735  			ContainerImageName:       "myImage",
   736  			ContainerImageTag:        "myTag",
   737  			ContainerRegistryURL:     "https://my.registry.com:50000",
   738  			ContainerMultiImageBuild: true,
   739  		}
   740  
   741  		cpe := kanikoExecuteCommonPipelineEnvironment{}
   742  		execRunner := &mock.ExecMockRunner{}
   743  
   744  		execRunner.ShouldFailOnCommand = map[string]error{"/kaniko/executor": fmt.Errorf("execution failed")}
   745  
   746  		fileUtils := &mock.FilesMock{}
   747  		fileUtils.AddFile("Dockerfile", []byte("some content"))
   748  
   749  		err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, execRunner, nil, fileUtils)
   750  
   751  		assert.Error(t, err)
   752  		assert.Contains(t, fmt.Sprint(err), "failed to build image")
   753  	})
   754  
   755  	t.Run("error case - Kaniko init failed", func(t *testing.T) {
   756  		config := &kanikoExecuteOptions{
   757  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   758  		}
   759  		execRunner := &mock.ExecMockRunner{
   760  			ShouldFailOnCommand: map[string]error{"rm": fmt.Errorf("rm failed")},
   761  		}
   762  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   763  
   764  		certClient := &kanikoMockClient{}
   765  		fileUtils := &mock.FilesMock{}
   766  
   767  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   768  
   769  		assert.EqualError(t, err, "failed to initialize Kaniko container: rm failed")
   770  	})
   771  
   772  	t.Run("error case - Kaniko execution failed", func(t *testing.T) {
   773  		config := &kanikoExecuteOptions{}
   774  		execRunner := &mock.ExecMockRunner{
   775  			ShouldFailOnCommand: map[string]error{"/kaniko/executor": fmt.Errorf("kaniko run failed")},
   776  		}
   777  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   778  
   779  		certClient := &kanikoMockClient{}
   780  		fileUtils := &mock.FilesMock{}
   781  
   782  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   783  
   784  		assert.EqualError(t, err, "execution of '/kaniko/executor' failed: kaniko run failed")
   785  	})
   786  
   787  	t.Run("error case - cert update failed", func(t *testing.T) {
   788  		config := &kanikoExecuteOptions{
   789  			BuildOptions:                []string{"--skip-tls-verify-pull"},
   790  			ContainerImageName:          "myImage",
   791  			ContainerImageTag:           "1.2.3-a+x",
   792  			ContainerRegistryURL:        "https://my.registry.com:50000",
   793  			ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
   794  			CustomTLSCertificateLinks:   []string{"https://test.url/cert.crt"},
   795  			DockerfilePath:              "Dockerfile",
   796  			DockerConfigJSON:            "path/to/docker/config.json",
   797  		}
   798  		execRunner := &mock.ExecMockRunner{}
   799  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   800  
   801  		certClient := &kanikoMockClient{}
   802  		fileUtils := &mock.FilesMock{}
   803  		fileUtils.FileReadErrors = map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")}
   804  
   805  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   806  
   807  		assert.EqualError(t, err, "failed to update certificates: failed to load file '/kaniko/ssl/certs/ca-certificates.crt': read error")
   808  	})
   809  
   810  	t.Run("error case - dockerconfig read failed", func(t *testing.T) {
   811  		config := &kanikoExecuteOptions{
   812  			DockerConfigJSON: "path/to/docker/config.json",
   813  		}
   814  		execRunner := &mock.ExecMockRunner{}
   815  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   816  
   817  		certClient := &kanikoMockClient{}
   818  		fileUtils := &mock.FilesMock{}
   819  		fileUtils.FileReadErrors = map[string]error{"path/to/docker/config.json": fmt.Errorf("read error")}
   820  
   821  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   822  
   823  		assert.EqualError(t, err, "failed to read existing docker config json at 'path/to/docker/config.json': read error")
   824  	})
   825  
   826  	t.Run("error case - dockerconfig write failed", func(t *testing.T) {
   827  		config := &kanikoExecuteOptions{
   828  			DockerConfigJSON: "path/to/docker/config.json",
   829  		}
   830  		execRunner := &mock.ExecMockRunner{}
   831  		commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
   832  
   833  		certClient := &kanikoMockClient{}
   834  		fileUtils := &mock.FilesMock{}
   835  		fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
   836  		fileUtils.FileWriteErrors = map[string]error{"/kaniko/.docker/config.json": fmt.Errorf("write error")}
   837  
   838  		err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, execRunner, certClient, fileUtils)
   839  
   840  		assert.EqualError(t, err, "failed to write file '/kaniko/.docker/config.json': write error")
   841  	})
   842  
   843  	t.Run("error case - multi context build: no subcontext provided", func(t *testing.T) {
   844  		config := &kanikoExecuteOptions{
   845  			ContainerImageName:   "myImage",
   846  			ContainerImageTag:    "myTag",
   847  			ContainerRegistryURL: "https://my.registry.com:50000",
   848  			MultipleImages: []map[string]interface{}{
   849  				{"containerImageName": "myImageOne"},
   850  				{"containerImageName": "myImageTwo"},
   851  			},
   852  		}
   853  
   854  		cpe := kanikoExecuteCommonPipelineEnvironment{}
   855  		execRunner := &mock.ExecMockRunner{}
   856  
   857  		fileUtils := &mock.FilesMock{}
   858  		fileUtils.AddFile("Dockerfile", []byte("some content"))
   859  
   860  		err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, execRunner, nil, fileUtils)
   861  
   862  		assert.Error(t, err)
   863  		assert.Contains(t, fmt.Sprint(err), "multipleImages: empty contextSubPath")
   864  	})
   865  }