Skip to content

Commit

Permalink
fix (gitOpsUpdateDeployment) add CA bundle options to plain clone and…
Browse files Browse the repository at this point in the history
… commit to trust enterprise github instances (#4602)

* downloading ca cert bundle when added as config

* adding logging statements

* allowing bats test to handle ca cert

* adding info message

* hard coding file names

* including correct http client util bundle

* removing logging message not needed

* adding cert bundle to commit and push

* improving the condition to add ca cert in commit and push

* fixing unit test

* fixing unit test

* fixing unit test

* fixing unit test

* fixing unit test
  • Loading branch information
anilkeshav27 authored Sep 28, 2023
1 parent ccd2acf commit b34ea9e
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 46 deletions.
4 changes: 3 additions & 1 deletion cmd/batsExecuteTests.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ func runBatsExecuteTests(config *batsExecuteTestsOptions, telemetryData *telemet
}

func (b *batsExecuteTestsUtilsBundle) CloneRepo(URL string) error {
_, err := pipergit.PlainClone("", "", URL, "bats-core")
// ToDo: BatsExecute test needs to check if the repo can come from a
// enterprise github instance and needs ca-cert handelling seperately
_, err := pipergit.PlainClone("", "", URL, "bats-core", []byte{})
return err

}
90 changes: 72 additions & 18 deletions cmd/gitopsUpdateDeployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@ package cmd
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"time"

"github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/docker"
gitUtil "github.com/SAP/jenkins-library/pkg/git"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/pkg/errors"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"time"
)

const toolKubectl = "kubectl"
Expand All @@ -27,15 +31,16 @@ const toolKustomize = "kustomize"

type iGitopsUpdateDeploymentGitUtils interface {
CommitFiles(filePaths []string, commitMessage, author string) (plumbing.Hash, error)
PushChangesToRepository(username, password string, force *bool) error
PlainClone(username, password, serverURL, directory string) error
PushChangesToRepository(username, password string, force *bool, caCerts []byte) error
PlainClone(username, password, serverURL, directory string, caCerts []byte) error
ChangeBranch(branchName string) error
}

type gitopsUpdateDeploymentFileUtils interface {
TempDir(dir, pattern string) (name string, err error)
RemoveAll(path string) error
FileWrite(path string, content []byte, perm os.FileMode) error
FileRead(path string) ([]byte, error)
Glob(pattern string) ([]string, error)
}

Expand All @@ -51,6 +56,25 @@ type gitopsUpdateDeploymentGitUtils struct {
repository *git.Repository
}

type gitopsUpdateDeploymentUtilsBundle struct {
*piperhttp.Client
}

type gitopsUpdateDeploymentUtils interface {
DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error
}

func newGitopsUpdateDeploymentUtilsBundle() gitopsUpdateDeploymentUtils {
utils := gitopsUpdateDeploymentUtilsBundle{
Client: &piperhttp.Client{},
}
return &utils
}

func (g *gitopsUpdateDeploymentUtilsBundle) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error {
return g.Client.DownloadFile(url, filename, header, cookies)
}

