github.com/jaylevin/jenkins-library@v1.230.4/cmd/cnbBuild_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"testing"
     8  
     9  	"github.com/SAP/jenkins-library/pkg/cnbutils"
    10  	piperconf "github.com/SAP/jenkins-library/pkg/config"
    11  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    12  	"github.com/SAP/jenkins-library/pkg/mock"
    13  	"github.com/SAP/jenkins-library/pkg/telemetry"
    14  	"github.com/jarcoal/httpmock"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  const imageRegistry = "some-registry"
    20  
    21  func newCnbBuildTestsUtils() cnbutils.MockUtils {
    22  	utils := cnbutils.MockUtils{
    23  		ExecMockRunner: &mock.ExecMockRunner{},
    24  		FilesMock:      &mock.FilesMock{},
    25  	}
    26  	utils.AddFile("/layers/report.toml", []byte(`[build]
    27  [image]
    28  tags = ["localhost:5000/not-found:0.0.1"]
    29  digest = "sha256:52eac630560210e5ae13eb10797c4246d6f02d425f32b9430ca00bde697c79ec"
    30  manifest-size = 2388`))
    31  	return utils
    32  }
    33  
    34  func addBuilderFiles(utils *cnbutils.MockUtils) {
    35  	utils.FilesMock.AddFile(creatorPath, []byte(`xyz`))
    36  }
    37  
    38  func assertLifecycleCalls(t *testing.T, runner *mock.ExecMockRunner, callNo int) {
    39  	require.GreaterOrEqual(t, len(runner.Calls), callNo)
    40  	assert.Equal(t, creatorPath, runner.Calls[callNo-1].Exec)
    41  	for _, arg := range []string{"-no-color", "-buildpacks", "/cnb/buildpacks", "-order", "/cnb/order.toml", "-platform", "/tmp/platform"} {
    42  		assert.Contains(t, runner.Calls[callNo-1].Params, arg)
    43  	}
    44  }
    45  
    46  func TestRunCnbBuild(t *testing.T) {
    47  	configOptions.openFile = piperconf.OpenPiperFile
    48  
    49  	t.Run("prefers direct configuration", func(t *testing.T) {
    50  		t.Parallel()
    51  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
    52  		config := cnbBuildOptions{
    53  			ContainerImageName:   "my-image",
    54  			ContainerImageTag:    "0.0.1",
    55  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
    56  			DockerConfigJSON:     "/path/to/config.json",
    57  		}
    58  
    59  		projectToml := `[project]
    60  		id = "io.buildpacks.my-app"
    61  		`
    62  
    63  		utils := newCnbBuildTestsUtils()
    64  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
    65  		utils.FilesMock.AddFile("project.toml", []byte(projectToml))
    66  		addBuilderFiles(&utils)
    67  
    68  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
    69  
    70  		require.NoError(t, err)
    71  		runner := utils.ExecMockRunner
    72  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
    73  		assertLifecycleCalls(t, runner, 1)
    74  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
    75  		assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
    76  		assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
    77  	})
    78  
    79  	t.Run("prefers project descriptor", func(t *testing.T) {
    80  		t.Parallel()
    81  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
    82  		config := cnbBuildOptions{
    83  			ContainerImageTag:    "0.0.1",
    84  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
    85  			DockerConfigJSON:     "/path/to/config.json",
    86  			ProjectDescriptor:    "project.toml",
    87  		}
    88  
    89  		projectToml := `[project]
    90  		id = "io.buildpacks.my-app"
    91  		`
    92  
    93  		utils := newCnbBuildTestsUtils()
    94  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
    95  		utils.FilesMock.AddFile("project.toml", []byte(projectToml))
    96  		addBuilderFiles(&utils)
    97  
    98  		telemetryData := telemetry.CustomData{}
    99  		err := callCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   100  
   101  		require.NoError(t, err)
   102  		runner := utils.ExecMockRunner
   103  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   104  		assertLifecycleCalls(t, runner, 1)
   105  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, "io-buildpacks-my-app", config.ContainerImageTag))
   106  		assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
   107  		assert.Equal(t, "io-buildpacks-my-app:0.0.1", commonPipelineEnvironment.container.imageNameTag)
   108  
   109  		assert.Equal(t, "sha256:52eac630560210e5ae13eb10797c4246d6f02d425f32b9430ca00bde697c79ec", commonPipelineEnvironment.container.imageDigest)
   110  		assert.Contains(t, commonPipelineEnvironment.container.imageDigests, "sha256:52eac630560210e5ae13eb10797c4246d6f02d425f32b9430ca00bde697c79ec")
   111  
   112  		customDataAsString := telemetryData.Custom1
   113  		customData := cnbBuildTelemetry{}
   114  		err = json.Unmarshal([]byte(customDataAsString), &customData)
   115  		require.NoError(t, err)
   116  		assert.Equal(t, 1, len(customData.Data))
   117  		assert.Equal(t, "root", string(customData.Data[0].Path))
   118  	})
   119  
   120  	t.Run("success case (registry with https)", func(t *testing.T) {
   121  		t.Parallel()
   122  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   123  		config := cnbBuildOptions{
   124  			ContainerImageName:   "my-image",
   125  			ContainerImageTag:    "0.0.1",
   126  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
   127  			DockerConfigJSON:     "/path/to/config.json",
   128  		}
   129  
   130  		utils := newCnbBuildTestsUtils()
   131  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   132  		addBuilderFiles(&utils)
   133  
   134  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   135  
   136  		require.NoError(t, err)
   137  		runner := utils.ExecMockRunner
   138  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   139  		assertLifecycleCalls(t, runner, 1)
   140  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
   141  		assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
   142  		assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
   143  	})
   144  
   145  	t.Run("success case (registry without https)", func(t *testing.T) {
   146  		t.Parallel()
   147  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   148  		config := cnbBuildOptions{
   149  			ContainerImageName:   "my-image",
   150  			ContainerImageTag:    "0.0.1",
   151  			ContainerRegistryURL: imageRegistry,
   152  			DockerConfigJSON:     "/path/to/config.json",
   153  		}
   154  
   155  		utils := newCnbBuildTestsUtils()
   156  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   157  		addBuilderFiles(&utils)
   158  
   159  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   160  
   161  		require.NoError(t, err)
   162  		runner := utils.ExecMockRunner
   163  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   164  		assertLifecycleCalls(t, runner, 1)
   165  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
   166  		assert.Equal(t, fmt.Sprintf("https://%s", config.ContainerRegistryURL), commonPipelineEnvironment.container.registryURL)
   167  		assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
   168  	})
   169  
   170  	t.Run("success case (custom buildpacks and custom env variables, renaming docker conf file, additional tag)", func(t *testing.T) {
   171  		t.Parallel()
   172  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   173  		config := cnbBuildOptions{
   174  			ContainerImageName:   "my-image",
   175  			ContainerImageTag:    "0.0.1",
   176  			ContainerRegistryURL: imageRegistry,
   177  			DockerConfigJSON:     "/path/to/test.json",
   178  			Buildpacks:           []string{"test"},
   179  			BuildEnvVars: map[string]interface{}{
   180  				"FOO": "BAR",
   181  			},
   182  			AdditionalTags: []string{"latest"},
   183  		}
   184  
   185  		utils := newCnbBuildTestsUtils()
   186  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   187  		addBuilderFiles(&utils)
   188  
   189  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   190  
   191  		require.NoError(t, err)
   192  		runner := utils.ExecMockRunner
   193  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   194  		assert.Equal(t, creatorPath, runner.Calls[0].Exec)
   195  		assert.Contains(t, runner.Calls[0].Params, "/tmp/buildpacks")
   196  		assert.Contains(t, runner.Calls[0].Params, "/tmp/buildpacks/order.toml")
   197  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
   198  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:latest", config.ContainerRegistryURL, config.ContainerImageName))
   199  
   200  		initialFileExists, _ := utils.FileExists("/path/to/test.json")
   201  		renamedFileExists, _ := utils.FileExists("/path/to/config.json")
   202  
   203  		assert.False(t, initialFileExists)
   204  		assert.True(t, renamedFileExists)
   205  	})
   206  
   207  	t.Run("success case (customTlsCertificates)", func(t *testing.T) {
   208  		t.Parallel()
   209  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   210  		httpmock.Activate()
   211  		defer httpmock.DeactivateAndReset()
   212  		httpmock.RegisterResponder(http.MethodGet, "https://test-cert.com/cert.crt", httpmock.NewStringResponder(200, "testCert"))
   213  		client := &piperhttp.Client{}
   214  		client.SetOptions(piperhttp.ClientOptions{MaxRetries: -1, UseDefaultTransport: true})
   215  
   216  		caCertsFile := "/etc/ssl/certs/ca-certificates.crt"
   217  		caCertsTmpFile := "/tmp/ca-certificates.crt"
   218  		registry := "some-registry"
   219  		config := cnbBuildOptions{
   220  			ContainerImageName:        "my-image",
   221  			ContainerImageTag:         "0.0.1",
   222  			ContainerRegistryURL:      registry,
   223  			DockerConfigJSON:          "/path/to/config.json",
   224  			CustomTLSCertificateLinks: []string{"https://test-cert.com/cert.crt", "https://test-cert.com/cert.crt"},
   225  		}
   226  
   227  		utils := newCnbBuildTestsUtils()
   228  		utils.FilesMock.AddFile(caCertsFile, []byte("test\n"))
   229  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   230  		addBuilderFiles(&utils)
   231  
   232  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, client)
   233  		require.NoError(t, err)
   234  
   235  		result, err := utils.FilesMock.FileRead(caCertsTmpFile)
   236  		require.NoError(t, err)
   237  		assert.Equal(t, "test\ntestCert\ntestCert\n", string(result))
   238  
   239  		require.NoError(t, err)
   240  		runner := utils.ExecMockRunner
   241  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   242  		assert.Contains(t, runner.Env, fmt.Sprintf("SSL_CERT_FILE=%s", caCertsTmpFile))
   243  		assertLifecycleCalls(t, runner, 1)
   244  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
   245  	})
   246  
   247  	t.Run("success case (additionalTags)", func(t *testing.T) {
   248  		t.Parallel()
   249  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   250  		config := cnbBuildOptions{
   251  			ContainerImageName:   "my-image",
   252  			ContainerImageTag:    "3.1.5",
   253  			ContainerRegistryURL: imageRegistry,
   254  			DockerConfigJSON:     "/path/to/config.json",
   255  			AdditionalTags:       []string{"3", "3.1", "3.1", "3.1.5"},
   256  		}
   257  
   258  		utils := newCnbBuildTestsUtils()
   259  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   260  		addBuilderFiles(&utils)
   261  
   262  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   263  		require.NoError(t, err)
   264  
   265  		runner := utils.ExecMockRunner
   266  		assertLifecycleCalls(t, runner, 1)
   267  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
   268  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:3", config.ContainerRegistryURL, config.ContainerImageName))
   269  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:3.1", config.ContainerRegistryURL, config.ContainerImageName))
   270  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:3.1.5", config.ContainerRegistryURL, config.ContainerImageName))
   271  	})
   272  
   273  	t.Run("pom.xml exists (symlink for the target folder)", func(t *testing.T) {
   274  		t.Parallel()
   275  		config := cnbBuildOptions{
   276  			ContainerImageName:   "my-image",
   277  			ContainerImageTag:    "3.1.5",
   278  			ContainerRegistryURL: imageRegistry,
   279  			DockerConfigJSON:     "/path/to/config.json",
   280  		}
   281  
   282  		utils := newCnbBuildTestsUtils()
   283  		utils.FilesMock.CurrentDir = "/jenkins"
   284  		utils.FilesMock.AddDir("/jenkins")
   285  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   286  		utils.FilesMock.AddFile("/workspace/pom.xml", []byte("test"))
   287  		addBuilderFiles(&utils)
   288  
   289  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   290  		require.NoError(t, err)
   291  
   292  		runner := utils.ExecMockRunner
   293  		assertLifecycleCalls(t, runner, 1)
   294  
   295  		assert.True(t, utils.FilesMock.HasCreatedSymlink("/jenkins/target", "/workspace/target"))
   296  	})
   297  
   298  	t.Run("no pom.xml exists (no symlink for the target folder)", func(t *testing.T) {
   299  		t.Parallel()
   300  		config := cnbBuildOptions{
   301  			ContainerImageName:   "my-image",
   302  			ContainerImageTag:    "3.1.5",
   303  			ContainerRegistryURL: imageRegistry,
   304  			DockerConfigJSON:     "/path/to/config.json",
   305  		}
   306  
   307  		utils := newCnbBuildTestsUtils()
   308  		utils.FilesMock.CurrentDir = "/jenkins"
   309  		utils.FilesMock.AddDir("/jenkins")
   310  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   311  		addBuilderFiles(&utils)
   312  
   313  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
   314  		require.NoError(t, err)
   315  
   316  		runner := utils.ExecMockRunner
   317  		assertLifecycleCalls(t, runner, 1)
   318  
   319  		assert.False(t, utils.FilesMock.HasCreatedSymlink("/jenkins/target", "/workspace/target"))
   320  	})
   321  
   322  	t.Run("error case: Invalid DockerConfigJSON file", func(t *testing.T) {
   323  		t.Parallel()
   324  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   325  		config := cnbBuildOptions{
   326  			ContainerImageTag:    "0.0.1",
   327  			ContainerRegistryURL: imageRegistry,
   328  			ContainerImageName:   "my-image",
   329  			DockerConfigJSON:     "/path/to/config.json",
   330  		}
   331  
   332  		utils := newCnbBuildTestsUtils()
   333  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":"dXNlcjpwYXNz"}}`))
   334  		addBuilderFiles(&utils)
   335  
   336  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   337  		assert.EqualError(t, err, "failed to generate CNB_REGISTRY_AUTH: json: cannot unmarshal string into Go struct field ConfigFile.auths of type types.AuthConfig")
   338  	})
   339  
   340  	t.Run("error case: DockerConfigJSON file not there (config.json)", func(t *testing.T) {
   341  		t.Parallel()
   342  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   343  		config := cnbBuildOptions{
   344  			ContainerImageTag:    "0.0.1",
   345  			ContainerRegistryURL: imageRegistry,
   346  			ContainerImageName:   "my-image",
   347  			DockerConfigJSON:     "not-there/config.json",
   348  		}
   349  
   350  		utils := newCnbBuildTestsUtils()
   351  		addBuilderFiles(&utils)
   352  
   353  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   354  		assert.EqualError(t, err, "failed to generate CNB_REGISTRY_AUTH: could not read 'not-there/config.json'")
   355  	})
   356  
   357  	t.Run("error case: DockerConfigJSON file not there (not config.json)", func(t *testing.T) {
   358  		t.Parallel()
   359  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   360  		config := cnbBuildOptions{
   361  			ContainerImageTag:    "0.0.1",
   362  			ContainerRegistryURL: imageRegistry,
   363  			ContainerImageName:   "my-image",
   364  			DockerConfigJSON:     "not-there",
   365  		}
   366  
   367  		utils := newCnbBuildTestsUtils()
   368  		addBuilderFiles(&utils)
   369  
   370  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   371  		assert.EqualError(t, err, "failed to rename DockerConfigJSON file 'not-there': renaming file 'not-there' is not supported, since it does not exist, or is not a leaf-entry")
   372  	})
   373  
   374  	t.Run("error case: dockerImage is not a valid builder", func(t *testing.T) {
   375  		t.Parallel()
   376  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   377  		config := cnbBuildOptions{}
   378  
   379  		utils := newCnbBuildTestsUtils()
   380  
   381  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   382  		assert.EqualError(t, err, "the provided dockerImage is not a valid builder: binary '/cnb/lifecycle/creator' not found")
   383  	})
   384  
   385  	t.Run("error case: builder image does not contain tls certificates", func(t *testing.T) {
   386  		t.Parallel()
   387  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   388  		config := cnbBuildOptions{
   389  			ContainerImageName:        "my-image",
   390  			ContainerImageTag:         "0.0.1",
   391  			ContainerRegistryURL:      imageRegistry,
   392  			DockerConfigJSON:          "/path/to/config.json",
   393  			Buildpacks:                []string{"test"},
   394  			CustomTLSCertificateLinks: []string{"http://example.com/certs.pem"},
   395  		}
   396  
   397  		utils := newCnbBuildTestsUtils()
   398  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   399  		addBuilderFiles(&utils)
   400  
   401  		err := callCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   402  		assert.EqualError(t, err, "failed to copy certificates: cannot copy '/etc/ssl/certs/ca-certificates.crt': file does not exist")
   403  	})
   404  
   405  	t.Run("success case (telemetry was added)", func(t *testing.T) {
   406  		t.Parallel()
   407  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   408  		registry := "some-registry"
   409  		config := cnbBuildOptions{
   410  			ContainerImageName:   "my-image",
   411  			ContainerImageTag:    "3.1.5",
   412  			ContainerRegistryURL: registry,
   413  			DockerConfigJSON:     "/path/to/config.json",
   414  			ProjectDescriptor:    "project.toml",
   415  			AdditionalTags:       []string{"latest"},
   416  			Buildpacks:           []string{"paketobuildpacks/java", "gcr.io/paketo-buildpacks/node"},
   417  			Bindings:             map[string]interface{}{"SECRET": map[string]string{"key": "KEY", "file": "a_file"}},
   418  			Path:                 "target",
   419  		}
   420  
   421  		utils := newCnbBuildTestsUtils()
   422  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   423  		utils.FilesMock.AddDir("target")
   424  		utils.FilesMock.AddFile("target/project.toml", []byte(`[project]
   425  id = "test"
   426  name = "test"
   427  version = "1.0.0"
   428  
   429  [build]
   430  include = []
   431  exclude = ["*.tar"]
   432  
   433  [[build.buildpacks]]
   434  uri = "some-buildpack"`))
   435  		utils.FilesMock.AddFile("a_file", []byte(`{}`))
   436  		utils.FilesMock.AddFile("target/somelib.jar", []byte(`FFFFFF`))
   437  
   438  		addBuilderFiles(&utils)
   439  
   440  		telemetryData := telemetry.CustomData{}
   441  		err := callCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   442  		require.NoError(t, err)
   443  
   444  		customDataAsString := telemetryData.Custom1
   445  		customData := cnbBuildTelemetry{}
   446  		err = json.Unmarshal([]byte(customDataAsString), &customData)
   447  
   448  		require.NoError(t, err)
   449  		assert.Equal(t, 3, customData.Version)
   450  		require.Equal(t, 1, len(customData.Data))
   451  		assert.Equal(t, "3.1.5", customData.Data[0].ImageTag)
   452  		assert.Equal(t, "folder", string(customData.Data[0].Path))
   453  		assert.Contains(t, customData.Data[0].AdditionalTags, "latest")
   454  		assert.Contains(t, customData.Data[0].BindingKeys, "SECRET")
   455  		assert.Equal(t, "paketobuildpacks/builder:base", customData.Data[0].Builder)
   456  
   457  		assert.Contains(t, customData.Data[0].Buildpacks.FromConfig, "paketobuildpacks/java")
   458  		assert.NotContains(t, customData.Data[0].Buildpacks.FromProjectDescriptor, "paketobuildpacks/java")
   459  		assert.Contains(t, customData.Data[0].Buildpacks.FromProjectDescriptor, "<redacted>")
   460  		assert.NotContains(t, customData.Data[0].Buildpacks.Overall, "<redacted>")
   461  		assert.Contains(t, customData.Data[0].Buildpacks.Overall, "paketobuildpacks/java")
   462  
   463  		assert.True(t, customData.Data[0].ProjectDescriptor.Used)
   464  		assert.False(t, customData.Data[0].ProjectDescriptor.IncludeUsed)
   465  		assert.True(t, customData.Data[0].ProjectDescriptor.ExcludeUsed)
   466  	})
   467  
   468  	t.Run("error case, multiple artifacts in path", func(t *testing.T) {
   469  		t.Parallel()
   470  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   471  		config := cnbBuildOptions{
   472  			ContainerImageName:   "my-image",
   473  			ContainerImageTag:    "3.1.5",
   474  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
   475  			DockerConfigJSON:     "/path/to/config.json",
   476  			ProjectDescriptor:    "project.toml",
   477  			AdditionalTags:       []string{"latest"},
   478  			Buildpacks:           []string{"paketobuildpacks/java", "gcr.io/paketo-buildpacks/node"},
   479  			Path:                 "target/*.jar",
   480  		}
   481  
   482  		utils := newCnbBuildTestsUtils()
   483  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   484  		utils.FilesMock.AddDir("target")
   485  		utils.FilesMock.AddFile("target/app.jar", []byte(`FFFFFF`))
   486  		utils.FilesMock.AddFile("target/app-src.jar", []byte(`FFFFFF`))
   487  
   488  		addBuilderFiles(&utils)
   489  
   490  		telemetryData := telemetry.CustomData{}
   491  		err := callCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   492  		require.EqualError(t, err, "could not resolve path: Failed to resolve glob for 'target/*.jar', matching 2 file(s)")
   493  	})
   494  
   495  	t.Run("success case, artifacts found by glob", func(t *testing.T) {
   496  		t.Parallel()
   497  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   498  		config := cnbBuildOptions{
   499  			ContainerImageName:   "my-image",
   500  			ContainerImageTag:    "3.1.5",
   501  			ContainerRegistryURL: fmt.Sprintf("https://%s", imageRegistry),
   502  			DockerConfigJSON:     "/path/to/config.json",
   503  			ProjectDescriptor:    "project.toml",
   504  			AdditionalTags:       []string{"latest"},
   505  			Buildpacks:           []string{"paketobuildpacks/java", "gcr.io/paketo-buildpacks/node"},
   506  			Path:                 "**/target",
   507  		}
   508  
   509  		utils := newCnbBuildTestsUtils()
   510  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   511  		utils.FilesMock.AddDir("target")
   512  		utils.FilesMock.AddFile("target/app.jar", []byte(`FFFFFF`))
   513  
   514  		addBuilderFiles(&utils)
   515  
   516  		telemetryData := telemetry.CustomData{}
   517  		err := callCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   518  
   519  		require.NoError(t, err)
   520  		runner := utils.ExecMockRunner
   521  		assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
   522  		assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
   523  		assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
   524  		assert.Equal(t, "my-image:3.1.5", commonPipelineEnvironment.container.imageNameTag)
   525  	})
   526  
   527  	t.Run("success case (build env telemetry was added)", func(t *testing.T) {
   528  		t.Parallel()
   529  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   530  		registry := "some-registry"
   531  		config := cnbBuildOptions{
   532  			ContainerImageName:   "my-image",
   533  			ContainerImageTag:    "3.1.5",
   534  			ContainerRegistryURL: registry,
   535  			ProjectDescriptor:    "project.toml",
   536  			BuildEnvVars:         map[string]interface{}{"CONFIG_KEY": "var", "BP_JVM_VERSION": "8"},
   537  		}
   538  
   539  		utils := newCnbBuildTestsUtils()
   540  		utils.FilesMock.AddFile("project.toml", []byte(`[project]
   541  id = "test"
   542  
   543  [build]
   544  include = []
   545  
   546  [[build.env]]
   547  name='PROJECT_KEY'
   548  value='var'
   549  
   550  [[build.env]]
   551  name='BP_NODE_VERSION'
   552  value='11'
   553  
   554  [[build.buildpacks]]
   555  uri = "some-buildpack"
   556  `))
   557  
   558  		addBuilderFiles(&utils)
   559  
   560  		telemetryData := telemetry.CustomData{}
   561  		err := callCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   562  		require.NoError(t, err)
   563  
   564  		customDataAsString := telemetryData.Custom1
   565  		customData := cnbBuildTelemetry{}
   566  		err = json.Unmarshal([]byte(customDataAsString), &customData)
   567  
   568  		require.NoError(t, err)
   569  		require.Equal(t, 1, len(customData.Data))
   570  		assert.Contains(t, customData.Data[0].BuildEnv.KeysFromConfig, "CONFIG_KEY")
   571  		assert.NotContains(t, customData.Data[0].BuildEnv.KeysFromProjectDescriptor, "CONFIG_KEY")
   572  		assert.Contains(t, customData.Data[0].BuildEnv.KeysOverall, "CONFIG_KEY")
   573  
   574  		assert.NotContains(t, customData.Data[0].BuildEnv.KeysFromConfig, "PROJECT_KEY")
   575  		assert.Contains(t, customData.Data[0].BuildEnv.KeysFromProjectDescriptor, "PROJECT_KEY")
   576  		assert.Contains(t, customData.Data[0].BuildEnv.KeysOverall, "PROJECT_KEY")
   577  
   578  		assert.Equal(t, "8", customData.Data[0].BuildEnv.KeyValues["BP_JVM_VERSION"])
   579  		assert.Equal(t, "11", customData.Data[0].BuildEnv.KeyValues["BP_NODE_VERSION"])
   580  		assert.NotContains(t, customData.Data[0].BuildEnv.KeyValues, "PROJECT_KEY")
   581  
   582  		assert.Contains(t, customData.Data[0].Buildpacks.Overall, "<redacted>")
   583  	})
   584  
   585  	t.Run("success case (multiple images configured)", func(t *testing.T) {
   586  		t.Parallel()
   587  		commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
   588  		config := cnbBuildOptions{
   589  			ContainerImageTag:    "3.1.5",
   590  			ContainerRegistryURL: imageRegistry,
   591  			DockerConfigJSON:     "/path/to/my-config.json",
   592  			AdditionalTags:       []string{"3", "3.1", "3.1", "3.1.5"},
   593  			MultipleImages:       []map[string]interface{}{{"ContainerImageName": "my-image-0"}, {"ContainerImageName": "my-image-1"}},
   594  		}
   595  
   596  		expectedImageCount := len(config.MultipleImages)
   597  
   598  		utils := newCnbBuildTestsUtils()
   599  		utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
   600  		addBuilderFiles(&utils)
   601  
   602  		telemetryData := telemetry.CustomData{}
   603  		err := callCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
   604  		require.NoError(t, err)
   605  
   606  		customDataAsString := telemetryData.Custom1
   607  		customData := cnbBuildTelemetry{}
   608  		err = json.Unmarshal([]byte(customDataAsString), &customData)
   609  		assert.NoError(t, err)
   610  		require.Equal(t, expectedImageCount, len(customData.Data))
   611  
   612  		runner := utils.ExecMockRunner
   613  		require.Equal(t, expectedImageCount, len(runner.Calls))
   614  		for i, call := range runner.Calls {
   615  			assert.Equal(t, 4, len(customData.Data[i].AdditionalTags))
   616  			assertLifecycleCalls(t, runner, i+1)
   617  			containerImageName := fmt.Sprintf("my-image-%d", i)
   618  			assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, containerImageName, config.ContainerImageTag))
   619  			assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3", config.ContainerRegistryURL, containerImageName))
   620  			assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3.1", config.ContainerRegistryURL, containerImageName))
   621  			assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3.1.5", config.ContainerRegistryURL, containerImageName))
   622  		}
   623  
   624  		assert.Equal(t, "my-image-0:3.1.5", commonPipelineEnvironment.container.imageNameTag)
   625  	})
   626  
   627  }