2020-05-18 21:39:35 +02:00
package cmd
import (
2021-05-10 14:05:21 +02:00
"bytes"
"encoding/json"
"fmt"
2020-05-18 21:39:35 +02:00
"io/ioutil"
2021-05-10 14:05:21 +02:00
"net/http"
2020-05-18 21:39:35 +02:00
"net/http/cookiejar"
"net/url"
2021-05-10 14:05:21 +02:00
"strings"
2020-05-18 21:39:35 +02:00
"github.com/Jeffail/gabs/v2"
2021-05-10 14:05:21 +02:00
"github.com/SAP/jenkins-library/pkg/command"
2020-05-18 21:39:35 +02:00
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"
)
2021-05-10 14:05:21 +02:00
const repoStateExists = "RepoExists"
const repoStateNew = "RepoNew"
2020-05-18 21:39:35 +02:00
func gctsDeploy ( config gctsDeployOptions , telemetryData * telemetry . CustomData ) {
2021-05-10 14:05:21 +02:00
// for command execution use Command
c := command . Command { }
// reroute command output to logging framework
c . Stdout ( log . Writer ( ) )
c . Stderr ( log . Writer ( ) )
2020-05-18 21:39:35 +02:00
// for http calls import piperhttp "github.com/SAP/jenkins-library/pkg/http"
// and use a &piperhttp.Client{} in a custom system
// Example: step checkmarxExecuteScan.go
httpClient := & piperhttp . Client { }
// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end
2021-05-10 14:05:21 +02:00
err := gctsDeployRepository ( & config , telemetryData , & c , httpClient )
2020-05-18 21:39:35 +02:00
if err != nil {
log . Entry ( ) . WithError ( err ) . Fatal ( "step execution failed" )
}
}
2021-05-10 14:05:21 +02:00
func gctsDeployRepository ( config * gctsDeployOptions , telemetryData * telemetry . CustomData , command command . ExecRunner , httpClient piperhttp . Sender ) error {
2022-01-20 11:46:24 +02:00
var maxRetries int
maxRetries = - 1
2021-05-10 14:05:21 +02:00
cookieJar , cookieErr := cookiejar . New ( nil )
repoState := repoStateExists
branchRollbackRequired := false
if cookieErr != nil {
return errors . Wrap ( cookieErr , "creating a cookie jar failed" )
}
clientOptions := piperhttp . ClientOptions {
2022-01-20 11:46:24 +02:00
CookieJar : cookieJar ,
Username : config . Username ,
Password : config . Password ,
MaxRetries : maxRetries ,
2021-05-10 14:05:21 +02:00
}
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 {
2020-05-18 21:39:35 +02:00
cookieJar , cookieErr := cookiejar . New ( nil )
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 )
requestURL := config . Host +
"/sap/bc/cts_abapvcs/repository/" + config . Repository +
2021-05-10 14:05:21 +02:00
"/pullByCommit?sap-client=" + config . Client + "&request=" + config . Commit
2020-05-18 21:39:35 +02:00
if config . Commit != "" {
log . Entry ( ) . Infof ( "preparing to deploy specified commit %v" , config . Commit )
params := url . Values { }
params . Add ( "request" , config . Commit )
requestURL = requestURL + "&" + params . Encode ( )
}
resp , httpErr := httpClient . SendRequest ( "GET" , requestURL , nil , nil , nil )
defer func ( ) {
if resp != nil && resp . Body != nil {
resp . Body . Close ( )
}
} ( )
if httpErr != nil {
2021-05-10 14:05:21 +02:00
_ , errorDumpParseErr := parseErrorDumpFromResponseBody ( resp )
if errorDumpParseErr != nil {
return errorDumpParseErr
}
2020-05-18 21:39:35 +02:00
return httpErr
} else if resp == nil {
return errors . New ( "did not retrieve a HTTP response" )
}
bodyText , readErr := ioutil . ReadAll ( resp . Body )
if readErr != nil {
return errors . Wrapf ( readErr , "HTTP response body could not be read" )
}
response , parsingErr := gabs . ParseJSON ( [ ] byte ( bodyText ) )
if parsingErr != nil {
return errors . Wrapf ( parsingErr , "HTTP response body could not be parsed as JSON: %v" , string ( bodyText ) )
}
log . Entry ( ) .
WithField ( "repository" , config . Repository ) .
Infof ( "successfully deployed commit %v (previous commit was %v)" , response . Path ( "toCommit" ) . Data ( ) . ( string ) , response . Path ( "fromCommit" ) . Data ( ) . ( string ) )
return nil
}
2021-05-10 14:05:21 +02:00
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" `
}