1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-09-16 09:26:22 +02:00

fix (kanikoExecute) enhance existing docker config json with additional credential params : user, password and registry Url (#3892)

* passing registry username and password

* enhance the case for creating docker config json with user credentials

* refactoring code

* unit test and maintaing user provided docker config json file

* go generate

* remove addtional file addition to unit test

Co-authored-by: anilkeshav27 <you@example.com>
This commit is contained in:
Anil Keshav
2022-07-15 08:40:33 +02:00
committed by GitHub
parent 53f4ce96ae
commit 72896fab70
4 changed files with 158 additions and 9 deletions

View File

@@ -74,11 +74,37 @@ func runKanikoExecute(config *kanikoExecuteOptions, telemetryData *telemetry.Cus
}
dockerConfig := []byte(`{"auths":{}}`)
// respect user provided docker config json file
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)
return errors.Wrapf(err, "failed to read existing docker config json at '%v'", config.DockerConfigJSON)
}
}
// if : user provided docker config json and registry credentials present then enahance the user provided docker provided json with the registry credentials
// else if : no user provided docker config json then create a new docker config json for kaniko
if len(config.DockerConfigJSON) > 0 && len(config.ContainerRegistryURL) > 0 && len(config.ContainerRegistryPassword) > 0 && len(config.ContainerRegistryUser) > 0 {
targetConfigJson, err := docker.CreateDockerConfigJSON(config.ContainerRegistryURL, config.ContainerRegistryUser, config.ContainerRegistryPassword, "", config.DockerConfigJSON, fileUtils)
if err != nil {
return errors.Wrapf(err, "failed to update existing docker config json file '%v'", config.DockerConfigJSON)
}
dockerConfig, err = fileUtils.FileRead(targetConfigJson)
if err != nil {
return errors.Wrapf(err, "failed to read enhanced file '%v'", config.DockerConfigJSON)
}
} else if len(config.DockerConfigJSON) == 0 && len(config.ContainerRegistryURL) > 0 && len(config.ContainerRegistryPassword) > 0 && len(config.ContainerRegistryUser) > 0 {
targetConfigJson, err := docker.CreateDockerConfigJSON(config.ContainerRegistryURL, config.ContainerRegistryUser, config.ContainerRegistryPassword, "", "/kaniko/.docker/config.json", fileUtils)
if err != nil {
return errors.Wrap(err, "failed to create new docker config json at /kaniko/.docker/config.json")
}
dockerConfig, err = fileUtils.FileRead(targetConfigJson)
if err != nil {
return errors.Wrapf(err, "failed to read new docker config file at /kaniko/.docker/config.json")
}
}

View File

