mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-02-21 19:48:53 +02:00
Fix(gctsDeploy) Refactoring gctsDeploy step (#2789)
* Changed gcts deploy step to include create, clone and switching branches * Added create only flag for repository * Changed the logic of Rollback * Added more logs to deployCommitToAbapSystem * Changes to deploy to abap system * Changes to deploy to abap system * Changes to condition for pullbycommit * Added Current commit deploy scope handling * Changed VCS_NO_IMPORT to take abap bool * Added delete config functionality * Functionality to parse boolean configuration * Fix to get config metadata url * Added additional error messages for switch branch * Better error dump handling * Better error dump handling contd * Added dump errors to all http requests * Error logging changes * More Unit Tests * Added more logs * Updated docs for gCTS deploy * Added scope in documentation * Removal of some nested loops, fix of unit tests * Documentation changes and more comments in the code Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com>
This commit is contained in:
parent
c43afe1355
commit
77557c41c3
@ -1,18 +1,32 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/Jeffail/gabs/v2"
|
||||
"github.com/SAP/jenkins-library/pkg/command"
|
||||
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const repoStateExists = "RepoExists"
|
||||
const repoStateNew = "RepoNew"
|
||||
|
||||
func gctsDeploy(config gctsDeployOptions, telemetryData *telemetry.CustomData) {
|
||||
// for command execution use Command
|
||||
c := command.Command{}
|
||||
// reroute command output to logging framework
|
||||
c.Stdout(log.Writer())
|
||||
c.Stderr(log.Writer())
|
||||
|
||||
// for http calls import piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||
// and use a &piperhttp.Client{} in a custom system
|
||||
@ -20,13 +34,434 @@ func gctsDeploy(config gctsDeployOptions, telemetryData *telemetry.CustomData) {
|
||||
httpClient := &piperhttp.Client{}
|
||||
|
||||
// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end
|
||||
err := deployCommit(&config, nil, httpClient)
|
||||
err := gctsDeployRepository(&config, telemetryData, &c, httpClient)
|
||||
if err != nil {
|
||||
log.Entry().WithError(err).Fatal("step execution failed")
|
||||
}
|
||||
}
|
||||
|
||||
func deployCommit(config *gctsDeployOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender) error {
|
||||
func gctsDeployRepository(config *gctsDeployOptions, telemetryData *telemetry.CustomData, command command.ExecRunner, httpClient piperhttp.Sender) error {
|
||||
cookieJar, cookieErr := cookiejar.New(nil)
|
||||
repoState := repoStateExists
|
||||
branchRollbackRequired := false
|
||||
if cookieErr != nil {
|
||||
return errors.Wrap(cookieErr, "creating a cookie jar failed")
|
||||
}
|
||||
clientOptions := piperhttp.ClientOptions{
|
||||
CookieJar: cookieJar,
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
}
|
||||
httpClient.SetOptions(clientOptions)
|
||||
log.Entry().Infof("Start of gCTS Deploy Step with Configuration Values: %v", config)
|
||||
configurationMetadata, getConfigMetadataErr := getConfigurationMetadata(config, httpClient)
|
||||
|
||||
if getConfigMetadataErr != nil {
|
||||
log.Entry().WithError(getConfigMetadataErr).Error("step execution failed at configuration metadata retrieval. Please Check if system is up!.")
|
||||
return getConfigMetadataErr
|
||||
}
|
||||
|
||||
createRepoOptions := gctsCreateRepositoryOptions{
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
Repository: config.Repository,
|
||||
Host: config.Host,
|
||||
Client: config.Client,
|
||||
RemoteRepositoryURL: config.RemoteRepositoryURL,
|
||||
Role: config.Role,
|
||||
VSID: config.VSID,
|
||||
Type: config.Type,
|
||||
}
|
||||
log.Entry().Infof("gCTS Deploy : Checking if repository %v already exists", config.Repository)
|
||||
repoMetadataInitState, getRepositoryErr := getRepository(config, httpClient)
|
||||
currentBranch := repoMetadataInitState.Result.Branch
|
||||
// If Repository does not exist in the system then Create and Clone Repository
|
||||
if getRepositoryErr != nil {
|
||||
// If scope is set for a new repository then creation/cloning of the repository cannot be done
|
||||
if config.Scope != "" {
|
||||
log.Entry().Error("Error during deploy : deploy scope cannot be provided while deploying a new repo")
|
||||
return errors.New("Error in config file")
|
||||
}
|
||||
// State of the repository set for further processing during the step
|
||||
repoState = repoStateNew
|
||||
log.Entry().Infof("gCTS Deploy : Creating Repository Step for repository : %v", config.Repository)
|
||||
// Parse the configuration parameter to a format that is accepted by the gcts api
|
||||
configurations, _ := splitConfigurationToMap(config.Configuration, *configurationMetadata)
|
||||
createErr := createRepositoryForDeploy(&createRepoOptions, telemetryData, command, httpClient, configurations)
|
||||
if createErr != nil {
|
||||
//Dump error log (Log it)
|
||||
log.Entry().WithError(createErr).Error("step execution failed at Create Repository")
|
||||
return createErr
|
||||
}
|
||||
|
||||
cloneRepoOptions := gctsCloneRepositoryOptions{
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
Repository: config.Repository,
|
||||
Host: config.Host,
|
||||
Client: config.Client,
|
||||
}
|
||||
// No Import has to be set when there is a commit or branch parameter set
|
||||
// This is required so that during the clone of the repo it is not imported into the system
|
||||
// The import would be done at a later stage with the help of gcts deploy api call
|
||||
if config.Branch != "" || config.Commit != "" {
|
||||
setNoImportAndCloneRepoErr := setNoImportAndCloneRepo(config, &cloneRepoOptions, httpClient, telemetryData)
|
||||
if setNoImportAndCloneRepoErr != nil {
|
||||
log.Entry().WithError(setNoImportAndCloneRepoErr).Error("step execution failed")
|
||||
return setNoImportAndCloneRepoErr
|
||||
}
|
||||
|
||||
} else {
|
||||
// Clone Repository and Exit the step since there is no commit or branch parameters provided
|
||||
cloneErr := cloneRepository(&cloneRepoOptions, telemetryData, httpClient)
|
||||
if cloneErr != nil {
|
||||
// Dump Error Log
|
||||
log.Entry().WithError(cloneErr).Error("step execution failed at Clone Repository")
|
||||
return cloneErr
|
||||
}
|
||||
log.Entry().Infof("gCTS Deploy : Step has completed for the repository %v : ", config.Repository)
|
||||
// End of the step.
|
||||
return nil
|
||||
}
|
||||
log.Entry().Infof("gCTS Deploy : Reading repo information after cloning repository %v : ", config.Repository)
|
||||
// Get the repository information for further processing of the step.
|
||||
repoMetadataInitState, getRepositoryErr = getRepository(config, httpClient)
|
||||
if getRepositoryErr != nil {
|
||||
// Dump Error Log
|
||||
log.Entry().WithError(getRepositoryErr).Error("step execution failed at get repository after clone")
|
||||
return getRepositoryErr
|
||||
}
|
||||
currentBranch = repoMetadataInitState.Result.Branch
|
||||
} else {
|
||||
log.Entry().Infof("Repository %v already exists in the system, Checking for deploy scope", config.Repository)
|
||||
// If deploy scope provided for an existing repository then deploy api is called and then execution ends
|
||||
if config.Scope != "" {
|
||||
log.Entry().Infof("Deploy scope exists for the repository in the configuration file")
|
||||
log.Entry().Infof("gCTS Deploy: Deploying Commit to ABAP System for Repository %v with scope %v", config.Repository, config.Scope)
|
||||
deployErr := deployCommitToAbapSystem(config, httpClient)
|
||||
if deployErr != nil {
|
||||
log.Entry().WithError(deployErr).Error("step execution failed at Deploying Commit to ABAP system.")
|
||||
return deployErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
log.Entry().Infof("Deploy scope not set in the configuration file for repository : %v", config.Repository)
|
||||
}
|
||||
// branch to which the switching has to be done to
|
||||
targetBranch := config.Branch
|
||||
if config.Branch != "" {
|
||||
// switch to a target branch, and if it fails rollback to the previous working state
|
||||
_, switchBranchWithRollbackErr := switchBranchWithRollback(config, httpClient, currentBranch, targetBranch, repoState, repoMetadataInitState)
|
||||
if switchBranchWithRollbackErr != nil {
|
||||
return switchBranchWithRollbackErr
|
||||
}
|
||||
currentBranch = config.Branch
|
||||
branchRollbackRequired = true
|
||||
}
|
||||
|
||||
if config.Commit != "" {
|
||||
// switch to a target commit and if it fails rollback to the previous working state
|
||||
pullByCommitWithRollbackErr := pullByCommitWithRollback(config, telemetryData, command, httpClient, repoState, repoMetadataInitState, currentBranch, targetBranch, branchRollbackRequired)
|
||||
if pullByCommitWithRollbackErr != nil {
|
||||
return pullByCommitWithRollbackErr
|
||||
}
|
||||
} else {
|
||||
// if commit parameter is not provided and its a new repo , then set config scope to "Current Commit" to be used with deploy api
|
||||
if repoState == repoStateNew && (config.Commit != "" || config.Branch != "") {
|
||||
log.Entry().Infof("Setting deploy scope as current commit")
|
||||
config.Scope = "CRNTCOMMIT"
|
||||
}
|
||||
|
||||
if config.Scope != "" {
|
||||
removeNoImportAndDeployToSystemErr := removeNoImportAndDeployToSystem(config, httpClient)
|
||||
if removeNoImportAndDeployToSystemErr != nil {
|
||||
return removeNoImportAndDeployToSystemErr
|
||||
}
|
||||
// Step Execution Ends here
|
||||
return nil
|
||||
}
|
||||
|
||||
pullByCommitWithRollbackErr := pullByCommitWithRollback(config, telemetryData, command, httpClient, repoState, repoMetadataInitState, currentBranch, targetBranch, branchRollbackRequired)
|
||||
if pullByCommitWithRollbackErr != nil {
|
||||
return pullByCommitWithRollbackErr
|
||||
}
|
||||
}
|
||||
// A deploy is done with scope current commit if the repository is a new repo and
|
||||
// branch and a commit parameters where also provided
|
||||
// This is required so that the code base is imported into the system because during the
|
||||
// switch branch and pull by commit the no import flag was set as true
|
||||
if repoState == repoStateNew {
|
||||
log.Entry().Infof("Setting deploy scope as current commit")
|
||||
config.Scope = "CRNTCOMMIT"
|
||||
removeNoImportAndDeployToSystemErr := removeNoImportAndDeployToSystem(config, httpClient)
|
||||
if removeNoImportAndDeployToSystemErr != nil {
|
||||
return removeNoImportAndDeployToSystemErr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Function to remove the VCS_NO_IMPORT flag and do deploy into the abap system
|
||||
func removeNoImportAndDeployToSystem(config *gctsDeployOptions, httpClient piperhttp.Sender) error {
|
||||
log.Entry().Infof("Removing VCS_NO_IMPORT configuration")
|
||||
configToDelete := "VCS_NO_IMPORT"
|
||||
deleteConfigKeyErr := deleteConfigKey(config, httpClient, configToDelete)
|
||||
if deleteConfigKeyErr != nil {
|
||||
log.Entry().WithError(deleteConfigKeyErr).Error("step execution failed at Set Config key for VCS_NO_IMPORT")
|
||||
return deleteConfigKeyErr
|
||||
}
|
||||
// Get deploy scope and gctsDeploy
|
||||
log.Entry().Infof("gCTS Deploy: Deploying Commit to ABAP System for Repository %v with scope %v", config.Repository, config.Scope)
|
||||
deployErr := deployCommitToAbapSystem(config, httpClient)
|
||||
if deployErr != nil {
|
||||
log.Entry().WithError(deployErr).Error("step execution failed at Deploying Commit to ABAP system.")
|
||||
return deployErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Function to pull by commit, it also does a rollback incase of errors during the pull
|
||||
func pullByCommitWithRollback(config *gctsDeployOptions, telemetryData *telemetry.CustomData, command command.ExecRunner,
|
||||
httpClient piperhttp.Sender, repoState string, repoMetadataInitState *getRepositoryResponseBody,
|
||||
currentBranch string, targetBranch string, branchRollbackRequired bool) error {
|
||||
|
||||
log.Entry().Infof("gCTS Deploy: Pull by Commit step execution to commit %v", config.Commit)
|
||||
pullByCommitErr := pullByCommit(config, telemetryData, command, httpClient)
|
||||
if pullByCommitErr != nil {
|
||||
log.Entry().WithError(pullByCommitErr).Error("step execution failed at Pull By Commit. Trying to rollback to last commit")
|
||||
if config.Rollback {
|
||||
//Rollback to last commit.
|
||||
rollbackOptions := gctsRollbackOptions{
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
Repository: config.Repository,
|
||||
Host: config.Host,
|
||||
Client: config.Client,
|
||||
}
|
||||
rollbackErr := rollback(&rollbackOptions, telemetryData, command, httpClient)
|
||||
if rollbackErr != nil {
|
||||
log.Entry().WithError(rollbackErr).Error("step execution failed while rolling back commit")
|
||||
return rollbackErr
|
||||
}
|
||||
if repoState == repoStateNew && branchRollbackRequired {
|
||||
// Rollback branch
|
||||
// Rollback branch. Resetting branches
|
||||
targetBranch = repoMetadataInitState.Result.Branch
|
||||
currentBranch = config.Branch
|
||||
log.Entry().Errorf("Rolling Back from %v to %v", currentBranch, targetBranch)
|
||||
switchBranch(config, httpClient, currentBranch, targetBranch)
|
||||
}
|
||||
}
|
||||
return pullByCommitErr
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// Function to switch branches, it also does a rollback incase of errors during the switch
|
||||
func switchBranchWithRollback(config *gctsDeployOptions, httpClient piperhttp.Sender, currentBranch string, targetBranch string, repoState string, repoMetadataInitState *getRepositoryResponseBody) (*switchBranchResponseBody, error) {
|
||||
var response *switchBranchResponseBody
|
||||
response, switchBranchErr := switchBranch(config, httpClient, currentBranch, targetBranch)
|
||||
if switchBranchErr != nil {
|
||||
log.Entry().WithError(switchBranchErr).Error("step execution failed at Switch Branch")
|
||||
if repoState == repoStateNew && config.Rollback {
|
||||
// Rollback branch. Resetting branches
|
||||
targetBranch = repoMetadataInitState.Result.Branch
|
||||
currentBranch = config.Branch
|
||||
log.Entry().WithError(switchBranchErr).Errorf("Rolling Back from %v to %v", currentBranch, targetBranch)
|
||||
switchBranch(config, httpClient, currentBranch, targetBranch)
|
||||
}
|
||||
return nil, switchBranchErr
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Set VCS_NO_IMPORT flag to true and do a clone of the repo. This disables the repository objects to be pulled in to the system
|
||||
func setNoImportAndCloneRepo(config *gctsDeployOptions, cloneRepoOptions *gctsCloneRepositoryOptions, httpClient piperhttp.Sender, telemetryData *telemetry.CustomData) error {
|
||||
log.Entry().Infof("Setting VCS_NO_IMPORT to true")
|
||||
noImportConfig := setConfigKeyBody{
|
||||
Key: "VCS_NO_IMPORT",
|
||||
Value: "X",
|
||||
}
|
||||
setConfigKeyErr := setConfigKey(config, httpClient, &noImportConfig)
|
||||
if setConfigKeyErr != nil {
|
||||
log.Entry().WithError(setConfigKeyErr).Error("step execution failed at Set Config key for VCS_NO_IMPORT")
|
||||
return setConfigKeyErr
|
||||
}
|
||||
cloneErr := cloneRepository(cloneRepoOptions, telemetryData, httpClient)
|
||||
|
||||
if cloneErr != nil {
|
||||
log.Entry().WithError(cloneErr).Error("step execution failed at Clone Repository")
|
||||
return cloneErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Function to switch branch
|
||||
func switchBranch(config *gctsDeployOptions, httpClient piperhttp.Sender, currentBranch string, targetBranch string) (*switchBranchResponseBody, error) {
|
||||
var response switchBranchResponseBody
|
||||
log.Entry().Infof("gCTS Deploy : Switching branch for repository : %v, from branch: %v to %v", config.Repository, currentBranch, targetBranch)
|
||||
requestURL := config.Host +
|
||||
"/sap/bc/cts_abapvcs/repository/" + config.Repository + "/branches/" + currentBranch +
|
||||
"/switch?branch=" + targetBranch + "&sap-client=" + config.Client
|
||||
resp, httpErr := httpClient.SendRequest("GET", requestURL, nil, nil, nil)
|
||||
defer func() {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
if httpErr != nil {
|
||||
_, errorDumpParseErr := parseErrorDumpFromResponseBody(resp)
|
||||
if errorDumpParseErr != nil {
|
||||
return nil, errorDumpParseErr
|
||||
}
|
||||
return &response, httpErr
|
||||
} else if resp == nil {
|
||||
return &response, errors.New("did not retrieve a HTTP response")
|
||||
}
|
||||
parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response)
|
||||
if parsingErr != nil {
|
||||
return &response, parsingErr
|
||||
}
|
||||
log.Entry().Infof("Switched branches from %v to %v. The commits where switched from %v to %v", currentBranch, config.Branch, response.Result.FromCommit, response.Result.ToCommit)
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func deployCommitToAbapSystem(config *gctsDeployOptions, httpClient piperhttp.Sender) error {
|
||||
var response getRepositoryResponseBody
|
||||
deployRequestBody := deployCommitToAbapSystemBody{
|
||||
Scope: config.Scope,
|
||||
}
|
||||
log.Entry().Info("gCTS Deploy : Start of deploying commit to ABAP System.")
|
||||
requestURL := config.Host +
|
||||
"/sap/bc/cts_abapvcs/repository/" + config.Repository +
|
||||
"/deploy?sap-client=" + config.Client
|
||||
reqBody := deployRequestBody
|
||||
jsonBody, marshalErr := json.Marshal(reqBody)
|
||||
if marshalErr != nil {
|
||||
return errors.Wrapf(marshalErr, "Deploying repository to abap system failed json body marshalling")
|
||||
}
|
||||
header := make(http.Header)
|
||||
header.Set("Content-Type", "application/json")
|
||||
header.Add("Accept", "application/json")
|
||||
resp, httpErr := httpClient.SendRequest("POST", requestURL, bytes.NewBuffer(jsonBody), header, nil)
|
||||
defer func() {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
if httpErr != nil {
|
||||
_, errorDumpParseErr := parseErrorDumpFromResponseBody(resp)
|
||||
if errorDumpParseErr != nil {
|
||||
return errorDumpParseErr
|
||||
}
|
||||
log.Entry().Error("Failed During Deploy to Abap system")
|
||||
return httpErr
|
||||
}
|
||||
parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response)
|
||||
if parsingErr != nil {
|
||||
return parsingErr
|
||||
}
|
||||
log.Entry().Infof("Response for deploy command : %v", response.Result)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uses the repository details to check if the repository already exists in the system or not
|
||||
func getRepository(config *gctsDeployOptions, httpClient piperhttp.Sender) (*getRepositoryResponseBody, error) {
|
||||
var response getRepositoryResponseBody
|
||||
requestURL := config.Host +
|
||||
"/sap/bc/cts_abapvcs/repository/" + config.Repository +
|
||||
"?sap-client=" + config.Client
|
||||
|
||||
resp, httpErr := httpClient.SendRequest("GET", requestURL, nil, nil, nil)
|
||||
defer func() {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
if httpErr != nil {
|
||||
_, errorDumpParseErr := parseErrorDumpFromResponseBody(resp)
|
||||
if errorDumpParseErr != nil {
|
||||
return nil, errorDumpParseErr
|
||||
}
|
||||
log.Entry().Infof("Error while repository Check : %v", httpErr)
|
||||
return &response, httpErr
|
||||
} else if resp == nil {
|
||||
return &response, errors.New("did not retrieve a HTTP response")
|
||||
}
|
||||
|
||||
parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response)
|
||||
if parsingErr != nil {
|
||||
return &response, parsingErr
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// Function to delete configuration key for repositories
|
||||
func deleteConfigKey(deployConfig *gctsDeployOptions, httpClient piperhttp.Sender, configToDelete string) error {
|
||||
log.Entry().Infof("gCTS Deploy : Delete configuration key %v", configToDelete)
|
||||
requestURL := deployConfig.Host +
|
||||
"/sap/bc/cts_abapvcs/repository/" + deployConfig.Repository +
|
||||
"/config/" + configToDelete + "?sap-client=" + deployConfig.Client
|
||||
header := make(http.Header)
|
||||
header.Set("Content-Type", "application/json")
|
||||
header.Add("Accept", "application/json")
|
||||
resp, httpErr := httpClient.SendRequest("DELETE", requestURL, nil, header, nil)
|
||||
defer func() {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
if httpErr != nil {
|
||||
_, errorDumpParseErr := parseErrorDumpFromResponseBody(resp)
|
||||
if errorDumpParseErr != nil {
|
||||
return errorDumpParseErr
|
||||
}
|
||||
log.Entry().Error("Failure during deletion of configuration value")
|
||||
return httpErr
|
||||
}
|
||||
log.Entry().Infof("gCTS Deploy : Delete configuration key %v successful", configToDelete)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Function to set configuration key for repositories
|
||||
func setConfigKey(deployConfig *gctsDeployOptions, httpClient piperhttp.Sender, configToSet *setConfigKeyBody) error {
|
||||
log.Entry().Infof("gCTS Deploy : Start of set configuration key %v and value %v", configToSet.Key, configToSet.Value)
|
||||
requestURL := deployConfig.Host +
|
||||
"/sap/bc/cts_abapvcs/repository/" + deployConfig.Repository +
|
||||
"/config?sap-client=" + deployConfig.Client
|
||||
|
||||
reqBody := configToSet
|
||||
jsonBody, marshalErr := json.Marshal(reqBody)
|
||||
if marshalErr != nil {
|
||||
return errors.Wrapf(marshalErr, "Setting config key: %v and value: %v on the ABAP system %v failed", configToSet.Key, configToSet.Value, deployConfig.Host)
|
||||
}
|
||||
header := make(http.Header)
|
||||
header.Set("Content-Type", "application/json")
|
||||
header.Add("Accept", "application/json")
|
||||
resp, httpErr := httpClient.SendRequest("POST", requestURL, bytes.NewBuffer(jsonBody), header, nil)
|
||||
defer func() {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
if httpErr != nil {
|
||||
_, errorDumpParseErr := parseErrorDumpFromResponseBody(resp)
|
||||
if errorDumpParseErr != nil {
|
||||
return errorDumpParseErr
|
||||
}
|
||||
log.Entry().Error("Failure during setting configuration value")
|
||||
return httpErr
|
||||
}
|
||||
log.Entry().
|
||||
WithField("repository", deployConfig.Repository).
|
||||
Infof("successfully set configuration value key %v and value %v", configToSet.Key, configToSet.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func pullByCommit(config *gctsDeployOptions, telemetryData *telemetry.CustomData, command command.ExecRunner, httpClient piperhttp.Sender) error {
|
||||
|
||||
cookieJar, cookieErr := cookiejar.New(nil)
|
||||
if cookieErr != nil {
|
||||
@ -41,7 +476,7 @@ func deployCommit(config *gctsDeployOptions, telemetryData *telemetry.CustomData
|
||||
|
||||
requestURL := config.Host +
|
||||
"/sap/bc/cts_abapvcs/repository/" + config.Repository +
|
||||
"/pullByCommit?sap-client=" + config.Client
|
||||
"/pullByCommit?sap-client=" + config.Client + "&request=" + config.Commit
|
||||
|
||||
if config.Commit != "" {
|
||||
log.Entry().Infof("preparing to deploy specified commit %v", config.Commit)
|
||||
@ -59,6 +494,10 @@ func deployCommit(config *gctsDeployOptions, telemetryData *telemetry.CustomData
|
||||
}()
|
||||
|
||||
if httpErr != nil {
|
||||
_, errorDumpParseErr := parseErrorDumpFromResponseBody(resp)
|
||||
if errorDumpParseErr != nil {
|
||||
return errorDumpParseErr
|
||||
}
|
||||
return httpErr
|
||||
} else if resp == nil {
|
||||
return errors.New("did not retrieve a HTTP response")
|
||||
@ -81,3 +520,287 @@ func deployCommit(config *gctsDeployOptions, telemetryData *telemetry.CustomData
|
||||
Infof("successfully deployed commit %v (previous commit was %v)", response.Path("toCommit").Data().(string), response.Path("fromCommit").Data().(string))
|
||||
return nil
|
||||
}
|
||||
|
||||
func createRepositoryForDeploy(config *gctsCreateRepositoryOptions, telemetryData *telemetry.CustomData, command command.ExecRunner, httpClient piperhttp.Sender, repositoryConfig []repositoryConfiguration) error {
|
||||
|
||||
cookieJar, cookieErr := cookiejar.New(nil)
|
||||
if cookieErr != nil {
|
||||
return errors.Wrapf(cookieErr, "creating repository on the ABAP system %v failed", config.Host)
|
||||
}
|
||||
clientOptions := piperhttp.ClientOptions{
|
||||
CookieJar: cookieJar,
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
}
|
||||
httpClient.SetOptions(clientOptions)
|
||||
|
||||
type repoData struct {
|
||||
RID string `json:"rid"`
|
||||
Name string `json:"name"`
|
||||
Role string `json:"role"`
|
||||
Type string `json:"type"`
|
||||
VSID string `json:"vsid"`
|
||||
RemoteRepositoryURL string `json:"url"`
|
||||
Config []repositoryConfiguration `json:"config"`
|
||||
}
|
||||
|
||||
type createRequestBody struct {
|
||||
Repository string `json:"repository"`
|
||||
Data repoData `json:"data"`
|
||||
}
|
||||
|
||||
reqBody := createRequestBody{
|
||||
Repository: config.Repository,
|
||||
Data: repoData{
|
||||
RID: config.Repository,
|
||||
Name: config.Repository,
|
||||
Role: config.Role,
|
||||
Type: config.Type,
|
||||
VSID: config.VSID,
|
||||
RemoteRepositoryURL: config.RemoteRepositoryURL,
|
||||
Config: repositoryConfig,
|
||||
},
|
||||
}
|
||||
jsonBody, marshalErr := json.Marshal(reqBody)
|
||||
|
||||
if marshalErr != nil {
|
||||
return errors.Wrapf(marshalErr, "creating repository on the ABAP system %v failed", config.Host)
|
||||
}
|
||||
|
||||
header := make(http.Header)
|
||||
header.Set("Content-Type", "application/json")
|
||||
header.Add("Accept", "application/json")
|
||||
|
||||
url := config.Host + "/sap/bc/cts_abapvcs/repository?sap-client=" + config.Client
|
||||
|
||||
resp, httpErr := httpClient.SendRequest("POST", url, bytes.NewBuffer(jsonBody), header, nil)
|
||||
|
||||
defer func() {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if resp == nil {
|
||||
return errors.Errorf("creating repository on the ABAP system %v failed: %v", config.Host, httpErr)
|
||||
}
|
||||
|
||||
if httpErr != nil {
|
||||
response, errorDumpParseErr := parseErrorDumpFromResponseBody(resp)
|
||||
if errorDumpParseErr != nil {
|
||||
return errorDumpParseErr
|
||||
}
|
||||
if resp.StatusCode == 500 {
|
||||
if response.Exception == "Repository already exists" {
|
||||
log.Entry().
|
||||
WithField("repository", config.Repository).
|
||||
Infof("the repository already exists on the ABAP system %v", config.Host)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
log.Entry().Errorf("a HTTP error occurred! Response body: %v", response)
|
||||
return errors.Wrapf(httpErr, "creating repository on the ABAP system %v failed", config.Host)
|
||||
}
|
||||
|
||||
log.Entry().
|
||||
WithField("repository", config.Repository).
|
||||
Infof("successfully created the repository on ABAP system %v", config.Host)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getConfigurationMetadata(config *gctsDeployOptions, httpClient piperhttp.Sender) (*configurationMetadataBody, error) {
|
||||
var response configurationMetadataBody
|
||||
log.Entry().Infof("Starting to retrieve configuration metadata from the system")
|
||||
requestURL := config.Host +
|
||||
"/sap/bc/cts_abapvcs/config"
|
||||
|
||||
resp, httpErr := httpClient.SendRequest("GET", requestURL, nil, nil, nil)
|
||||
defer func() {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
if httpErr != nil {
|
||||
_, errorDumpParseErr := parseErrorDumpFromResponseBody(resp)
|
||||
if errorDumpParseErr != nil {
|
||||
return nil, errorDumpParseErr
|
||||
}
|
||||
log.Entry().Infof("Error while repository Check : %v", httpErr)
|
||||
return &response, httpErr
|
||||
} else if resp == nil {
|
||||
return &response, errors.New("did not retrieve a HTTP response")
|
||||
}
|
||||
|
||||
parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response)
|
||||
if parsingErr != nil {
|
||||
return &response, parsingErr
|
||||
}
|
||||
log.Entry().Infof("System Available for further step processing. The configuration metadata was successfully retrieved.")
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func splitConfigurationToMap(inputConfigMap map[string]interface{}, configMetadataInSystem configurationMetadataBody) ([]repositoryConfiguration, error) {
|
||||
log.Entry().Infof("Parsing the configurations from the yml file")
|
||||
var configurations []repositoryConfiguration
|
||||
for key, value := range inputConfigMap {
|
||||
foundConfigMetadata, _ := findConfigurationMetadata(key, configMetadataInSystem)
|
||||
configValue := fmt.Sprint(value)
|
||||
if (configMetadata{}) != foundConfigMetadata {
|
||||
if foundConfigMetadata.Datatype == "BOOLEAN" && foundConfigMetadata.Example == "X" {
|
||||
if configValue == "false" || configValue == "" {
|
||||
configValue = ""
|
||||
} else if configValue == "true" || configValue == "X" {
|
||||
configValue = "X"
|
||||
}
|
||||
}
|
||||
}
|
||||
configuration := repositoryConfiguration{
|
||||
Key: key,
|
||||
Value: configValue,
|
||||
}
|
||||
configurations = append(configurations, configuration)
|
||||
|
||||
}
|
||||
log.Entry().Infof("The Configurations for the repoistory creation are : %v", configurations)
|
||||
return configurations, nil
|
||||
}
|
||||
|
||||
func findConfigurationMetadata(configToFind string, configurationsAvailable configurationMetadataBody) (configMetadata, error) {
|
||||
var configStruct configMetadata
|
||||
for _, config := range configurationsAvailable.Config {
|
||||
if config.Ckey == configToFind {
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
return configStruct, nil
|
||||
}
|
||||
|
||||
// Error handling for failure responses from the gcts api calls
|
||||
func parseErrorDumpFromResponseBody(responseBody *http.Response) (*errorLogBody, error) {
|
||||
var errorDump errorLogBody
|
||||
parsingErr := piperhttp.ParseHTTPResponseBodyJSON(responseBody, &errorDump)
|
||||
if parsingErr != nil {
|
||||
return &errorDump, parsingErr
|
||||
}
|
||||
for _, errorLogData := range errorDump.ErrorLog {
|
||||
log.Entry().Errorf("Time: %v, User: %v, Section: %v, Action: %v, Severity: %v, Message: %v",
|
||||
errorLogData.Time, errorLogData.User, errorLogData.Section,
|
||||
errorLogData.Action, errorLogData.Severity, errorLogData.Message)
|
||||
for _, protocolErrorData := range errorLogData.Protocol {
|
||||
log.Entry().Errorf("Type: %v", protocolErrorData.Type)
|
||||
for _, protocols := range protocolErrorData.Protocol {
|
||||
if strings.Contains(protocols, "4 ETW000 ") {
|
||||
protocols = strings.ReplaceAll(protocols, "4 ETW000 ", "")
|
||||
} else if strings.Contains(protocols, "4EETW000 ") {
|
||||
protocols = strings.ReplaceAll(protocols, "4EETW000 ", "ERROR: ")
|
||||
}
|
||||
log.Entry().Error(protocols)
|
||||
}
|
||||
}
|
||||
}
|
||||
return &errorDump, nil
|
||||
}
|
||||
|
||||
type repositoryConfiguration struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type getRepositoryResponseBody struct {
|
||||
Result struct {
|
||||
Rid string `json:"rid"`
|
||||
Name string `json:"name"`
|
||||
Role string `json:"role"`
|
||||
Vsid string `json:"vsid"`
|
||||
Status string `json:"status"`
|
||||
Branch string `json:"branch"`
|
||||
Url string `json:"url"`
|
||||
Config []struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Category string `json:"category"`
|
||||
} `json:"config"`
|
||||
Objects int64 `json:"objects"`
|
||||
CurrentCommit string `json:"currentCommit"`
|
||||
Connection string `json:"connection"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
type setConfigKeyBody struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type switchBranchResponseBody struct {
|
||||
Result struct {
|
||||
FromCommit string `json:"fromCommit"`
|
||||
ToCommit string `json:"ToCommit"`
|
||||
} `json:"result"`
|
||||
Log []struct {
|
||||
Time string `json:"time"`
|
||||
User string `json:"user"`
|
||||
Section string `json:"section"`
|
||||
Action string `json:"Action"`
|
||||
Severity string `json:"Severity"`
|
||||
Message string `json:"Message"`
|
||||
} `json:"log"`
|
||||
}
|
||||
|
||||
type deployCommitToAbapSystemBody struct {
|
||||
Repository string `json:"repository"`
|
||||
Scope string `json:"scope"`
|
||||
Commit string `json:"commit"`
|
||||
Objects []struct {
|
||||
Object string `json:"object"`
|
||||
Type string `json:"type"`
|
||||
User string `json:"user"`
|
||||
Pgmid string `json:"pgmid"`
|
||||
Keys []struct {
|
||||
Tabname string `json:"tabname"`
|
||||
Columns []struct {
|
||||
Key string `json:"key"`
|
||||
Field string `json:"field"`
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type"`
|
||||
Inttype string `json:"inttype"`
|
||||
Length string `json:"length"`
|
||||
}
|
||||
}
|
||||
} `json:"objects"`
|
||||
}
|
||||
|
||||
type configMetadata struct {
|
||||
Ckey string `json:"ckey"`
|
||||
Ctype string `json:"ctype"`
|
||||
Cvisible string `json:"cvisible"`
|
||||
Datatype string `json:"datatype"`
|
||||
DefaultValue string `json:"defaultValue"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
UiElement string `json:"uiElement"`
|
||||
Example string `json:"example"`
|
||||
}
|
||||
|
||||
type configurationMetadataBody struct {
|
||||
Config []configMetadata `json:"config"`
|
||||
}
|
||||
|
||||
type errorProtocolbody struct {
|
||||
Type string `json:"type"`
|
||||
Protocol []string `json:"protocol"`
|
||||
}
|
||||
|
||||
type errorLog struct {
|
||||
Time int `json:"time"`
|
||||
User string `json:"user"`
|
||||
Section string `json:"section"`
|
||||
Action string `json:"action"`
|
||||
Severity string `json:"severity"`
|
||||
Message string `json:"message"`
|
||||
Protocol []errorProtocolbody `json:"protocol"`
|
||||
}
|
||||
|
||||
type errorLogBody struct {
|
||||
ErrorLog []errorLog `json:"errorLog"`
|
||||
Exception string `json:"exception"`
|
||||
}
|
||||
|
@ -14,15 +14,23 @@ import (
|
||||
)
|
||||
|
||||
type gctsDeployOptions struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Repository string `json:"repository,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Client string `json:"client,omitempty"`
|
||||
Commit string `json:"commit,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Repository string `json:"repository,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Client string `json:"client,omitempty"`
|
||||
Commit string `json:"commit,omitempty"`
|
||||
RemoteRepositoryURL string `json:"remoteRepositoryURL,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
VSID string `json:"vSID,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Branch string `json:"branch,omitempty"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
Rollback bool `json:"rollback,omitempty"`
|
||||
Configuration map[string]interface{} `json:"configuration,omitempty"`
|
||||
}
|
||||
|
||||
// GctsDeployCommand Pulls a commit from the remote Git repository to a local repository
|
||||
// GctsDeployCommand Deploys a Git Repository to a local Repository and then to an ABAP System
|
||||
func GctsDeployCommand() *cobra.Command {
|
||||
const STEP_NAME = "gctsDeploy"
|
||||
|
||||
@ -32,8 +40,9 @@ func GctsDeployCommand() *cobra.Command {
|
||||
|
||||
var createGctsDeployCmd = &cobra.Command{
|
||||
Use: STEP_NAME,
|
||||
Short: "Pulls a commit from the remote Git repository to a local repository",
|
||||
Long: `Pulls a commit from the corresponding remote Git repository to a specified local repository on an ABAP system. If no <commit> parameter is specified, this step will pull the latest commit available on the remote repository.`,
|
||||
Short: "Deploys a Git Repository to a local Repository and then to an ABAP System",
|
||||
Long: `The steps deploys a git repository to an ABAP System. If a repository does not exists in the system, it creates and clones it to the local
|
||||
repository and then deploys it into the ABAP system.`,
|
||||
PreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
startTime = time.Now()
|
||||
log.SetStepName(STEP_NAME)
|
||||
@ -87,12 +96,20 @@ func addGctsDeployFlags(cmd *cobra.Command, stepConfig *gctsDeployOptions) {
|
||||
cmd.Flags().StringVar(&stepConfig.Host, "host", os.Getenv("PIPER_host"), "Specifies the protocol and host address, including the port. Please provide in the format `<protocol>://<host>:<port>`. Supported protocols are `http` and `https`.")
|
||||
cmd.Flags().StringVar(&stepConfig.Client, "client", os.Getenv("PIPER_client"), "Specifies the client of the ABAP system to be addressed")
|
||||
cmd.Flags().StringVar(&stepConfig.Commit, "commit", os.Getenv("PIPER_commit"), "Specifies the commit to be deployed")
|
||||
cmd.Flags().StringVar(&stepConfig.RemoteRepositoryURL, "remoteRepositoryURL", os.Getenv("PIPER_remoteRepositoryURL"), "URL of the corresponding remote repository")
|
||||
cmd.Flags().StringVar(&stepConfig.Role, "role", os.Getenv("PIPER_role"), "Role of the local repository. Choose between 'TARGET' and 'SOURCE'. Local repositories with a TARGET role will NOT be able to be the source of code changes")
|
||||
cmd.Flags().StringVar(&stepConfig.VSID, "vSID", os.Getenv("PIPER_vSID"), "Virtual SID of the local repository. The vSID corresponds to the transport route that delivers content to the remote Git repository")
|
||||
cmd.Flags().StringVar(&stepConfig.Type, "type", `GIT`, "Type of the used source code management tool")
|
||||
cmd.Flags().StringVar(&stepConfig.Branch, "branch", os.Getenv("PIPER_branch"), "Name of the branch to which the deploy has to be done to.")
|
||||
cmd.Flags().StringVar(&stepConfig.Scope, "scope", os.Getenv("PIPER_scope"), "The scope of the gcts deploy api call")
|
||||
cmd.Flags().BoolVar(&stepConfig.Rollback, "rollback", false, "The rollback flag for a failure during the deploy step. A true value would mean gCTS would roll back to the last clean state")
|
||||
|
||||
cmd.MarkFlagRequired("username")
|
||||
cmd.MarkFlagRequired("password")
|
||||
cmd.MarkFlagRequired("repository")
|
||||
cmd.MarkFlagRequired("host")
|
||||
cmd.MarkFlagRequired("client")
|
||||
cmd.MarkFlagRequired("remoteRepositoryURL")
|
||||
}
|
||||
|
||||
// retrieve step metadata
|
||||
@ -101,7 +118,7 @@ func gctsDeployMetadata() config.StepData {
|
||||
Metadata: config.StepMetadata{
|
||||
Name: "gctsDeploy",
|
||||
Aliases: []config.Alias{},
|
||||
Description: "Pulls a commit from the remote Git repository to a local repository",
|
||||
Description: "Deploys a Git Repository to a local Repository and then to an ABAP System",
|
||||
},
|
||||
Spec: config.StepSpec{
|
||||
Inputs: config.StepInputs{
|
||||
@ -166,6 +183,70 @@ func gctsDeployMetadata() config.StepData {
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "remoteRepositoryURL",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: true,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "role",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "vSID",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "type",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "branch",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "scope",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "rollback",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "bool",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "configuration",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
|
||||
Type: "map[string]interface{}",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{{Name: "gctsRepositoryConfigurations"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGctsDeploySuccess(t *testing.T) {
|
||||
func TestGctsPullByCommitSuccess(t *testing.T) {
|
||||
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
@ -35,12 +35,12 @@ func TestGctsDeploySuccess(t *testing.T) {
|
||||
]
|
||||
}`}
|
||||
|
||||
err := deployCommit(&config, nil, &httpClient)
|
||||
err := pullByCommit(&config, nil, nil, &httpClient)
|
||||
|
||||
if assert.NoError(t, err) {
|
||||
|
||||
t.Run("check url", func(t *testing.T) {
|
||||
assert.Equal(t, "http://testHost.com:50000/sap/bc/cts_abapvcs/repository/testRepo/pullByCommit?sap-client=000", httpClient.URL)
|
||||
assert.Equal(t, "http://testHost.com:50000/sap/bc/cts_abapvcs/repository/testRepo/pullByCommit?sap-client=000&request=", httpClient.URL)
|
||||
})
|
||||
|
||||
t.Run("check method", func(t *testing.T) {
|
||||
@ -60,7 +60,7 @@ func TestGctsDeploySuccess(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGctsDeployFailure(t *testing.T) {
|
||||
func TestGctsPullByCommitFailure(t *testing.T) {
|
||||
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
@ -73,39 +73,617 @@ func TestGctsDeployFailure(t *testing.T) {
|
||||
t.Run("http error occurred", func(t *testing.T) {
|
||||
|
||||
httpClient := httpMockGcts{StatusCode: 500, ResponseBody: `{
|
||||
"log": [
|
||||
{
|
||||
"time": 20180606130524,
|
||||
"user": "JENKINS",
|
||||
"section": "REPOSITORY_FACTORY",
|
||||
"action": "CREATE_REPOSITORY",
|
||||
"severity": "INFO",
|
||||
"message": "Start action CREATE_REPOSITORY review",
|
||||
"code": "GCTS.API.410"
|
||||
}
|
||||
],
|
||||
"errorLog": [
|
||||
{
|
||||
"time": 20180606130524,
|
||||
"user": "JENKINS",
|
||||
"section": "REPOSITORY_FACTORY",
|
||||
"action": "CREATE_REPOSITORY",
|
||||
"severity": "INFO",
|
||||
"message": "Start action CREATE_REPOSITORY review",
|
||||
"code": "GCTS.API.410"
|
||||
}
|
||||
],
|
||||
"exception": {
|
||||
"message": "repository_not_found",
|
||||
"description": "Repository not found",
|
||||
"code": 404
|
||||
}
|
||||
}`}
|
||||
"exception": "No relation between system and repository"
|
||||
}`}
|
||||
|
||||
err := deployCommit(&config, nil, &httpClient)
|
||||
err := pullByCommit(&config, nil, nil, &httpClient)
|
||||
|
||||
assert.EqualError(t, err, "a http error occurred")
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGctsGetRepositorySuccess(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepo",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
t.Run("Get Repository Success Test", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepo" {
|
||||
httpClient = httpMockGcts{StatusCode: 200, ResponseBody: `{
|
||||
"result": {
|
||||
"rid": "testrepo",
|
||||
"name": "testRepo",
|
||||
"role": "SOURCE",
|
||||
"type": "GIT",
|
||||
"vsid": "GIT",
|
||||
"status": "READY",
|
||||
"branch": "dummy_branch",
|
||||
"url": "https://example.git.com/testRepo",
|
||||
"createdBy": "testUser",
|
||||
"createdDate": "dummy_date",
|
||||
"config": [
|
||||
{
|
||||
"key": "CURRENT_COMMIT",
|
||||
"value": "dummy_commit_number",
|
||||
"category": "GENERAL",
|
||||
"scope": "local"
|
||||
}
|
||||
],
|
||||
"objects": 1,
|
||||
"currentCommit": "dummy_commit_number",
|
||||
"connection": "ssl"
|
||||
}
|
||||
}`}
|
||||
}
|
||||
|
||||
repository, err := getRepository(&config, &httpClient)
|
||||
|
||||
if assert.NoError(t, err) {
|
||||
t.Run("check url", func(t *testing.T) {
|
||||
assert.Equal(t, "https://example.git.com/testRepo", repository.Result.Url)
|
||||
})
|
||||
t.Run("check rid", func(t *testing.T) {
|
||||
assert.Equal(t, "testrepo", repository.Result.Rid)
|
||||
})
|
||||
t.Run("check commit id", func(t *testing.T) {
|
||||
assert.Equal(t, "dummy_commit_number", repository.Result.CurrentCommit)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGctsGetRepositoryFailure(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepoNotExists",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
t.Run("Get Repository Success Test", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepoNotExists" {
|
||||
httpClient = httpMockGcts{StatusCode: 500, ResponseBody: `{
|
||||
"exception": "No relation between system and repository"
|
||||
}`}
|
||||
}
|
||||
|
||||
_, err := getRepository(&config, &httpClient)
|
||||
|
||||
assert.EqualError(t, err, "a http error occurred")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGctsSwitchBranchSuccess(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepo",
|
||||
Branch: "dummyBranch",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
|
||||
t.Run("Switch Branch success", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Branch == "dummyBranch" {
|
||||
httpClient = httpMockGcts{StatusCode: 200, ResponseBody: `{
|
||||
"result": {
|
||||
"rid": "testrepo",
|
||||
"checkoutTime": 20210413082242,
|
||||
"fromCommit": "from_dummy_commit",
|
||||
"toCommit": "to_dummy_commit",
|
||||
"caller": "testUser",
|
||||
"request": "GITKUKDUMMY",
|
||||
"type": "BRANCH_SW",
|
||||
"state": "DONE",
|
||||
"rc": "0000"
|
||||
}
|
||||
}`}
|
||||
}
|
||||
|
||||
responseBody, err := switchBranch(&config, &httpClient, "dummyCurrentBranch", "dummyTargetBranch")
|
||||
|
||||
if assert.NoError(t, err) {
|
||||
t.Run("check from commit", func(t *testing.T) {
|
||||
assert.Equal(t, "from_dummy_commit", responseBody.Result.FromCommit)
|
||||
})
|
||||
t.Run("check to commit", func(t *testing.T) {
|
||||
assert.Equal(t, "to_dummy_commit", responseBody.Result.ToCommit)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGctsSwitchBranchFailure(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepo",
|
||||
Branch: "dummyBranchNotExists",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
t.Run("Switch Branch failure Test", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Branch == "dummyBranchNotExists" {
|
||||
httpClient = httpMockGcts{StatusCode: 500, ResponseBody: `{
|
||||
"errorLog": [
|
||||
{
|
||||
"time": 20210414102742,
|
||||
"severity": "ERROR",
|
||||
"message": "The branch to switch to - 'feature1' - does not exist",
|
||||
"code": "GCTS.CLIENT.1320"
|
||||
}
|
||||
],
|
||||
"log": [
|
||||
{
|
||||
"time": 20210414102742,
|
||||
"user": "testUser",
|
||||
"section": "REPOSITORY",
|
||||
"action": "SWITCH_BRANCH",
|
||||
"severity": "ERROR",
|
||||
"message": "20210414102742: Error action SWITCH_BRANCH 20210414_102740_B4EC329722B5C611B35B345F3B5F8FAA"
|
||||
},
|
||||
{
|
||||
"time": 20210414102742,
|
||||
"user": "testUser",
|
||||
"section": "REPOSITORY",
|
||||
"action": "SWITCH_BRANCH",
|
||||
"severity": "ERROR",
|
||||
"message": "20210414102742: Error action SWITCH_BRANCH Client error"
|
||||
}
|
||||
],
|
||||
"exception": "Cannot switch branch of local repository to selected branch."
|
||||
}`}
|
||||
}
|
||||
|
||||
_, err := getRepository(&config, &httpClient)
|
||||
|
||||
assert.EqualError(t, err, "a http error occurred")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestCreateRepositorySuccess(t *testing.T) {
|
||||
config := gctsCreateRepositoryOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepo",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
RemoteRepositoryURL: "http://testRepoUrl.com",
|
||||
Role: "dummyRole",
|
||||
VSID: "dummyVsid",
|
||||
Type: "dummyType",
|
||||
}
|
||||
t.Run("Create Repository Success", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepo" {
|
||||
httpClient = httpMockGcts{StatusCode: 200, ResponseBody: `{
|
||||
"repository": {
|
||||
"rid": "testrepo",
|
||||
"name": "testRepo",
|
||||
"role": "dummyRole",
|
||||
"type": "dummyType",
|
||||
"vsid": "dummyVsid",
|
||||
"status": "CREATED",
|
||||
"branch": "dummyBranch",
|
||||
"url": "http://testRepoUrl.com",
|
||||
"createdBy": "testUser",
|
||||
"createdDate": "2021-04-14",
|
||||
"config": [
|
||||
{
|
||||
"key": "CLIENT_VCS_CONNTYPE",
|
||||
"value": "ssl",
|
||||
"category": "CONNECTION",
|
||||
"scope": "local"
|
||||
},
|
||||
{
|
||||
"key": "CLIENT_VCS_URI",
|
||||
"value": "http://testRepoUrl.com",
|
||||
"category": "CONNECTION",
|
||||
"scope": "local"
|
||||
}
|
||||
],
|
||||
"connection": "ssl"
|
||||
}
|
||||
}`}
|
||||
}
|
||||
|
||||
err := createRepositoryForDeploy(&config, nil, nil, &httpClient, nil)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateRepositoryFailure(t *testing.T) {
|
||||
config := gctsCreateRepositoryOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepoExists",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
RemoteRepositoryURL: "http://testRepoUrlFail.com",
|
||||
Role: "dummyRole",
|
||||
VSID: "dummyVsid",
|
||||
Type: "dummyType",
|
||||
}
|
||||
t.Run("Create Repository Failure", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepoExists" {
|
||||
httpClient = httpMockGcts{StatusCode: 500, ResponseBody: `{
|
||||
"errorLog": [
|
||||
{
|
||||
"time": 20210506153611,
|
||||
"user": "testUser",
|
||||
"section": "SYSTEM",
|
||||
"action": "CREATE_REPOSITORY",
|
||||
"severity": "ERROR",
|
||||
"message": "20210506153611: Error action CREATE_REPOSITORY Repository already exists"
|
||||
}
|
||||
],
|
||||
"log": [
|
||||
{
|
||||
"time": 20210506153611,
|
||||
"user": "testUser",
|
||||
"section": "SYSTEM",
|
||||
"action": "CREATE_REPOSITORY",
|
||||
"severity": "ERROR",
|
||||
"message": "20210506153611: Error action CREATE_REPOSITORY Repository already exists"
|
||||
}
|
||||
],
|
||||
"exception": "Some Error"
|
||||
}`}
|
||||
}
|
||||
|
||||
err := createRepositoryForDeploy(&config, nil, nil, &httpClient, nil)
|
||||
assert.EqualError(t, err, "creating repository on the ABAP system http://testHost.com:50000 failed: a http error occurred")
|
||||
})
|
||||
t.Run("Create Repository Failure", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepoExists" {
|
||||
httpClient = httpMockGcts{StatusCode: 500, ResponseBody: `{
|
||||
"errorLog": [
|
||||
{
|
||||
"time": 20210506153611,
|
||||
"user": "testUser",
|
||||
"section": "SYSTEM",
|
||||
"action": "CREATE_REPOSITORY",
|
||||
"severity": "ERROR",
|
||||
"message": "20210506153611: Error action CREATE_REPOSITORY Repository already exists"
|
||||
}
|
||||
],
|
||||
"log": [
|
||||
{
|
||||
"time": 20210506153611,
|
||||
"user": "testUser",
|
||||
"section": "SYSTEM",
|
||||
"action": "CREATE_REPOSITORY",
|
||||
"severity": "ERROR",
|
||||
"message": "20210506153611: Error action CREATE_REPOSITORY Repository already exists"
|
||||
}
|
||||
],
|
||||
"exception": "Repository already exists"
|
||||
}`}
|
||||
}
|
||||
|
||||
err := createRepositoryForDeploy(&config, nil, nil, &httpClient, nil)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGctsSetConfigByKeySuccess(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepo",
|
||||
Branch: "dummyBranch",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
configKey := setConfigKeyBody{
|
||||
Key: "dummy_key",
|
||||
Value: "dummy_value",
|
||||
}
|
||||
t.Run("Set Config By key Success", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepo" {
|
||||
httpClient = httpMockGcts{StatusCode: 200, ResponseBody: `{
|
||||
"result": {
|
||||
"key": "dummy_key",
|
||||
"value": "dummy_value"
|
||||
}
|
||||
}`}
|
||||
}
|
||||
|
||||
err := setConfigKey(&config, &httpClient, &configKey)
|
||||
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGctsSetConfigByKeyFailure(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepoNotExists",
|
||||
Branch: "dummyBranchNotExists",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
configKey := setConfigKeyBody{
|
||||
Key: "dummy_key",
|
||||
Value: "dummy_value",
|
||||
}
|
||||
t.Run("Set Config By key Success", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepoNotExists" {
|
||||
httpClient = httpMockGcts{StatusCode: 500, ResponseBody: `{
|
||||
"exception": "No relation between system and repository"
|
||||
}`}
|
||||
}
|
||||
|
||||
err := setConfigKey(&config, &httpClient, &configKey)
|
||||
|
||||
assert.EqualError(t, err, "a http error occurred")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGctsDeleteConfigByKeySuccess(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepo",
|
||||
Branch: "dummyBranch",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
t.Run("Delete Config By key Success", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepo" {
|
||||
httpClient = httpMockGcts{StatusCode: 200, ResponseBody: `{
|
||||
}`}
|
||||
}
|
||||
|
||||
err := deleteConfigKey(&config, &httpClient, "dummy_config")
|
||||
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGctsDeleteConfigByKeyFailure(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepoNotExists",
|
||||
Branch: "dummyBranchNotExists",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
t.Run("Delete Config By key Failure", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepoNotExists" {
|
||||
httpClient = httpMockGcts{StatusCode: 500, ResponseBody: `{
|
||||
"exception": "No relation between system and repository"
|
||||
}`}
|
||||
}
|
||||
|
||||
err := deleteConfigKey(&config, &httpClient, "dummy_config")
|
||||
|
||||
assert.EqualError(t, err, "a http error occurred")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGctsConfigMetadataSuccess(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepo",
|
||||
Branch: "dummyBranch",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
t.Run("Test Config Metadata Success", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepo" {
|
||||
httpClient = httpMockGcts{StatusCode: 200, ResponseBody: `{
|
||||
"config": [
|
||||
{
|
||||
"ckey": "dummy_key_system",
|
||||
"ctype": "SYSTEM",
|
||||
"cvisible": "X",
|
||||
"datatype": "STRING",
|
||||
"defaultValue": "dummy_default_system",
|
||||
"description": "Dummy Key System",
|
||||
"category": "SYSTEM",
|
||||
"example": "dummy"
|
||||
},
|
||||
{
|
||||
"ckey": "dummy_key_repo",
|
||||
"ctype": "REPOSITORY",
|
||||
"cvisible": "X",
|
||||
"datatype": "STRING",
|
||||
"defaultValue": "dummy_default",
|
||||
"description": "Dummy Key repository",
|
||||
"category": "INTERNAL",
|
||||
"example": "dummy"
|
||||
}
|
||||
]
|
||||
}`}
|
||||
}
|
||||
|
||||
configMetadata, err := getConfigurationMetadata(&config, &httpClient)
|
||||
|
||||
if assert.NoError(t, err) {
|
||||
t.Run("Check if system config matches", func(t *testing.T) {
|
||||
for _, config := range configMetadata.Config {
|
||||
if config.Ctype == "SYSTEM" {
|
||||
assert.Equal(t, "dummy_key_system", config.Ckey)
|
||||
} else if config.Ctype == "REPOSITORY" {
|
||||
assert.Equal(t, "dummy_key_repo", config.Ckey)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGctsConfigMetadataFailure(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHostNotregistered.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepo",
|
||||
Branch: "dummyBranch",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
t.Run("Test Config Metadata Failure", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Host == "http://testHostNotregistered.com:50000" {
|
||||
httpClient = httpMockGcts{StatusCode: 500, ResponseBody: `{
|
||||
}`}
|
||||
}
|
||||
|
||||
_, err := getConfigurationMetadata(&config, &httpClient)
|
||||
|
||||
assert.EqualError(t, err, "a http error occurred")
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestDeployToAbapSystemSuccess(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepo",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
Scope: "dummyScope",
|
||||
}
|
||||
|
||||
t.Run("Deploy to ABAP system sucess", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepo" {
|
||||
httpClient = httpMockGcts{StatusCode: 200, ResponseBody: `{
|
||||
"result": {
|
||||
"rid": "testrepo",
|
||||
"name": "testRepo",
|
||||
"role": "dummyRole",
|
||||
"type": "dummyType",
|
||||
"vsid": "dummyVsid",
|
||||
"status": "CREATED",
|
||||
"branch": "dummyBranch",
|
||||
"url": "http://testRepoUrl.com",
|
||||
"createdBy": "testUser",
|
||||
"createdDate": "2021-04-14",
|
||||
"config": [
|
||||
{
|
||||
"key": "CLIENT_VCS_CONNTYPE",
|
||||
"value": "ssl",
|
||||
"category": "CONNECTION",
|
||||
"scope": "local"
|
||||
},
|
||||
{
|
||||
"key": "CLIENT_VCS_URI",
|
||||
"value": "http://testRepoUrl.com",
|
||||
"category": "CONNECTION",
|
||||
"scope": "local"
|
||||
}
|
||||
],
|
||||
"connection": "ssl"
|
||||
}
|
||||
}`}
|
||||
}
|
||||
|
||||
err := deployCommitToAbapSystem(&config, &httpClient)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGctsDeployToAbapSystemFailure(t *testing.T) {
|
||||
config := gctsDeployOptions{
|
||||
Host: "http://testHost.com:50000",
|
||||
Client: "000",
|
||||
Repository: "testRepoNotExists",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
Scope: "dummyScope",
|
||||
}
|
||||
t.Run("Deploy to ABAP system Failure", func(t *testing.T) {
|
||||
var httpClient httpMockGcts
|
||||
if config.Repository == "testRepoNotExists" {
|
||||
httpClient = httpMockGcts{StatusCode: 500, ResponseBody: `{
|
||||
"exception": "No relation between system and repository"
|
||||
}`}
|
||||
}
|
||||
|
||||
err := deployCommitToAbapSystem(&config, &httpClient)
|
||||
|
||||
assert.EqualError(t, err, "a http error occurred")
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGctsSplitConfigurationToMap(t *testing.T) {
|
||||
config := []configMetadata{
|
||||
{
|
||||
Ckey: "dummyKey1",
|
||||
Ctype: "REPOSITORY",
|
||||
Datatype: "BOOLEAN",
|
||||
Example: "X",
|
||||
},
|
||||
{
|
||||
Ckey: "dummyKey2",
|
||||
Ctype: "REPOSITORY",
|
||||
Datatype: "BOOLEAN",
|
||||
Example: "true",
|
||||
},
|
||||
{
|
||||
Ckey: "dummyKey3",
|
||||
Ctype: "REPOSITORY",
|
||||
Datatype: "STRING",
|
||||
Example: "dummyValue",
|
||||
},
|
||||
}
|
||||
configMetadata := configurationMetadataBody{
|
||||
Config: config,
|
||||
}
|
||||
|
||||
configMap := map[string]interface{}{
|
||||
"dummyKey1": "true",
|
||||
"dummyKey2": "true",
|
||||
"dummyKey3": "dummyValue2",
|
||||
}
|
||||
|
||||
t.Run("Config Mapping test", func(t *testing.T) {
|
||||
repoConfig, err := splitConfigurationToMap(configMap, configMetadata)
|
||||
|
||||
if assert.NoError(t, err) {
|
||||
for _, config := range repoConfig {
|
||||
if config.Key == "dummyKey1" {
|
||||
assert.Equal(t, "X", config.Value)
|
||||
} else if config.Key == "dummyKey2" {
|
||||
assert.Equal(t, "true", config.Value)
|
||||
} else if config.Key == "dummyKey3" {
|
||||
assert.Equal(t, "dummyValue2", config.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -54,7 +54,6 @@ func rollback(config *gctsRollbackOptions, telemetryData *telemetry.CustomData,
|
||||
return errors.Errorf("no remote repository URL configured")
|
||||
}
|
||||
|
||||
parsedURL, err := url.Parse(repoInfo.Result.URL)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not parse remote repository URL as valid URL")
|
||||
}
|
||||
@ -73,28 +72,6 @@ func rollback(config *gctsRollbackOptions, telemetryData *telemetry.CustomData,
|
||||
Commit: config.Commit,
|
||||
}
|
||||
|
||||
} else if parsedURL.Host == "github.com" {
|
||||
log.Entry().Info("Remote repository domain is 'github.com'. Trying to rollback to last commit with status 'success'.")
|
||||
|
||||
commitList, err := getCommits(config, telemetryData, httpClient)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get repository commits")
|
||||
}
|
||||
|
||||
successCommit, err := getLastSuccessfullCommit(config, telemetryData, httpClient, parsedURL, commitList)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not determine successful commit")
|
||||
}
|
||||
|
||||
deployOptions = gctsDeployOptions{
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
Host: config.Host,
|
||||
Repository: config.Repository,
|
||||
Client: config.Client,
|
||||
Commit: successCommit,
|
||||
}
|
||||
|
||||
} else {
|
||||
repoHistory, err := getRepoHistory(config, telemetryData, httpClient)
|
||||
if err != nil {
|
||||
@ -117,7 +94,7 @@ func rollback(config *gctsRollbackOptions, telemetryData *telemetry.CustomData,
|
||||
}
|
||||
}
|
||||
|
||||
deployErr := deployCommit(&deployOptions, telemetryData, httpClient)
|
||||
deployErr := pullByCommit(&deployOptions, telemetryData, command, httpClient)
|
||||
|
||||
if deployErr != nil {
|
||||
return errors.Wrap(deployErr, "rollback commit failed")
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
## Prerequisites
|
||||
|
||||
With this step you can deploy a commit from a remote Git repository to a local repository on an ABAP server. If no `commit` parameter is specified, this step will pull the latest commit available on the remote repository.
|
||||
With this step you can deploy a remote Git repository to a local repository on an ABAP server. If a `commit` parameter is specified the step would pull the
|
||||
repository to the commit that was mentioned. If a `branch` is provided then the repository would be switched to the respective branch specified. The rollback flag can be used to decide if you want a rollback to the working state of the reposiotry
|
||||
in the case of a failure.
|
||||
Learn more about the SAP Git-enabled Change & Transport System (gCTS) [here](https://help.sap.com/viewer/4a368c163b08418890a406d413933ba7/201909.001/en-US/f319b168e87e42149e25e13c08d002b9.html). With gCTS, ABAP developments on ABAP servers can be maintained in Git repositories.
|
||||
|
||||
## ${docGenParameters}
|
||||
@ -23,7 +25,15 @@ gctsDeploy(
|
||||
host: 'https://abap.server.com:port',
|
||||
client: '000',
|
||||
abapCredentialsId: 'ABAPUserPasswordCredentialsId',
|
||||
repository: 'myrepo'
|
||||
repository: 'myrepo',
|
||||
remoteRepositoryURL: "https://remote.repository.url.com",
|
||||
role: 'SOURCE',
|
||||
vSID: 'ABC',
|
||||
branch: 'branch',
|
||||
commit: 'commit',
|
||||
scope: 'scope',
|
||||
rollback: false,
|
||||
configuration: [dummyConfig: 'dummyval']
|
||||
)
|
||||
```
|
||||
|
||||
@ -37,4 +47,13 @@ steps:
|
||||
client: '000'
|
||||
abapCredentialsId: 'ABAPUserPasswordCredentialsId'
|
||||
repository: 'myrepo'
|
||||
remoteRepositoryURL: "https://remote.repository.url.com"
|
||||
role: 'SOURCE'
|
||||
vSID: 'ABC'
|
||||
branch: 'branch'
|
||||
commit: 'commit'
|
||||
scope: 'scope'
|
||||
rollback: false
|
||||
configuration:
|
||||
dummyconfig: "dummyval"
|
||||
```
|
||||
|
138
resources/metadata/gctsDeploy.yaml
Normal file
138
resources/metadata/gctsDeploy.yaml
Normal file
@ -0,0 +1,138 @@
|
||||
metadata:
|
||||
name: gctsDeploy
|
||||
description: Deploys a Git Repository to a local Repository and then to an ABAP System
|
||||
longDescription: |
|
||||
The steps deploys a git repository to an ABAP System. If a repository does not exists in the system, it creates and clones it to the local
|
||||
repository and then deploys it into the ABAP system.
|
||||
|
||||
spec:
|
||||
inputs:
|
||||
secrets:
|
||||
- name: abapCredentialsId
|
||||
description: Jenkins credentials ID containing username and password for authentication to the ABAP system on which you want to deploy a commit
|
||||
type: jenkins
|
||||
params:
|
||||
- name: username
|
||||
type: string
|
||||
description: User to authenticate to the ABAP system
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
secret: true
|
||||
resourceRef:
|
||||
- name: abapCredentialsId
|
||||
type: secret
|
||||
param: username
|
||||
- name: password
|
||||
type: string
|
||||
description: Password to authenticate to the ABAP system
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
secret: true
|
||||
resourceRef:
|
||||
- name: abapCredentialsId
|
||||
type: secret
|
||||
param: password
|
||||
- name: repository
|
||||
type: string
|
||||
description: Specifies the name (ID) of the local repsitory on the ABAP system
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
- name: host
|
||||
type: string
|
||||
description: Specifies the protocol and host address, including the port. Please provide in the format `<protocol>://<host>:<port>`. Supported protocols are `http` and `https`.
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
- name: client
|
||||
type: string
|
||||
description: Specifies the client of the ABAP system to be addressed
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
- name: commit
|
||||
type: string
|
||||
description: Specifies the commit to be deployed
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: remoteRepositoryURL
|
||||
type: string
|
||||
description: URL of the corresponding remote repository
|
||||
mandatory: true
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: role
|
||||
type: string
|
||||
description: Role of the local repository. Choose between 'TARGET' and 'SOURCE'. Local repositories with a TARGET role will NOT be able to be the source of code changes
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
possibleValues:
|
||||
- SOURCE
|
||||
- TARGET
|
||||
- name: vSID
|
||||
type: string
|
||||
description: Virtual SID of the local repository. The vSID corresponds to the transport route that delivers content to the remote Git repository
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: type
|
||||
type: string
|
||||
description: Type of the used source code management tool
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
default: GIT
|
||||
possibleValues:
|
||||
- GIT
|
||||
- name: branch
|
||||
type: string
|
||||
description: Name of the branch to which the deploy has to be done to.
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: scope
|
||||
type: string
|
||||
description: The scope of the gcts deploy api call
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: rollback
|
||||
type: bool
|
||||
description: The rollback flag for a failure during the deploy step. A true value would mean gCTS would roll back to the last clean state
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: configuration
|
||||
type: "map[string]interface{}"
|
||||
description: "Defines the configuration values for the repository. This map needs to be created as `configKeyName`:`configKeyValue`"
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- GENERAL
|
||||
mandatory: false
|
||||
aliases:
|
||||
- name: gctsRepositoryConfigurations
|
@ -1,70 +0,0 @@
|
||||
metadata:
|
||||
name: gctsDeploy
|
||||
description: Pulls a commit from the remote Git repository to a local repository
|
||||
longDescription: |
|
||||
Pulls a commit from the corresponding remote Git repository to a specified local repository on an ABAP system. If no <commit> parameter is specified, this step will pull the latest commit available on the remote repository.
|
||||
|
||||
spec:
|
||||
inputs:
|
||||
secrets:
|
||||
- name: abapCredentialsId
|
||||
description: Jenkins credentials ID containing username and password for authentication to the ABAP system on which you want to deploy a commit
|
||||
type: jenkins
|
||||
params:
|
||||
- name: username
|
||||
type: string
|
||||
description: User to authenticate to the ABAP system
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
secret: true
|
||||
resourceRef:
|
||||
- name: abapCredentialsId
|
||||
type: secret
|
||||
param: username
|
||||
- name: password
|
||||
type: string
|
||||
description: Password to authenticate to the ABAP system
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
secret: true
|
||||
resourceRef:
|
||||
- name: abapCredentialsId
|
||||
type: secret
|
||||
param: password
|
||||
- name: repository
|
||||
type: string
|
||||
description: Specifies the name (ID) of the local repsitory on the ABAP system
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
- name: host
|
||||
type: string
|
||||
description: Specifies the protocol and host address, including the port. Please provide in the format `<protocol>://<host>:<port>`. Supported protocols are `http` and `https`.
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
- name: client
|
||||
type: string
|
||||
description: Specifies the client of the ABAP system to be addressed
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
- name: commit
|
||||
type: string
|
||||
description: Specifies the commit to be deployed
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
Loading…
x
Reference in New Issue
Block a user