You've already forked sap-jenkins-library
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:
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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",
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user