github.com/jaylevin/jenkins-library@v1.230.4/cmd/abapEnvironmentCheckoutBranch.go (about) 1 package cmd 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http/cookiejar" 8 "reflect" 9 "time" 10 11 "github.com/SAP/jenkins-library/pkg/abaputils" 12 "github.com/SAP/jenkins-library/pkg/command" 13 piperhttp "github.com/SAP/jenkins-library/pkg/http" 14 "github.com/SAP/jenkins-library/pkg/log" 15 "github.com/SAP/jenkins-library/pkg/telemetry" 16 "github.com/pkg/errors" 17 ) 18 19 func abapEnvironmentCheckoutBranch(options abapEnvironmentCheckoutBranchOptions, _ *telemetry.CustomData) { 20 21 // for command execution use Command 22 c := command.Command{} 23 // reroute command output to logging framework 24 c.Stdout(log.Writer()) 25 c.Stderr(log.Writer()) 26 27 var autils = abaputils.AbapUtils{ 28 Exec: &c, 29 } 30 31 client := piperhttp.Client{} 32 33 // error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end 34 err := runAbapEnvironmentCheckoutBranch(&options, &autils, &client) 35 if err != nil { 36 log.Entry().WithError(err).Fatal("step execution failed") 37 } 38 } 39 40 func runAbapEnvironmentCheckoutBranch(options *abapEnvironmentCheckoutBranchOptions, com abaputils.Communication, client piperhttp.Sender) (err error) { 41 42 // Mapping for options 43 subOptions := convertCheckoutConfig(options) 44 45 // Determine the host, user and password, either via the input parameters or via a cloud foundry service key 46 connectionDetails, errorGetInfo := com.GetAbapCommunicationArrangementInfo(subOptions, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/") 47 if errorGetInfo != nil { 48 log.Entry().WithError(errorGetInfo).Fatal("Parameters for the ABAP Connection not available") 49 } 50 51 // Configuring the HTTP Client and CookieJar 52 cookieJar, errorCookieJar := cookiejar.New(nil) 53 if errorCookieJar != nil { 54 return errors.Wrap(errorCookieJar, "Could not create a Cookie Jar") 55 } 56 clientOptions := piperhttp.ClientOptions{ 57 MaxRequestDuration: 180 * time.Second, 58 CookieJar: cookieJar, 59 Username: connectionDetails.User, 60 Password: connectionDetails.Password, 61 } 62 client.SetOptions(clientOptions) 63 pollIntervall := com.GetPollIntervall() 64 65 repositories := []abaputils.Repository{} 66 err = checkCheckoutBranchRepositoryConfiguration(*options) 67 68 if err == nil { 69 repositories, err = abaputils.GetRepositories(&abaputils.RepositoriesConfig{BranchName: options.BranchName, RepositoryName: options.RepositoryName, Repositories: options.Repositories}) 70 } 71 if err == nil { 72 err = checkoutBranches(repositories, connectionDetails, client, pollIntervall) 73 } 74 if err != nil { 75 return fmt.Errorf("Something failed during the checkout: %w", err) 76 } 77 log.Entry().Info("-------------------------") 78 log.Entry().Info("All branches were checked out successfully") 79 return nil 80 } 81 82 func checkoutBranches(repositories []abaputils.Repository, checkoutConnectionDetails abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, pollIntervall time.Duration) (err error) { 83 log.Entry().Infof("Start switching %v branches", len(repositories)) 84 for _, repo := range repositories { 85 err = handleCheckout(repo, checkoutConnectionDetails, client, pollIntervall) 86 if err != nil { 87 break 88 } 89 } 90 return err 91 } 92 93 func triggerCheckout(repositoryName string, branchName string, checkoutConnectionDetails abaputils.ConnectionDetailsHTTP, client piperhttp.Sender) (abaputils.ConnectionDetailsHTTP, error) { 94 uriConnectionDetails := checkoutConnectionDetails 95 uriConnectionDetails.URL = "" 96 checkoutConnectionDetails.XCsrfToken = "fetch" 97 98 if repositoryName == "" || branchName == "" { 99 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")) 100 } 101 102 // Loging into the ABAP System - getting the x-csrf-token and cookies 103 resp, err := abaputils.GetHTTPResponse("HEAD", checkoutConnectionDetails, nil, client) 104 if err != nil { 105 err = abaputils.HandleHTTPError(resp, err, "Authentication on the ABAP system failed", checkoutConnectionDetails) 106 return uriConnectionDetails, err 107 } 108 defer resp.Body.Close() 109 110 log.Entry().WithField("StatusCode", resp.Status).WithField("ABAP Endpoint", checkoutConnectionDetails.URL).Debug("Authentication on the ABAP system was successful") 111 uriConnectionDetails.XCsrfToken = resp.Header.Get("X-Csrf-Token") 112 checkoutConnectionDetails.XCsrfToken = uriConnectionDetails.XCsrfToken 113 114 // the request looks like: POST/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/checkout_branch?branch_name='newBranch'&sc_name=/DMO/GIT_REPOSITORY' 115 checkoutConnectionDetails.URL = checkoutConnectionDetails.URL + `/checkout_branch?branch_name='` + branchName + `'&sc_name='` + repositoryName + `'` 116 jsonBody := []byte(``) 117 118 // no JSON body needed 119 resp, err = abaputils.GetHTTPResponse("POST", checkoutConnectionDetails, jsonBody, client) 120 if err != nil { 121 err = abaputils.HandleHTTPError(resp, err, "Could not trigger checkout of branch "+branchName, uriConnectionDetails) 122 return uriConnectionDetails, err 123 } 124 defer resp.Body.Close() 125 log.Entry().WithField("StatusCode", resp.StatusCode).WithField("repositoryName", repositoryName).WithField("branchName", branchName).Debug("Triggered checkout of branch") 126 127 // Parse Response 128 var body abaputils.PullEntity 129 var abapResp map[string]*json.RawMessage 130 bodyText, errRead := ioutil.ReadAll(resp.Body) 131 if errRead != nil { 132 return uriConnectionDetails, err 133 } 134 json.Unmarshal(bodyText, &abapResp) 135 json.Unmarshal(*abapResp["d"], &body) 136 137 if reflect.DeepEqual(abaputils.PullEntity{}, body) { 138 log.Entry().WithField("StatusCode", resp.Status).WithField("branchName", branchName).Error("Could not switch to specified branch") 139 err := errors.New("Request to ABAP System failed") 140 return uriConnectionDetails, err 141 } 142 143 uriConnectionDetails.URL = body.Metadata.URI 144 return uriConnectionDetails, nil 145 } 146 147 func checkCheckoutBranchRepositoryConfiguration(options abapEnvironmentCheckoutBranchOptions) error { 148 if options.Repositories == "" && options.RepositoryName == "" && options.BranchName == "" { 149 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")) 150 } 151 if options.Repositories != "" && options.RepositoryName != "" && options.BranchName != "" { 152 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.") 153 } 154 if options.Repositories != "" && ((options.RepositoryName == "") != (options.BranchName == "")) { 155 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.") 156 if options.RepositoryName != "" { 157 log.Entry().Info("Please also add the value for the branchName parameter or remove the repositoryName parameter.") 158 } else { 159 log.Entry().Info("Please also add the value for the repositoryName parameter or remove the branchName parameter.") 160 } 161 } 162 return nil 163 } 164 165 func handleCheckout(repo abaputils.Repository, checkoutConnectionDetails abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, pollIntervall time.Duration) (err error) { 166 if reflect.DeepEqual(abaputils.Repository{}, repo) { 167 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")) 168 } 169 startCheckoutLogs(repo.Branch, repo.Name) 170 171 uriConnectionDetails, err := triggerCheckout(repo.Name, repo.Branch, checkoutConnectionDetails, client) 172 if err != nil { 173 return fmt.Errorf("Failed to trigger Checkout: %w", errors.New("Checkout of "+repo.Branch+" for software component "+repo.Name+" failed on the ABAP System")) 174 } 175 176 // Polling the status of the repository import on the ABAP Environment system 177 status, err := abaputils.PollEntity(repo.Name, uriConnectionDetails, client, pollIntervall) 178 if err != nil { 179 return fmt.Errorf("Failed to poll Checkout: %w", errors.New("Status of checkout action on repository"+repo.Name+" failed on the ABAP System")) 180 } 181 const abapStatusCheckoutFail = "E" 182 if status == abapStatusCheckoutFail { 183 return fmt.Errorf("Checkout failed: %w", errors.New("Checkout of branch "+repo.Branch+" failed on the ABAP System")) 184 } 185 finishCheckoutLogs(repo.Branch, repo.Name) 186 187 return err 188 } 189 190 func startCheckoutLogs(branchName string, repositoryName string) { 191 log.Entry().Infof("Starting to switch branch to branch '%v' on repository '%v'", branchName, repositoryName) 192 log.Entry().Info("--------------------------------") 193 log.Entry().Info("Start checkout branch: " + branchName) 194 log.Entry().Info("--------------------------------") 195 } 196 197 func finishCheckoutLogs(branchName string, repositoryName string) { 198 log.Entry().Info("--------------------------------") 199 log.Entry().Infof("Checkout of branch %v on repository %v was successful", branchName, repositoryName) 200 log.Entry().Info("--------------------------------") 201 } 202 203 func convertCheckoutConfig(config *abapEnvironmentCheckoutBranchOptions) abaputils.AbapEnvironmentOptions { 204 subOptions := abaputils.AbapEnvironmentOptions{} 205 206 subOptions.CfAPIEndpoint = config.CfAPIEndpoint 207 subOptions.CfServiceInstance = config.CfServiceInstance 208 subOptions.CfServiceKeyName = config.CfServiceKeyName 209 subOptions.CfOrg = config.CfOrg 210 subOptions.CfSpace = config.CfSpace 211 subOptions.Host = config.Host 212 subOptions.Password = config.Password 213 subOptions.Username = config.Username 214 return subOptions 215 }