@@ -29,6 +29,8 @@ type kanikoExecuteOptions struct {
ContainerMultiImageBuildTrimDir string `json:"containerMultiImageBuildTrimDir,omitempty"`
ContainerPreparationCommand string `json:"containerPreparationCommand,omitempty"`
ContainerRegistryURL string `json:"containerRegistryUrl,omitempty"`
ContainerRegistryUser string `json:"containerRegistryUser,omitempty"`
ContainerRegistryPassword string `json:"containerRegistryPassword,omitempty"`
CustomTLSCertificateLinks []string `json:"customTlsCertificateLinks,omitempty"`
DockerConfigJSON string `json:"dockerConfigJSON,omitempty"`
DockerfilePath string `json:"dockerfilePath,omitempty"`
@@ -248,6 +250,8 @@ func addKanikoExecuteFlags(cmd *cobra.Command, stepConfig *kanikoExecuteOptions)
cmd.Flags().StringVar(&stepConfig.ContainerMultiImageBuildTrimDir, "containerMultiImageBuildTrimDir", os.Getenv("PIPER_containerMultiImageBuildTrimDir"), "Defines a trailing directory part which should not be considered in the final image name.")
cmd.Flags().StringVar(&stepConfig.ContainerPreparationCommand, "containerPreparationCommand", `rm -f /kaniko/.docker/config.json`, "Defines the command to prepare the Kaniko container. By default the contained credentials are removed in order to allow anonymous access to container registries.")
cmd.Flags().StringVar(&stepConfig.ContainerRegistryURL, "containerRegistryUrl", os.Getenv("PIPER_containerRegistryUrl"), "http(s) url of the Container registry where the image should be pushed to - will be used instead of parameter `containerImage`")
cmd.Flags().StringVar(&stepConfig.ContainerRegistryUser, "containerRegistryUser", os.Getenv("PIPER_containerRegistryUser"), "Username of the Container registry where the image should be pushed to - which will updated in a docker config json file. If a docker config json file is provided via parameter `dockerConfigJSON` , then the existing file will be enhanced")
cmd.Flags().StringVar(&stepConfig.ContainerRegistryPassword, "containerRegistryPassword", os.Getenv("PIPER_containerRegistryPassword"), "Password of the Container registry where the image should be pushed to - which will updated in a docker config json file. If a docker config json file is provided via parameter `dockerConfigJSON` , then the existing file will be enhanced")
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().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().StringVar(&stepConfig.DockerfilePath, "dockerfilePath", `Dockerfile`, "Defines the location of the Dockerfile relative to the Jenkins workspace.")
@@ -384,6 +388,34 @@ func kanikoExecuteMetadata() config.StepData {
Aliases: []config.Alias{{Name: "dockerRegistryUrl"}},
Default: os.Getenv("PIPER_containerRegistryUrl"),
},
{
Name: "containerRegistryUser",
ResourceRef: []config.ResourceReference{
{
Name: "commonPipelineEnvironment",
Param: "container/repositoryUsername",
},
},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "dockerRegistryUser"}},
Default: os.Getenv("PIPER_containerRegistryUser"),
},
{
Name: "containerRegistryPassword",
ResourceRef: []config.ResourceReference{
{
Name: "commonPipelineEnvironment",
Param: "container/repositoryPassword",
},
},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "dockerRegistryPassword"}},
Default: os.Getenv("PIPER_containerRegistryPassword"),
},
{
Name: "customTlsCertificateLinks",
ResourceRef: []config.ResourceReference{},
@@ -396,11 +428,6 @@ func kanikoExecuteMetadata() config.StepData {
{
Name: "dockerConfigJSON",
ResourceRef: []config.ResourceReference{
{
Name: "commonPipelineEnvironment",
Param: "custom/dockerConfigJSON",
},
{
Name: "dockerConfigJsonCredentialsId",
Type: "secret",

View File

@@ -426,6 +426,78 @@ func TestRunKanikoExecute(t *testing.T) {
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub2:myTag")
})
t.Run("success case - updating an existing docker config json with addtional credentials", func(t *testing.T) {
config := &kanikoExecuteOptions{
BuildOptions: []string{"--skip-tls-verify-pull"},
ContainerImageName: "myImage",
ContainerImageTag: "1.2.3-a+x",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
DockerConfigJSON: "path/to/docker/config.json",
ContainerRegistryUser: "dummyUser",
ContainerRegistryPassword: "dummyPassword",
}
runner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{
responseBody: "testCert",
}
fileUtils := &mock.FilesMock{}
fileUtils.AddFile("path/to/docker/config.json", []byte(`{"auths": {"dummyUrl": {"auth": "XXXXXXX"}}}`))
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, "rm", runner.Calls[0].Exec)
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, runner.Calls[0].Params)
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
assert.NoError(t, err)
assert.Equal(t, `{"auths":{"dummyUrl":{"auth":"XXXXXXX"},"https://my.registry.com:50000":{"auth":"ZHVtbXlVc2VyOmR1bW15UGFzc3dvcmQ="}}}`, string(c))
})
t.Run("success case - creating new docker config json with provided container credentials", func(t *testing.T) {
config := &kanikoExecuteOptions{
BuildOptions: []string{"--skip-tls-verify-pull"},
ContainerImageName: "myImage",
ContainerImageTag: "1.2.3-a+x",
ContainerRegistryURL: "https://my.registry.com:50000",
ContainerPreparationCommand: "rm -f /kaniko/.docker/config.json",
CustomTLSCertificateLinks: []string{"https://test.url/cert.crt"},
DockerfilePath: "Dockerfile",
ContainerRegistryUser: "dummyUser",
ContainerRegistryPassword: "dummyPassword",
}
runner := &mock.ExecMockRunner{}
commonPipelineEnvironment := kanikoExecuteCommonPipelineEnvironment{}
certClient := &kanikoMockClient{
responseBody: "testCert",
}
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, "rm", runner.Calls[0].Exec)
assert.Equal(t, []string{"-f", "/kaniko/.docker/config.json"}, runner.Calls[0].Params)
assert.Equal(t, config.CustomTLSCertificateLinks, certClient.urlsCalled)
c, err := fileUtils.FileRead("/kaniko/.docker/config.json")
assert.NoError(t, err)
assert.Equal(t, `{"auths":{"https://my.registry.com:50000":{"auth":"ZHVtbXlVc2VyOmR1bW15UGFzc3dvcmQ="}}}`, string(c))
})
t.Run("error case - multi image build: no docker files", func(t *testing.T) {
config := &kanikoExecuteOptions{
ContainerImageName: "myImage",
@@ -559,7 +631,7 @@ func TestRunKanikoExecute(t *testing.T) {
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
assert.EqualError(t, err, "failed to read file 'path/to/docker/config.json': read error")
assert.EqualError(t, err, "failed to read existing docker config json at 'path/to/docker/config.json': read error")
})
t.Run("error case - dockerconfig write failed", func(t *testing.T) {

View File

@@ -181,6 +181,32 @@ spec:
resourceRef:
- name: commonPipelineEnvironment
param: container/registryUrl
- name: containerRegistryUser
aliases:
- name: dockerRegistryUser
type: string
description: Username of the Container registry where the image should be pushed to - which will updated in a docker config json file. If a docker config json file is provided via parameter `dockerConfigJSON` , then the existing file will be enhanced
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
resourceRef:
- name: commonPipelineEnvironment
param: container/repositoryUsername
- name: containerRegistryPassword
aliases:
- name: dockerRegistryPassword
type: string
description: Password of the Container registry where the image should be pushed to - which will updated in a docker config json file. If a docker config json file is provided via parameter `dockerConfigJSON` , then the existing file will be enhanced
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
resourceRef:
- name: commonPipelineEnvironment
param: container/repositoryPassword
- name: customTlsCertificateLinks
type: "[]string"
description: List containing download links of custom TLS certificates. This is required to ensure trusted connections to registries with custom certificates.
@@ -197,8 +223,6 @@ spec:
- STEPS
secret: true
resourceRef:
- name: commonPipelineEnvironment
param: custom/dockerConfigJSON
- name: dockerConfigJsonCredentialsId
type: secret
- type: vaultSecretFile