1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-10-30 23:57:50 +02:00

feat(gitopsUpdateDeployment) forcePush (#3665)

* feat(gitopsUpdateDeployment) forcePush

fix(gitopsUpdateDeployment) include registry

The push operation in this step can be forced to bypass branch-protection

Signed-off-by: Michael Sprauer <Michael.Sprauer@sap.com>

* add unit test

Signed-off-by: Michael Sprauer <Michael.Sprauer@sap.com>

Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com>
This commit is contained in:
Michael
2022-04-07 16:33:46 +02:00
committed by GitHub
parent fb9792ad71
commit 6676da1f9d
6 changed files with 63 additions and 16 deletions

View File

@@ -27,7 +27,7 @@ const toolKustomize = "kustomize"
type iGitopsUpdateDeploymentGitUtils interface {
CommitFiles(filePaths []string, commitMessage, author string) (plumbing.Hash, error)
PushChangesToRepository(username, password string) error
PushChangesToRepository(username, password string, force *bool) error
PlainClone(username, password, serverURL, directory string) error
ChangeBranch(branchName string) error
}
@@ -71,8 +71,8 @@ func (g *gitopsUpdateDeploymentGitUtils) CommitFiles(filePaths []string, commitM
return commit, nil
}
func (g *gitopsUpdateDeploymentGitUtils) PushChangesToRepository(username, password string) error {
return gitUtil.PushChangesToRepository(username, password, g.repository)
func (g *gitopsUpdateDeploymentGitUtils) PushChangesToRepository(username, password string, force *bool) error {
return gitUtil.PushChangesToRepository(username, password, force, g.repository)
}
func (g *gitopsUpdateDeploymentGitUtils) PlainClone(username, password, serverURL, directory string) error {
@@ -390,18 +390,19 @@ func runHelmCommand(command gitopsUpdateDeploymentExecRunner, config *gitopsUpda
func runKustomizeCommand(command gitopsUpdateDeploymentExecRunner, config *gitopsUpdateDeploymentOptions, filePath string) ([]byte, error) {
var kustomizeOutput = bytes.Buffer{}
command.Stdout(&kustomizeOutput)
registryImage, imageTag, err := buildRegistryPlusImageAndTagSeparately(config)
kustomizeParams := []string{
"edit",
"set",
"image",
config.DeploymentName + "=" + config.ContainerImageNameTag,
config.DeploymentName + "=" + registryImage + ":" + imageTag,
}
command.SetDir(filepath.Dir(filePath))
log.Entry().Infof("[kustomize] updating '%s'", filePath)
err := command.RunExecutable(toolKustomize, kustomizeParams...)
err = command.RunExecutable(toolKustomize, kustomizeParams...)
if err != nil {
return nil, errors.Wrap(err, "failed to execute kustomize command")
}
@@ -459,7 +460,7 @@ func commitAndPushChanges(config *gitopsUpdateDeploymentOptions, gitUtils iGitop
return [20]byte{}, errors.Wrap(err, "committing changes failed")
}
err = gitUtils.PushChangesToRepository(config.Username, config.Password)
err = gitUtils.PushChangesToRepository(config.Username, config.Password, &config.ForcePush)
if err != nil {
return [20]byte{}, errors.Wrap(err, "pushing changes failed")
}

View File

@@ -19,6 +19,7 @@ type gitopsUpdateDeploymentOptions struct {
BranchName string `json:"branchName,omitempty"`
CommitMessage string `json:"commitMessage,omitempty"`
ServerURL string `json:"serverUrl,omitempty"`
ForcePush bool `json:"forcePush,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
FilePath string `json:"filePath,omitempty"`
@@ -133,6 +134,7 @@ func addGitopsUpdateDeploymentFlags(cmd *cobra.Command, stepConfig *gitopsUpdate
cmd.Flags().StringVar(&stepConfig.BranchName, "branchName", `master`, "The name of the branch where the changes should get pushed into.")
cmd.Flags().StringVar(&stepConfig.CommitMessage, "commitMessage", os.Getenv("PIPER_commitMessage"), "The commit message of the commit that will be done to do the changes.")
cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", `https://github.com`, "GitHub server url to the repository.")
cmd.Flags().BoolVar(&stepConfig.ForcePush, "forcePush", false, "Force push to serverUrl")
cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "User name for git authentication")
cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "Password/token for git authentication.")
cmd.Flags().StringVar(&stepConfig.FilePath, "filePath", os.Getenv("PIPER_filePath"), "Relative path in the git repository to the deployment descriptor file that shall be updated. For different tools this has different semantics:\n\n * `kubectl` - path to the `deployment.yaml` that should be patched. Supports globbing.\n * `helm` - path where the helm chart will be generated into. Here no globbing is supported.\n * `kustomize` - path to the `kustomization.yaml`. Supports globbing.\n")
@@ -198,6 +200,15 @@ func gitopsUpdateDeploymentMetadata() config.StepData {
Aliases: []config.Alias{{Name: "githubServerUrl"}},
Default: `https://github.com`,
},
{
Name: "forcePush",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
Default: false,
},
{
Name: "username",
ResourceRef: []config.ResourceReference{

View File

@@ -611,7 +611,7 @@ func TestRunGitopsUpdateDeploymentWithKustomize(t *testing.T) {
Password: "validAccessToken",
FilePath: "kustomization.yaml",
ContainerRegistryURL: "https://myregistry.com",
ContainerImageNameTag: "registry/containers/myFancyContainer:1337",
ContainerImageNameTag: "containers/myFancyContainer:1337",
Tool: "kustomize",
DeploymentName: "myFancyDeployment",
}
@@ -634,7 +634,7 @@ func TestRunGitopsUpdateDeploymentWithKustomize(t *testing.T) {
assert.Equal(t, "edit", runnerMock.params[0])
assert.Equal(t, "set", runnerMock.params[1])
assert.Equal(t, "image", runnerMock.params[2])
assert.Equal(t, "myFancyDeployment=registry/containers/myFancyContainer:1337", runnerMock.params[3])
assert.Equal(t, "myFancyDeployment=myregistry.com/containers/myFancyContainer:1337", runnerMock.params[3])
})
t.Run("successful run with glob", func(t *testing.T) {
t.Parallel()
@@ -656,11 +656,21 @@ func TestRunGitopsUpdateDeploymentWithKustomize(t *testing.T) {
assert.Equal(t, "edit", runnerMock.params[0])
assert.Equal(t, "set", runnerMock.params[1])
assert.Equal(t, "image", runnerMock.params[2])
assert.Equal(t, "myFancyDeployment=registry/containers/myFancyContainer:1337", runnerMock.params[3])
assert.Equal(t, "myFancyDeployment=myregistry.com/containers/myFancyContainer:1337", runnerMock.params[3])
assert.Equal(t, "edit", runnerMock.params[4])
assert.Equal(t, "set", runnerMock.params[5])
assert.Equal(t, "image", runnerMock.params[6])
assert.Equal(t, "myFancyDeployment=registry/containers/myFancyContainer:1337", runnerMock.params[7])
assert.Equal(t, "myFancyDeployment=myregistry.com/containers/myFancyContainer:1337", runnerMock.params[7])
})
t.Run("with forcePush", func(t *testing.T) {
t.Parallel()
runner := &gitOpsExecRunnerMock{}
validConfiguration.ForcePush = true
gitUtilsMock := &gitUtilsMock{forcePush: true}
err := runGitopsUpdateDeployment(validConfiguration, runner, gitUtilsMock, &filesMock{})
assert.NoError(t, err)
assert.Equal(t, "This is the commit message", gitUtilsMock.commitMessage)
})
t.Run("error on kustomize execution", func(t *testing.T) {
@@ -802,6 +812,7 @@ type gitUtilsMock struct {
failOnCommit bool
failOnPush bool
skipClone bool
forcePush bool
}
func (gitUtilsMock) GetWorktree() (*git.Worktree, error) {
@@ -834,10 +845,13 @@ func (v *gitUtilsMock) CommitFiles(newFiles []string, commitMessage string, _ st
return [20]byte{123}, nil
}
func (v gitUtilsMock) PushChangesToRepository(string, string) error {
func (v gitUtilsMock) PushChangesToRepository(_ string, _ string, force *bool) error {
if v.failOnPush {
return errors.New("error on push")
}
if v.forcePush && !*force {
return errors.New("expected forcePush but not defined")
}
return nil
}

View File

@@ -53,14 +53,17 @@ func commitSingleFile(filePath, commitMessage, author string, worktree utilsWork
}
// PushChangesToRepository Pushes all committed changes in the repository to the remote repository
func PushChangesToRepository(username, password string, repository *git.Repository) error {
return pushChangesToRepository(username, password, repository)
func PushChangesToRepository(username, password string, force *bool, repository *git.Repository) error {
return pushChangesToRepository(username, password, force, repository)
}
func pushChangesToRepository(username, password string, repository utilsRepository) error {
func pushChangesToRepository(username, password string, force *bool, repository utilsRepository) error {
pushOptions := &git.PushOptions{
Auth: &http.BasicAuth{Username: username, Password: password},
}
if force != nil {
pushOptions.Force = *force
}
err := repository.Push(pushOptions)
if err != nil {
return errors.Wrap(err, "failed to push commit")

View File

@@ -46,7 +46,7 @@ func TestPushChangesToRepository(t *testing.T) {
t.Parallel()
t.Run("successful push", func(t *testing.T) {
t.Parallel()
err := pushChangesToRepository("user", "password", RepositoryMock{
err := pushChangesToRepository("user", "password", nil, RepositoryMock{
test: t,
})
assert.NoError(t, err)
@@ -54,7 +54,7 @@ func TestPushChangesToRepository(t *testing.T) {
t.Run("error pushing", func(t *testing.T) {
t.Parallel()
err := pushChangesToRepository("user", "password", RepositoryMockError{})
err := pushChangesToRepository("user", "password", nil, RepositoryMockError{})
assert.EqualError(t, err, "failed to push commit: error on push commits")
})
}

View File

@@ -52,6 +52,24 @@ spec:
type: string
default: https://github.com
mandatory: true
- name: forcePush
type: bool
description: Force push to serverUrl
longDescription: |
To bypass branch-protections the git push command can be forced.
Example:
```yaml
steps:
gitopsUpdateDeployment:
forcePush: true
```
scope:
- PARAMETERS
- STAGES
- STEPS
mandatory: false
default: false
- name: username
type: string
description: User name for git authentication