mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-02-21 19:48:53 +02:00
feat(kanikoExecute): allow building multiple images (#3443)
* feat(kanikoExecute): allow building multiple images * enhance tests * chore: allow running tests in parallel * small fixes * fix: fix destination bug * update formatting and defaults * fix yml formatting * chore: change cpe parameter names * chore: improve variable naming
This commit is contained in:
parent
56726d96f1
commit
2ae1d9dac1
@ -2,7 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/buildsettings"
|
||||
@ -52,9 +52,11 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus
|
||||
|
||||
// prepare kaniko container for running with proper Docker config.json and custom certificates
|
||||
// custom certificates will be downloaded and appended to ca-certificates.crt file used in container
|
||||
prepCommand := strings.Split(config.ContainerPreparationCommand, " ")
|
||||
if err := execRunner.RunExecutable(prepCommand[0], prepCommand[1:]...); err != nil {
|
||||
return errors.Wrap(err, "failed to initialize Kaniko container")
|
||||
if len(config.ContainerPreparationCommand) > 0 {
|
||||
prepCommand := strings.Split(config.ContainerPreparationCommand, " ")
|
||||
if err := execRunner.RunExecutable(prepCommand[0], prepCommand[1:]...); err != nil {
|
||||
return errors.Wrap(err, "failed to initialize Kaniko container")
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.CustomTLSCertificateLinks) > 0 {
|
||||
@ -66,6 +68,39 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus
|
||||
log.Entry().Info("skipping updation of certificates")
|
||||
}
|
||||
|
||||
dockerConfig := []byte(`{"auths":{}}`)
|
||||
if len(config.DockerConfigJSON) > 0 {
|
||||
var err error
|
||||
dockerConfig, err = fileUtils.FileRead(config.DockerConfigJSON)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read file '%v'", config.DockerConfigJSON)
|
||||
}
|
||||
}
|
||||
|
||||
if err := fileUtils.FileWrite("/kaniko/.docker/config.json", dockerConfig, 0644); err != nil {
|
||||
return errors.Wrap(err, "failed to write file '/kaniko/.docker/config.json'")
|
||||
}
|
||||
|
||||
log.Entry().Debugf("preparing build settings information...")
|
||||
stepName := "kanikoExecute"
|
||||
// ToDo: better testability required. So far retrieval of config is rather non deterministic
|
||||
dockerImage, err := getDockerImageValue(stepName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve dockerImage configuration: %w", err)
|
||||
}
|
||||
|
||||
kanikoConfig := buildsettings.BuildOptions{
|
||||
DockerImage: dockerImage,
|
||||
BuildSettingsInfo: config.BuildSettingsInfo,
|
||||
}
|
||||
|
||||
log.Entry().Debugf("creating build settings information...")
|
||||
buildSettingsInfo, err := buildsettings.CreateBuildSettingsInfo(&kanikoConfig, stepName)
|
||||
if err != nil {
|
||||
log.Entry().Warnf("failed to create build settings info: %v", err)
|
||||
}
|
||||
commonPipelineEnvironment.custom.buildSettingsInfo = buildSettingsInfo
|
||||
|
||||
if !piperutils.ContainsString(config.BuildOptions, "--destination") {
|
||||
dest := []string{"--no-push"}
|
||||
if len(config.ContainerRegistryURL) > 0 && len(config.ContainerImageName) > 0 && len(config.ContainerImageTag) > 0 {
|
||||
@ -74,11 +109,51 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus
|
||||
log.SetErrorCategory(log.ErrorConfiguration)
|
||||
return errors.Wrapf(err, "failed to read registry url %v", config.ContainerRegistryURL)
|
||||
}
|
||||
containerImageTag := fmt.Sprintf("%v:%v", config.ContainerImageName, strings.ReplaceAll(config.ContainerImageTag, "+", "-"))
|
||||
dest = []string{"--destination", fmt.Sprintf("%v/%v", containerRegistry, containerImageTag)}
|
||||
|
||||
commonPipelineEnvironment.container.registryURL = config.ContainerRegistryURL
|
||||
commonPipelineEnvironment.container.imageNameTag = containerImageTag
|
||||
|
||||
// Docker image tags don't allow plus signs in tags, thus replacing with dash
|
||||
containerImageTag := strings.ReplaceAll(config.ContainerImageTag, "+", "-")
|
||||
|
||||
if config.ContainerMultiImageBuild {
|
||||
log.Entry().Debugf("Multi-image build activated for image name '%v'", config.ContainerImageName)
|
||||
imageListWithFilePath, err := docker.ImageListWithFilePath(config.ContainerImageName, config.ContainerMultiImageBuildExcludes, fileUtils)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to identify image list for multi image build: %w", err)
|
||||
}
|
||||
if len(imageListWithFilePath) == 0 {
|
||||
return fmt.Errorf("no docker files to process, please check exclude list")
|
||||
}
|
||||
for image, file := range imageListWithFilePath {
|
||||
log.Entry().Debugf("Building image '%v' using file '%v'", image, file)
|
||||
containerImageNameAndTag := fmt.Sprintf("%v:%v", image, containerImageTag)
|
||||
dest = []string{"--destination", fmt.Sprintf("%v/%v", containerRegistry, containerImageNameAndTag)}
|
||||
buildOpts := append(config.BuildOptions, dest...)
|
||||
err = runKaniko(file, buildOpts, execRunner)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build image '%v' using '%v': %w", image, file, err)
|
||||
}
|
||||
commonPipelineEnvironment.container.imageNames = append(commonPipelineEnvironment.container.imageNames, image)
|
||||
commonPipelineEnvironment.container.imageNameTags = append(commonPipelineEnvironment.container.imageNameTags, containerImageNameAndTag)
|
||||
}
|
||||
|
||||
// for compatibility reasons also fill single imageNameTag field with "root" image in commonPipelineEnvironment
|
||||
// only consider if it has been built
|
||||
// ToDo: reconsider and possibly remove at a later point
|
||||
if len(imageListWithFilePath[config.ContainerImageName]) > 0 {
|
||||
containerImageNameAndTag := fmt.Sprintf("%v:%v", config.ContainerImageName, containerImageTag)
|
||||
commonPipelineEnvironment.container.imageNameTag = containerImageNameAndTag
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Entry().Debugf("Single image build for image name '%v'", config.ContainerImageName)
|
||||
containerImageNameAndTag := fmt.Sprintf("%v:%v", config.ContainerImageName, containerImageTag)
|
||||
dest = []string{"--destination", fmt.Sprintf("%v/%v", containerRegistry, containerImageNameAndTag)}
|
||||
commonPipelineEnvironment.container.imageNameTag = containerImageNameAndTag
|
||||
} else if len(config.ContainerImage) > 0 {
|
||||
log.Entry().Debugf("Single image build for image '%v'", config.ContainerImage)
|
||||
containerRegistry, err := docker.ContainerRegistryFromImage(config.ContainerImage)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorConfiguration)
|
||||
@ -91,45 +166,19 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus
|
||||
commonPipelineEnvironment.container.imageNameTag = containerImageNameTag
|
||||
}
|
||||
config.BuildOptions = append(config.BuildOptions, dest...)
|
||||
} else {
|
||||
log.Entry().Infof("Running Kaniko build with destination defined via buildOptions: %v", config.BuildOptions)
|
||||
}
|
||||
|
||||
dockerConfig := []byte(`{"auths":{}}`)
|
||||
if len(config.DockerConfigJSON) > 0 {
|
||||
var err error
|
||||
dockerConfig, err = fileUtils.FileRead(config.DockerConfigJSON)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read file '%v'", config.DockerConfigJSON)
|
||||
}
|
||||
}
|
||||
// no support for building multiple containers
|
||||
return runKaniko(config.DockerfilePath, config.BuildOptions, execRunner)
|
||||
}
|
||||
|
||||
log.Entry().Debugf("creating build settings information...")
|
||||
stepName := "kanikoExecute"
|
||||
dockerImage, err := getDockerImageValue(stepName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func runKaniko(dockerFilepath string, buildOptions []string, execRunner command.ExecRunner) error {
|
||||
kanikoOpts := []string{"--dockerfile", dockerFilepath, "--context", filepath.Dir(dockerFilepath)}
|
||||
kanikoOpts = append(kanikoOpts, buildOptions...)
|
||||
|
||||
kanikoConfig := buildsettings.BuildOptions{
|
||||
DockerImage: dockerImage,
|
||||
}
|
||||
buildSettingsInfo, err := buildsettings.CreateBuildSettingsInfo(&kanikoConfig, stepName)
|
||||
if err != nil {
|
||||
log.Entry().Warnf("failed to create build settings info: %v", err)
|
||||
}
|
||||
commonPipelineEnvironment.custom.buildSettingsInfo = buildSettingsInfo
|
||||
|
||||
if err := fileUtils.FileWrite("/kaniko/.docker/config.json", dockerConfig, 0644); err != nil {
|
||||
return errors.Wrap(err, "failed to write file '/kaniko/.docker/config.json'")
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get current working directory")
|
||||
}
|
||||
kanikoOpts := []string{"--dockerfile", config.DockerfilePath, "--context", cwd}
|
||||
kanikoOpts = append(kanikoOpts, config.BuildOptions...)
|
||||
|
||||
err = execRunner.RunExecutable("/kaniko/executor", kanikoOpts...)
|
||||
err := execRunner.RunExecutable("/kaniko/executor", kanikoOpts...)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorBuild)
|
||||
return errors.Wrap(err, "execution of '/kaniko/executor' failed")
|
||||
|
@ -18,22 +18,27 @@ import (
|
||||
)
|
||||
|
||||
type kanikoExecuteOptions struct {
|
||||
BuildOptions []string `json:"buildOptions,omitempty"`
|
||||
ContainerBuildOptions string `json:"containerBuildOptions,omitempty"`
|
||||
ContainerImage string `json:"containerImage,omitempty"`
|
||||
ContainerImageName string `json:"containerImageName,omitempty"`
|
||||
ContainerImageTag string `json:"containerImageTag,omitempty"`
|
||||
ContainerPreparationCommand string `json:"containerPreparationCommand,omitempty"`
|
||||
ContainerRegistryURL string `json:"containerRegistryUrl,omitempty"`
|
||||
CustomTLSCertificateLinks []string `json:"customTlsCertificateLinks,omitempty"`
|
||||
DockerConfigJSON string `json:"dockerConfigJSON,omitempty"`
|
||||
DockerfilePath string `json:"dockerfilePath,omitempty"`
|
||||
BuildOptions []string `json:"buildOptions,omitempty"`
|
||||
BuildSettingsInfo string `json:"buildSettingsInfo,omitempty"`
|
||||
ContainerMultiImageBuild bool `json:"containerMultiImageBuild,omitempty"`
|
||||
ContainerMultiImageBuildExcludes []string `json:"containerMultiImageBuildExcludes,omitempty"`
|
||||
ContainerBuildOptions string `json:"containerBuildOptions,omitempty"`
|
||||
ContainerImage string `json:"containerImage,omitempty"`
|
||||
ContainerImageName string `json:"containerImageName,omitempty"`
|
||||
ContainerImageTag string `json:"containerImageTag,omitempty"`
|
||||
ContainerPreparationCommand string `json:"containerPreparationCommand,omitempty"`
|
||||
ContainerRegistryURL string `json:"containerRegistryUrl,omitempty"`
|
||||
CustomTLSCertificateLinks []string `json:"customTlsCertificateLinks,omitempty"`
|
||||
DockerConfigJSON string `json:"dockerConfigJSON,omitempty"`
|
||||
DockerfilePath string `json:"dockerfilePath,omitempty"`
|
||||
}
|
||||
|
||||
type kanikoExecuteCommonPipelineEnvironment struct {
|
||||
container struct {
|
||||
registryURL string
|
||||
imageNameTag string
|
||||
registryURL string
|
||||
imageNameTag string
|
||||
imageNames []string
|
||||
imageNameTags []string
|
||||
}
|
||||
custom struct {
|
||||
buildSettingsInfo string
|
||||
@ -48,6 +53,8 @@ func (p *kanikoExecuteCommonPipelineEnvironment) persist(path, resourceName stri
|
||||
}{
|
||||
{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},
|
||||
{category: "custom", name: "buildSettingsInfo", value: p.custom.buildSettingsInfo},
|
||||
}
|
||||
|
||||
@ -79,7 +86,21 @@ func KanikoExecuteCommand() *cobra.Command {
|
||||
var createKanikoExecuteCmd = &cobra.Command{
|
||||
Use: STEP_NAME,
|
||||
Short: "Executes a [Kaniko](https://github.com/GoogleContainerTools/kaniko) build for creating a Docker container.",
|
||||
Long: `Executes a [Kaniko](https://github.com/GoogleContainerTools/kaniko) build for creating a Docker container.`,
|
||||
Long: `Executes a [Kaniko](https://github.com/GoogleContainerTools/kaniko) build for creating a Docker container.
|
||||
|
||||
### Building multiple container images
|
||||
|
||||
The step allows you to build multiple container images with one run.
|
||||
This is suitable in case you need to create multiple images for one microservice, e.g. for testing.
|
||||
|
||||
All images will get the same "root" name and the same versioning.<br />
|
||||
**Thus, this is not suitable to be used for a monorepo approach!** For monorepos you need to use a build tool natively capable to take care for monorepos
|
||||
or implement a custom logic and for example execute this ` + "`" + `kanikoExecute` + "`" + ` step multiple times in your custom pipeline.
|
||||
|
||||
You can activate multiple builds using the parameters
|
||||
|
||||
* [containerMultiImageBuild](#containermultiimagebuild) for activation
|
||||
* [containerMultiImageBuildExcludes](#containermultiimagebuildexcludes) for defining excludes`,
|
||||
PreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
startTime = time.Now()
|
||||
log.SetStepName(STEP_NAME)
|
||||
@ -156,7 +177,10 @@ func KanikoExecuteCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func addKanikoExecuteFlags(cmd *cobra.Command, stepConfig *kanikoExecuteOptions) {
|
||||
cmd.Flags().StringSliceVar(&stepConfig.BuildOptions, "buildOptions", []string{`--skip-tls-verify-pull`}, "Defines a list of build options for the [kaniko](https://github.com/GoogleContainerTools/kaniko) build.")
|
||||
cmd.Flags().StringSliceVar(&stepConfig.BuildOptions, "buildOptions", []string{`--skip-tls-verify-pull`, `--ignore-path`, `/busybox`}, "Defines a list of build options for the [kaniko](https://github.com/GoogleContainerTools/kaniko) build.")
|
||||
cmd.Flags().StringVar(&stepConfig.BuildSettingsInfo, "buildSettingsInfo", os.Getenv("PIPER_buildSettingsInfo"), "Build settings info is typically filled by the step automatically to create information about the build settings that were used during the mta build. This information is typically used for compliance related processes.")
|
||||
cmd.Flags().BoolVar(&stepConfig.ContainerMultiImageBuild, "containerMultiImageBuild", false, "Defines if multiple containers should be build. Dockerfiles are used using the pattern **/Dockerfile*. Excludes can be defined via [`containerMultiImageBuildExcludes`](#containermultiimagebuildexscludes).")
|
||||
cmd.Flags().StringSliceVar(&stepConfig.ContainerMultiImageBuildExcludes, "containerMultiImageBuildExcludes", []string{}, "Defines a list of Dockerfile paths to exclude from the build when using [`containerMultiImageBuild`](#containermultiimagebuild).")
|
||||
cmd.Flags().StringVar(&stepConfig.ContainerBuildOptions, "containerBuildOptions", os.Getenv("PIPER_containerBuildOptions"), "Deprected, please use buildOptions. Defines the build options for the [kaniko](https://github.com/GoogleContainerTools/kaniko) build.")
|
||||
cmd.Flags().StringVar(&stepConfig.ContainerImage, "containerImage", os.Getenv("PIPER_containerImage"), "Defines the full name of the Docker image to be created including registry, image name and tag like `my.docker.registry/path/myImageName:myTag`. If left empty, image will not be pushed.")
|
||||
cmd.Flags().StringVar(&stepConfig.ContainerImageName, "containerImageName", os.Getenv("PIPER_containerImageName"), "Name of the container which will be built - will be used instead of parameter `containerImage`")
|
||||
@ -190,7 +214,39 @@ func kanikoExecuteMetadata() config.StepData {
|
||||
Type: "[]string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
Default: []string{`--skip-tls-verify-pull`},
|
||||
Default: []string{`--skip-tls-verify-pull`, `--ignore-path`, `/busybox`},
|
||||
},
|
||||
{
|
||||
Name: "buildSettingsInfo",
|
||||
ResourceRef: []config.ResourceReference{
|
||||
{
|
||||
Name: "commonPipelineEnvironment",
|
||||
Param: "custom/buildSettingsInfo",
|
||||
},
|
||||
},
|
||||
Scope: []string{"STEPS", "STAGES", "PARAMETERS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
Default: os.Getenv("PIPER_buildSettingsInfo"),
|
||||
},
|
||||
{
|
||||
Name: "containerMultiImageBuild",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "bool",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
Default: false,
|
||||
},
|
||||
{
|
||||
Name: "containerMultiImageBuildExcludes",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "[]string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
Default: []string{},
|
||||
},
|
||||
{
|
||||
Name: "containerBuildOptions",
|
||||
@ -312,6 +368,8 @@ func kanikoExecuteMetadata() config.StepData {
|
||||
Parameters: []map[string]interface{}{
|
||||
{"name": "container/registryUrl"},
|
||||
{"name": "container/imageNameTag"},
|
||||
{"name": "container/imageNames", "type": "[]string"},
|
||||
{"name": "container/imageNameTags", "type": "[]string"},
|
||||
{"name": "custom/buildSettingsInfo"},
|
||||
},
|
||||
},
|
||||
|
@ -6,7 +6,8 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||
@ -36,32 +37,16 @@ func (c *kanikoMockClient) SendRequest(method, url string, body io.Reader, heade
|
||||
return &http.Response{StatusCode: c.httpStatusCode, Body: ioutil.NopCloser(bytes.NewReader([]byte(c.responseBody)))}, nil
|
||||
}
|
||||
|
||||
type kanikoFileMock struct {
|
||||
*mock.FilesMock
|
||||
fileReadContent map[string]string
|
||||
fileReadErr map[string]error
|
||||
fileWriteContent map[string]string
|
||||
fileWriteErr map[string]error
|
||||
}
|
||||
|
||||
func (f *kanikoFileMock) FileRead(path string) ([]byte, error) {
|
||||
if f.fileReadErr[path] != nil {
|
||||
return []byte{}, f.fileReadErr[path]
|
||||
}
|
||||
return []byte(f.fileReadContent[path]), nil
|
||||
}
|
||||
|
||||
func (f *kanikoFileMock) FileWrite(path string, content []byte, perm os.FileMode) error {
|
||||
if f.fileWriteErr[path] != nil {
|
||||
return f.fileWriteErr[path]
|
||||
}
|
||||
f.fileWriteContent[path] = string(content)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRunKanikoExecute(t *testing.T) {
|
||||
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
// required due to config resolution during build settings retrieval
|
||||
// ToDo: proper mocking
|
||||
openFileBak := configOptions.openFile
|
||||
defer func() {
|
||||
configOptions.openFile = openFileBak
|
||||
}()
|
||||
|
||||
configOptions.openFile = configOpenFileMock
|
||||
|
||||
t.Run("success case", func(t *testing.T) {
|
||||
config := &kanikoExecuteOptions{
|
||||
@ -71,17 +56,18 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
|
||||
DockerfilePath: "Dockerfile",
|
||||
DockerConfigJSON: "path/to/docker/config.json",
|
||||
BuildSettingsInfo: `{"mavenExecuteBuild":[{"dockerImage":"maven"}]}`,
|
||||
}
|
||||
|
||||
runner := &mock.ExecMockRunner{}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
certClient := &kanikoMockClient{
|
||||
responseBody: "testCert",
|
||||
}
|
||||
fileUtils := &kanikoFileMock{
|
||||
fileReadContent: map[string]string{"path/to/docker/config.json": `{"auths":{"custom":"test"}}`},
|
||||
fileWriteContent: map[string]string{},
|
||||
}
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
|
||||
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
||||
|
||||
@ -91,12 +77,15 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, runner.Calls[0].Params)
|
||||
|
||||
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
|
||||
assert.Equal(t, `{"auths":{"custom":"test"}}`, fileUtils.fileWriteContent["/kaniko/.docker/config.json"])
|
||||
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `{"auths":{"custom":"test"}}`, string(c))
|
||||
|
||||
assert.Equal(t, "/kaniko/executor", runner.Calls[1].Exec)
|
||||
cwd, _ := os.Getwd()
|
||||
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag"}, runner.Calls[1].Params)
|
||||
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", ".", "--skip-tls-verify-pull", "--destination", "myImage:tag"}, runner.Calls[1].Params)
|
||||
|
||||
assert.Contains(t, commonPipelineEnvironment.custom.buildSettingsInfo, `"mavenExecuteBuild":[{"dockerImage":"maven"}]`)
|
||||
assert.Contains(t, commonPipelineEnvironment.custom.buildSettingsInfo, `"kanikoExecute":[{"dockerImage":"gcr.io/kaniko-project/executor:debug"}]`)
|
||||
})
|
||||
|
||||
t.Run("success case - image params", func(t *testing.T) {
|
||||
@ -112,14 +101,14 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
}
|
||||
|
||||
runner := &mock.ExecMockRunner{}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
certClient := &kanikoMockClient{
|
||||
responseBody: "testCert",
|
||||
}
|
||||
fileUtils := &kanikoFileMock{
|
||||
fileReadContent: map[string]string{"path/to/docker/config.json": `{"auths":{"custom":"test"}}`},
|
||||
fileWriteContent: map[string]string{},
|
||||
}
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
|
||||
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
||||
|
||||
@ -129,11 +118,12 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, runner.Calls[0].Params)
|
||||
|
||||
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
|
||||
assert.Equal(t, `{"auths":{"custom":"test"}}`, fileUtils.fileWriteContent["/kaniko/.docker/config.json"])
|
||||
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `{"auths":{"custom":"test"}}`, string(c))
|
||||
|
||||
assert.Equal(t, "/kaniko/executor", runner.Calls[1].Exec)
|
||||
cwd, _ := os.Getwd()
|
||||
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--destination", "my.registry.com:50000/myImage:1.2.3-a-x"}, runner.Calls[1].Params)
|
||||
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", ".", "--skip-tls-verify-pull", "--destination", "my.registry.com:50000/myImage:1.2.3-a-x"}, runner.Calls[1].Params)
|
||||
|
||||
})
|
||||
|
||||
@ -150,12 +140,12 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
}
|
||||
|
||||
runner := &mock.ExecMockRunner{}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
certClient := &kanikoMockClient{}
|
||||
fileUtils := &kanikoFileMock{
|
||||
fileWriteContent: map[string]string{},
|
||||
fileReadErr: map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")},
|
||||
}
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.AddFile("path/to/docker/config.json", []byte(``))
|
||||
fileUtils.FileReadErrors = map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")}
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
||||
|
||||
@ -171,22 +161,23 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
}
|
||||
|
||||
runner := &mock.ExecMockRunner{}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
certClient := &kanikoMockClient{
|
||||
responseBody: "testCert",
|
||||
}
|
||||
fileUtils := &kanikoFileMock{
|
||||
fileWriteContent: map[string]string{},
|
||||
}
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, `{"auths":{}}`, fileUtils.fileWriteContent["/kaniko/.docker/config.json"])
|
||||
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `{"auths":{}}`, string(c))
|
||||
|
||||
cwd, _ := os.Getwd()
|
||||
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--no-push"}, runner.Calls[1].Params)
|
||||
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", ".", "--skip-tls-verify-pull", "--no-push"}, runner.Calls[1].Params)
|
||||
})
|
||||
|
||||
t.Run("success case - backward compatibility", func(t *testing.T) {
|
||||
@ -200,20 +191,181 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
}
|
||||
|
||||
runner := &mock.ExecMockRunner{}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
certClient := &kanikoMockClient{
|
||||
responseBody: "testCert",
|
||||
}
|
||||
fileUtils := &kanikoFileMock{
|
||||
fileReadContent: map[string]string{"path/to/docker/config.json": `{"auths":{"custom":"test"}}`},
|
||||
fileWriteContent: map[string]string{},
|
||||
}
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
|
||||
fileUtils.AddFile("/kaniko/ssl/certs/ca-certificates.crt", []byte(``))
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
||||
|
||||
assert.NoError(t, err)
|
||||
cwd, _ := os.Getwd()
|
||||
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", cwd, "--skip-tls-verify-pull", "--destination", "myImage:tag"}, runner.Calls[1].Params)
|
||||
assert.Equal(t, []string{"--dockerfile", "Dockerfile", "--context", ".", "--skip-tls-verify-pull", "--destination", "myImage:tag"}, runner.Calls[1].Params)
|
||||
})
|
||||
|
||||
t.Run("success case - multi image build with root image", func(t *testing.T) {
|
||||
config := &kanikoExecuteOptions{
|
||||
ContainerImageName: "myImage",
|
||||
ContainerImageTag: "myTag",
|
||||
ContainerRegistryURL: "https://my.registry.com:50000",
|
||||
ContainerMultiImageBuild: true,
|
||||
}
|
||||
|
||||
runner := &mock.ExecMockRunner{}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.AddFile("Dockerfile", []byte("some content"))
|
||||
fileUtils.AddFile("sub1/Dockerfile", []byte("some content"))
|
||||
fileUtils.AddFile("sub2/Dockerfile", []byte("some content"))
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, nil, fileUtils)
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 3, len(runner.Calls))
|
||||
assert.Equal(t, "/kaniko/executor", runner.Calls[0].Exec)
|
||||
assert.Equal(t, "/kaniko/executor", runner.Calls[1].Exec)
|
||||
assert.Equal(t, "/kaniko/executor", runner.Calls[2].Exec)
|
||||
|
||||
expectedParams := [][]string{
|
||||
{"--dockerfile", "Dockerfile", "--context", ".", "--destination", "my.registry.com:50000/myImage:myTag"},
|
||||
{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", "sub1", "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
|
||||
{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", "sub2", "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
|
||||
}
|
||||
// need to go this way since we cannot count on the correct order
|
||||
for _, call := range runner.Calls {
|
||||
found := false
|
||||
for _, expected := range expectedParams {
|
||||
if strings.Join(call.Params, " ") == strings.Join(expected, " ") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, fmt.Sprintf("%v not found", call.Params))
|
||||
}
|
||||
|
||||
assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
|
||||
assert.Equal(t, "myImage:myTag", commonPipelineEnvironment.container.imageNameTag)
|
||||
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage")
|
||||
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub1")
|
||||
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub2")
|
||||
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage:myTag")
|
||||
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub1:myTag")
|
||||
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub2:myTag")
|
||||
})
|
||||
|
||||
t.Run("success case - multi image build excluding root image", func(t *testing.T) {
|
||||
config := &kanikoExecuteOptions{
|
||||
ContainerImageName: "myImage",
|
||||
ContainerImageTag: "myTag",
|
||||
ContainerRegistryURL: "https://my.registry.com:50000",
|
||||
ContainerMultiImageBuild: true,
|
||||
ContainerMultiImageBuildExcludes: []string{"Dockerfile"},
|
||||
}
|
||||
|
||||
runner := &mock.ExecMockRunner{}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.AddFile("Dockerfile", []byte("some content"))
|
||||
fileUtils.AddFile("sub1/Dockerfile", []byte("some content"))
|
||||
fileUtils.AddFile("sub2/Dockerfile", []byte("some content"))
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, nil, fileUtils)
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(runner.Calls))
|
||||
assert.Equal(t, "/kaniko/executor", runner.Calls[0].Exec)
|
||||
assert.Equal(t, "/kaniko/executor", runner.Calls[1].Exec)
|
||||
|
||||
expectedParams := [][]string{
|
||||
{"--dockerfile", filepath.Join("sub1", "Dockerfile"), "--context", "sub1", "--destination", "my.registry.com:50000/myImage-sub1:myTag"},
|
||||
{"--dockerfile", filepath.Join("sub2", "Dockerfile"), "--context", "sub2", "--destination", "my.registry.com:50000/myImage-sub2:myTag"},
|
||||
}
|
||||
// need to go this way since we cannot count on the correct order
|
||||
for _, call := range runner.Calls {
|
||||
found := false
|
||||
for _, expected := range expectedParams {
|
||||
if strings.Join(call.Params, " ") == strings.Join(expected, " ") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, fmt.Sprintf("%v not found", call.Params))
|
||||
}
|
||||
|
||||
assert.Equal(t, "https://my.registry.com:50000", commonPipelineEnvironment.container.registryURL)
|
||||
assert.Equal(t, "", commonPipelineEnvironment.container.imageNameTag)
|
||||
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub1")
|
||||
assert.Contains(t, commonPipelineEnvironment.container.imageNames, "myImage-sub2")
|
||||
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub1:myTag")
|
||||
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub2:myTag")
|
||||
})
|
||||
|
||||
t.Run("error case - multi image build: no docker files", func(t *testing.T) {
|
||||
config := &kanikoExecuteOptions{
|
||||
ContainerImageName: "myImage",
|
||||
ContainerImageTag: "myTag",
|
||||
ContainerRegistryURL: "https://my.registry.com:50000",
|
||||
ContainerMultiImageBuild: true,
|
||||
}
|
||||
|
||||
cpe := kanikoExecuteCommonPipelineEnvironment{}
|
||||
runner := &mock.ExecMockRunner{}
|
||||
|
||||
fileUtils := &mock.FilesMock{}
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, runner, nil, fileUtils)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, fmt.Sprint(err), "failed to identify image list for multi image build")
|
||||
})
|
||||
|
||||
t.Run("error case - multi image build: no docker files to process", func(t *testing.T) {
|
||||
config := &kanikoExecuteOptions{
|
||||
ContainerImageName: "myImage",
|
||||
ContainerImageTag: "myTag",
|
||||
ContainerRegistryURL: "https://my.registry.com:50000",
|
||||
ContainerMultiImageBuild: true,
|
||||
ContainerMultiImageBuildExcludes: []string{"Dockerfile"},
|
||||
}
|
||||
|
||||
cpe := kanikoExecuteCommonPipelineEnvironment{}
|
||||
runner := &mock.ExecMockRunner{}
|
||||
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.AddFile("Dockerfile", []byte("some content"))
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, runner, nil, fileUtils)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, fmt.Sprint(err), "no docker files to process, please check exclude list")
|
||||
})
|
||||
|
||||
t.Run("error case - multi image build: build failed", func(t *testing.T) {
|
||||
config := &kanikoExecuteOptions{
|
||||
ContainerImageName: "myImage",
|
||||
ContainerImageTag: "myTag",
|
||||
ContainerRegistryURL: "https://my.registry.com:50000",
|
||||
ContainerMultiImageBuild: true,
|
||||
}
|
||||
|
||||
cpe := kanikoExecuteCommonPipelineEnvironment{}
|
||||
runner := &mock.ExecMockRunner{}
|
||||
runner.ShouldFailOnCommand = map[string]error{"/kaniko/executor": fmt.Errorf("execution failed")}
|
||||
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.AddFile("Dockerfile", []byte("some content"))
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &cpe, runner, nil, fileUtils)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, fmt.Sprint(err), "failed to build image")
|
||||
})
|
||||
|
||||
t.Run("error case - Kaniko init failed", func(t *testing.T) {
|
||||
@ -224,9 +376,10 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
runner := &mock.ExecMockRunner{
|
||||
ShouldFailOnCommand: map[string]error{"rm": fmt.Errorf("rm failed")},
|
||||
}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
certClient := &kanikoMockClient{}
|
||||
fileUtils := &kanikoFileMock{}
|
||||
fileUtils := &mock.FilesMock{}
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
||||
|
||||
@ -239,11 +392,10 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
runner := &mock.ExecMockRunner{
|
||||
ShouldFailOnCommand: map[string]error{"/kaniko/executor": fmt.Errorf("kaniko run failed")},
|
||||
}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
certClient := &kanikoMockClient{}
|
||||
fileUtils := &kanikoFileMock{
|
||||
fileWriteContent: map[string]string{},
|
||||
}
|
||||
fileUtils := &mock.FilesMock{}
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
||||
|
||||
@ -263,12 +415,11 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
}
|
||||
|
||||
runner := &mock.ExecMockRunner{}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
certClient := &kanikoMockClient{}
|
||||
fileUtils := &kanikoFileMock{
|
||||
fileWriteContent: map[string]string{},
|
||||
fileReadErr: map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")},
|
||||
}
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.FileReadErrors = map[string]error{"/kaniko/ssl/certs/ca-certificates.crt": fmt.Errorf("read error")}
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
||||
|
||||
@ -281,12 +432,11 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
}
|
||||
|
||||
runner := &mock.ExecMockRunner{}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
certClient := &kanikoMockClient{}
|
||||
fileUtils := &kanikoFileMock{
|
||||
fileWriteContent: map[string]string{},
|
||||
fileReadErr: map[string]error{"path/to/docker/config.json": fmt.Errorf("read error")},
|
||||
}
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.FileReadErrors = map[string]error{"path/to/docker/config.json": fmt.Errorf("read error")}
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
||||
|
||||
@ -299,12 +449,12 @@ func TestRunKanikoExecute(t *testing.T) {
|
||||
}
|
||||
|
||||
runner := &mock.ExecMockRunner{}
|
||||
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
|
||||
|
||||
certClient := &kanikoMockClient{}
|
||||
fileUtils := &kanikoFileMock{
|
||||
fileWriteContent: map[string]string{},
|
||||
fileWriteErr: map[string]error{"/kaniko/.docker/config.json": fmt.Errorf("write error")},
|
||||
}
|
||||
fileUtils := &mock.FilesMock{}
|
||||
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths":{"custom":"test"}}`))
|
||||
fileUtils.FileWriteErrors = map[string]error{"/kaniko/.docker/config.json": fmt.Errorf("write error")}
|
||||
|
||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
||||
|
||||
|
@ -89,7 +89,7 @@ func CreateBuildSettingsInfo(config *BuildOptions, buildTool string) (string, er
|
||||
}
|
||||
}
|
||||
|
||||
log.Entry().Infof("build settings infomration successfully created with '%v", string(jsonResult))
|
||||
log.Entry().Infof("build settings information successfully created with '%v", string(jsonResult))
|
||||
|
||||
return string(jsonResult), nil
|
||||
|
||||
|
@ -358,6 +358,9 @@ func GetYAML(data interface{}) (string, error) {
|
||||
|
||||
// OpenPiperFile provides functionality to retrieve configuration via file or http
|
||||
func OpenPiperFile(name string, accessTokens map[string]string) (io.ReadCloser, error) {
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("no filename provided")
|
||||
}
|
||||
if !strings.HasPrefix(name, "http://") && !strings.HasPrefix(name, "https://") {
|
||||
return os.Open(name)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/SAP/jenkins-library/pkg/piperutils"
|
||||
|
||||
pkgutil "github.com/GoogleContainerTools/container-diff/pkg/util"
|
||||
@ -165,3 +166,55 @@ func (c *Client) TarImage(writer io.Writer, image pkgutil.Image) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImageListWithFilePath compiles container image names based on all Dockerfiles found, considering excludes
|
||||
// according to following search pattern: **/Dockerfile*
|
||||
// Return value contains a map with image names and file path
|
||||
// Examples for image names with imageName testImage
|
||||
// * Dockerfile: `imageName`
|
||||
// * sub1/Dockerfile: `imageName-sub1`
|
||||
// * sub2/Dockerfile_proxy: `imageName-sub2-proxy`
|
||||
func ImageListWithFilePath(imageName string, excludes []string, utils piperutils.FileUtils) (map[string]string, error) {
|
||||
|
||||
imageList := map[string]string{}
|
||||
|
||||
pattern := "**/Dockerfile*"
|
||||
|
||||
matches, err := utils.Glob(pattern)
|
||||
if err != nil || len(matches) == 0 {
|
||||
return imageList, fmt.Errorf("failed to retrieve Dockerfiles")
|
||||
}
|
||||
|
||||
for _, dockerfilePath := range matches {
|
||||
// make sure that the path we have is relative
|
||||
// ToDo: needs rework
|
||||
//dockerfilePath = strings.ReplaceAll(dockerfilePath, cwd, ".")
|
||||
|
||||
if piperutils.ContainsString(excludes, dockerfilePath) {
|
||||
log.Entry().Infof("Discard %v since it is in the exclude list %v", dockerfilePath, excludes)
|
||||
continue
|
||||
}
|
||||
|
||||
if dockerfilePath == "Dockerfile" {
|
||||
imageList[imageName] = dockerfilePath
|
||||
} else {
|
||||
var finalName string
|
||||
if base := filepath.Base(dockerfilePath); base == "Dockerfile" {
|
||||
finalName = fmt.Sprintf("%v-%v", imageName, strings.ReplaceAll(filepath.Dir(dockerfilePath), string(filepath.Separator), "-"))
|
||||
} else {
|
||||
parts := strings.FieldsFunc(base, func(separator rune) bool {
|
||||
return separator == []rune("-")[0] || separator == []rune("_")[0]
|
||||
})
|
||||
if len(parts) == 1 {
|
||||
return imageList, fmt.Errorf("wrong format of Dockerfile, must be inside a sub-folder or contain a separator")
|
||||
}
|
||||
parts[0] = imageName
|
||||
finalName = strings.Join(parts, "-")
|
||||
}
|
||||
|
||||
imageList[finalName] = dockerfilePath
|
||||
}
|
||||
}
|
||||
|
||||
return imageList, nil
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/mock"
|
||||
@ -130,3 +131,44 @@ func TestGetImageSource(t *testing.T) {
|
||||
assert.Equal(t, c.want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageListWithFilePath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
imageName := "testImage"
|
||||
|
||||
tt := []struct {
|
||||
name string
|
||||
excludes []string
|
||||
fileList []string
|
||||
expected map[string]string
|
||||
expectedError error
|
||||
}{
|
||||
{name: "Dockerfile only", fileList: []string{"Dockerfile"}, expected: map[string]string{imageName: "Dockerfile"}},
|
||||
{name: "Dockerfile in subdir", fileList: []string{"sub/Dockerfile"}, expected: map[string]string{fmt.Sprintf("%v-sub", imageName): filepath.FromSlash("sub/Dockerfile")}},
|
||||
{name: "Dockerfiles in multiple subdirs & parent", fileList: []string{"Dockerfile", "sub1/Dockerfile", "sub2/Dockerfile"}, expected: map[string]string{fmt.Sprintf("%v", imageName): filepath.FromSlash("Dockerfile"), fmt.Sprintf("%v-sub1", imageName): filepath.FromSlash("sub1/Dockerfile"), fmt.Sprintf("%v-sub2", imageName): filepath.FromSlash("sub2/Dockerfile")}},
|
||||
{name: "Dockerfiles in multiple subdirs & parent - with excludes", excludes: []string{"Dockerfile"}, fileList: []string{"Dockerfile", "sub1/Dockerfile", "sub2/Dockerfile"}, expected: map[string]string{fmt.Sprintf("%v-sub1", imageName): filepath.FromSlash("sub1/Dockerfile"), fmt.Sprintf("%v-sub2", imageName): filepath.FromSlash("sub2/Dockerfile")}},
|
||||
{name: "Dockerfiles with extensions", fileList: []string{"Dockerfile_main", "Dockerfile_sub1", "Dockerfile_sub2"}, expected: map[string]string{fmt.Sprintf("%v-main", imageName): filepath.FromSlash("Dockerfile_main"), fmt.Sprintf("%v-sub1", imageName): filepath.FromSlash("Dockerfile_sub1"), fmt.Sprintf("%v-sub2", imageName): filepath.FromSlash("Dockerfile_sub2")}},
|
||||
{name: "Dockerfiles with extensions", fileList: []string{"Dockerfile_main", "Dockerfile_sub1", "Dockerfile_sub2"}, expected: map[string]string{fmt.Sprintf("%v-main", imageName): filepath.FromSlash("Dockerfile_main"), fmt.Sprintf("%v-sub1", imageName): filepath.FromSlash("Dockerfile_sub1"), fmt.Sprintf("%v-sub2", imageName): filepath.FromSlash("Dockerfile_sub2")}},
|
||||
{name: "No Dockerfile", fileList: []string{"NoDockerFile"}, expectedError: fmt.Errorf("failed to retrieve Dockerfiles")},
|
||||
{name: "Incorrect Dockerfile", fileList: []string{"DockerfileNotSupported"}, expectedError: fmt.Errorf("wrong format of Dockerfile, must be inside a sub-folder or contain a separator")},
|
||||
}
|
||||
|
||||
for _, test := range tt {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
fileMock := mock.FilesMock{}
|
||||
for _, file := range test.fileList {
|
||||
fileMock.AddFile(file, []byte("someContent"))
|
||||
}
|
||||
|
||||
imageList, err := ImageListWithFilePath(imageName, test.excludes, &fileMock)
|
||||
|
||||
if test.expectedError != nil {
|
||||
assert.EqualError(t, err, fmt.Sprint(test.expectedError))
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.expected, imageList)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,23 @@
|
||||
metadata:
|
||||
name: kanikoExecute
|
||||
description: Executes a [Kaniko](https://github.com/GoogleContainerTools/kaniko) build for creating a Docker container.
|
||||
longDescription: Executes a [Kaniko](https://github.com/GoogleContainerTools/kaniko) build for creating a Docker container.
|
||||
longDescription: |
|
||||
Executes a [Kaniko](https://github.com/GoogleContainerTools/kaniko) build for creating a Docker container.
|
||||
|
||||
### Building multiple container images
|
||||
|
||||
The step allows you to build multiple container images with one run.
|
||||
This is suitable in case you need to create multiple images for one microservice, e.g. for testing.
|
||||
|
||||
All images will get the same "root" name and the same versioning.<br />
|
||||
**Thus, this is not suitable to be used for a monorepo approach!** For monorepos you need to use a build tool natively capable to take care for monorepos
|
||||
or implement a custom logic and for example execute this `kanikoExecute` step multiple times in your custom pipeline.
|
||||
|
||||
You can activate multiple builds using the parameters
|
||||
|
||||
* [containerMultiImageBuild](#containermultiimagebuild) for activation
|
||||
* [containerMultiImageBuildExcludes](#containermultiimagebuildexcludes) for defining excludes
|
||||
|
||||
spec:
|
||||
inputs:
|
||||
secrets:
|
||||
@ -18,6 +34,36 @@ spec:
|
||||
- STEPS
|
||||
default:
|
||||
- --skip-tls-verify-pull
|
||||
# fixing Kaniko issue https://github.com/GoogleContainerTools/kaniko/issues/1586
|
||||
# as per comment https://github.com/GoogleContainerTools/kaniko/issues/1586#issuecomment-945718536
|
||||
- --ignore-path
|
||||
- /busybox
|
||||
- name: buildSettingsInfo
|
||||
type: string
|
||||
description: Build settings info is typically filled by the step automatically to create information about the build settings that were used during the mta build. This information is typically used for compliance related processes.
|
||||
scope:
|
||||
- STEPS
|
||||
- STAGES
|
||||
- PARAMETERS
|
||||
resourceRef:
|
||||
- name: commonPipelineEnvironment
|
||||
param: custom/buildSettingsInfo
|
||||
- name: containerMultiImageBuild
|
||||
type: bool
|
||||
description: Defines if multiple containers should be build. Dockerfiles are used using the pattern **/Dockerfile*. Excludes can be defined via [`containerMultiImageBuildExcludes`](#containermultiimagebuildexscludes).
|
||||
scope:
|
||||
- GENERAL
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: containerMultiImageBuildExcludes
|
||||
type: '[]string'
|
||||
description: Defines a list of Dockerfile paths to exclude from the build when using [`containerMultiImageBuild`](#containermultiimagebuild).
|
||||
scope:
|
||||
- GENERAL
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: containerBuildOptions
|
||||
type: string
|
||||
description: Deprected, please use buildOptions. Defines the build options for the [kaniko](https://github.com/GoogleContainerTools/kaniko) build.
|
||||
@ -119,6 +165,10 @@ spec:
|
||||
params:
|
||||
- name: container/registryUrl
|
||||
- name: container/imageNameTag
|
||||
- name: container/imageNames
|
||||
type: "[]string"
|
||||
- name: container/imageNameTags
|
||||
type: "[]string"
|
||||
- name: custom/buildSettingsInfo
|
||||
containers:
|
||||
- image: gcr.io/kaniko-project/executor:debug
|
||||
|
Loading…
x
Reference in New Issue
Block a user