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":{}}`)
|
dockerConfig := []byte(`{"auths":{}}`)
|
||||||
|
|
||||||
|
// respect user provided docker config json file
|
||||||
if len(config.DockerConfigJSON) > 0 {
|
if len(config.DockerConfigJSON) > 0 {
|
||||||
var err error
|
var err error
|
||||||
dockerConfig, err = fileUtils.FileRead(config.DockerConfigJSON)
|
dockerConfig, err = fileUtils.FileRead(config.DockerConfigJSON)
|
||||||
if err != nil {
|
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"`
|
ContainerMultiImageBuildTrimDir string `json:"containerMultiImageBuildTrimDir,omitempty"`
|
||||||
ContainerPreparationCommand string `json:"containerPreparationCommand,omitempty"`
|
ContainerPreparationCommand string `json:"containerPreparationCommand,omitempty"`
|
||||||
ContainerRegistryURL string `json:"containerRegistryUrl,omitempty"`
|
ContainerRegistryURL string `json:"containerRegistryUrl,omitempty"`
|
||||||
|
ContainerRegistryUser string `json:"containerRegistryUser,omitempty"`
|
||||||
|
ContainerRegistryPassword string `json:"containerRegistryPassword,omitempty"`
|
||||||
CustomTLSCertificateLinks []string `json:"customTlsCertificateLinks,omitempty"`
|
CustomTLSCertificateLinks []string `json:"customTlsCertificateLinks,omitempty"`
|
||||||
DockerConfigJSON string `json:"dockerConfigJSON,omitempty"`
|
DockerConfigJSON string `json:"dockerConfigJSON,omitempty"`
|
||||||
DockerfilePath string `json:"dockerfilePath,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.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.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.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().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.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.")
|
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"}},
|
Aliases: []config.Alias{{Name: "dockerRegistryUrl"}},
|
||||||
Default: os.Getenv("PIPER_containerRegistryUrl"),
|
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",
|
Name: "customTlsCertificateLinks",
|
||||||
ResourceRef: []config.ResourceReference{},
|
ResourceRef: []config.ResourceReference{},
|
||||||
@@ -396,11 +428,6 @@ func kanikoExecuteMetadata() config.StepData {
|
|||||||
{
|
{
|
||||||
Name: "dockerConfigJSON",
|
Name: "dockerConfigJSON",
|
||||||
ResourceRef: []config.ResourceReference{
|
ResourceRef: []config.ResourceReference{
|
||||||
{
|
|
||||||
Name: "commonPipelineEnvironment",
|
|
||||||
Param: "custom/dockerConfigJSON",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Name: "dockerConfigJsonCredentialsId",
|
Name: "dockerConfigJsonCredentialsId",
|
||||||
Type: "secret",
|
Type: "secret",
|
||||||
|
@@ -426,6 +426,78 @@ func TestRunKanikoExecute(t *testing.T) {
|
|||||||
assert.Contains(t, commonPipelineEnvironment.container.imageNameTags, "myImage-sub2:myTag")
|
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) {
|
t.Run("error case - multi image build: no docker files", func(t *testing.T) {
|
||||||
config := &kanikoExecuteOptions{
|
config := &kanikoExecuteOptions{
|
||||||
ContainerImageName: "myImage",
|
ContainerImageName: "myImage",
|
||||||
@@ -559,7 +631,7 @@ func TestRunKanikoExecute(t *testing.T) {
|
|||||||
|
|
||||||
err := runKanikoExecute(config, &telemetry.CustomData{}, &commonPipelineEnvironment, runner, certClient, fileUtils)
|
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) {
|
t.Run("error case - dockerconfig write failed", func(t *testing.T) {
|
||||||
|
@@ -181,6 +181,32 @@ spec:
|
|||||||
resourceRef:
|
resourceRef:
|
||||||
- name: commonPipelineEnvironment
|
- name: commonPipelineEnvironment
|
||||||
param: container/registryUrl
|
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
|
- name: customTlsCertificateLinks
|
||||||
type: "[]string"
|
type: "[]string"
|
||||||
description: List containing download links of custom TLS certificates. This is required to ensure trusted connections to registries with custom certificates.
|
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
|
- STEPS
|
||||||
secret: true
|
secret: true
|
||||||
resourceRef:
|
resourceRef:
|
||||||
- name: commonPipelineEnvironment
|
|
||||||
param: custom/dockerConfigJSON
|
|
||||||
- name: dockerConfigJsonCredentialsId
|
- name: dockerConfigJsonCredentialsId
|
||||||
type: secret
|
type: secret
|
||||||
- type: vaultSecretFile
|
- type: vaultSecretFile
|
||||||
|
Reference in New Issue
Block a user