From 7f719abeae3352dc4c89decd6a866c793f0c15f1 Mon Sep 17 00:00:00 2001 From: Simon Dold <48808400+doldsimo@users.noreply.github.com> Date: Tue, 25 Jun 2024 10:09:31 +0200 Subject: [PATCH] Adapt clone step to work with customer-managed Repos (BYOG) (#4966) * adding byog credentials for clone command * adding unit tests for clone body * adding parameters * adding optional byog parameters * fixing typo in username * remove aliases in config yaml * change yaml config * logs * change info log * change logs * remove logs * adding log statements * remove log statements * fixing typo in test class * change repoTest structure * remove comment * remove comment * generate * adding unit test comments * adding error handling * adding isByog check * fixing unit test * generate * Update manageGitRepositoryUtils_test.go * restructure isByog parameter * adding empty line for md linter * adding config.yaml example to docs * Update documentation/docs/steps/abapEnvironmentCloneGitRepo.md Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com> * Update documentation/docs/steps/abapEnvironmentCloneGitRepo.md Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com> * Update documentation/docs/steps/abapEnvironmentCloneGitRepo.md Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com> * Update documentation/docs/steps/abapEnvironmentCloneGitRepo.md Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com> * Update documentation/docs/steps/abapEnvironmentCloneGitRepo.md Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com> * adding release --------- Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com> --- cmd/abapEnvironmentCloneGitRepo.go | 10 ++- cmd/abapEnvironmentCloneGitRepo_generated.go | 50 +++++++++++++ .../docs/steps/abapEnvironmentCloneGitRepo.md | 36 +++++++++ pkg/abaputils/abaputils.go | 3 + pkg/abaputils/descriptor.go | 4 + pkg/abaputils/manageGitRepositoryUtils.go | 36 ++++++++- .../manageGitRepositoryUtils_test.go | 2 +- pkg/abaputils/sap_com_0510.go | 23 +++--- pkg/abaputils/sap_com_0510_test.go | 2 +- pkg/abaputils/sap_com_0948.go | 35 ++++++--- pkg/abaputils/sap_com_0948_test.go | 75 +++++++++++++++---- pkg/abaputils/softwareComponentApiManager.go | 7 +- .../metadata/abapEnvironmentCloneGitRepo.yaml | 39 ++++++++++ vars/abapEnvironmentCloneGitRepo.groovy | 3 +- 14 files changed, 281 insertions(+), 44 deletions(-) diff --git a/cmd/abapEnvironmentCloneGitRepo.go b/cmd/abapEnvironmentCloneGitRepo.go index 687044dd77..71cfe60abd 100644 --- a/cmd/abapEnvironmentCloneGitRepo.go +++ b/cmd/abapEnvironmentCloneGitRepo.go @@ -42,7 +42,7 @@ func runAbapEnvironmentCloneGitRepo(config *abapEnvironmentCloneGitRepoOptions, return errors.Wrap(errConfig, "The provided configuration is not allowed") } - repositories, errGetRepos := abaputils.GetRepositories(&abaputils.RepositoriesConfig{BranchName: config.BranchName, RepositoryName: config.RepositoryName, Repositories: config.Repositories}, false) + repositories, errGetRepos := abaputils.GetRepositories(&abaputils.RepositoriesConfig{BranchName: config.BranchName, RepositoryName: config.RepositoryName, Repositories: config.Repositories, ByogUsername: config.ByogUsername, ByogPassword: config.ByogPassword, ByogAuthMethod: config.ByogAuthMethod}, false) if errGetRepos != nil { return errors.Wrap(errGetRepos, "Could not read repositories") } @@ -85,12 +85,15 @@ func cloneSingleRepo(apiManager abaputils.SoftwareComponentApiManagerInterface, log.Entry().Info("Start cloning " + logString) abaputils.AddDefaultDashedLine(1) - alreadyCloned, activeBranch, errCheckCloned := api.GetRepository() + alreadyCloned, activeBranch, errCheckCloned, isByog := api.GetRepository() if errCheckCloned != nil { return errors.Wrapf(errCheckCloned, errorString) } if !alreadyCloned { + if isByog { + api.UpdateRepoWithBYOGCredentials(config.ByogAuthMethod, config.ByogUsername, config.ByogPassword) + } errClone := api.Clone() if errClone != nil { return errors.Wrapf(errClone, errorString) @@ -186,5 +189,8 @@ func convertCloneConfig(config *abapEnvironmentCloneGitRepoOptions) abaputils.Ab subOptions.Host = config.Host subOptions.Password = config.Password subOptions.Username = config.Username + subOptions.ByogUsername = config.ByogUsername + subOptions.ByogPassword = config.ByogPassword + subOptions.ByogAuthMethod = config.ByogAuthMethod return subOptions } diff --git a/cmd/abapEnvironmentCloneGitRepo_generated.go b/cmd/abapEnvironmentCloneGitRepo_generated.go index 9cba6305e5..8e160194f1 100644 --- a/cmd/abapEnvironmentCloneGitRepo_generated.go +++ b/cmd/abapEnvironmentCloneGitRepo_generated.go @@ -18,6 +18,9 @@ import ( type abapEnvironmentCloneGitRepoOptions struct { Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` + ByogUsername string `json:"byogUsername,omitempty"` + ByogPassword string `json:"byogPassword,omitempty"` + ByogAuthMethod string `json:"byogAuthMethod,omitempty"` Repositories string `json:"repositories,omitempty"` RepositoryName string `json:"repositoryName,omitempty"` BranchName string `json:"branchName,omitempty"` @@ -68,6 +71,8 @@ Please provide either of the following options: } log.RegisterSecret(stepConfig.Username) log.RegisterSecret(stepConfig.Password) + log.RegisterSecret(stepConfig.ByogUsername) + log.RegisterSecret(stepConfig.ByogPassword) if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 { sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID) @@ -138,6 +143,9 @@ Please provide either of the following options: func addAbapEnvironmentCloneGitRepoFlags(cmd *cobra.Command, stepConfig *abapEnvironmentCloneGitRepoOptions) { cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "User for either the Cloud Foundry API or the Communication Arrangement for SAP_COM_0948") cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "Password for either the Cloud Foundry API or the Communication Arrangement for SAP_COM_0948") + cmd.Flags().StringVar(&stepConfig.ByogUsername, "byogUsername", os.Getenv("PIPER_byogUsername"), "Username for bring your own git (BYOG) authentication") + cmd.Flags().StringVar(&stepConfig.ByogPassword, "byogPassword", os.Getenv("PIPER_byogPassword"), "Password for bring your own git (BYOG) authentication") + cmd.Flags().StringVar(&stepConfig.ByogAuthMethod, "byogAuthMethod", os.Getenv("PIPER_byogAuthMethod"), "Specifies which authentication method is used for bring your own git (BYOG) repositories") cmd.Flags().StringVar(&stepConfig.Repositories, "repositories", os.Getenv("PIPER_repositories"), "Specifies a YAML file containing the repositories configuration") cmd.Flags().StringVar(&stepConfig.RepositoryName, "repositoryName", os.Getenv("PIPER_repositoryName"), "Specifies a repository (Software Components) on the SAP BTP ABAP Environment system") cmd.Flags().StringVar(&stepConfig.BranchName, "branchName", os.Getenv("PIPER_branchName"), "Specifies a branch of a repository (Software Components) on the SAP BTP ABAP Environment system") @@ -151,6 +159,8 @@ func addAbapEnvironmentCloneGitRepoFlags(cmd *cobra.Command, stepConfig *abapEnv cmd.MarkFlagRequired("username") cmd.MarkFlagRequired("password") + cmd.MarkFlagRequired("byogUsername") + cmd.MarkFlagRequired("byogPassword") } // retrieve step metadata @@ -165,6 +175,7 @@ func abapEnvironmentCloneGitRepoMetadata() config.StepData { Inputs: config.StepInputs{ Secrets: []config.StepSecrets{ {Name: "abapCredentialsId", Description: "Jenkins credentials ID containing user and password to authenticate to the BTP ABAP Environment system or the Cloud Foundry API", Type: "jenkins", Aliases: []config.Alias{{Name: "cfCredentialsId", Deprecated: false}, {Name: "credentialsId", Deprecated: false}}}, + {Name: "byogCredentialsId", Description: "Jenkins credentials ID containing ByogUsername and ByogPassword to authenticate to a software component which is used in a BYOG scenario. (https://help.sap.com/docs/btp/sap-business-technology-platform/cloning-software-components-to-abap-environment-system-383ce2f9e2eb40f1b8ad538ddf79e656)", Type: "jenkins"}, }, Parameters: []config.StepParameters{ { @@ -197,6 +208,45 @@ func abapEnvironmentCloneGitRepoMetadata() config.StepData { Aliases: []config.Alias{}, Default: os.Getenv("PIPER_password"), }, + { + Name: "byogUsername", + ResourceRef: []config.ResourceReference{ + { + Name: "byogCredentialsId", + Param: "username", + Type: "secret", + }, + }, + Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"}, + Type: "string", + Mandatory: true, + Aliases: []config.Alias{}, + Default: os.Getenv("PIPER_byogUsername"), + }, + { + Name: "byogPassword", + ResourceRef: []config.ResourceReference{ + { + Name: "byogCredentialsId", + Param: "password", + Type: "secret", + }, + }, + Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"}, + Type: "string", + Mandatory: true, + Aliases: []config.Alias{}, + Default: os.Getenv("PIPER_byogPassword"), + }, + { + Name: "byogAuthMethod", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: os.Getenv("PIPER_byogAuthMethod"), + }, { Name: "repositories", ResourceRef: []config.ResourceReference{}, diff --git a/documentation/docs/steps/abapEnvironmentCloneGitRepo.md b/documentation/docs/steps/abapEnvironmentCloneGitRepo.md index 8bdf906a74..53c88aec7c 100644 --- a/documentation/docs/steps/abapEnvironmentCloneGitRepo.md +++ b/documentation/docs/steps/abapEnvironmentCloneGitRepo.md @@ -97,3 +97,39 @@ abapEnvironmentCloneGitRepo ( cfServiceKeyName: 'cfServiceKeyName' ) ``` + +## Example: Cloning a Bring Your Own Git (BYOG) repository + +> Feature will be available in November 2024. + +Since a ByoG repository is an external repository, you must be authenticated to clone it. +For this, the corresponding credentials must be stored in Jenkins as a username and password/token. + + Store the credentials:
+A new credential with the type username and password must be stored.
+`Jenkins Dashboard > Manage Jenkins > Credentials`
+These credentials are used to clone the ByoG repository. +More information on configuring the credentials can be found [here](https://www.jenkins.io/doc/book/using/using-credentials/). + +The config.yaml should look like this: + +```yaml +steps: + abapEnvironmentCloneGitRepo: + repositories: 'repos.yaml' + byogCredentialsId: 'byogCredentialsId' + abapCredentialsId: 'abapCredentialsId' + host: '1234-abcd-5678-efgh-ijk.abap.eu10.hana.ondemand.com' +``` + +`byogCredentialsId: 'byogCredentialsId'` is the reference to the defined credential in Jenkins. So take care that this matches with your setup. + +After that, the ByoG repository that is to be cloned must be specified in the repos.yaml: + +```yaml +repositories: + - name: '/DMO/REPO_BYOG' + branch: 'main' +``` + +After the pipeline has run through, the repository has been cloned. diff --git a/pkg/abaputils/abaputils.go b/pkg/abaputils/abaputils.go index caf6cb9938..2f43a003c6 100644 --- a/pkg/abaputils/abaputils.go +++ b/pkg/abaputils/abaputils.go @@ -285,6 +285,9 @@ type AbapEnvironmentRunATCCheckOptions struct { type AbapEnvironmentOptions struct { Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` + ByogUsername string `json:"byogUsername,omitempty"` + ByogPassword string `json:"byogPassword,omitempty"` + ByogAuthMethod string `json:"byogAuthMethod,omitempty"` Host string `json:"host,omitempty"` CfAPIEndpoint string `json:"cfApiEndpoint,omitempty"` CfOrg string `json:"cfOrg,omitempty"` diff --git a/pkg/abaputils/descriptor.go b/pkg/abaputils/descriptor.go index e63e9c2196..1a044d23e4 100644 --- a/pkg/abaputils/descriptor.go +++ b/pkg/abaputils/descriptor.go @@ -42,6 +42,10 @@ type Repository struct { Tag string `json:"tag,omitempty"` Branch string `json:"branch,omitempty"` CommitID string `json:"commitID,omitempty"` + ByogUsername string `json:"byogUsername"` + ByogPassword string `json:"byogPassword"` + ByogAuthMethod string `json:"byogAuthMethod"` + IsByog bool `json:",omitempty"` VersionYAML string `json:"version,omitempty"` Version string `json:"versionAAK"` AdditionalPiecelist string `json:"additionalPiecelist,omitempty"` diff --git a/pkg/abaputils/manageGitRepositoryUtils.go b/pkg/abaputils/manageGitRepositoryUtils.go index d53755a50e..73da35c043 100644 --- a/pkg/abaputils/manageGitRepositoryUtils.go +++ b/pkg/abaputils/manageGitRepositoryUtils.go @@ -163,7 +163,7 @@ func printHeader(logEntry LogResultsV2, api SoftwareComponentApiInterface) { } } -// GetRepositories for parsing one or multiple branches and repositories from repositories file or branchName and repositoryName configuration +// GetRepositories for parsing one or multiple branches and repositories from repositories file or branchName and repositoryName configuration func GetRepositories(config *RepositoriesConfig, branchRequired bool) ([]Repository, error) { var repositories = make([]Repository, 0) if reflect.DeepEqual(RepositoriesConfig{}, config) { @@ -193,6 +193,7 @@ func GetRepositories(config *RepositoriesConfig, branchRequired bool) ([]Reposit if config.RepositoryName != "" && !branchRequired { repositories = append(repositories, Repository{Name: config.RepositoryName, CommitID: config.CommitID}) } + if len(config.RepositoryNames) > 0 { for _, repository := range config.RepositoryNames { repositories = append(repositories, Repository{Name: repository}) @@ -210,6 +211,25 @@ func (repo *Repository) GetRequestBodyForCommitOrTag() (requestBodyString string return requestBodyString } +func (repo *Repository) GetRequestBodyForBYOGCredentials() (string, error) { + var byogBodyString string + + if repo.ByogAuthMethod != "" { + byogBodyString += `, "auth_method":"` + repo.ByogAuthMethod + `"` + } + if repo.ByogUsername != "" { + byogBodyString += `, "username":"` + repo.ByogUsername + `"` + } else { + return "", fmt.Errorf("Failed to get BYOG credentials: %w", errors.New("Username for BYOG is missing, please provide git username to authenticate")) + } + if repo.ByogPassword != "" { + byogBodyString += `, "password":"` + repo.ByogPassword + `"` + } else { + return "", fmt.Errorf("Failed to get BYOG credentials: %w", errors.New("Password/Token for BYOG is missing, please provide git password or token to authenticate")) + } + return byogBodyString, nil +} + func (repo *Repository) GetLogStringForCommitOrTag() (logString string) { if repo.CommitID != "" { logString = ", commit '" + repo.CommitID + "'" @@ -228,13 +248,21 @@ func (repo *Repository) GetCloneRequestBodyWithSWC() (body string) { return body } -func (repo *Repository) GetCloneRequestBody() (body string) { +func (repo *Repository) GetCloneRequestBody() (body string, err error) { if repo.CommitID != "" && repo.Tag != "" { log.Entry().WithField("Tag", repo.Tag).WithField("Commit ID", repo.CommitID).Info("The commit ID takes precedence over the tag") } requestBodyString := repo.GetRequestBodyForCommitOrTag() - body = `{"branch_name":"` + repo.Branch + `"` + requestBodyString + `}` - return body + var byogBodyString = "" + if repo.IsByog { + byogBodyString, err = repo.GetRequestBodyForBYOGCredentials() + if err != nil { + return "", err + } + } + + body = `{"branch_name":"` + repo.Branch + `"` + requestBodyString + byogBodyString + `}` + return body, nil } func (repo *Repository) GetCloneLogString() (logString string) { diff --git a/pkg/abaputils/manageGitRepositoryUtils_test.go b/pkg/abaputils/manageGitRepositoryUtils_test.go index ffd96a614b..557c742cea 100644 --- a/pkg/abaputils/manageGitRepositoryUtils_test.go +++ b/pkg/abaputils/manageGitRepositoryUtils_test.go @@ -268,7 +268,7 @@ func TestCreateRequestBodies(t *testing.T) { CommitID: "1234567", Tag: "myTag", } - body := repo.GetCloneRequestBody() + body, _ := repo.GetCloneRequestBody() assert.Equal(t, `{"branch_name":"main", "commit_id":"1234567"}`, body, "Expected different body") }) t.Run("Clone Body Tag", func(t *testing.T) { diff --git a/pkg/abaputils/sap_com_0510.go b/pkg/abaputils/sap_com_0510.go index f543c9ff6f..24d3d99251 100644 --- a/pkg/abaputils/sap_com_0510.go +++ b/pkg/abaputils/sap_com_0510.go @@ -225,10 +225,10 @@ func (api *SAP_COM_0510) GetAction() (string, error) { return abapStatusCode, nil } -func (api *SAP_COM_0510) GetRepository() (bool, string, error) { +func (api *SAP_COM_0510) GetRepository() (bool, string, error, bool) { if api.repository.Name == "" { - return false, "", errors.New("An empty string was passed for the parameter 'repositoryName'") + return false, "", errors.New("An empty string was passed for the parameter 'repositoryName'"), false } swcConnectionDetails := api.con @@ -236,7 +236,7 @@ func (api *SAP_COM_0510) GetRepository() (bool, string, error) { resp, err := GetHTTPResponse("GET", swcConnectionDetails, nil, api.client) if err != nil { _, errRepo := HandleHTTPError(resp, err, "Reading the Repository / Software Component failed", api.con) - return false, "", errRepo + return false, "", errRepo, false } defer resp.Body.Close() @@ -244,25 +244,25 @@ func (api *SAP_COM_0510) GetRepository() (bool, string, error) { var abapResp map[string]*json.RawMessage bodyText, errRead := io.ReadAll(resp.Body) if errRead != nil { - return false, "", err + return false, "", err, false } if err := json.Unmarshal(bodyText, &abapResp); err != nil { - return false, "", err + return false, "", err, false } if err := json.Unmarshal(*abapResp["d"], &body); err != nil { - return false, "", err + return false, "", err, false } if reflect.DeepEqual(RepositoryEntity{}, body) { log.Entry().WithField("StatusCode", resp.Status).WithField("repositoryName", api.repository.Name).WithField("branchName", api.repository.Branch).WithField("commitID", api.repository.CommitID).WithField("Tag", api.repository.Tag).Error("Could not Clone the Repository / Software Component") err := errors.New("Request to ABAP System not successful") - return false, "", err + return false, "", err, false } if body.AvailOnInst { - return true, body.ActiveBranch, nil + return true, body.ActiveBranch, nil, false } - return false, "", err + return false, "", err, false } @@ -399,3 +399,8 @@ func (api *SAP_COM_0510) ConvertTime(logTimeStamp string) time.Time { t := time.Unix(n, 0).UTC() return t } + +// Dummy implementation of the "optional" method UpdateRepoWithBYOGCredentials (only used in SAP_COM_0948) +func (api *SAP_COM_0510) UpdateRepoWithBYOGCredentials(byogAuthMethod string, byogUsername string, byogPassword string) { + panic("UpdateRepoWithBYOGCredentials cannot be used in SAP_COM_0510") +} diff --git a/pkg/abaputils/sap_com_0510_test.go b/pkg/abaputils/sap_com_0510_test.go index 28c3af01e6..1e953560c0 100644 --- a/pkg/abaputils/sap_com_0510_test.go +++ b/pkg/abaputils/sap_com_0510_test.go @@ -368,7 +368,7 @@ func TestGetRepo(t *testing.T) { assert.NoError(t, err) assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type") - cloned, activeBranch, errAction := api.GetRepository() + cloned, activeBranch, errAction, _ := api.GetRepository() assert.True(t, cloned) assert.Equal(t, "testBranch1", activeBranch) assert.NoError(t, errAction) diff --git a/pkg/abaputils/sap_com_0948.go b/pkg/abaputils/sap_com_0948.go index de1ac13eba..ef25db3bb6 100644 --- a/pkg/abaputils/sap_com_0948.go +++ b/pkg/abaputils/sap_com_0948.go @@ -238,10 +238,10 @@ func (api *SAP_COM_0948) GetAction() (string, error) { return abapStatusCode, nil } -func (api *SAP_COM_0948) GetRepository() (bool, string, error) { +func (api *SAP_COM_0948) GetRepository() (bool, string, error, bool) { if api.repository.Name == "" { - return false, "", errors.New("An empty string was passed for the parameter 'repositoryName'") + return false, "", errors.New("An empty string was passed for the parameter 'repositoryName'"), false } swcConnectionDetails := api.con @@ -249,32 +249,44 @@ func (api *SAP_COM_0948) GetRepository() (bool, string, error) { resp, err := GetHTTPResponse("GET", swcConnectionDetails, nil, api.client) if err != nil { _, errRepo := HandleHTTPError(resp, err, "Reading the Repository / Software Component failed", api.con) - return false, "", errRepo + return false, "", errRepo, false } defer resp.Body.Close() var body RepositoryEntity bodyText, errRead := io.ReadAll(resp.Body) if errRead != nil { - return false, "", err + return false, "", err, false } if err := json.Unmarshal(bodyText, &body); err != nil { - return false, "", err + return false, "", err, false } if reflect.DeepEqual(RepositoryEntity{}, body) { log.Entry().WithField("StatusCode", resp.Status).WithField("repositoryName", api.repository.Name).WithField("branchName", api.repository.Branch).WithField("commitID", api.repository.CommitID).WithField("Tag", api.repository.Tag).Error("Could not Clone the Repository / Software Component") err := errors.New("Request to ABAP System not successful") - return false, "", err + return false, "", err, false } if body.AvailOnInst { - return true, body.ActiveBranch, nil + return true, body.ActiveBranch, nil, false } - return false, "", err + + if body.ByogUrl != "" { + return false, "", err, true + } + + return false, "", err, false } +func (api *SAP_COM_0948) UpdateRepoWithBYOGCredentials(byogAuthMethod string, byogUsername string, byogPassword string) { + api.repository.ByogAuthMethod = byogAuthMethod + api.repository.ByogUsername = byogUsername + api.repository.ByogPassword = byogPassword + api.repository.IsByog = true +} + func (api *SAP_COM_0948) Clone() error { // Trigger the Clone of a Repository @@ -284,9 +296,12 @@ func (api *SAP_COM_0948) Clone() error { cloneConnectionDetails := api.con cloneConnectionDetails.URL = api.con.URL + api.path + api.softwareComponentEntity + api.getRepoNameForPath() + api.cloneAction - body := []byte(api.repository.GetCloneRequestBody()) + body, err := api.repository.GetCloneRequestBody() + if err != nil { + return errors.Wrap(err, "Failed to clone repository") + } - return api.triggerRequest(cloneConnectionDetails, body) + return api.triggerRequest(cloneConnectionDetails, []byte(body)) } diff --git a/pkg/abaputils/sap_com_0948_test.go b/pkg/abaputils/sap_com_0948_test.go index 8855e994ef..629f23c040 100644 --- a/pkg/abaputils/sap_com_0948_test.go +++ b/pkg/abaputils/sap_com_0948_test.go @@ -23,6 +23,11 @@ func init() { repoTest0948.Name = "/DMO/REPO" repoTest0948.Branch = "main" + repoTest0948.IsByog = false + repoTest0948.ByogAuthMethod = "token" + repoTest0948.ByogUsername = "byogUser" + repoTest0948.ByogPassword = "byogToken" + } func TestRetry0948(t *testing.T) { @@ -52,7 +57,7 @@ func TestRetry0948(t *testing.T) { errAction := api.(*SAP_COM_0948).triggerRequest(ConnectionDetailsHTTP{User: "CC_USER", Password: "abc123", URL: "https://example.com/path"}, []byte("{}")) assert.NoError(t, errAction) - assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID") + assert.Equal(t, "GUID", api.getUUID(), "API does not contain correct UUID") }) @@ -82,7 +87,7 @@ func TestRetry0948(t *testing.T) { errAction := api.(*SAP_COM_0948).triggerRequest(ConnectionDetailsHTTP{User: "CC_USER", Password: "abc123", URL: "https://example.com/path"}, []byte("{}")) assert.ErrorContains(t, errAction, "HTTP 400: A4C_A2G/224 - Error Text") - assert.Empty(t, api.getUUID(), "API does not cotain correct UUID") + assert.Empty(t, api.getUUID(), "API does not contain correct UUID") }) @@ -128,7 +133,7 @@ func TestRetry0948(t *testing.T) { errAction := api.(*SAP_COM_0948).triggerRequest(ConnectionDetailsHTTP{User: "CC_USER", Password: "abc123", URL: "https://example.com/path"}, []byte("{}")) assert.ErrorContains(t, errAction, "HTTP 400: A4C_A2G/228 - Error Text") - assert.Empty(t, api.getUUID(), "API does not cotain correct UUID") + assert.Empty(t, api.getUUID(), "API does not contain correct UUID") assert.Equal(t, 6, len(client.BodyList), "Expected maxSleepTime to limit requests") }) @@ -175,7 +180,7 @@ func TestRetry0948(t *testing.T) { errAction := api.(*SAP_COM_0948).triggerRequest(ConnectionDetailsHTTP{User: "CC_USER", Password: "abc123", URL: "https://example.com/path"}, []byte("{}")) assert.ErrorContains(t, errAction, "HTTP 400: A4C_A2G/228 - Error Text") - assert.Empty(t, api.getUUID(), "API does not cotain correct UUID") + assert.Empty(t, api.getUUID(), "API does not contain correct UUID") assert.Equal(t, 5, len(client.BodyList), "Expected maxRetries to limit requests") }) @@ -201,7 +206,7 @@ func TestClone0948(t *testing.T) { errClone := api.Clone() assert.NoError(t, errClone) - assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID") + assert.Equal(t, "GUID", api.getUUID(), "API does not contain correct UUID") }) t.Run("Test Clone Failure", func(t *testing.T) { @@ -225,7 +230,7 @@ func TestClone0948(t *testing.T) { errClone := api.Clone() assert.ErrorContains(t, errClone, "Request to ABAP System not successful") - assert.Empty(t, api.getUUID(), "API does not cotain correct UUID") + assert.Empty(t, api.getUUID(), "API does not contain correct UUID") }) t.Run("Test Clone Retry", func(t *testing.T) { @@ -254,8 +259,48 @@ func TestClone0948(t *testing.T) { errClone := api.Clone() assert.NoError(t, errClone) - assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID") + assert.Equal(t, "GUID", api.getUUID(), "API does not contain correct UUID") + }) + + t.Run("Test Clone Body Success", func(t *testing.T) { + + cloneBody, _ := repoTest0948.GetCloneRequestBody() + assert.Equal(t, "{\"branch_name\":\"main\"}", string([]byte(cloneBody)), "Clone body is not correct") }) + + t.Run("Test Clone Body Failure", func(t *testing.T) { + + repoTest0948.Branch = "wrongBranch" + + cloneBody, _ := repoTest0948.GetCloneRequestBody() + assert.NotEqual(t, "{\"branch_name\":\"main\"}", string([]byte(cloneBody)), "Clone body should not match") + + repoTest0948.Branch = "main" + + }) + + t.Run("Test Clone Body BYOG Success", func(t *testing.T) { + + repoTest0948.IsByog = true + + cloneBody, _ := repoTest0948.GetCloneRequestBody() + assert.Equal(t, "{\"branch_name\":\"main\", \"auth_method\":\"token\", \"username\":\"byogUser\", \"password\":\"byogToken\"}", string([]byte(cloneBody)), "Clone body for byog parameter is not correct") + + repoTest0948.IsByog = false + }) + + t.Run("Test Clone Body BYOG Failure", func(t *testing.T) { + + repoTest0948.ByogPassword = "wrongToken" + repoTest0948.IsByog = true + + cloneBody, _ := repoTest0948.GetCloneRequestBody() + assert.NotEqual(t, "{\"branch_name\":\"main\", \"auth_method\":\"token\", \"username\":\"byogUser\", \"password\":\"byogToken\"}", string([]byte(cloneBody)), "Clone body for byog parameter should not match") + + repoTest0948.ByogPassword = "byogToken" + repoTest0948.IsByog = false + }) + } func TestPull0948(t *testing.T) { @@ -278,7 +323,7 @@ func TestPull0948(t *testing.T) { errPull := api.Pull() assert.NoError(t, errPull) - assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID") + assert.Equal(t, "GUID", api.getUUID(), "API does not contain correct UUID") }) t.Run("Test Pull Failure", func(t *testing.T) { @@ -300,7 +345,7 @@ func TestPull0948(t *testing.T) { errPull := api.Pull() assert.ErrorContains(t, errPull, "Request to ABAP System not successful") - assert.Empty(t, api.getUUID(), "API does not cotain correct UUID") + assert.Empty(t, api.getUUID(), "API does not contain correct UUID") }) } @@ -324,7 +369,7 @@ func TestCheckout0948(t *testing.T) { errCheckout := api.CheckoutBranch() assert.NoError(t, errCheckout) - assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID") + assert.Equal(t, "GUID", api.getUUID(), "API does not contain correct UUID") }) t.Run("Test Checkout Failure", func(t *testing.T) { @@ -346,7 +391,7 @@ func TestCheckout0948(t *testing.T) { errCheckoput := api.CheckoutBranch() assert.ErrorContains(t, errCheckoput, "Request to ABAP System not successful") - assert.Empty(t, api.getUUID(), "API does not cotain correct UUID") + assert.Empty(t, api.getUUID(), "API does not contain correct UUID") }) } @@ -368,7 +413,7 @@ func TestGetRepo0948(t *testing.T) { assert.NoError(t, err) assert.IsType(t, &SAP_COM_0948{}, api.(*SAP_COM_0948), "API has wrong type") - cloned, activeBranch, errAction := api.GetRepository() + cloned, activeBranch, errAction, _ := api.GetRepository() assert.True(t, cloned) assert.Equal(t, "testBranch1", activeBranch) assert.NoError(t, errAction) @@ -395,7 +440,7 @@ func TestCreateTag0948(t *testing.T) { errCreateTag := api.CreateTag(Tag{TagName: "myTag", TagDescription: "descr"}) assert.NoError(t, errCreateTag) - assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID") + assert.Equal(t, "GUID", api.getUUID(), "API does not contain correct UUID") }) t.Run("Test Tag Failure", func(t *testing.T) { @@ -417,7 +462,7 @@ func TestCreateTag0948(t *testing.T) { errCreateTag := api.CreateTag(Tag{TagName: "myTag", TagDescription: "descr"}) assert.ErrorContains(t, errCreateTag, "Request to ABAP System not successful") - assert.Empty(t, api.getUUID(), "API does not cotain correct UUID") + assert.Empty(t, api.getUUID(), "API does not contain correct UUID") }) t.Run("Test Tag Empty", func(t *testing.T) { @@ -439,7 +484,7 @@ func TestCreateTag0948(t *testing.T) { errCreateTag := api.CreateTag(Tag{}) assert.ErrorContains(t, errCreateTag, "No Tag provided") - assert.Empty(t, api.getUUID(), "API does not cotain correct UUID") + assert.Empty(t, api.getUUID(), "API does not contain correct UUID") }) } diff --git a/pkg/abaputils/softwareComponentApiManager.go b/pkg/abaputils/softwareComponentApiManager.go index c4d02e7115..0bd7b712f9 100644 --- a/pkg/abaputils/softwareComponentApiManager.go +++ b/pkg/abaputils/softwareComponentApiManager.go @@ -59,7 +59,7 @@ type SoftwareComponentApiInterface interface { setSleepTimeConfig(timeUnit time.Duration, maxSleepTime time.Duration) getSleepTime(n int) (time.Duration, error) getUUID() string - GetRepository() (bool, string, error) + GetRepository() (bool, string, error, bool) Clone() error Pull() error CheckoutBranch() error @@ -69,6 +69,7 @@ type SoftwareComponentApiInterface interface { GetLogProtocol(LogResultsV2, int) (result []LogProtocol, count int, err error) ConvertTime(logTimeStamp string) time.Time GetExecutionLog() (ExecutionLog, error) + UpdateRepoWithBYOGCredentials(string, string, string) } /**************************************** @@ -128,6 +129,7 @@ type CloneEntity struct { type RepositoryEntity struct { Metadata AbapMetadata `json:"__metadata"` ScName string `json:"sc_name"` + ByogUrl string `json:"url"` ActiveBranch string `json:"active_branch"` AvailOnInst bool `json:"avail_on_inst"` } @@ -201,6 +203,9 @@ type RepositoriesConfig struct { BranchName string CommitID string RepositoryName string + ByogUsername string + ByogPassword string + ByogAuthMethod string RepositoryNames []string Repositories string } diff --git a/resources/metadata/abapEnvironmentCloneGitRepo.yaml b/resources/metadata/abapEnvironmentCloneGitRepo.yaml index e3d3828388..746c2c3a82 100644 --- a/resources/metadata/abapEnvironmentCloneGitRepo.yaml +++ b/resources/metadata/abapEnvironmentCloneGitRepo.yaml @@ -18,6 +18,9 @@ spec: aliases: - name: cfCredentialsId - name: credentialsId + - name: byogCredentialsId + description: Jenkins credentials ID containing ByogUsername and ByogPassword to authenticate to a software component which is used in a BYOG scenario. (https://help.sap.com/docs/btp/sap-business-technology-platform/cloning-software-components-to-abap-environment-system-383ce2f9e2eb40f1b8ad538ddf79e656) + type: jenkins params: - name: username type: string @@ -45,6 +48,42 @@ spec: - name: abapCredentialsId type: secret param: password + - name: byogUsername + type: string + description: Username for bring your own git (BYOG) authentication + scope: + - PARAMETERS + - STAGES + - STEPS + - GENERAL + mandatory: true + secret: true + resourceRef: + - name: byogCredentialsId + type: secret + param: username + - name: byogPassword + type: string + description: Password for bring your own git (BYOG) authentication + scope: + - PARAMETERS + - STAGES + - STEPS + - GENERAL + mandatory: true + secret: true + resourceRef: + - name: byogCredentialsId + type: secret + param: password + - name: byogAuthMethod + type: string + description: Specifies which authentication method is used for bring your own git (BYOG) repositories + scope: + - PARAMETERS + - STAGES + - STEPS + - GENERAL - name: repositories type: string description: Specifies a YAML file containing the repositories configuration diff --git a/vars/abapEnvironmentCloneGitRepo.groovy b/vars/abapEnvironmentCloneGitRepo.groovy index 96d948cbda..46aaf03120 100644 --- a/vars/abapEnvironmentCloneGitRepo.groovy +++ b/vars/abapEnvironmentCloneGitRepo.groovy @@ -5,7 +5,8 @@ import groovy.transform.Field void call(Map parameters = [:]) { List credentials = [ - [type: 'usernamePassword', id: 'abapCredentialsId', env: ['PIPER_username', 'PIPER_password']] + [type: 'usernamePassword', id: 'abapCredentialsId', env: ['PIPER_username', 'PIPER_password']], + [type: 'usernamePassword', id: 'byogCredentialsId', env: ['PIPER_byogUsername', 'PIPER_byogPassword']] ] piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials, false, false, true) }