func (g *gitopsUpdateDeploymentGitUtils) CommitFiles(filePaths []string, commitMessage, author string) (plumbing.Hash, error) {
for _, path := range filePaths {
_, err := g.worktree.Add(path)
Expand All @@ -71,13 +95,13 @@ func (g *gitopsUpdateDeploymentGitUtils) CommitFiles(filePaths []string, commitM
return commit, nil
}

func (g *gitopsUpdateDeploymentGitUtils) PushChangesToRepository(username, password string, force *bool) error {
return gitUtil.PushChangesToRepository(username, password, force, g.repository)
func (g *gitopsUpdateDeploymentGitUtils) PushChangesToRepository(username, password string, force *bool, caCerts []byte) error {
return gitUtil.PushChangesToRepository(username, password, force, g.repository, caCerts)
}

func (g *gitopsUpdateDeploymentGitUtils) PlainClone(username, password, serverURL, directory string) error {
func (g *gitopsUpdateDeploymentGitUtils) PlainClone(username, password, serverURL, directory string, caCerts []byte) error {
var err error
g.repository, err = gitUtil.PlainClone(username, password, serverURL, directory)
g.repository, err = gitUtil.PlainClone(username, password, serverURL, directory, caCerts)
if err != nil {
return errors.Wrapf(err, "plain clone failed '%s'", serverURL)
}
Expand Down Expand Up @@ -126,7 +150,12 @@ func runGitopsUpdateDeployment(config *gitopsUpdateDeploymentOptions, command gi
}
}()

err = cloneRepositoryAndChangeBranch(config, gitUtils, temporaryFolder)
certs, err := downloadCACertbunde(config.CustomTLSCertificateLinks, gitUtils, fileUtils)
if err != nil {
return err
}

err = cloneRepositoryAndChangeBranch(config, gitUtils, fileUtils, temporaryFolder, certs)
if err != nil {
return errors.Wrap(err, "repository could not get prepared")
}
Expand Down Expand Up @@ -190,7 +219,7 @@ func runGitopsUpdateDeployment(config *gitopsUpdateDeploymentOptions, command gi
}
}

commit, err := commitAndPushChanges(config, gitUtils, allFiles)
commit, err := commitAndPushChanges(config, gitUtils, allFiles, certs)
if err != nil {
return errors.Wrap(err, "failed to commit and push changes")
}
Expand Down Expand Up @@ -292,8 +321,9 @@ func logNotRequiredButFilledFieldForKustomize(config *gitopsUpdateDeploymentOpti
}
}

func cloneRepositoryAndChangeBranch(config *gitopsUpdateDeploymentOptions, gitUtils iGitopsUpdateDeploymentGitUtils, temporaryFolder string) error {
err := gitUtils.PlainClone(config.Username, config.Password, config.ServerURL, temporaryFolder)
func cloneRepositoryAndChangeBranch(config *gitopsUpdateDeploymentOptions, gitUtils iGitopsUpdateDeploymentGitUtils, fileUtils gitopsUpdateDeploymentFileUtils, temporaryFolder string, certs []byte) error {

err := gitUtils.PlainClone(config.Username, config.Password, config.ServerURL, temporaryFolder, certs)
if err != nil {
return errors.Wrap(err, "failed to plain clone repository")
}
Expand All @@ -305,6 +335,30 @@ func cloneRepositoryAndChangeBranch(config *gitopsUpdateDeploymentOptions, gitUt
return nil
}

func downloadCACertbunde(customTlsCertificateLinks []string, gitUtils iGitopsUpdateDeploymentGitUtils, fileUtils gitopsUpdateDeploymentFileUtils) ([]byte, error) {
certs := []byte{}
utils := newGitopsUpdateDeploymentUtilsBundle()
if len(customTlsCertificateLinks) > 0 {
for _, customTlsCertificateLink := range customTlsCertificateLinks {
log.Entry().Infof("Downloading CA certs %s into file '%s'", customTlsCertificateLink, path.Base(customTlsCertificateLink))
err := utils.DownloadFile(customTlsCertificateLink, path.Base(customTlsCertificateLink), nil, nil)
if err != nil {
return certs, nil
}

content, err := fileUtils.FileRead(path.Base(customTlsCertificateLink))
if err != nil {
return certs, nil
}
log.Entry().Infof("CA certs added successfully to cert pool")

certs = append(certs, content...)
}
}

return certs, nil
}

func executeKubectl(config *gitopsUpdateDeploymentOptions, command gitopsUpdateDeploymentExecRunner, filePath string) ([]byte, error) {
var outputBytes []byte
registryImage, err := buildRegistryPlusImage(config)
Expand Down Expand Up @@ -444,7 +498,7 @@ func buildRegistryPlusImageAndTagSeparately(config *gitopsUpdateDeploymentOption

}

func commitAndPushChanges(config *gitopsUpdateDeploymentOptions, gitUtils iGitopsUpdateDeploymentGitUtils, filePaths []string) (plumbing.Hash, error) {
func commitAndPushChanges(config *gitopsUpdateDeploymentOptions, gitUtils iGitopsUpdateDeploymentGitUtils, filePaths []string, certs []byte) (plumbing.Hash, error) {
commitMessage := config.CommitMessage

if commitMessage == "" {
Expand All @@ -456,7 +510,7 @@ func commitAndPushChanges(config *gitopsUpdateDeploymentOptions, gitUtils iGitop
return [20]byte{}, errors.Wrap(err, "committing changes failed")
}

err = gitUtils.PushChangesToRepository(config.Username, config.Password, &config.ForcePush)
err = gitUtils.PushChangesToRepository(config.Username, config.Password, &config.ForcePush, certs)
if err != nil {
return [20]byte{}, errors.Wrap(err, "pushing changes failed")
}
Expand Down
39 changes: 25 additions & 14 deletions cmd/gitopsUpdateDeployment_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions cmd/gitopsUpdateDeployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ type filesMock struct {
failOnCreation bool
failOnDeletion bool
failOnWrite bool
failOnRead bool
failOnGlob bool
path string
}
Expand All @@ -783,6 +784,13 @@ func (f filesMock) FileWrite(path string, content []byte, perm os.FileMode) erro
return piperutils.Files{}.FileWrite(path, content, perm)
}

func (f filesMock) FileRead(path string) ([]byte, error) {
if f.failOnRead {
return []byte{}, errors.New("error appeared")
}
return piperutils.Files{}.FileRead(path)
}

func (f filesMock) TempDir(dir string, pattern string) (name string, err error) {
if f.failOnCreation {
return "", errors.New("error appeared")
Expand Down Expand Up @@ -848,7 +856,7 @@ func (v *gitUtilsMock) CommitFiles(newFiles []string, commitMessage string, _ st
return [20]byte{123}, nil
}

func (v gitUtilsMock) PushChangesToRepository(_ string, _ string, force *bool) error {
func (v gitUtilsMock) PushChangesToRepository(_ string, _ string, force *bool, caCerts []byte) error {
if v.failOnPush {
return errors.New("error on push")
}
Expand All @@ -858,7 +866,7 @@ func (v gitUtilsMock) PushChangesToRepository(_ string, _ string, force *bool) e
return nil
}

func (v *gitUtilsMock) PlainClone(_, _, _, directory string) error {
func (v *gitUtilsMock) PlainClone(_, _, _, directory string, caCerts []byte) error {
if v.skipClone {
return nil
}
Expand Down
24 changes: 17 additions & 7 deletions pkg/git/git.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package git

import (
"time"

"github.com/SAP/jenkins-library/pkg/log"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/pkg/errors"
"time"
)

// utilsWorkTree interface abstraction of git.Worktree to enable tests
Expand Down Expand Up @@ -53,14 +54,18 @@ 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, force *bool, repository *git.Repository) error {
return pushChangesToRepository(username, password, force, repository)
func PushChangesToRepository(username, password string, force *bool, repository *git.Repository, caCerts []byte) error {
return pushChangesToRepository(username, password, force, repository, caCerts)
}

func pushChangesToRepository(username, password string, force *bool, repository utilsRepository) error {
func pushChangesToRepository(username, password string, force *bool, repository utilsRepository, caCerts []byte) error {
pushOptions := &git.PushOptions{
Auth: &http.BasicAuth{Username: username, Password: password},
}

if len(caCerts) > 0 {
pushOptions.CABundle = caCerts
}
if force != nil {
pushOptions.Force = *force
}
Expand All @@ -72,16 +77,21 @@ func pushChangesToRepository(username, password string, force *bool, repository
}

// PlainClone Clones a non-bare repository to the provided directory
func PlainClone(username, password, serverURL, directory string) (*git.Repository, error) {
func PlainClone(username, password, serverURL, directory string, caCerts []byte) (*git.Repository, error) {
abstractedGit := &abstractionGit{}
return plainClone(username, password, serverURL, directory, abstractedGit)
return plainClone(username, password, serverURL, directory, abstractedGit, caCerts)
}

func plainClone(username, password, serverURL, directory string, abstractionGit utilsGit) (*git.Repository, error) {
func plainClone(username, password, serverURL, directory string, abstractionGit utilsGit, caCerts []byte) (*git.Repository, error) {
gitCloneOptions := git.CloneOptions{
Auth: &http.BasicAuth{Username: username, Password: password},
URL: serverURL,
}

if len(caCerts) > 0 {
gitCloneOptions.CABundle = caCerts
}

repository, err := abstractionGit.plainClone(directory, false, &gitCloneOptions)
if err != nil {
return nil, errors.Wrap(err, "failed to clone git")
Expand Down
8 changes: 4 additions & 4 deletions pkg/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ func TestPushChangesToRepository(t *testing.T) {
t.Parallel()
err := pushChangesToRepository("user", "password", nil, RepositoryMock{
test: t,
})
}, []byte{})
assert.NoError(t, err)
})

t.Run("error pushing", func(t *testing.T) {
t.Parallel()
err := pushChangesToRepository("user", "password", nil, RepositoryMockError{})
err := pushChangesToRepository("user", "password", nil, RepositoryMockError{}, []byte{})
assert.EqualError(t, err, "failed to push commit: error on push commits")
})
}
Expand All @@ -67,7 +67,7 @@ func TestPlainClone(t *testing.T) {
t.Run("successful clone", func(t *testing.T) {
t.Parallel()
abstractedGit := &UtilsGitMock{}
_, err := plainClone("user", "password", "URL", "directory", abstractedGit)
_, err := plainClone("user", "password", "URL", "directory", abstractedGit, []byte{})
assert.NoError(t, err)
assert.Equal(t, "directory", abstractedGit.path)
assert.False(t, abstractedGit.isBare)
Expand All @@ -78,7 +78,7 @@ func TestPlainClone(t *testing.T) {
t.Run("error on cloning", func(t *testing.T) {
t.Parallel()
abstractedGit := UtilsGitMockError{}
_, err := plainClone("user", "password", "URL", "directory", abstractedGit)
_, err := plainClone("user", "password", "URL", "directory", abstractedGit, []byte{})
assert.EqualError(t, err, "failed to clone git: error during clone")
})
}
Expand Down
Loading

0 comments on commit b34ea9e

Please sign in to comment.