2020-08-04 17:52:28 +02:00
package cmd
import (
"encoding/json"
2020-10-05 14:38:35 +02:00
"fmt"
2023-08-16 12:57:04 +02:00
"io"
2020-08-04 17:52:28 +02:00
"net/http/cookiejar"
"reflect"
"time"
"github.com/SAP/jenkins-library/pkg/abaputils"
"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"
)
2021-08-04 17:31:16 +02:00
func abapEnvironmentCheckoutBranch ( options abapEnvironmentCheckoutBranchOptions , _ * telemetry . CustomData ) {
2020-08-04 17:52:28 +02:00
// for command execution use Command
c := command . Command { }
// reroute command output to logging framework
c . Stdout ( log . Writer ( ) )
c . Stderr ( log . Writer ( ) )
var autils = abaputils . AbapUtils {
Exec : & c ,
}
client := piperhttp . Client { }
// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end
2021-08-04 17:31:16 +02:00
err := runAbapEnvironmentCheckoutBranch ( & options , & autils , & client )
2020-08-04 17:52:28 +02:00
if err != nil {
log . Entry ( ) . WithError ( err ) . Fatal ( "step execution failed" )
}
}
2021-08-04 17:31:16 +02:00
func runAbapEnvironmentCheckoutBranch ( options * abapEnvironmentCheckoutBranchOptions , com abaputils . Communication , client piperhttp . Sender ) ( err error ) {
2020-08-04 17:52:28 +02:00
// Mapping for options
2020-11-02 15:17:13 +02:00
subOptions := convertCheckoutConfig ( options )
2020-08-04 17:52:28 +02:00
// Determine the host, user and password, either via the input parameters or via a cloud foundry service key
connectionDetails , errorGetInfo := com . GetAbapCommunicationArrangementInfo ( subOptions , "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/" )
if errorGetInfo != nil {
log . Entry ( ) . WithError ( errorGetInfo ) . Fatal ( "Parameters for the ABAP Connection not available" )
}
// Configuring the HTTP Client and CookieJar
cookieJar , errorCookieJar := cookiejar . New ( nil )
if errorCookieJar != nil {
return errors . Wrap ( errorCookieJar , "Could not create a Cookie Jar" )
}
clientOptions := piperhttp . ClientOptions {
MaxRequestDuration : 180 * time . Second ,
CookieJar : cookieJar ,
Username : connectionDetails . User ,
Password : connectionDetails . Password ,
}
client . SetOptions ( clientOptions )
2020-08-07 11:09:58 +02:00
pollIntervall := com . GetPollIntervall ( )
2020-08-04 17:52:28 +02:00
2020-10-05 14:38:35 +02:00
repositories := [ ] abaputils . Repository { }
err = checkCheckoutBranchRepositoryConfiguration ( * options )
2020-08-04 17:52:28 +02:00
2020-10-05 14:38:35 +02:00
if err == nil {
2022-06-28 11:02:15 +02:00
repositories , err = abaputils . GetRepositories ( & abaputils . RepositoriesConfig { BranchName : options . BranchName , RepositoryName : options . RepositoryName , Repositories : options . Repositories } , true )
2020-08-04 17:52:28 +02:00
}
2020-10-05 14:38:35 +02:00
if err == nil {
err = checkoutBranches ( repositories , connectionDetails , client , pollIntervall )
2020-08-04 17:52:28 +02:00
}
2020-10-05 14:38:35 +02:00
if err != nil {
return fmt . Errorf ( "Something failed during the checkout: %w" , err )
2020-08-04 17:52:28 +02:00
}
2022-06-30 10:43:33 +02:00
log . Entry ( ) . Infof ( "-------------------------" )
2020-10-05 14:38:35 +02:00
log . Entry ( ) . Info ( "All branches were checked out successfully" )
2020-08-04 17:52:28 +02:00
return nil
}
2020-10-05 14:38:35 +02:00
func checkoutBranches ( repositories [ ] abaputils . Repository , checkoutConnectionDetails abaputils . ConnectionDetailsHTTP , client piperhttp . Sender , pollIntervall time . Duration ) ( err error ) {
2020-11-02 15:17:13 +02:00
log . Entry ( ) . Infof ( "Start switching %v branches" , len ( repositories ) )
2020-10-05 14:38:35 +02:00
for _ , repo := range repositories {
err = handleCheckout ( repo , checkoutConnectionDetails , client , pollIntervall )
if err != nil {
break
}
}
return err
}
2020-08-04 17:52:28 +02:00
2020-10-05 14:38:35 +02:00
func triggerCheckout ( repositoryName string , branchName string , checkoutConnectionDetails abaputils . ConnectionDetailsHTTP , client piperhttp . Sender ) ( abaputils . ConnectionDetailsHTTP , error ) {
2020-08-04 17:52:28 +02:00
uriConnectionDetails := checkoutConnectionDetails
uriConnectionDetails . URL = ""
checkoutConnectionDetails . XCsrfToken = "fetch"
2020-10-05 14:38:35 +02:00
if repositoryName == "" || branchName == "" {
return uriConnectionDetails , fmt . Errorf ( "Failed to trigger checkout: %w" , errors . New ( "Repository and/or Branch Configuration is empty. Please make sure that you have specified the correct values" ) )
}
2020-08-04 17:52:28 +02:00
// Loging into the ABAP System - getting the x-csrf-token and cookies
resp , err := abaputils . GetHTTPResponse ( "HEAD" , checkoutConnectionDetails , nil , client )
if err != nil {
err = abaputils . HandleHTTPError ( resp , err , "Authentication on the ABAP system failed" , checkoutConnectionDetails )
return uriConnectionDetails , err
}
defer resp . Body . Close ( )
2020-10-08 11:08:58 +02:00
2020-11-02 15:17:13 +02:00
log . Entry ( ) . WithField ( "StatusCode" , resp . Status ) . WithField ( "ABAP Endpoint" , checkoutConnectionDetails . URL ) . Debug ( "Authentication on the ABAP system was successful" )
2020-08-04 17:52:28 +02:00
uriConnectionDetails . XCsrfToken = resp . Header . Get ( "X-Csrf-Token" )
checkoutConnectionDetails . XCsrfToken = uriConnectionDetails . XCsrfToken
// the request looks like: POST/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/checkout_branch?branch_name='newBranch'&sc_name=/DMO/GIT_REPOSITORY'
checkoutConnectionDetails . URL = checkoutConnectionDetails . URL + ` /checkout_branch?branch_name=' ` + branchName + ` '&sc_name=' ` + repositoryName + ` ' `
jsonBody := [ ] byte ( ` ` )
// no JSON body needed
resp , err = abaputils . GetHTTPResponse ( "POST" , checkoutConnectionDetails , jsonBody , client )
if err != nil {
err = abaputils . HandleHTTPError ( resp , err , "Could not trigger checkout of branch " + branchName , uriConnectionDetails )
return uriConnectionDetails , err
}
defer resp . Body . Close ( )
2020-11-02 15:17:13 +02:00
log . Entry ( ) . WithField ( "StatusCode" , resp . StatusCode ) . WithField ( "repositoryName" , repositoryName ) . WithField ( "branchName" , branchName ) . Debug ( "Triggered checkout of branch" )
2020-08-04 17:52:28 +02:00
// Parse Response
var body abaputils . PullEntity
var abapResp map [ string ] * json . RawMessage
2023-08-16 12:57:04 +02:00
bodyText , errRead := io . ReadAll ( resp . Body )
2020-08-04 17:52:28 +02:00
if errRead != nil {
return uriConnectionDetails , err
}
2022-07-06 14:29:04 +02:00
if err := json . Unmarshal ( bodyText , & abapResp ) ; err != nil {
return uriConnectionDetails , err
}
if err := json . Unmarshal ( * abapResp [ "d" ] , & body ) ; err != nil {
return uriConnectionDetails , err
}
2020-08-04 17:52:28 +02:00
if reflect . DeepEqual ( abaputils . PullEntity { } , body ) {
log . Entry ( ) . WithField ( "StatusCode" , resp . Status ) . WithField ( "branchName" , branchName ) . Error ( "Could not switch to specified branch" )
err := errors . New ( "Request to ABAP System failed" )
return uriConnectionDetails , err
}
2022-02-11 11:16:40 +02:00
uriConnectionDetails . URL = body . Metadata . URI
2020-08-04 17:52:28 +02:00
return uriConnectionDetails , nil
}
2020-10-05 14:38:35 +02:00
func checkCheckoutBranchRepositoryConfiguration ( options abapEnvironmentCheckoutBranchOptions ) error {
if options . Repositories == "" && options . RepositoryName == "" && options . BranchName == "" {
return fmt . Errorf ( "Checking configuration failed: %w" , errors . New ( "You have not specified any repository or branch configuration to be checked out in the ABAP Environment System. Please make sure that you specified the repositories with their branches that should be checked out either in a dedicated file or via the parameters 'repositoryName' and 'branchName'. For more information please read the User documentation" ) )
}
if options . Repositories != "" && options . RepositoryName != "" && options . BranchName != "" {
log . Entry ( ) . Info ( "It seems like you have specified repositories directly via the configuration parameters 'repositoryName' and 'branchName' as well as in the dedicated repositories configuration file. Please note that in this case both configurations will be handled and checked out." )
}
if options . Repositories != "" && ( ( options . RepositoryName == "" ) != ( options . BranchName == "" ) ) {
log . Entry ( ) . Info ( "It seems like you have specified a dedicated repository configuration file but also a wrong configuration for the parameters 'repositoryName' and 'branchName' to be checked out." )
if options . RepositoryName != "" {
log . Entry ( ) . Info ( "Please also add the value for the branchName parameter or remove the repositoryName parameter." )
} else {
log . Entry ( ) . Info ( "Please also add the value for the repositoryName parameter or remove the branchName parameter." )
}
}
return nil
}
func handleCheckout ( repo abaputils . Repository , checkoutConnectionDetails abaputils . ConnectionDetailsHTTP , client piperhttp . Sender , pollIntervall time . Duration ) ( err error ) {
if reflect . DeepEqual ( abaputils . Repository { } , repo ) {
return fmt . Errorf ( "Failed to read repository configuration: %w" , errors . New ( "Error in configuration, most likely you have entered empty or wrong configuration values. Please make sure that you have correctly specified the branches in the repositories to be checked out" ) )
}
startCheckoutLogs ( repo . Branch , repo . Name )
uriConnectionDetails , err := triggerCheckout ( repo . Name , repo . Branch , checkoutConnectionDetails , client )
if err != nil {
return fmt . Errorf ( "Failed to trigger Checkout: %w" , errors . New ( "Checkout of " + repo . Branch + " for software component " + repo . Name + " failed on the ABAP System" ) )
}
// Polling the status of the repository import on the ABAP Environment system
status , err := abaputils . PollEntity ( repo . Name , uriConnectionDetails , client , pollIntervall )
if err != nil {
return fmt . Errorf ( "Failed to poll Checkout: %w" , errors . New ( "Status of checkout action on repository" + repo . Name + " failed on the ABAP System" ) )
}
const abapStatusCheckoutFail = "E"
if status == abapStatusCheckoutFail {
return fmt . Errorf ( "Checkout failed: %w" , errors . New ( "Checkout of branch " + repo . Branch + " failed on the ABAP System" ) )
}
finishCheckoutLogs ( repo . Branch , repo . Name )
return err
}
func startCheckoutLogs ( branchName string , repositoryName string ) {
log . Entry ( ) . Infof ( "Starting to switch branch to branch '%v' on repository '%v'" , branchName , repositoryName )
2022-06-30 10:43:33 +02:00
log . Entry ( ) . Infof ( "-------------------------" )
2020-10-05 14:38:35 +02:00
log . Entry ( ) . Info ( "Start checkout branch: " + branchName )
2022-06-30 10:43:33 +02:00
log . Entry ( ) . Infof ( "-------------------------" )
2020-10-05 14:38:35 +02:00
}
func finishCheckoutLogs ( branchName string , repositoryName string ) {
2022-06-30 10:43:33 +02:00
log . Entry ( ) . Infof ( "-------------------------" )
2020-10-05 14:38:35 +02:00
log . Entry ( ) . Infof ( "Checkout of branch %v on repository %v was successful" , branchName , repositoryName )
2022-06-30 10:43:33 +02:00
log . Entry ( ) . Infof ( "-------------------------" )
2020-10-05 14:38:35 +02:00
}
2020-11-02 15:17:13 +02:00
func convertCheckoutConfig ( config * abapEnvironmentCheckoutBranchOptions ) abaputils . AbapEnvironmentOptions {
subOptions := abaputils . AbapEnvironmentOptions { }
subOptions . CfAPIEndpoint = config . CfAPIEndpoint
subOptions . CfServiceInstance = config . CfServiceInstance
subOptions . CfServiceKeyName = config . CfServiceKeyName
subOptions . CfOrg = config . CfOrg
subOptions . CfSpace = config . CfSpace
subOptions . Host = config . Host
subOptions . Password = config . Password
subOptions . Username = config . Username
return subOptions
}