mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-01-16 05:16:08 +02:00
feat(cnbbuild) enable multi image build (#3521)
Co-authored-by: Ralf Pannemans <ralf.pannemans@sap.com> Co-authored-by: Pavel Busko <pavel.busko@sap.com> Co-authored-by: Sumit Kulhadia <sumit.kulhadia@sap.com>
This commit is contained in:
parent
32acc9f91f
commit
4b2f61589d
157
cmd/cnbBuild.go
157
cmd/cnbBuild.go
@ -21,6 +21,8 @@ import (
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/SAP/jenkins-library/pkg/piperutils"
|
||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/pkg/errors"
|
||||
ignore "github.com/sabhiram/go-gitignore"
|
||||
)
|
||||
@ -44,8 +46,12 @@ type cnbBuildUtilsBundle struct {
|
||||
*docker.Client
|
||||
}
|
||||
|
||||
type cnbBuildTelemetry struct {
|
||||
Version int `json:"version"`
|
||||
Data []cnbBuildTelemetryData `json:"data"`
|
||||
}
|
||||
|
||||
type cnbBuildTelemetryData struct {
|
||||
Version int `json:"version"`
|
||||
ImageTag string `json:"imageTag"`
|
||||
AdditionalTags []string `json:"additionalTags"`
|
||||
BindingKeys []string `json:"bindingKeys"`
|
||||
@ -76,6 +82,32 @@ type cnbBuildTelemetryDataProjectDescriptor struct {
|
||||
ExcludeUsed bool `json:"excludeUsed"`
|
||||
}
|
||||
|
||||
func processConfigs(main cnbBuildOptions, multipleImages []map[string]interface{}) ([]cnbBuildOptions, error) {
|
||||
var result []cnbBuildOptions
|
||||
|
||||
if len(multipleImages) == 0 {
|
||||
result = append(result, main)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
for _, conf := range multipleImages {
|
||||
var structuredConf cnbBuildOptions
|
||||
err := mapstructure.Decode(conf, &structuredConf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = mergo.Merge(&structuredConf, main)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = append(result, structuredConf)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func setCustomBuildpacks(bpacks []string, dockerCreds string, utils cnbutils.BuildUtils) (string, string, error) {
|
||||
buildpacksPath := "/tmp/buildpacks"
|
||||
orderPath := "/tmp/buildpacks/order.toml"
|
||||
@ -107,8 +139,11 @@ func cnbBuild(config cnbBuildOptions, telemetryData *telemetry.CustomData, commo
|
||||
utils := newCnbBuildUtils()
|
||||
|
||||
client := &piperhttp.Client{}
|
||||
cnbTelemetry := cnbBuildTelemetry{
|
||||
Version: 2,
|
||||
}
|
||||
|
||||
err := runCnbBuild(&config, telemetryData, utils, commonPipelineEnvironment, client)
|
||||
err := callCnbBuild(&config, telemetryData, &cnbTelemetry, utils, commonPipelineEnvironment, client)
|
||||
if err != nil {
|
||||
log.Entry().WithError(err).Fatal("step execution failed")
|
||||
}
|
||||
@ -166,6 +201,22 @@ func isZip(path string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func cleanDir(dir string, utils cnbutils.BuildUtils) error {
|
||||
dirContent, err := utils.Glob(filepath.Join(dir, "*"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, obj := range dirContent {
|
||||
err = utils.RemoveAll(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFile(source, target string, utils cnbutils.BuildUtils) error {
|
||||
targetDir := filepath.Dir(target)
|
||||
|
||||
@ -243,7 +294,15 @@ func prepareDockerConfig(source string, utils cnbutils.BuildUtils) (string, erro
|
||||
log.Entry().Debugf("Renaming docker config file from '%s' to 'config.json'", filepath.Base(source))
|
||||
|
||||
newPath := filepath.Join(filepath.Dir(source), "config.json")
|
||||
err := utils.FileRename(source, newPath)
|
||||
alreadyExists, err := utils.FileExists(newPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if alreadyExists {
|
||||
return newPath, nil
|
||||
}
|
||||
|
||||
err = utils.FileRename(source, newPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -275,18 +334,18 @@ func linkTargetFolder(utils cnbutils.BuildUtils, source, target string) error {
|
||||
return utils.Symlink(targetPath, linkPath)
|
||||
}
|
||||
|
||||
func (c *cnbBuildOptions) mergeEnvVars(vars map[string]interface{}) {
|
||||
if c.BuildEnvVars == nil {
|
||||
c.BuildEnvVars = vars
|
||||
func (config *cnbBuildOptions) mergeEnvVars(vars map[string]interface{}) {
|
||||
if config.BuildEnvVars == nil {
|
||||
config.BuildEnvVars = vars
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range vars {
|
||||
_, exists := c.BuildEnvVars[k]
|
||||
_, exists := config.BuildEnvVars[k]
|
||||
|
||||
if !exists {
|
||||
c.BuildEnvVars[k] = v
|
||||
config.BuildEnvVars[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -301,7 +360,11 @@ func (config *cnbBuildOptions) resolvePath(utils cnbutils.BuildUtils) (pathEnum,
|
||||
if config.Path == "" {
|
||||
return pathEnumRoot, pwd, nil
|
||||
}
|
||||
source := config.Path
|
||||
source, err := utils.Abs(config.Path)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorConfiguration)
|
||||
return "", "", errors.Wrapf(err, "Failed to resolve absolute path for '%s'", config.Path)
|
||||
}
|
||||
|
||||
dir, err := utils.DirExists(source)
|
||||
if err != nil {
|
||||
@ -317,7 +380,7 @@ func (config *cnbBuildOptions) resolvePath(utils cnbutils.BuildUtils) (pathEnum,
|
||||
}
|
||||
|
||||
func addConfigTelemetryData(utils cnbutils.BuildUtils, data *cnbBuildTelemetryData, config *cnbBuildOptions) {
|
||||
bindingKeys := []string{}
|
||||
var bindingKeys []string
|
||||
for k := range config.Bindings {
|
||||
bindingKeys = append(bindingKeys, k)
|
||||
}
|
||||
@ -358,10 +421,31 @@ func addProjectDescriptorTelemetryData(data *cnbBuildTelemetryData, descriptor p
|
||||
data.ProjectDescriptor.ExcludeUsed = descriptor.Exclude != nil
|
||||
}
|
||||
|
||||
func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, utils cnbutils.BuildUtils, commonPipelineEnvironment *cnbBuildCommonPipelineEnvironment, httpClient piperhttp.Sender) error {
|
||||
func callCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, telemetry *cnbBuildTelemetry, utils cnbutils.BuildUtils, commonPipelineEnvironment *cnbBuildCommonPipelineEnvironment, httpClient piperhttp.Sender) error {
|
||||
mergedConfigs, err := processConfigs(*config, config.MultipleImages)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to process config")
|
||||
}
|
||||
for _, c := range mergedConfigs {
|
||||
err = runCnbBuild(&c, telemetryData, telemetry, utils, commonPipelineEnvironment, httpClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
telemetryData.Custom1Label = "cnbBuildStepData"
|
||||
customData, err := json.Marshal(telemetry)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to marshal custom telemetry data")
|
||||
}
|
||||
telemetryData.Custom1 = string(customData)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, telemetry *cnbBuildTelemetry, utils cnbutils.BuildUtils, commonPipelineEnvironment *cnbBuildCommonPipelineEnvironment, httpClient piperhttp.Sender) error {
|
||||
var err error
|
||||
|
||||
customTelemetryData := cnbBuildTelemetryData{Version: 1}
|
||||
customTelemetryData := cnbBuildTelemetryData{}
|
||||
addConfigTelemetryData(utils, &customTelemetryData, config)
|
||||
|
||||
err = isBuilder(utils)
|
||||
@ -373,18 +457,18 @@ func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, u
|
||||
include := ignore.CompileIgnoreLines("**/*")
|
||||
exclude := ignore.CompileIgnoreLines("piper", ".pipeline")
|
||||
|
||||
projDescExists, err := utils.FileExists(config.ProjectDescriptor)
|
||||
projDescPath, err := project.ResolvePath(config.ProjectDescriptor, config.Path, utils)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorConfiguration)
|
||||
return errors.Wrap(err, "failed to check if project descriptor exists")
|
||||
}
|
||||
|
||||
var projectID string
|
||||
if projDescExists {
|
||||
descriptor, err := project.ParseDescriptor(config.ProjectDescriptor, utils, httpClient)
|
||||
if projDescPath != "" {
|
||||
descriptor, err := project.ParseDescriptor(projDescPath, utils, httpClient)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorConfiguration)
|
||||
return errors.Wrapf(err, "failed to parse %s", config.ProjectDescriptor)
|
||||
return errors.Wrapf(err, "failed to parse %s", projDescPath)
|
||||
}
|
||||
addProjectDescriptorTelemetryData(&customTelemetryData, *descriptor)
|
||||
|
||||
@ -412,17 +496,14 @@ func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, u
|
||||
}
|
||||
customTelemetryData.Buildpacks.Overall = config.Buildpacks
|
||||
customTelemetryData.BuildEnv.KeyValues = privacy.FilterEnv(config.BuildEnvVars)
|
||||
telemetry.Data = append(telemetry.Data, customTelemetryData)
|
||||
|
||||
telemetryData.Custom1Label = "cnbBuildStepData"
|
||||
customData, err := json.Marshal(customTelemetryData)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorCustom)
|
||||
return errors.Wrap(err, "failed to marshal custom telemetry data")
|
||||
if commonPipelineEnvironment.container.imageNameTag == "" {
|
||||
commonPipelineEnvironment.container.registryURL = fmt.Sprintf("%s://%s", targetImage.ContainerRegistry.Scheme, targetImage.ContainerRegistry.Host)
|
||||
commonPipelineEnvironment.container.imageNameTag = fmt.Sprintf("%v:%v", targetImage.ContainerImageName, targetImage.ContainerImageTag)
|
||||
}
|
||||
telemetryData.Custom1 = string(customData)
|
||||
|
||||
commonPipelineEnvironment.container.registryURL = fmt.Sprintf("%s://%s", targetImage.ContainerRegistry.Scheme, targetImage.ContainerRegistry.Host)
|
||||
commonPipelineEnvironment.container.imageNameTag = fmt.Sprintf("%v:%v", targetImage.ContainerImageName, targetImage.ContainerImageTag)
|
||||
commonPipelineEnvironment.container.imageNameTags = append(commonPipelineEnvironment.container.imageNameTags, fmt.Sprintf("%v:%v", targetImage.ContainerImageName, targetImage.ContainerImageTag))
|
||||
commonPipelineEnvironment.container.imageNames = append(commonPipelineEnvironment.container.imageNames, targetImage.ContainerImageName)
|
||||
|
||||
if config.BuildEnvVars != nil && len(config.BuildEnvVars) > 0 {
|
||||
log.Entry().Infof("Setting custom environment variables: '%v'", config.BuildEnvVars)
|
||||
@ -451,6 +532,16 @@ func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, u
|
||||
target := "/workspace"
|
||||
|
||||
pathType, source, err := config.resolvePath(utils)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorBuild)
|
||||
return errors.Wrapf(err, "could no resolve path")
|
||||
}
|
||||
|
||||
err = cleanDir(target, utils)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorBuild)
|
||||
return errors.Wrapf(err, "failed to clean up target folder %s", target)
|
||||
}
|
||||
|
||||
if pathType != pathEnumArchive {
|
||||
err = copyProject(source, target, include, exclude, utils)
|
||||
@ -467,13 +558,7 @@ func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, u
|
||||
}
|
||||
|
||||
if ok, _ := utils.FileExists(filepath.Join(target, "pom.xml")); ok {
|
||||
pwd, err := utils.Getwd()
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorBuild)
|
||||
return errors.Wrap(err, "failed to get current working directory")
|
||||
}
|
||||
|
||||
err = linkTargetFolder(utils, pwd, target)
|
||||
err = linkTargetFolder(utils, source, target)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorBuild)
|
||||
return err
|
||||
@ -502,14 +587,6 @@ func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, u
|
||||
return errors.Wrap(err, "failed to generate CNB_REGISTRY_AUTH")
|
||||
}
|
||||
|
||||
if dockerConfigFile != "" {
|
||||
err = utils.FileRemove(dockerConfigFile)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorBuild)
|
||||
return errors.Wrap(err, "failed to remove docker config.json file")
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.CustomTLSCertificateLinks) > 0 {
|
||||
caCertificates := "/tmp/ca-certificates.crt"
|
||||
_, err := utils.Copy("/etc/ssl/certs/ca-certificates.crt", caCertificates)
|
||||
|
@ -18,23 +18,26 @@ import (
|
||||
)
|
||||
|
||||
type cnbBuildOptions struct {
|
||||
ContainerImageName string `json:"containerImageName,omitempty"`
|
||||
ContainerImageTag string `json:"containerImageTag,omitempty"`
|
||||
ContainerRegistryURL string `json:"containerRegistryUrl,omitempty"`
|
||||
Buildpacks []string `json:"buildpacks,omitempty"`
|
||||
BuildEnvVars map[string]interface{} `json:"buildEnvVars,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
ProjectDescriptor string `json:"projectDescriptor,omitempty"`
|
||||
DockerConfigJSON string `json:"dockerConfigJSON,omitempty"`
|
||||
CustomTLSCertificateLinks []string `json:"customTlsCertificateLinks,omitempty"`
|
||||
AdditionalTags []string `json:"additionalTags,omitempty"`
|
||||
Bindings map[string]interface{} `json:"bindings,omitempty"`
|
||||
ContainerImageName string `json:"containerImageName,omitempty"`
|
||||
ContainerImageTag string `json:"containerImageTag,omitempty"`
|
||||
ContainerRegistryURL string `json:"containerRegistryUrl,omitempty"`
|
||||
Buildpacks []string `json:"buildpacks,omitempty"`
|
||||
BuildEnvVars map[string]interface{} `json:"buildEnvVars,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
ProjectDescriptor string `json:"projectDescriptor,omitempty"`
|
||||
DockerConfigJSON string `json:"dockerConfigJSON,omitempty"`
|
||||
CustomTLSCertificateLinks []string `json:"customTlsCertificateLinks,omitempty"`
|
||||
AdditionalTags []string `json:"additionalTags,omitempty"`
|
||||
Bindings map[string]interface{} `json:"bindings,omitempty"`
|
||||
MultipleImages []map[string]interface{} `json:"multipleImages,omitempty"`
|
||||
}
|
||||
|
||||
type cnbBuildCommonPipelineEnvironment struct {
|
||||
container struct {
|
||||
registryURL string
|
||||
imageNameTag string
|
||||
registryURL string
|
||||
imageNameTag string
|
||||
imageNames []string
|
||||
imageNameTags []string
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +49,8 @@ func (p *cnbBuildCommonPipelineEnvironment) persist(path, resourceName string) {
|
||||
}{
|
||||
{category: "container", name: "registryUrl", value: p.container.registryURL},
|
||||
{category: "container", name: "imageNameTag", value: p.container.imageNameTag},
|
||||
{category: "container", name: "imageNames", value: p.container.imageNames},
|
||||
{category: "container", name: "imageNameTags", value: p.container.imageNameTags},
|
||||
}
|
||||
|
||||
errCount := 0
|
||||
@ -160,7 +165,7 @@ func addCnbBuildFlags(cmd *cobra.Command, stepConfig *cnbBuildOptions) {
|
||||
cmd.Flags().StringSliceVar(&stepConfig.Buildpacks, "buildpacks", []string{}, "List of custom buildpacks to use in the form of '$HOSTNAME/$REPO[:$TAG]'.")
|
||||
|
||||
cmd.Flags().StringVar(&stepConfig.Path, "path", os.Getenv("PIPER_path"), "The path should either point to a directory with your sources or an artifact in zip format.\nThis property determines the input to the buildpack.\n")
|
||||
cmd.Flags().StringVar(&stepConfig.ProjectDescriptor, "projectDescriptor", `project.toml`, "Path to the project.toml file.\nSee [buildpacks.io](https://buildpacks.io/docs/reference/config/project-descriptor/) for the reference.\nParameters passed to the cnbBuild step will take precedence over the parameters set in the project.toml file, except the `env` block.\nEnvironment variables declared in a project descriptor file, will be merged with the `buildEnvVars` property, with the `buildEnvVars` having a precedence.\n\nNote: Inline buildpacks (see [specification](https://buildpacks.io/docs/reference/config/project-descriptor/#build-_table-optional_)) are not supported yet.\n")
|
||||
cmd.Flags().StringVar(&stepConfig.ProjectDescriptor, "projectDescriptor", `project.toml`, "Relative path to the project.toml file.\nSee [buildpacks.io](https://buildpacks.io/docs/reference/config/project-descriptor/) for the reference.\nParameters passed to the cnbBuild step will take precedence over the parameters set in the project.toml file, except the `env` block.\nEnvironment variables declared in a project descriptor file, will be merged with the `buildEnvVars` property, with the `buildEnvVars` having a precedence.\n\nNote: The project descriptor path should be relative to what is set in the [path](#path) property. If the `path` property is pointing to a zip archive (e.g. jar file), project descriptor path will be relative to the root of the workspace.\nNote: Inline buildpacks (see [specification](https://buildpacks.io/docs/reference/config/project-descriptor/#build-_table-optional_)) are not supported yet.\n")
|
||||
cmd.Flags().StringVar(&stepConfig.DockerConfigJSON, "dockerConfigJSON", os.Getenv("PIPER_dockerConfigJSON"), "Path to the file `.docker/config.json` - this is typically provided by your CI/CD system. You can find more details about the Docker credentials in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/login/).")
|
||||
cmd.Flags().StringSliceVar(&stepConfig.CustomTLSCertificateLinks, "customTlsCertificateLinks", []string{}, "List containing download links of custom TLS certificates. This is required to ensure trusted connections to registries with custom certificates.")
|
||||
cmd.Flags().StringSliceVar(&stepConfig.AdditionalTags, "additionalTags", []string{}, "List of tags which will be pushed to the registry (additionally to the provided `containerImageTag`), e.g. \"latest\".")
|
||||
@ -311,6 +316,14 @@ func cnbBuildMetadata() config.StepData {
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "multipleImages",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "[]map[string]interface{}",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{{Name: "images"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Containers: []config.Container{
|
||||
@ -324,6 +337,8 @@ func cnbBuildMetadata() config.StepData {
|
||||
Parameters: []map[string]interface{}{
|
||||
{"name": "container/registryUrl"},
|
||||
{"name": "container/imageNameTag"},
|
||||
{"name": "container/imageNames", "type": "[]string"},
|
||||
{"name": "container/imageNameTags", "type": "[]string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -29,10 +29,11 @@ func addBuilderFiles(utils *cnbutils.MockUtils) {
|
||||
utils.FilesMock.AddFile(creatorPath, []byte(`xyz`))
|
||||
}
|
||||
|
||||
func assertLifecycleCalls(t *testing.T, runner *mock.ExecMockRunner) {
|
||||
assert.Equal(t, creatorPath, runner.Calls[0].Exec)
|
||||
func assertLifecycleCalls(t *testing.T, runner *mock.ExecMockRunner, callNo int) {
|
||||
require.GreaterOrEqual(t, len(runner.Calls), callNo)
|
||||
assert.Equal(t, creatorPath, runner.Calls[callNo-1].Exec)
|
||||
for _, arg := range []string{"-no-color", "-buildpacks", "/cnb/buildpacks", "-order", "/cnb/order.toml", "-platform", "/tmp/platform"} {
|
||||
assert.Contains(t, runner.Calls[0].Params, arg)
|
||||
assert.Contains(t, runner.Calls[callNo-1].Params, arg)
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,12 +59,12 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils.FilesMock.AddFile("project.toml", []byte(projectToml))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
runner := utils.ExecMockRunner
|
||||
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
|
||||
assertLifecycleCalls(t, runner)
|
||||
assertLifecycleCalls(t, runner, 1)
|
||||
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
|
||||
assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
|
||||
assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
|
||||
@ -89,21 +90,22 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
telemetryData := telemetry.CustomData{}
|
||||
err := runCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
err := callCnbBuild(&config, &telemetryData, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
|
||||
require.NoError(t, err)
|
||||
runner := utils.ExecMockRunner
|
||||
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
|
||||
assertLifecycleCalls(t, runner)
|
||||
assertLifecycleCalls(t, runner, 1)
|
||||
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, "io-buildpacks-my-app", config.ContainerImageTag))
|
||||
assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
|
||||
assert.Equal(t, "io-buildpacks-my-app:0.0.1", commonPipelineEnvironment.container.imageNameTag)
|
||||
|
||||
customDataAsString := telemetryData.Custom1
|
||||
customData := cnbBuildTelemetryData{}
|
||||
customData := cnbBuildTelemetry{}
|
||||
err = json.Unmarshal([]byte(customDataAsString), &customData)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "root", string(customData.Path))
|
||||
assert.Equal(t, 1, len(customData.Data))
|
||||
assert.Equal(t, "root", string(customData.Data[0].Path))
|
||||
})
|
||||
|
||||
t.Run("success case (registry with https)", func(t *testing.T) {
|
||||
@ -120,12 +122,12 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
runner := utils.ExecMockRunner
|
||||
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
|
||||
assertLifecycleCalls(t, runner)
|
||||
assertLifecycleCalls(t, runner, 1)
|
||||
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", imageRegistry, config.ContainerImageName, config.ContainerImageTag))
|
||||
assert.Equal(t, config.ContainerRegistryURL, commonPipelineEnvironment.container.registryURL)
|
||||
assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
|
||||
@ -145,12 +147,12 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
runner := utils.ExecMockRunner
|
||||
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
|
||||
assertLifecycleCalls(t, runner)
|
||||
assertLifecycleCalls(t, runner, 1)
|
||||
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
|
||||
assert.Equal(t, fmt.Sprintf("https://%s", config.ContainerRegistryURL), commonPipelineEnvironment.container.registryURL)
|
||||
assert.Equal(t, "my-image:0.0.1", commonPipelineEnvironment.container.imageNameTag)
|
||||
@ -175,9 +177,9 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
runner := utils.ExecMockRunner
|
||||
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
|
||||
assert.Equal(t, creatorPath, runner.Calls[0].Exec)
|
||||
@ -190,7 +192,7 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
renamedFileExists, _ := utils.FileExists("/path/to/config.json")
|
||||
|
||||
assert.False(t, initialFileExists)
|
||||
assert.False(t, renamedFileExists)
|
||||
assert.True(t, renamedFileExists)
|
||||
})
|
||||
|
||||
t.Run("success case (customTlsCertificates)", func(t *testing.T) {
|
||||
@ -218,18 +220,18 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, client)
|
||||
assert.NoError(t, err)
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, client)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := utils.FilesMock.FileRead(caCertsTmpFile)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "test\ntestCert\ntestCert\n", string(result))
|
||||
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
runner := utils.ExecMockRunner
|
||||
assert.Contains(t, runner.Env, "CNB_REGISTRY_AUTH={\"my-registry\":\"Basic dXNlcjpwYXNz\"}")
|
||||
assert.Contains(t, runner.Env, fmt.Sprintf("SSL_CERT_FILE=%s", caCertsTmpFile))
|
||||
assertLifecycleCalls(t, runner)
|
||||
assertLifecycleCalls(t, runner, 1)
|
||||
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
|
||||
})
|
||||
|
||||
@ -248,11 +250,11 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
assert.NoError(t, err)
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
require.NoError(t, err)
|
||||
|
||||
runner := utils.ExecMockRunner
|
||||
assertLifecycleCalls(t, runner)
|
||||
assertLifecycleCalls(t, runner, 1)
|
||||
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, config.ContainerImageName, config.ContainerImageTag))
|
||||
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:3", config.ContainerRegistryURL, config.ContainerImageName))
|
||||
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:3.1", config.ContainerRegistryURL, config.ContainerImageName))
|
||||
@ -275,11 +277,11 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils.FilesMock.AddFile("/workspace/pom.xml", []byte("test"))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
|
||||
assert.NoError(t, err)
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
|
||||
require.NoError(t, err)
|
||||
|
||||
runner := utils.ExecMockRunner
|
||||
assertLifecycleCalls(t, runner)
|
||||
assertLifecycleCalls(t, runner, 1)
|
||||
|
||||
assert.True(t, utils.FilesMock.HasCreatedSymlink("/jenkins/target", "/workspace/target"))
|
||||
})
|
||||
@ -299,11 +301,11 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
|
||||
assert.NoError(t, err)
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
|
||||
require.NoError(t, err)
|
||||
|
||||
runner := utils.ExecMockRunner
|
||||
assertLifecycleCalls(t, runner)
|
||||
assertLifecycleCalls(t, runner, 1)
|
||||
|
||||
assert.False(t, utils.FilesMock.HasCreatedSymlink("/jenkins/target", "/workspace/target"))
|
||||
})
|
||||
@ -322,7 +324,7 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":"dXNlcjpwYXNz"}}`))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
assert.EqualError(t, err, "failed to generate CNB_REGISTRY_AUTH: json: cannot unmarshal string into Go struct field ConfigFile.auths of type types.AuthConfig")
|
||||
})
|
||||
|
||||
@ -339,7 +341,7 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils := newCnbBuildTestsUtils()
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
assert.EqualError(t, err, "failed to generate CNB_REGISTRY_AUTH: could not read 'not-there/config.json'")
|
||||
})
|
||||
|
||||
@ -356,7 +358,7 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils := newCnbBuildTestsUtils()
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
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")
|
||||
})
|
||||
|
||||
@ -367,7 +369,7 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
|
||||
utils := newCnbBuildTestsUtils()
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
assert.EqualError(t, err, "the provided dockerImage is not a valid builder: binary '/cnb/lifecycle/creator' not found")
|
||||
})
|
||||
|
||||
@ -387,7 +389,7 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
err := callCnbBuild(&config, &telemetry.CustomData{}, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
assert.EqualError(t, err, "failed to copy certificates: cannot copy '/etc/ssl/certs/ca-certificates.crt': file does not exist")
|
||||
})
|
||||
|
||||
@ -409,7 +411,8 @@ func TestRunCnbBuild(t *testing.T) {
|
||||
|
||||
utils := newCnbBuildTestsUtils()
|
||||
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||
utils.FilesMock.AddFile("project.toml", []byte(`[project]
|
||||
utils.FilesMock.AddDir("target")
|
||||
utils.FilesMock.AddFile("target/project.toml", []byte(`[project]
|
||||
id = "test"
|
||||
name = "test"
|
||||
version = "1.0.0"
|
||||
@ -421,35 +424,36 @@ exclude = ["*.tar"]
|
||||
[[build.buildpacks]]
|
||||
uri = "some-buildpack"`))
|
||||
utils.FilesMock.AddFile("a_file", []byte(`{}`))
|
||||
utils.FilesMock.AddDir("target")
|
||||
utils.FilesMock.AddFile("target/somelib.jar", []byte(`FFFFFF`))
|
||||
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
telemetryData := telemetry.CustomData{}
|
||||
err := runCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
assert.NoError(t, err)
|
||||
cnbTelemetry := cnbBuildTelemetry{Version: 2}
|
||||
err := callCnbBuild(&config, &telemetryData, &cnbTelemetry, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
require.NoError(t, err)
|
||||
|
||||
customDataAsString := telemetryData.Custom1
|
||||
customData := cnbBuildTelemetryData{}
|
||||
customData := cnbBuildTelemetry{}
|
||||
err = json.Unmarshal([]byte(customDataAsString), &customData)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, customData.Version)
|
||||
assert.Equal(t, "3.1.5", customData.ImageTag)
|
||||
assert.Equal(t, "folder", string(customData.Path))
|
||||
assert.Contains(t, customData.AdditionalTags, "latest")
|
||||
assert.Contains(t, customData.BindingKeys, "SECRET")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, customData.Version)
|
||||
require.Equal(t, 1, len(customData.Data))
|
||||
assert.Equal(t, "3.1.5", customData.Data[0].ImageTag)
|
||||
assert.Equal(t, "folder", string(customData.Data[0].Path))
|
||||
assert.Contains(t, customData.Data[0].AdditionalTags, "latest")
|
||||
assert.Contains(t, customData.Data[0].BindingKeys, "SECRET")
|
||||
|
||||
assert.Contains(t, customData.Buildpacks.FromConfig, "paketobuildpacks/java")
|
||||
assert.NotContains(t, customData.Buildpacks.FromProjectDescriptor, "paketobuildpacks/java")
|
||||
assert.Contains(t, customData.Buildpacks.FromProjectDescriptor, "<redacted>")
|
||||
assert.NotContains(t, customData.Buildpacks.Overall, "<redacted>")
|
||||
assert.Contains(t, customData.Buildpacks.Overall, "paketobuildpacks/java")
|
||||
assert.Contains(t, customData.Data[0].Buildpacks.FromConfig, "paketobuildpacks/java")
|
||||
assert.NotContains(t, customData.Data[0].Buildpacks.FromProjectDescriptor, "paketobuildpacks/java")
|
||||
assert.Contains(t, customData.Data[0].Buildpacks.FromProjectDescriptor, "<redacted>")
|
||||
assert.NotContains(t, customData.Data[0].Buildpacks.Overall, "<redacted>")
|
||||
assert.Contains(t, customData.Data[0].Buildpacks.Overall, "paketobuildpacks/java")
|
||||
|
||||
assert.True(t, customData.ProjectDescriptor.Used)
|
||||
assert.False(t, customData.ProjectDescriptor.IncludeUsed)
|
||||
assert.True(t, customData.ProjectDescriptor.ExcludeUsed)
|
||||
assert.True(t, customData.Data[0].ProjectDescriptor.Used)
|
||||
assert.False(t, customData.Data[0].ProjectDescriptor.IncludeUsed)
|
||||
assert.True(t, customData.Data[0].ProjectDescriptor.ExcludeUsed)
|
||||
})
|
||||
|
||||
t.Run("success case (build env telemetry was added)", func(t *testing.T) {
|
||||
@ -486,27 +490,70 @@ uri = "some-buildpack"
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
telemetryData := telemetry.CustomData{}
|
||||
err := runCnbBuild(&config, &telemetryData, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
assert.NoError(t, err)
|
||||
cnbTelemetry := cnbBuildTelemetry{Version: 2}
|
||||
err := callCnbBuild(&config, &telemetryData, &cnbTelemetry, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
require.NoError(t, err)
|
||||
|
||||
customDataAsString := telemetryData.Custom1
|
||||
customData := cnbBuildTelemetryData{}
|
||||
customData := cnbBuildTelemetry{}
|
||||
err = json.Unmarshal([]byte(customDataAsString), &customData)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, customData.BuildEnv.KeysFromConfig, "CONFIG_KEY")
|
||||
assert.NotContains(t, customData.BuildEnv.KeysFromProjectDescriptor, "CONFIG_KEY")
|
||||
assert.Contains(t, customData.BuildEnv.KeysOverall, "CONFIG_KEY")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(customData.Data))
|
||||
assert.Contains(t, customData.Data[0].BuildEnv.KeysFromConfig, "CONFIG_KEY")
|
||||
assert.NotContains(t, customData.Data[0].BuildEnv.KeysFromProjectDescriptor, "CONFIG_KEY")
|
||||
assert.Contains(t, customData.Data[0].BuildEnv.KeysOverall, "CONFIG_KEY")
|
||||
|
||||
assert.NotContains(t, customData.BuildEnv.KeysFromConfig, "PROJECT_KEY")
|
||||
assert.Contains(t, customData.BuildEnv.KeysFromProjectDescriptor, "PROJECT_KEY")
|
||||
assert.Contains(t, customData.BuildEnv.KeysOverall, "PROJECT_KEY")
|
||||
assert.NotContains(t, customData.Data[0].BuildEnv.KeysFromConfig, "PROJECT_KEY")
|
||||
assert.Contains(t, customData.Data[0].BuildEnv.KeysFromProjectDescriptor, "PROJECT_KEY")
|
||||
assert.Contains(t, customData.Data[0].BuildEnv.KeysOverall, "PROJECT_KEY")
|
||||
|
||||
assert.Equal(t, "8", customData.BuildEnv.KeyValues["BP_JVM_VERSION"])
|
||||
assert.Equal(t, "11", customData.BuildEnv.KeyValues["BP_NODE_VERSION"])
|
||||
assert.NotContains(t, customData.BuildEnv.KeyValues, "PROJECT_KEY")
|
||||
assert.Equal(t, "8", customData.Data[0].BuildEnv.KeyValues["BP_JVM_VERSION"])
|
||||
assert.Equal(t, "11", customData.Data[0].BuildEnv.KeyValues["BP_NODE_VERSION"])
|
||||
assert.NotContains(t, customData.Data[0].BuildEnv.KeyValues, "PROJECT_KEY")
|
||||
|
||||
assert.Contains(t, customData.Buildpacks.Overall, "some-buildpack")
|
||||
assert.Contains(t, customData.Data[0].Buildpacks.Overall, "some-buildpack")
|
||||
})
|
||||
|
||||
t.Run("success case (multiple images configured)", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
|
||||
config := cnbBuildOptions{
|
||||
ContainerImageTag: "3.1.5",
|
||||
ContainerRegistryURL: imageRegistry,
|
||||
DockerConfigJSON: "/path/to/my-config.json",
|
||||
AdditionalTags: []string{"3", "3.1", "3.1", "3.1.5"},
|
||||
MultipleImages: []map[string]interface{}{{"ContainerImageName": "my-image-0"}, {"ContainerImageName": "my-image-1"}},
|
||||
}
|
||||
|
||||
expectedImageCount := len(config.MultipleImages)
|
||||
|
||||
utils := newCnbBuildTestsUtils()
|
||||
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||
addBuilderFiles(&utils)
|
||||
|
||||
telemetryData := telemetry.CustomData{}
|
||||
err := callCnbBuild(&config, &telemetryData, &cnbBuildTelemetry{}, &utils, &commonPipelineEnvironment, &piperhttp.Client{})
|
||||
require.NoError(t, err)
|
||||
|
||||
customDataAsString := telemetryData.Custom1
|
||||
customData := cnbBuildTelemetry{}
|
||||
err = json.Unmarshal([]byte(customDataAsString), &customData)
|
||||
require.Equal(t, expectedImageCount, len(customData.Data))
|
||||
|
||||
runner := utils.ExecMockRunner
|
||||
require.Equal(t, expectedImageCount, len(runner.Calls))
|
||||
for i, call := range runner.Calls {
|
||||
assert.Equal(t, 4, len(customData.Data[i].AdditionalTags))
|
||||
assertLifecycleCalls(t, runner, i+1)
|
||||
containerImageName := fmt.Sprintf("my-image-%d", i)
|
||||
assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:%s", config.ContainerRegistryURL, containerImageName, config.ContainerImageTag))
|
||||
assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3", config.ContainerRegistryURL, containerImageName))
|
||||
assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3.1", config.ContainerRegistryURL, containerImageName))
|
||||
assert.Contains(t, call.Params, fmt.Sprintf("%s/%s:3.1.5", config.ContainerRegistryURL, containerImageName))
|
||||
}
|
||||
|
||||
assert.Equal(t, "my-image-0:3.1.5", commonPipelineEnvironment.container.imageNameTag)
|
||||
})
|
||||
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@ -30,6 +30,7 @@ require (
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0
|
||||
github.com/hashicorp/vault v1.8.5
|
||||
github.com/hashicorp/vault/api v1.1.2-0.20210713235431-1fc8af4c041f
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.5.1
|
||||
github.com/jarcoal/httpmock v1.0.8
|
||||
github.com/magiconair/properties v1.8.5
|
||||
|
@ -124,7 +124,7 @@ func TestNonZipPath(t *testing.T) {
|
||||
|
||||
container.whenRunningPiperCommand("cnbBuild", "--containerImageName", "not-found", "--containerImageTag", "0.0.1", "--containerRegistryUrl", registryURL, "--path", "mta.yaml")
|
||||
|
||||
container.assertHasOutput(t, "Copying 'mta.yaml' into '/workspace' failed: application path must be a directory or zip")
|
||||
container.assertHasOutput(t, "Copying '/project/mta.yaml' into '/workspace' failed: application path must be a directory or zip")
|
||||
container.terminate(t)
|
||||
}
|
||||
|
||||
@ -211,3 +211,25 @@ func TestBindings(t *testing.T) {
|
||||
container.assertHasFile(t, "/tmp/platform/bindings/dummy-binding/dummy.yml")
|
||||
container.terminate(t)
|
||||
}
|
||||
|
||||
func TestMultiImage(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
registryContainer := setupDockerRegistry(t, ctx)
|
||||
defer registryContainer.Terminate(ctx)
|
||||
|
||||
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
|
||||
Image: "paketobuildpacks/builder:full",
|
||||
User: "cnb",
|
||||
TestDir: []string{"testdata", "TestCnbIntegration"},
|
||||
Network: fmt.Sprintf("container:%s", registryContainer.GetContainerID()),
|
||||
})
|
||||
|
||||
container.whenRunningPiperCommand("cnbBuild", "--customConfig", "config_multi_image.yml")
|
||||
|
||||
container.assertHasOutput(t, "Previous image with name \"localhost:5000/io-buildpacks-my-app:latest\" not found")
|
||||
container.assertHasOutput(t, "Saving localhost:5000/io-buildpacks-my-app:latest...")
|
||||
container.assertHasOutput(t, "Previous image with name \"localhost:5000/go-app:v1.0.0\" not found")
|
||||
container.assertHasOutput(t, "Saving localhost:5000/go-app:v1.0.0...")
|
||||
container.terminate(t)
|
||||
}
|
||||
|
9
integration/testdata/TestCnbIntegration/config_multi_image.yml
vendored
Normal file
9
integration/testdata/TestCnbIntegration/config_multi_image.yml
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
steps:
|
||||
cnbBuild:
|
||||
containerRegistryUrl: localhost:5000
|
||||
containerImageTag: latest
|
||||
multipleImages:
|
||||
- path: project
|
||||
- containerImageName: go-app
|
||||
containerImageTag: v1.0.0
|
||||
path: zip/go.zip
|
42
pkg/cnbutils/project/resolve_path.go
Normal file
42
pkg/cnbutils/project/resolve_path.go
Normal file
@ -0,0 +1,42 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/cnbutils"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
)
|
||||
|
||||
// 1. If "path" is a directory, check for "${path}/${descriptor}"
|
||||
// 2. If "path" is a file, check for "${PWD}/${descriptor}"
|
||||
func ResolvePath(descriptor, path string, utils cnbutils.BuildUtils) (string, error) {
|
||||
isDir, err := utils.DirExists(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if isDir {
|
||||
return getFilename(path, descriptor, utils)
|
||||
}
|
||||
|
||||
pwd, err := utils.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return getFilename(pwd, descriptor, utils)
|
||||
}
|
||||
|
||||
func getFilename(folder, filename string, utils cnbutils.BuildUtils) (string, error) {
|
||||
descPath := filepath.Join(folder, filename)
|
||||
exists, err := utils.FileExists(descPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if exists {
|
||||
return descPath, nil
|
||||
}
|
||||
|
||||
log.Entry().Infof("Project descriptor with the path '%s' was not found", descPath)
|
||||
return "", nil
|
||||
}
|
63
pkg/cnbutils/project/resolve_path_test.go
Normal file
63
pkg/cnbutils/project/resolve_path_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
package project_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/cnbutils"
|
||||
"github.com/SAP/jenkins-library/pkg/cnbutils/project"
|
||||
"github.com/SAP/jenkins-library/pkg/mock"
|
||||
)
|
||||
|
||||
func TestResolvePath(t *testing.T) {
|
||||
t.Run("project descriptor and no path is maintained, it is located in the root", func(t *testing.T) {
|
||||
utils := &cnbutils.MockUtils{
|
||||
FilesMock: &mock.FilesMock{},
|
||||
}
|
||||
utils.AddFile("project.toml", []byte(""))
|
||||
|
||||
location, err := project.ResolvePath("project.toml", "", utils)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "project.toml", location)
|
||||
})
|
||||
|
||||
t.Run("project descriptor and path is is a file, it is located in the root", func(t *testing.T) {
|
||||
utils := &cnbutils.MockUtils{
|
||||
FilesMock: &mock.FilesMock{},
|
||||
}
|
||||
utils.CurrentDir = "/workdir"
|
||||
utils.AddFile("project.toml", []byte(""))
|
||||
utils.AddFile("test/file.zip", []byte(""))
|
||||
|
||||
location, err := project.ResolvePath("project.toml", "test/file.zip", utils)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "/workdir/project.toml", location)
|
||||
})
|
||||
|
||||
t.Run("project descriptor and path is is a dir, it is located in the path", func(t *testing.T) {
|
||||
utils := &cnbutils.MockUtils{
|
||||
FilesMock: &mock.FilesMock{},
|
||||
}
|
||||
utils.AddFile("test/project.toml", []byte(""))
|
||||
|
||||
location, err := project.ResolvePath("project.toml", "test", utils)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, filepath.Join("test", "project.toml"), location)
|
||||
})
|
||||
|
||||
t.Run("project descriptor does not exist, empty string", func(t *testing.T) {
|
||||
utils := &cnbutils.MockUtils{
|
||||
FilesMock: &mock.FilesMock{},
|
||||
}
|
||||
location, err := project.ResolvePath("project.toml", "", utils)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "", location)
|
||||
})
|
||||
}
|
@ -37,7 +37,7 @@ type stepInfo struct {
|
||||
Secrets []config.StepSecrets
|
||||
}
|
||||
|
||||
//StepGoTemplate ...
|
||||
// StepGoTemplate ...
|
||||
const stepGoTemplate = `// Code generated by piper's step-generator. DO NOT EDIT.
|
||||
|
||||
package cmd
|
||||
@ -167,10 +167,10 @@ func {{.CobraCmdFuncName}}() *cobra.Command {
|
||||
handler := func() {
|
||||
{{- range $notused, $oRes := .OutputResources }}
|
||||
{{ index $oRes "name" }}.persist(
|
||||
{{- if eq (index $oRes "type") "reports" -}}stepConfig,
|
||||
{{- if $.ExportPrefix}}{{ $.ExportPrefix }}.{{end}}GeneralConfig.GCPJsonKeyFilePath,
|
||||
{{- if $.ExportPrefix}}{{ $.ExportPrefix }}.{{end}}GeneralConfig.GCSBucketId,
|
||||
{{- if $.ExportPrefix}}{{ $.ExportPrefix }}.{{end}}GeneralConfig.GCSFolderPath,
|
||||
{{- if eq (index $oRes "type") "reports" -}}stepConfig,
|
||||
{{- if $.ExportPrefix}}{{ $.ExportPrefix }}.{{end}}GeneralConfig.GCPJsonKeyFilePath,
|
||||
{{- if $.ExportPrefix}}{{ $.ExportPrefix }}.{{end}}GeneralConfig.GCSBucketId,
|
||||
{{- if $.ExportPrefix}}{{ $.ExportPrefix }}.{{end}}GeneralConfig.GCSFolderPath,
|
||||
{{- if $.ExportPrefix}}{{ $.ExportPrefix }}.{{end}}GeneralConfig.GCSSubFolder
|
||||
{{- else -}}
|
||||
{{if $.ExportPrefix}}{{ $.ExportPrefix }}.{{end}}GeneralConfig.EnvRootPath, {{ index $oRes "name" | quote }}{{- end -}}
|
||||
@ -330,7 +330,7 @@ func {{ .StepName }}Metadata() config.StepData {
|
||||
}
|
||||
`
|
||||
|
||||
//StepTestGoTemplate ...
|
||||
// StepTestGoTemplate ...
|
||||
const stepTestGoTemplate = `package cmd
|
||||
|
||||
import (
|
||||
@ -574,7 +574,7 @@ func ProcessMetaFiles(metadataFiles []string, targetDir string, stepHelperData S
|
||||
}
|
||||
|
||||
func setDefaultParameters(stepData *config.StepData) (bool, error) {
|
||||
//ToDo: custom function for default handling, support all relevant parameter types
|
||||
// ToDo: custom function for default handling, support all relevant parameter types
|
||||
osImportRequired := false
|
||||
for k, param := range stepData.Spec.Inputs.Parameters {
|
||||
|
||||
@ -591,7 +591,7 @@ func setDefaultParameters(stepData *config.StepData) (bool, error) {
|
||||
case "[]string":
|
||||
// ToDo: Check if default should be read from env
|
||||
param.Default = "[]string{}"
|
||||
case "map[string]interface{}":
|
||||
case "map[string]interface{}", "[]map[string]interface{}":
|
||||
// Currently we don't need to set a default here since in this case the default
|
||||
// is never used. Needs to be changed in case we enable cli parameter handling
|
||||
// for that type.
|
||||
@ -612,7 +612,7 @@ func setDefaultParameters(stepData *config.StepData) (bool, error) {
|
||||
param.Default = fmt.Sprintf("`%v`", param.Default)
|
||||
case "[]string":
|
||||
param.Default = fmt.Sprintf("[]string{`%v`}", strings.Join(getStringSliceFromInterface(param.Default), "`, `"))
|
||||
case "map[string]interface{}":
|
||||
case "map[string]interface{}", "[]map[string]interface{}":
|
||||
// Currently we don't need to set a default here since in this case the default
|
||||
// is never used. Needs to be changed in case we enable cli parameter handling
|
||||
// for that type.
|
||||
@ -771,7 +771,7 @@ func MetadataFiles(sourceDirectory string) ([]string, error) {
|
||||
}
|
||||
|
||||
func isCLIParam(myType string) bool {
|
||||
return myType != "map[string]interface{}"
|
||||
return myType != "map[string]interface{}" && myType != "[]map[string]interface{}"
|
||||
}
|
||||
|
||||
func stepTemplate(myStepInfo stepInfo, templateName, goTemplate string) []byte {
|
||||
@ -812,7 +812,7 @@ func longName(long string) string {
|
||||
}
|
||||
|
||||
func resourceFieldType(fieldType string) string {
|
||||
//TODO: clarify why fields are initialized with <nil> and tags are initialized with ''
|
||||
// TODO: clarify why fields are initialized with <nil> and tags are initialized with ''
|
||||
if len(fieldType) == 0 || fieldType == "<nil>" {
|
||||
return "string"
|
||||
}
|
||||
|
@ -113,11 +113,12 @@ spec:
|
||||
- name: projectDescriptor
|
||||
type: string
|
||||
description: |
|
||||
Path to the project.toml file.
|
||||
Relative path to the project.toml file.
|
||||
See [buildpacks.io](https://buildpacks.io/docs/reference/config/project-descriptor/) for the reference.
|
||||
Parameters passed to the cnbBuild step will take precedence over the parameters set in the project.toml file, except the `env` block.
|
||||
Environment variables declared in a project descriptor file, will be merged with the `buildEnvVars` property, with the `buildEnvVars` having a precedence.
|
||||
|
||||
Note: The project descriptor path should be relative to what is set in the [path](#path) property. If the `path` property is pointing to a zip archive (e.g. jar file), project descriptor path will be relative to the root of the workspace.
|
||||
Note: Inline buildpacks (see [specification](https://buildpacks.io/docs/reference/config/project-descriptor/#build-_table-optional_)) are not supported yet.
|
||||
default: project.toml
|
||||
scope:
|
||||
@ -188,6 +189,36 @@ spec:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: multipleImages
|
||||
aliases:
|
||||
- name: images
|
||||
type: "[]map[string]interface{}"
|
||||
description: |
|
||||
This parameter is only needed if `cnbBuild` should create multiple images.
|
||||
Otherwise it can be ignored!!!
|
||||
|
||||
In case of multiple images, this array contains one entry for each image. That
|
||||
entry can override any parameter from the main section, e.g.
|
||||
|
||||
```yaml
|
||||
containerImageTag: latest
|
||||
containerRegistryUrl: docker.io/example
|
||||
dockerConfigJsonCredentialsId: CREDENTIALS
|
||||
multipleImages:
|
||||
- containerImageName: java-app
|
||||
buildpacks:
|
||||
- "java"
|
||||
path: "source/java"
|
||||
- containerImageName: nodejs-app
|
||||
containerImageTag: v1.0.0
|
||||
buildpacks:
|
||||
- "nodejs"
|
||||
path: "source/nodejs"
|
||||
```
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
outputs:
|
||||
resources:
|
||||
- name: commonPipelineEnvironment
|
||||
@ -195,5 +226,9 @@ spec:
|
||||
params:
|
||||
- name: container/registryUrl
|
||||
- name: container/imageNameTag
|
||||
- name: container/imageNames
|
||||
type: "[]string"
|
||||
- name: container/imageNameTags
|
||||
type: "[]string"
|
||||
containers:
|
||||
- image: "paketobuildpacks/builder:full"
|
||||
|
Loading…
Reference in New Issue
Block a user