github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/cmd/cnbBuild_test.go (about)

     1  //go:build unit
     2  // +build unit
     3  
     4  package cmd
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"path/filepath"
    11  	"testing"
    12  
    13  	"github.com/SAP/jenkins-library/pkg/buildpacks"
    14  	"github.com/SAP/jenkins-library/pkg/cnbutils"
    15  	piperconf "github.com/SAP/jenkins-library/pkg/config"
    16  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    17  	"github.com/SAP/jenkins-library/pkg/mock"
    18  	"github.com/SAP/jenkins-library/pkg/telemetry"
    19  	v1 "github.com/google/go-containerregistry/pkg/v1"
    20  	"github.com/google/go-containerregistry/pkg/v1/fake"
    21  	"github.com/jarcoal/httpmock"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  const imageRegistry = "some-registry"
    27  
    28  func newCnbBuildTestsUtils() cnbutils.MockUtils {
    29  	imageStub := func(imageRef, target string) (v1.Image, error) {
    30  		fakeImage := &fake.FakeImage{}
    31  		var imageConfig v1.Config
    32  		switch imageRef {
    33  		case "pre-test":
    34  			imageConfig = v1.Config{
    35  				Labels: map[string]string{
    36  					"io.buildpacks.buildpackage.metadata": "{\"id\": \"pre-testbuildpack\", \"version\": \"0.0.1\"}",
    37  				},
    38  			}
    39  		case "post-test":
    40  			imageConfig = v1.Config{
    41  				Labels: map[string]string{
    42  					"io.buildpacks.buildpackage.metadata": "{\"id\": \"post-testbuildpack\", \"version\": \"0.0.1\"}",
    43  				},
    44  			}
    45  		default:
    46  			imageConfig = v1.Config{
    47  				Labels: map[string]string{
    48  					"io.buildpacks.buildpackage.metadata": "{\"id\": \"testbuildpack\", \"version\": \"0.0.1\"}",
    49  				},
    50  			}
    51  		}
    52  
    53  		fakeImage.ConfigFileReturns(&v1.ConfigFile{
    54  			Config: imageConfig,
    55  		}, nil)
    56  
    57  		return fakeImage, nil
    58  	}
    59  
    60  	utils := cnbutils.MockUtils{
    61  		ExecMockRunner: &mock.ExecMockRunner{},
    62  		FilesMock:      &mock.FilesMock{},
    63  		DownloadMock: &mock.DownloadMock{
    64  			ImageContentStub: imageStub,
    65  			ImageInfoStub: func(imageRef string) (v1.Image, error) {
    66  				return imageStub(imageRef, "")
    67  			},
    68  		},
    69  	}
    70  
    71  	utils.AddFile("/cnb/order.toml", []byte(`[[order]]
    72      [[order.group]]
    73        id = "buildpacks/java"
    74        version = "1.8.0"
    75  [[order]]
    76      [[order.group]]
    77        id = "buildpacks/nodejs"
    78        version = "1.6.0"`))
    79  	utils.AddFile("/layers/report.toml", []byte(`[build]
    80  [image]
    81  tags = ["localhost:5000/not-found:0.0.1"]
    82  digest = "sha256:52eac630560210e5ae13eb10797c4246d6f02d425f32b9430ca00bde697c79ec"
    83  manifest-size = 2388`))
    84  	return utils
    85  }
    86  
    87  func addBuilderFiles(utils *cnbutils.MockUtils) {
    88  	utils.FilesMock.AddFile(creatorPath, []byte(`xyz`))
    89  }
    90  
    91  func assertLifecycleCalls(t *testing.T, runner *mock.ExecMockRunner, callNo int) {
    92  	require.GreaterOrEqual(t, len(runner.Calls), callNo)
    93  	assert.Equal(t, creatorPath, runner.Calls[callNo-1].Exec)
    94  	for _, arg := range []string{"-no-color", "-buildpacks", "/cnb/buildpacks", "-order", "/cnb/order.toml", "-platform", "/tmp/platform"} {
    95  		assert.Contains(t, runner.Calls[callNo-1].Params, arg)
    96  	}
    97  }
    98  
    99  func assetBuildEnv(t *testing.T, utils cnbutils.MockUtils, key, value string) bool {
   100  	env, err := utils.FilesMock.ReadFile(filepath.Join("/tmp/platform/env/", key))
   101  	if !assert.NoError(t, err) {
   102  		return false
   103  	}
   104  	return assert.Equal(t, value, string(env))
   105  }
   106  
   107  func TestRunCnbBuild(t *testing.T) {
   108  	configOptions.OpenFile = piperconf.OpenPiperFile
   109  
   110  	t.Run("prefers direct configuration", func(t *testing.T) {
   111  		t.Parallel()
   112  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   113  		config := cnbBuildOptions{
   114  			ContainerImageName:   "my-image",
   115  			ContainerImageTag:    "0.0.1",
   116  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
   117  			DockerConfigJSON:     "/path/to/config.json",
   118  			RunImage:             "my-run-image",
   119  			DefaultProcess:       "my-process",
   120  		}
   121  
   122  		projectToml := `[project]
   123  		id = "io.buildpacks.my-app"
   124  		`
   125  
   126  		utils := newCnbBuildTestsUtils()
   127  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   128  		utils.FilesMock.AddFile("project.toml", []byte(projectToml))
   129  		addBuilderFiles(&utils)
   130  
   131  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   132  
   133  		require.NoError(t, err)
   134  		runner := utils.ExecMockRunner
   135  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   136  		assertLifecycleCalls(t, runner, 2)
   137  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
   138  		assert.Contains(t, runner.Calls[1].Params, "-run-image")
   139  		assert.Contains(t, runner.Calls[1].Params, "my-run-image")
   140  		assert.Contains(t, runner.Calls[1].Params, "-process-type")
   141  		assert.Contains(t, runner.Calls[1].Params, "my-process")
   142  		assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
   143  		assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
   144  		assert.Equal(t, `{"cnbBuild":[{"dockerImage":"paketobuildpacks/builder:base"}]}`, commonPipelineEnvironment.custom.buildSettingsInfo)
   145  	})
   146  
   147  	t.Run("prefers project descriptor", func(t *testing.T) {
   148  		t.Parallel()
   149  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   150  		config := cnbBuildOptions{
   151  			ContainerImageTag:    "0.0.1",
   152  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
   153  			DockerConfigJSON:     "/path/to/config.json",
   154  			ProjectDescriptor:    "project.toml",
   155  		}
   156  
   157  		projectToml := `[project]
   158  		id = "io.buildpacks.my-app"
   159  		`
   160  
   161  		utils := newCnbBuildTestsUtils()
   162  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   163  		utils.FilesMock.AddFile("project.toml", []byte(projectToml))
   164  		addBuilderFiles(&utils)
   165  
   166  		telemetryData := &telemetry.CustomData{}
   167  		err := callCnbBuild(&config, telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   168  
   169  		require.NoError(t, err)
   170  		runner := utils.ExecMockRunner
   171  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   172  		assertLifecycleCalls(t, runner, 2)
   173  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, "io-buildpacks-my-app", config.ContainerImageTag))
   174  		assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
   175  		assert.Equal(t, "io-buildpacks-my-app:0.0.1", commonPipelineEnvironment.container.imageNameTag)
   176  
   177  		assert.Equal(t, "sha256:52eac630560210e5ae13eb10797c4246d6f02d425f32b9430ca00bde697c79ec", commonPipelineEnvironment.container.imageDigest)
   178  		assert.Contains(t, commonPipelineEnvironment.container.imageDigests, "sha256:52eac630560210e5ae13eb10797c4246d6f02d425f32b9430ca00bde697c79ec")
   179  
   180  		customDataAsString := telemetryData.Custom1
   181  		customData := &buildpacks.BuildpacksTelemetry{}
   182  		err = json.Unmarshal([]byte(customDataAsString), customData)
   183  		require.NoError(t, err)
   184  		assert.Equal(t, 1, len(customData.Data))
   185  		assert.Equal(t, "root", string(customData.Data[0].Path))
   186  	})
   187  
   188  	t.Run("success case (registry with https)", func(t *testing.T) {
   189  		t.Parallel()
   190  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   191  		config := cnbBuildOptions{
   192  			ContainerImageName:   "my-image",
   193  			ContainerImageTag:    "0.0.1",
   194  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
   195  			DockerConfigJSON:     "/path/to/config.json",
   196  		}
   197  
   198  		utils := newCnbBuildTestsUtils()
   199  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   200  		addBuilderFiles(&utils)
   201  
   202  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   203  
   204  		require.NoError(t, err)
   205  		runner := utils.ExecMockRunner
   206  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   207  		assertLifecycleCalls(t, runner, 2)
   208  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
   209  		assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
   210  		assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
   211  	})
   212  
   213  	t.Run("success case (registry without https)", func(t *testing.T) {
   214  		t.Parallel()
   215  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   216  		config := cnbBuildOptions{
   217  			ContainerImageName:   "my-image",
   218  			ContainerImageTag:    "0.0.1",
   219  			ContainerRegistryURL: imageRegistry,
   220  			DockerConfigJSON:     "/path/to/config.json",
   221  		}
   222  
   223  		utils := newCnbBuildTestsUtils()
   224  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   225  		addBuilderFiles(&utils)
   226  
   227  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   228  
   229  		require.NoError(t, err)
   230  		runner := utils.ExecMockRunner
   231  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   232  		assertLifecycleCalls(t, runner, 2)
   233  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
   234  		assert.Equal(t, fmt.Sprintf("https://%s", config.ContainerRegistryURL), commonPipelineEnvironment.container.registryURL)
   235  		assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
   236  	})
   237  
   238  	t.Run("success case (custom buildpacks and custom env variables, renaming docker conf file, additional tag)", func(t *testing.T) {
   239  		t.Parallel()
   240  		config := cnbBuildOptions{
   241  			ContainerImageName:   "my-image",
   242  			ContainerImageTag:    "0.0.1",
   243  			ContainerRegistryURL: imageRegistry,
   244  			DockerConfigJSON:     "/path/to/test.json",
   245  			Buildpacks:           []string{"test"},
   246  			BuildEnvVars: map[string]interface{}{
   247  				"FOO": "BAR",
   248  			},
   249  			AdditionalTags: []string{"latest"},
   250  		}
   251  
   252  		utils := newCnbBuildTestsUtils()
   253  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   254  		addBuilderFiles(&utils)
   255  
   256  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   257  
   258  		require.NoError(t, err)
   259  		runner := utils.ExecMockRunner
   260  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   261  		assert.Equal(t, creatorPath, runner.Calls[1].Exec)
   262  		assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks")
   263  		assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks/order.toml")
   264  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
   265  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:latest", config.ContainerRegistryURL, config.ContainerImageName))
   266  
   267  		copiedFileExists, _ := utils.FileExists("/tmp/config.json")
   268  		assert.True(t, copiedFileExists)
   269  	})
   270  
   271  	t.Run("success case (custom buildpacks, pre and post buildpacks and custom env variables, renaming docker conf file, additional tag)", func(t *testing.T) {
   272  		t.Parallel()
   273  		config := cnbBuildOptions{
   274  			ContainerImageName:   "my-image",
   275  			ContainerImageTag:    "0.0.1",
   276  			ContainerRegistryURL: imageRegistry,
   277  			DockerConfigJSON:     "/path/to/test.json",
   278  			PreBuildpacks:        []string{"pre-test"},
   279  			PostBuildpacks:       []string{"post-test"},
   280  			Buildpacks:           []string{"test"},
   281  			BuildEnvVars: map[string]interface{}{
   282  				"FOO": "BAR",
   283  			},
   284  			AdditionalTags: []string{"latest"},
   285  		}
   286  
   287  		utils := newCnbBuildTestsUtils()
   288  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   289  		addBuilderFiles(&utils)
   290  
   291  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   292  
   293  		require.NoError(t, err)
   294  		runner := utils.ExecMockRunner
   295  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   296  		assert.Equal(t, creatorPath, runner.Calls[1].Exec)
   297  		assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks")
   298  		assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks/order.toml")
   299  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
   300  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:latest", config.ContainerRegistryURL, config.ContainerImageName))
   301  
   302  		copiedFileExists, _ := utils.FileExists("/tmp/config.json")
   303  		assert.True(t, copiedFileExists)
   304  	})
   305  
   306  	t.Run("success case (custom pre and post buildpacks and custom env variables, renaming docker conf file, additional tag)", func(t *testing.T) {
   307  		t.Parallel()
   308  		config := cnbBuildOptions{
   309  			ContainerImageName:   "my-image",
   310  			ContainerImageTag:    "0.0.1",
   311  			ContainerRegistryURL: imageRegistry,
   312  			DockerConfigJSON:     "/path/to/test.json",
   313  			PostBuildpacks:       []string{"post-test"},
   314  			PreBuildpacks:        []string{"pre-test"},
   315  			BuildEnvVars: map[string]interface{}{
   316  				"FOO": "BAR",
   317  			},
   318  			AdditionalTags: []string{"latest"},
   319  		}
   320  
   321  		utils := newCnbBuildTestsUtils()
   322  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   323  		addBuilderFiles(&utils)
   324  
   325  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   326  
   327  		require.NoError(t, err)
   328  		runner := utils.ExecMockRunner
   329  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   330  		assert.Equal(t, creatorPath, runner.Calls[1].Exec)
   331  		assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks")
   332  		assert.Contains(t, runner.Calls[1].Params, "/tmp/buildpacks/order.toml")
   333  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
   334  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:latest", config.ContainerRegistryURL, config.ContainerImageName))
   335  
   336  		copiedFileExists, _ := utils.FileExists("/tmp/config.json")
   337  		assert.True(t, copiedFileExists)
   338  	})
   339  
   340  	t.Run("success case (customTlsCertificates)", func(t *testing.T) {
   341  		t.Parallel()
   342  		httpmock.Activate()
   343  		defer httpmock.DeactivateAndReset()
   344  		httpmock.RegisterResponder(http.MethodGet, "https://test-cert.com/cert.crt", httpmock.NewStringResponder(200, "testCert"))
   345  		client := &piperhttp.Client{}
   346  		client.SetOptions(piperhttp.ClientOptions{MaxRetries: -1, UseDefaultTransport: true})
   347  
   348  		caCertsFile := "/etc/ssl/certs/ca-certificates.crt"
   349  		caCertsTmpFile := "/tmp/ca-certificates.crt"
   350  		registry := "some-registry"
   351  		config := cnbBuildOptions{
   352  			ContainerImageName:        "my-image",
   353  			ContainerImageTag:         "0.0.1",
   354  			ContainerRegistryURL:      registry,
   355  			DockerConfigJSON:          "/path/to/config.json",
   356  			CustomTLSCertificateLinks: []string{"https://test-cert.com/cert.crt", "https://test-cert.com/cert.crt"},
   357  		}
   358  
   359  		utils := newCnbBuildTestsUtils()
   360  		utils.FilesMock.AddFile(caCertsFile, []byte("test\n"))
   361  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   362  		addBuilderFiles(&utils)
   363  
   364  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, client)
   365  		require.NoError(t, err)
   366  
   367  		result, err := utils.FilesMock.FileRead(caCertsTmpFile)
   368  		require.NoError(t, err)
   369  		assert.Equal(t, "test\ntestCert\ntestCert\n", string(result))
   370  
   371  		require.NoError(t, err)
   372  		runner := utils.ExecMockRunner
   373  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   374  		assert.Contains(t, runner.Env, fmt.Sprintf("SSL_CERT_FILE=%s", caCertsTmpFile))
   375  		assertLifecycleCalls(t, runner, 2)
   376  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
   377  	})
   378  
   379  	t.Run("success case (additionalTags)", func(t *testing.T) {
   380  		t.Parallel()
   381  		config := cnbBuildOptions{
   382  			ContainerImageName:   "my-image",
   383  			ContainerImageTag:    "3.1.5",
   384  			ContainerRegistryURL: imageRegistry,
   385  			DockerConfigJSON:     "/path/to/config.json",
   386  			AdditionalTags:       []string{"3", "3.1", "3.1", "3.1.5"},
   387  		}
   388  
   389  		utils := newCnbBuildTestsUtils()
   390  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   391  		addBuilderFiles(&utils)
   392  
   393  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   394  		require.NoError(t, err)
   395  
   396  		runner := utils.ExecMockRunner
   397  		assertLifecycleCalls(t, runner, 2)
   398  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
   399  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:3", config.ContainerRegistryURL, config.ContainerImageName))
   400  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:3.1", config.ContainerRegistryURL, config.ContainerImageName))
   401  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:3.1.5", config.ContainerRegistryURL, config.ContainerImageName))
   402  	})
   403  
   404  	t.Run("success case: build environment variables", func(t *testing.T) {
   405  		t.Parallel()
   406  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   407  		config := cnbBuildOptions{
   408  			ContainerImageTag:    "0.0.1",
   409  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
   410  			ProjectDescriptor:    "project.toml",
   411  			BuildEnvVars: map[string]interface{}{
   412  				"OPTIONS_KEY": "OPTIONS_VALUE",
   413  				"OVERWRITE":   "this should win",
   414  			},
   415  		}
   416  
   417  		projectToml := `[project]
   418  		id = "io.buildpacks.my-app"
   419  
   420  		[[build.env]]
   421  		name="PROJECT_DESCRIPTOR_KEY"
   422  		value="PROJECT_DESCRIPTOR_VALUE"
   423  
   424  		[[build.env]]
   425  		name="OVERWRITE"
   426  		value="this should be overwritten"
   427  		`
   428  
   429  		utils := newCnbBuildTestsUtils()
   430  		utils.FilesMock.AddFile("project.toml", []byte(projectToml))
   431  		addBuilderFiles(&utils)
   432  
   433  		telemetryData := telemetry.CustomData{}
   434  		err := callCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   435  
   436  		require.NoError(t, err)
   437  		assertLifecycleCalls(t, utils.ExecMockRunner, 2)
   438  
   439  		assetBuildEnv(t, utils, "OPTIONS_KEY", "OPTIONS_VALUE")
   440  		assetBuildEnv(t, utils, "PROJECT_DESCRIPTOR_KEY", "PROJECT_DESCRIPTOR_VALUE")
   441  		assetBuildEnv(t, utils, "OVERWRITE", "this should win")
   442  	})
   443  
   444  	t.Run("pom.xml exists (symlink for the target folder)", func(t *testing.T) {
   445  		t.Parallel()
   446  		config := cnbBuildOptions{
   447  			ContainerImageName:   "my-image",
   448  			ContainerImageTag:    "3.1.5",
   449  			ContainerRegistryURL: imageRegistry,
   450  			DockerConfigJSON:     "/path/to/config.json",
   451  		}
   452  
   453  		utils := newCnbBuildTestsUtils()
   454  		utils.FilesMock.CurrentDir = "/jenkins"
   455  		utils.FilesMock.AddDir("/jenkins")
   456  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   457  		utils.FilesMock.AddFile("/workspace/pom.xml", []byte("test"))
   458  		addBuilderFiles(&utils)
   459  
   460  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   461  		require.NoError(t, err)
   462  
   463  		runner := utils.ExecMockRunner
   464  		assertLifecycleCalls(t, runner, 2)
   465  
   466  		assert.True(t, utils.FilesMock.HasCreatedSymlink("/jenkins/target", "/workspace/target"))
   467  	})
   468  
   469  	t.Run("no pom.xml exists (no symlink for the target folder)", func(t *testing.T) {
   470  		t.Parallel()
   471  		config := cnbBuildOptions{
   472  			ContainerImageName:   "my-image",
   473  			ContainerImageTag:    "3.1.5",
   474  			ContainerRegistryURL: imageRegistry,
   475  			DockerConfigJSON:     "/path/to/config.json",
   476  		}
   477  
   478  		utils := newCnbBuildTestsUtils()
   479  		utils.FilesMock.CurrentDir = "/jenkins"
   480  		utils.FilesMock.AddDir("/jenkins")
   481  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   482  		addBuilderFiles(&utils)
   483  
   484  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   485  		require.NoError(t, err)
   486  
   487  		runner := utils.ExecMockRunner
   488  		assertLifecycleCalls(t, runner, 2)
   489  
   490  		assert.False(t, utils.FilesMock.HasCreatedSymlink("/jenkins/target", "/workspace/target"))
   491  	})
   492  
   493  	t.Run("error case: Invalid DockerConfigJSON file", func(t *testing.T) {
   494  		t.Parallel()
   495  		config := cnbBuildOptions{
   496  			ContainerImageTag:    "0.0.1",
   497  			ContainerRegistryURL: imageRegistry,
   498  			ContainerImageName:   "my-image",
   499  			DockerConfigJSON:     "/path/to/config.json",
   500  		}
   501  
   502  		utils := newCnbBuildTestsUtils()
   503  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":"dXNlcjpwYXNz"}}`))
   504  		addBuilderFiles(&utils)
   505  
   506  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   507  		assert.EqualError(t, err, "failed to generate CNB_REGISTRY_AUTH: json: cannot unmarshal string into Go struct field ConfigFile.auths of type types.AuthConfig")
   508  	})
   509  
   510  	t.Run("error case: DockerConfigJSON file not there (config.json)", func(t *testing.T) {
   511  		t.Parallel()
   512  		config := cnbBuildOptions{
   513  			ContainerImageTag:    "0.0.1",
   514  			ContainerRegistryURL: imageRegistry,
   515  			ContainerImageName:   "my-image",
   516  			DockerConfigJSON:     "not-there/config.json",
   517  		}
   518  
   519  		utils := newCnbBuildTestsUtils()
   520  		addBuilderFiles(&utils)
   521  
   522  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   523  		assert.EqualError(t, err, "failed to create/rename DockerConfigJSON file: cannot copy 'not-there/config.json': file does not exist")
   524  	})
   525  
   526  	t.Run("error case: DockerConfigJSON file not there (not config.json)", func(t *testing.T) {
   527  		t.Parallel()
   528  		config := cnbBuildOptions{
   529  			ContainerImageTag:    "0.0.1",
   530  			ContainerRegistryURL: imageRegistry,
   531  			ContainerImageName:   "my-image",
   532  			DockerConfigJSON:     "not-there",
   533  		}
   534  
   535  		utils := newCnbBuildTestsUtils()
   536  		addBuilderFiles(&utils)
   537  
   538  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   539  		assert.EqualError(t, err, "failed to create/rename DockerConfigJSON file: cannot copy 'not-there': file does not exist")
   540  	})
   541  
   542  	t.Run("error case: dockerImage is not a valid builder", func(t *testing.T) {
   543  		t.Parallel()
   544  		config := cnbBuildOptions{}
   545  
   546  		utils := newCnbBuildTestsUtils()
   547  
   548  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   549  		assert.EqualError(t, err, "the provided dockerImage is not a valid builder: binary '/cnb/lifecycle/creator' not found")
   550  	})
   551  
   552  	t.Run("error case: builder image does not contain tls certificates", func(t *testing.T) {
   553  		t.Parallel()
   554  		config := cnbBuildOptions{
   555  			ContainerImageName:        "my-image",
   556  			ContainerImageTag:         "0.0.1",
   557  			ContainerRegistryURL:      imageRegistry,
   558  			DockerConfigJSON:          "/path/to/config.json",
   559  			Buildpacks:                []string{"test"},
   560  			CustomTLSCertificateLinks: []string{"http://example.com/certs.pem"},
   561  		}
   562  
   563  		utils := newCnbBuildTestsUtils()
   564  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   565  		addBuilderFiles(&utils)
   566  
   567  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   568  		assert.EqualError(t, err, "failed to copy certificates: cannot copy '/etc/ssl/certs/ca-certificates.crt': file does not exist")
   569  	})
   570  
   571  	t.Run("success case (telemetry was added)", func(t *testing.T) {
   572  		t.Parallel()
   573  		registry := "some-registry"
   574  		config := cnbBuildOptions{
   575  			ContainerImageName:   "my-image",
   576  			ContainerImageTag:    "3.1.5",
   577  			ContainerRegistryURL: registry,
   578  			DockerConfigJSON:     "/path/to/config.json",
   579  			ProjectDescriptor:    "project.toml",
   580  			AdditionalTags:       []string{"latest"},
   581  			Buildpacks:           []string{"paketobuildpacks/java", "gcr.io/paketo-buildpacks/node"},
   582  			Bindings:             map[string]interface{}{"SECRET": map[string]string{"key": "KEY", "file": "a_file"}},
   583  			Path:                 "target",
   584  		}
   585  
   586  		utils := newCnbBuildTestsUtils()
   587  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   588  		utils.FilesMock.AddDir("target")
   589  		utils.FilesMock.AddFile("target/project.toml", []byte(`[project]
   590  id = "test"
   591  name = "test"
   592  version = "1.0.0"
   593  
   594  [build]
   595  include = []
   596  exclude = ["*.tar"]
   597  
   598  [[build.buildpacks]]
   599  uri = "some-buildpack"`))
   600  		utils.FilesMock.AddFile("a_file", []byte(`{}`))
   601  		utils.FilesMock.AddFile("target/somelib.jar", []byte(`FFFFFF`))
   602  
   603  		addBuilderFiles(&utils)
   604  
   605  		telemetryData := &telemetry.CustomData{}
   606  		err := callCnbBuild(&config, telemetryData, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   607  		require.NoError(t, err)
   608  
   609  		customDataAsString := telemetryData.Custom1
   610  		customData := &buildpacks.BuildpacksTelemetry{}
   611  		err = json.Unmarshal([]byte(customDataAsString), customData)
   612  
   613  		require.NoError(t, err)
   614  		assert.Equal(t, 3, customData.Version)
   615  		require.Equal(t, 1, len(customData.Data))
   616  		assert.Equal(t, "3.1.5", customData.Data[0].ImageTag)
   617  		assert.Equal(t, "folder", string(customData.Data[0].Path))
   618  		assert.Contains(t, customData.Data[0].AdditionalTags, "latest")
   619  		assert.Contains(t, customData.Data[0].BindingKeys, "SECRET")
   620  		assert.Equal(t, "paketobuildpacks/builder:base", customData.Data[0].Builder)
   621  
   622  		assert.Contains(t, customData.Data[0].Buildpacks.FromConfig, "paketobuildpacks/java")
   623  		assert.NotContains(t, customData.Data[0].Buildpacks.FromProjectDescriptor, "paketobuildpacks/java")
   624  		assert.Contains(t, customData.Data[0].Buildpacks.FromProjectDescriptor, "bcc73ab1f0a0d3fb0d1bf2b6df5510a25ccd14a761dbc0f5044ea24ead30452b")
   625  		assert.Contains(t, customData.Data[0].Buildpacks.Overall, "paketobuildpacks/java")
   626  
   627  		assert.True(t, customData.Data[0].ProjectDescriptor.Used)
   628  		assert.False(t, customData.Data[0].ProjectDescriptor.IncludeUsed)
   629  		assert.True(t, customData.Data[0].ProjectDescriptor.ExcludeUsed)
   630  	})
   631  
   632  	t.Run("error case, multiple artifacts in path", func(t *testing.T) {
   633  		t.Parallel()
   634  		config := cnbBuildOptions{
   635  			ContainerImageName:   "my-image",
   636  			ContainerImageTag:    "3.1.5",
   637  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
   638  			DockerConfigJSON:     "/path/to/config.json",
   639  			ProjectDescriptor:    "project.toml",
   640  			AdditionalTags:       []string{"latest"},
   641  			Buildpacks:           []string{"paketobuildpacks/java", "gcr.io/paketo-buildpacks/node"},
   642  			Path:                 "target/*.jar",
   643  		}
   644  
   645  		utils := newCnbBuildTestsUtils()
   646  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   647  		utils.FilesMock.AddDir("target")
   648  		utils.FilesMock.AddFile("target/app.jar", []byte(`FFFFFF`))
   649  		utils.FilesMock.AddFile("target/app-src.jar", []byte(`FFFFFF`))
   650  
   651  		addBuilderFiles(&utils)
   652  
   653  		telemetryData := telemetry.CustomData{}
   654  		err := callCnbBuild(&config, &telemetryData, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   655  		require.EqualError(t, err, "could not resolve path: Failed to resolve glob for 'target/*.jar', matching 2 file(s)")
   656  	})
   657  
   658  	t.Run("success case, artifacts found by glob", func(t *testing.T) {
   659  		t.Parallel()
   660  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   661  		config := cnbBuildOptions{
   662  			ContainerImageName:   "my-image",
   663  			ContainerImageTag:    "3.1.5",
   664  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
   665  			DockerConfigJSON:     "/path/to/config.json",
   666  			ProjectDescriptor:    "project.toml",
   667  			AdditionalTags:       []string{"latest"},
   668  			Buildpacks:           []string{"paketobuildpacks/java", "gcr.io/paketo-buildpacks/node"},
   669  			Path:                 "**/target",
   670  		}
   671  
   672  		utils := newCnbBuildTestsUtils()
   673  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   674  		utils.FilesMock.AddDir("target")
   675  		utils.FilesMock.AddFile("target/app.jar", []byte(`FFFFFF`))
   676  
   677  		addBuilderFiles(&utils)
   678  
   679  		telemetryData := telemetry.CustomData{}
   680  		err := callCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   681  
   682  		require.NoError(t, err)
   683  		runner := utils.ExecMockRunner
   684  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   685  		assert.Contains(t, runner.Calls[1].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
   686  		assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
   687  		assert.Equal(t, "my-image:3.1.5", commonPipelineEnvironment.container.imageNameTag)
   688  	})
   689  
   690  	t.Run("success case (build env telemetry was added)", func(t *testing.T) {
   691  		t.Parallel()
   692  		registry := "some-registry"
   693  		config := cnbBuildOptions{
   694  			ContainerImageName:   "my-image",
   695  			ContainerImageTag:    "3.1.5",
   696  			ContainerRegistryURL: registry,
   697  			ProjectDescriptor:    "project.toml",
   698  			BuildEnvVars:         map[string]interface{}{"CONFIG_KEY": "var", "BP_JVM_VERSION": "8"},
   699  		}
   700  
   701  		utils := newCnbBuildTestsUtils()
   702  		utils.FilesMock.AddFile("project.toml", []byte(`[project]
   703  id = "test"
   704  
   705  [build]
   706  include = []
   707  
   708  [[build.env]]
   709  name='PROJECT_KEY'
   710  value='var'
   711  
   712  [[build.env]]
   713  name='BP_NODE_VERSION'
   714  value='11'
   715  
   716  [[build.buildpacks]]
   717  uri = "some-buildpack"
   718  `))
   719  
   720  		addBuilderFiles(&utils)
   721  
   722  		telemetryData := &telemetry.CustomData{}
   723  		err := callCnbBuild(&config, telemetryData, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   724  		require.NoError(t, err)
   725  
   726  		customDataAsString := telemetryData.Custom1
   727  		customData := &buildpacks.BuildpacksTelemetry{}
   728  		err = json.Unmarshal([]byte(customDataAsString), customData)
   729  
   730  		require.NoError(t, err)
   731  		require.Equal(t, 1, len(customData.Data))
   732  		assert.Contains(t, customData.Data[0].BuildEnv.KeysFromConfig, "CONFIG_KEY")
   733  		assert.NotContains(t, customData.Data[0].BuildEnv.KeysFromProjectDescriptor, "CONFIG_KEY")
   734  		assert.Contains(t, customData.Data[0].BuildEnv.KeysOverall, "CONFIG_KEY")
   735  
   736  		assert.NotContains(t, customData.Data[0].BuildEnv.KeysFromConfig, "PROJECT_KEY")
   737  		assert.Contains(t, customData.Data[0].BuildEnv.KeysFromProjectDescriptor, "PROJECT_KEY")
   738  		assert.Contains(t, customData.Data[0].BuildEnv.KeysOverall, "PROJECT_KEY")
   739  
   740  		assert.Equal(t, "8", customData.Data[0].BuildEnv.KeyValues["BP_JVM_VERSION"])
   741  		assert.Equal(t, "11", customData.Data[0].BuildEnv.KeyValues["BP_NODE_VERSION"])
   742  		assert.NotContains(t, customData.Data[0].BuildEnv.KeyValues, "PROJECT_KEY")
   743  
   744  		assert.Contains(t, customData.Data[0].Buildpacks.Overall, "bcc73ab1f0a0d3fb0d1bf2b6df5510a25ccd14a761dbc0f5044ea24ead30452b")
   745  	})
   746  
   747  	t.Run("success case (multiple images configured)", func(t *testing.T) {
   748  		t.Parallel()
   749  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   750  		config := cnbBuildOptions{
   751  			ContainerImageTag:    "3.1.5",
   752  			ContainerRegistryURL: imageRegistry,
   753  			DockerConfigJSON:     "/path/to/my-config.json",
   754  			AdditionalTags:       []string{"3", "3.1", "3.1", "3.1.5"},
   755  			MultipleImages:       []map[string]interface{}{{"ContainerImageName": "my-image-0", "ContainerImageAlias": "simple"}, {"ContainerImageName": "my-image-1"}},
   756  		}
   757  
   758  		expectedImageCount := len(config.MultipleImages)
   759  
   760  		utils := newCnbBuildTestsUtils()
   761  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   762  		addBuilderFiles(&utils)
   763  
   764  		telemetryData := &telemetry.CustomData{}
   765  		err := callCnbBuild(&config, telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   766  		require.NoError(t, err)
   767  
   768  		customDataAsString := telemetryData.Custom1
   769  		customData := &buildpacks.BuildpacksTelemetry{}
   770  		err = json.Unmarshal([]byte(customDataAsString), customData)
   771  		assert.NoError(t, err)
   772  		require.Equal(t, expectedImageCount, len(customData.Data))
   773  
   774  		runner := utils.ExecMockRunner
   775  		require.Equal(t, expectedImageCount, len(runner.Calls)-1)
   776  		for i, call := range runner.Calls {
   777  			if i == 0 { // first call is -version
   778  				continue
   779  			}
   780  			lifecycleCall := i - 1
   781  			assert.Equal(t, 4, len(customData.Data[lifecycleCall].AdditionalTags))
   782  			assertLifecycleCalls(t, runner, i+1)
   783  			containerImageName := fmt.Sprintf("my-image-%d", lifecycleCall)
   784  			assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, containerImageName, config.ContainerImageTag))
   785  			assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3", config.ContainerRegistryURL, containerImageName))
   786  			assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3.1", config.ContainerRegistryURL, containerImageName))
   787  			assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3.1.5", config.ContainerRegistryURL, containerImageName))
   788  		}
   789  
   790  		assert.Equal(t, "my-image-0:3.1.5", commonPipelineEnvironment.container.imageNameTag)
   791  		assert.Equal(t, []string{"simple", "my-image-1"}, commonPipelineEnvironment.container.imageNames)
   792  	})
   793  }