github.com/xgoffin/jenkins-library@v1.154.0/cmd/gctsDeploy.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "net/http" 9 "net/http/cookiejar" 10 "net/url" 11 "strings" 12 13 "github.com/Jeffail/gabs/v2" 14 "github.com/SAP/jenkins-library/pkg/command" 15 piperhttp "github.com/SAP/jenkins-library/pkg/http" 16 "github.com/SAP/jenkins-library/pkg/log" 17 "github.com/SAP/jenkins-library/pkg/telemetry" 18 "github.com/pkg/errors" 19 ) 20 21 const repoStateExists = "RepoExists" 22 const repoStateNew = "RepoNew" 23 24 func gctsDeploy(config gctsDeployOptions, telemetryData *telemetry.CustomData) { 25 // for command execution use Command 26 c := command.Command{} 27 // reroute command output to logging framework 28 c.Stdout(log.Writer()) 29 c.Stderr(log.Writer()) 30 31 // for http calls import piperhttp "github.com/SAP/jenkins-library/pkg/http" 32 // and use a &piperhttp.Client{} in a custom system 33 // Example: step checkmarxExecuteScan.go 34 httpClient := &piperhttp.Client{} 35 36 // error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end 37 err := gctsDeployRepository(&config, telemetryData, &c, httpClient) 38 if err != nil { 39 log.Entry().WithError(err).Fatal("step execution failed") 40 } 41 } 42 43 func gctsDeployRepository(config *gctsDeployOptions, telemetryData *telemetry.CustomData, command command.ExecRunner, httpClient piperhttp.Sender) error { 44 var maxRetries int 45 maxRetries = -1 46 cookieJar, cookieErr := cookiejar.New(nil) 47 repoState := repoStateExists 48 branchRollbackRequired := false 49 if cookieErr != nil { 50 return errors.Wrap(cookieErr, "creating a cookie jar failed") 51 } 52 clientOptions := piperhttp.ClientOptions{ 53 CookieJar: cookieJar, 54 Username: config.Username, 55 Password: config.Password, 56 MaxRetries: maxRetries, 57 } 58 httpClient.SetOptions(clientOptions) 59 log.Entry().Infof("Start of gCTS Deploy Step with Configuration Values: %v", config) 60 configurationMetadata, getConfigMetadataErr := getConfigurationMetadata(config, httpClient) 61 62 if getConfigMetadataErr != nil { 63 log.Entry().WithError(getConfigMetadataErr).Error("step execution failed at configuration metadata retrieval. Please Check if system is up!.") 64 return getConfigMetadataErr 65 } 66 67 createRepoOptions := gctsCreateRepositoryOptions{ 68 Username: config.Username, 69 Password: config.Password, 70 Repository: config.Repository, 71 Host: config.Host, 72 Client: config.Client, 73 RemoteRepositoryURL: config.RemoteRepositoryURL, 74 Role: config.Role, 75 VSID: config.VSID, 76 Type: config.Type, 77 } 78 log.Entry().Infof("gCTS Deploy : Checking if repository %v already exists", config.Repository) 79 repoMetadataInitState, getRepositoryErr := getRepository(config, httpClient) 80 currentBranch := repoMetadataInitState.Result.Branch 81 // If Repository does not exist in the system then Create and Clone Repository 82 if getRepositoryErr != nil { 83 // If scope is set for a new repository then creation/cloning of the repository cannot be done 84 if config.Scope != "" { 85 log.Entry().Error("Error during deploy : deploy scope cannot be provided while deploying a new repo") 86 return errors.New("Error in config file") 87 } 88 // State of the repository set for further processing during the step 89 repoState = repoStateNew 90 log.Entry().Infof("gCTS Deploy : Creating Repository Step for repository : %v", config.Repository) 91 // Parse the configuration parameter to a format that is accepted by the gcts api 92 configurations, _ := splitConfigurationToMap(config.Configuration, *configurationMetadata) 93 createErr := createRepositoryForDeploy(&createRepoOptions, telemetryData, command, httpClient, configurations) 94 if createErr != nil { 95 //Dump error log (Log it) 96 log.Entry().WithError(createErr).Error("step execution failed at Create Repository") 97 return createErr 98 } 99 100 cloneRepoOptions := gctsCloneRepositoryOptions{ 101 Username: config.Username, 102 Password: config.Password, 103 Repository: config.Repository, 104 Host: config.Host, 105 Client: config.Client, 106 } 107 // No Import has to be set when there is a commit or branch parameter set 108 // This is required so that during the clone of the repo it is not imported into the system 109 // The import would be done at a later stage with the help of gcts deploy api call 110 if config.Branch != "" || config.Commit != "" { 111 setNoImportAndCloneRepoErr := setNoImportAndCloneRepo(config, &cloneRepoOptions, httpClient, telemetryData) 112 if setNoImportAndCloneRepoErr != nil { 113 log.Entry().WithError(setNoImportAndCloneRepoErr).Error("step execution failed") 114 return setNoImportAndCloneRepoErr 115 } 116 117 } else { 118 // Clone Repository and Exit the step since there is no commit or branch parameters provided 119 cloneErr := cloneRepository(&cloneRepoOptions, telemetryData, httpClient) 120 if cloneErr != nil { 121 // Dump Error Log 122 log.Entry().WithError(cloneErr).Error("step execution failed at Clone Repository") 123 return cloneErr 124 } 125 log.Entry().Infof("gCTS Deploy : Step has completed for the repository %v : ", config.Repository) 126 // End of the step. 127 return nil 128 } 129 log.Entry().Infof("gCTS Deploy : Reading repo information after cloning repository %v : ", config.Repository) 130 // Get the repository information for further processing of the step. 131 repoMetadataInitState, getRepositoryErr = getRepository(config, httpClient) 132 if getRepositoryErr != nil { 133 // Dump Error Log 134 log.Entry().WithError(getRepositoryErr).Error("step execution failed at get repository after clone") 135 return getRepositoryErr 136 } 137 currentBranch = repoMetadataInitState.Result.Branch 138 } else { 139 log.Entry().Infof("Repository %v already exists in the system, Checking for deploy scope", config.Repository) 140 // If deploy scope provided for an existing repository then deploy api is called and then execution ends 141 if config.Scope != "" { 142 log.Entry().Infof("Deploy scope exists for the repository in the configuration file") 143 log.Entry().Infof("gCTS Deploy: Deploying Commit to ABAP System for Repository %v with scope %v", config.Repository, config.Scope) 144 deployErr := deployCommitToAbapSystem(config, httpClient) 145 if deployErr != nil { 146 log.Entry().WithError(deployErr).Error("step execution failed at Deploying Commit to ABAP system.") 147 return deployErr 148 } 149 return nil 150 } 151 log.Entry().Infof("Deploy scope not set in the configuration file for repository : %v", config.Repository) 152 } 153 // branch to which the switching has to be done to 154 targetBranch := config.Branch 155 if config.Branch != "" { 156 // switch to a target branch, and if it fails rollback to the previous working state 157 _, switchBranchWithRollbackErr := switchBranchWithRollback(config, httpClient, currentBranch, targetBranch, repoState, repoMetadataInitState) 158 if switchBranchWithRollbackErr != nil { 159 return switchBranchWithRollbackErr 160 } 161 currentBranch = config.Branch 162 branchRollbackRequired = true 163 } 164 165 if config.Commit != "" { 166 // switch to a target commit and if it fails rollback to the previous working state 167 pullByCommitWithRollbackErr := pullByCommitWithRollback(config, telemetryData, command, httpClient, repoState, repoMetadataInitState, currentBranch, targetBranch, branchRollbackRequired) 168 if pullByCommitWithRollbackErr != nil { 169 return pullByCommitWithRollbackErr 170 } 171 } else { 172 // if commit parameter is not provided and its a new repo , then set config scope to "Current Commit" to be used with deploy api 173 if repoState == repoStateNew && (config.Commit != "" || config.Branch != "") { 174 log.Entry().Infof("Setting deploy scope as current commit") 175 config.Scope = "CRNTCOMMIT" 176 } 177 178 if config.Scope != "" { 179 removeNoImportAndDeployToSystemErr := removeNoImportAndDeployToSystem(config, httpClient) 180 if removeNoImportAndDeployToSystemErr != nil { 181 return removeNoImportAndDeployToSystemErr 182 } 183 // Step Execution Ends here 184 return nil 185 } 186 187 pullByCommitWithRollbackErr := pullByCommitWithRollback(config, telemetryData, command, httpClient, repoState, repoMetadataInitState, currentBranch, targetBranch, branchRollbackRequired) 188 if pullByCommitWithRollbackErr != nil { 189 return pullByCommitWithRollbackErr 190 } 191 } 192 // A deploy is done with scope current commit if the repository is a new repo and 193 // branch and a commit parameters where also provided 194 // This is required so that the code base is imported into the system because during the 195 // switch branch and pull by commit the no import flag was set as true 196 if repoState == repoStateNew { 197 log.Entry().Infof("Setting deploy scope as current commit") 198 config.Scope = "CRNTCOMMIT" 199 removeNoImportAndDeployToSystemErr := removeNoImportAndDeployToSystem(config, httpClient) 200 if removeNoImportAndDeployToSystemErr != nil { 201 return removeNoImportAndDeployToSystemErr 202 } 203 204 } 205 206 return nil 207 } 208 209 // Function to remove the VCS_NO_IMPORT flag and do deploy into the abap system 210 func removeNoImportAndDeployToSystem(config *gctsDeployOptions, httpClient piperhttp.Sender) error { 211 log.Entry().Infof("Removing VCS_NO_IMPORT configuration") 212 configToDelete := "VCS_NO_IMPORT" 213 deleteConfigKeyErr := deleteConfigKey(config, httpClient, configToDelete) 214 if deleteConfigKeyErr != nil { 215 log.Entry().WithError(deleteConfigKeyErr).Error("step execution failed at Set Config key for VCS_NO_IMPORT") 216 return deleteConfigKeyErr 217 } 218 // Get deploy scope and gctsDeploy 219 log.Entry().Infof("gCTS Deploy: Deploying Commit to ABAP System for Repository %v with scope %v", config.Repository, config.Scope) 220 deployErr := deployCommitToAbapSystem(config, httpClient) 221 if deployErr != nil { 222 log.Entry().WithError(deployErr).Error("step execution failed at Deploying Commit to ABAP system.") 223 return deployErr 224 } 225 return nil 226 } 227 228 // Function to pull by commit, it also does a rollback incase of errors during the pull 229 func pullByCommitWithRollback(config *gctsDeployOptions, telemetryData *telemetry.CustomData, command command.ExecRunner, 230 httpClient piperhttp.Sender, repoState string, repoMetadataInitState *getRepositoryResponseBody, 231 currentBranch string, targetBranch string, branchRollbackRequired bool) error { 232 233 log.Entry().Infof("gCTS Deploy: Pull by Commit step execution to commit %v", config.Commit) 234 pullByCommitErr := pullByCommit(config, telemetryData, command, httpClient) 235 if pullByCommitErr != nil { 236 log.Entry().WithError(pullByCommitErr).Error("step execution failed at Pull By Commit. Trying to rollback to last commit") 237 if config.Rollback { 238 //Rollback to last commit. 239 rollbackOptions := gctsRollbackOptions{ 240 Username: config.Username, 241 Password: config.Password, 242 Repository: config.Repository, 243 Host: config.Host, 244 Client: config.Client, 245 } 246 rollbackErr := rollback(&rollbackOptions, telemetryData, command, httpClient) 247 if rollbackErr != nil { 248 log.Entry().WithError(rollbackErr).Error("step execution failed while rolling back commit") 249 return rollbackErr 250 } 251 if repoState == repoStateNew && branchRollbackRequired { 252 // Rollback branch 253 // Rollback branch. Resetting branches 254 targetBranch = repoMetadataInitState.Result.Branch 255 currentBranch = config.Branch 256 log.Entry().Errorf("Rolling Back from %v to %v", currentBranch, targetBranch) 257 switchBranch(config, httpClient, currentBranch, targetBranch) 258 } 259 } 260 return pullByCommitErr 261 } 262 return nil 263 264 } 265 266 // Function to switch branches, it also does a rollback incase of errors during the switch 267 func switchBranchWithRollback(config *gctsDeployOptions, httpClient piperhttp.Sender, currentBranch string, targetBranch string, repoState string, repoMetadataInitState *getRepositoryResponseBody) (*switchBranchResponseBody, error) { 268 var response *switchBranchResponseBody 269 response, switchBranchErr := switchBranch(config, httpClient, currentBranch, targetBranch) 270 if switchBranchErr != nil { 271 log.Entry().WithError(switchBranchErr).Error("step execution failed at Switch Branch") 272 if repoState == repoStateNew && config.Rollback { 273 // Rollback branch. Resetting branches 274 targetBranch = repoMetadataInitState.Result.Branch 275 currentBranch = config.Branch 276 log.Entry().WithError(switchBranchErr).Errorf("Rolling Back from %v to %v", currentBranch, targetBranch) 277 switchBranch(config, httpClient, currentBranch, targetBranch) 278 } 279 return nil, switchBranchErr 280 } 281 return response, nil 282 } 283 284 // 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 285 func setNoImportAndCloneRepo(config *gctsDeployOptions, cloneRepoOptions *gctsCloneRepositoryOptions, httpClient piperhttp.Sender, telemetryData *telemetry.CustomData) error { 286 log.Entry().Infof("Setting VCS_NO_IMPORT to true") 287 noImportConfig := setConfigKeyBody{ 288 Key: "VCS_NO_IMPORT", 289 Value: "X", 290 } 291 setConfigKeyErr := setConfigKey(config, httpClient, &noImportConfig) 292 if setConfigKeyErr != nil { 293 log.Entry().WithError(setConfigKeyErr).Error("step execution failed at Set Config key for VCS_NO_IMPORT") 294 return setConfigKeyErr 295 } 296 cloneErr := cloneRepository(cloneRepoOptions, telemetryData, httpClient) 297 298 if cloneErr != nil { 299 log.Entry().WithError(cloneErr).Error("step execution failed at Clone Repository") 300 return cloneErr 301 } 302 return nil 303 } 304 305 // Function to switch branch 306 func switchBranch(config *gctsDeployOptions, httpClient piperhttp.Sender, currentBranch string, targetBranch string) (*switchBranchResponseBody, error) { 307 var response switchBranchResponseBody 308 log.Entry().Infof("gCTS Deploy : Switching branch for repository : %v, from branch: %v to %v", config.Repository, currentBranch, targetBranch) 309 requestURL := config.Host + 310 "/sap/bc/cts_abapvcs/repository/" + config.Repository + "/branches/" + currentBranch + 311 "/switch?branch=" + targetBranch + "&sap-client=" + config.Client 312 resp, httpErr := httpClient.SendRequest("GET", requestURL, nil, nil, nil) 313 defer func() { 314 if resp != nil && resp.Body != nil { 315 resp.Body.Close() 316 } 317 }() 318 if httpErr != nil { 319 _, errorDumpParseErr := parseErrorDumpFromResponseBody(resp) 320 if errorDumpParseErr != nil { 321 return nil, errorDumpParseErr 322 } 323 return &response, httpErr 324 } else if resp == nil { 325 return &response, errors.New("did not retrieve a HTTP response") 326 } 327 parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response) 328 if parsingErr != nil { 329 return &response, parsingErr 330 } 331 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) 332 return &response, nil 333 } 334 335 func deployCommitToAbapSystem(config *gctsDeployOptions, httpClient piperhttp.Sender) error { 336 var response getRepositoryResponseBody 337 deployRequestBody := deployCommitToAbapSystemBody{ 338 Scope: config.Scope, 339 } 340 log.Entry().Info("gCTS Deploy : Start of deploying commit to ABAP System.") 341 requestURL := config.Host + 342 "/sap/bc/cts_abapvcs/repository/" + config.Repository + 343 "/deploy?sap-client=" + config.Client 344 reqBody := deployRequestBody 345 jsonBody, marshalErr := json.Marshal(reqBody) 346 if marshalErr != nil { 347 return errors.Wrapf(marshalErr, "Deploying repository to abap system failed json body marshalling") 348 } 349 header := make(http.Header) 350 header.Set("Content-Type", "application/json") 351 header.Add("Accept", "application/json") 352 resp, httpErr := httpClient.SendRequest("POST", requestURL, bytes.NewBuffer(jsonBody), header, nil) 353 defer func() { 354 if resp != nil && resp.Body != nil { 355 resp.Body.Close() 356 } 357 }() 358 if httpErr != nil { 359 _, errorDumpParseErr := parseErrorDumpFromResponseBody(resp) 360 if errorDumpParseErr != nil { 361 return errorDumpParseErr 362 } 363 log.Entry().Error("Failed During Deploy to Abap system") 364 return httpErr 365 } 366 parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response) 367 if parsingErr != nil { 368 return parsingErr 369 } 370 log.Entry().Infof("Response for deploy command : %v", response.Result) 371 return nil 372 } 373 374 // Uses the repository details to check if the repository already exists in the system or not 375 func getRepository(config *gctsDeployOptions, httpClient piperhttp.Sender) (*getRepositoryResponseBody, error) { 376 var response getRepositoryResponseBody 377 requestURL := config.Host + 378 "/sap/bc/cts_abapvcs/repository/" + config.Repository + 379 "?sap-client=" + config.Client 380 381 resp, httpErr := httpClient.SendRequest("GET", requestURL, nil, nil, nil) 382 defer func() { 383 if resp != nil && resp.Body != nil { 384 resp.Body.Close() 385 } 386 }() 387 if httpErr != nil { 388 _, errorDumpParseErr := parseErrorDumpFromResponseBody(resp) 389 if errorDumpParseErr != nil { 390 return nil, errorDumpParseErr 391 } 392 log.Entry().Infof("Error while repository Check : %v", httpErr) 393 return &response, httpErr 394 } else if resp == nil { 395 return &response, errors.New("did not retrieve a HTTP response") 396 } 397 398 parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response) 399 if parsingErr != nil { 400 return &response, parsingErr 401 } 402 return &response, nil 403 } 404 405 // Function to delete configuration key for repositories 406 func deleteConfigKey(deployConfig *gctsDeployOptions, httpClient piperhttp.Sender, configToDelete string) error { 407 log.Entry().Infof("gCTS Deploy : Delete configuration key %v", configToDelete) 408 requestURL := deployConfig.Host + 409 "/sap/bc/cts_abapvcs/repository/" + deployConfig.Repository + 410 "/config/" + configToDelete + "?sap-client=" + deployConfig.Client 411 header := make(http.Header) 412 header.Set("Content-Type", "application/json") 413 header.Add("Accept", "application/json") 414 resp, httpErr := httpClient.SendRequest("DELETE", requestURL, nil, header, nil) 415 defer func() { 416 if resp != nil && resp.Body != nil { 417 resp.Body.Close() 418 } 419 }() 420 if httpErr != nil { 421 _, errorDumpParseErr := parseErrorDumpFromResponseBody(resp) 422 if errorDumpParseErr != nil { 423 return errorDumpParseErr 424 } 425 log.Entry().Error("Failure during deletion of configuration value") 426 return httpErr 427 } 428 log.Entry().Infof("gCTS Deploy : Delete configuration key %v successful", configToDelete) 429 return nil 430 } 431 432 // Function to set configuration key for repositories 433 func setConfigKey(deployConfig *gctsDeployOptions, httpClient piperhttp.Sender, configToSet *setConfigKeyBody) error { 434 log.Entry().Infof("gCTS Deploy : Start of set configuration key %v and value %v", configToSet.Key, configToSet.Value) 435 requestURL := deployConfig.Host + 436 "/sap/bc/cts_abapvcs/repository/" + deployConfig.Repository + 437 "/config?sap-client=" + deployConfig.Client 438 439 reqBody := configToSet 440 jsonBody, marshalErr := json.Marshal(reqBody) 441 if marshalErr != nil { 442 return errors.Wrapf(marshalErr, "Setting config key: %v and value: %v on the ABAP system %v failed", configToSet.Key, configToSet.Value, deployConfig.Host) 443 } 444 header := make(http.Header) 445 header.Set("Content-Type", "application/json") 446 header.Add("Accept", "application/json") 447 resp, httpErr := httpClient.SendRequest("POST", requestURL, bytes.NewBuffer(jsonBody), header, nil) 448 defer func() { 449 if resp != nil && resp.Body != nil { 450 resp.Body.Close() 451 } 452 }() 453 if httpErr != nil { 454 _, errorDumpParseErr := parseErrorDumpFromResponseBody(resp) 455 if errorDumpParseErr != nil { 456 return errorDumpParseErr 457 } 458 log.Entry().Error("Failure during setting configuration value") 459 return httpErr 460 } 461 log.Entry(). 462 WithField("repository", deployConfig.Repository). 463 Infof("successfully set configuration value key %v and value %v", configToSet.Key, configToSet.Value) 464 return nil 465 } 466 467 func pullByCommit(config *gctsDeployOptions, telemetryData *telemetry.CustomData, command command.ExecRunner, httpClient piperhttp.Sender) error { 468 469 cookieJar, cookieErr := cookiejar.New(nil) 470 if cookieErr != nil { 471 return errors.Wrap(cookieErr, "creating a cookie jar failed") 472 } 473 clientOptions := piperhttp.ClientOptions{ 474 CookieJar: cookieJar, 475 Username: config.Username, 476 Password: config.Password, 477 MaxRetries: -1, 478 } 479 httpClient.SetOptions(clientOptions) 480 481 requestURL := config.Host + 482 "/sap/bc/cts_abapvcs/repository/" + config.Repository + 483 "/pullByCommit?sap-client=" + config.Client + "&request=" + config.Commit 484 485 if config.Commit != "" { 486 log.Entry().Infof("preparing to deploy specified commit %v", config.Commit) 487 params := url.Values{} 488 params.Add("request", config.Commit) 489 requestURL = requestURL + "&" + params.Encode() 490 } 491 492 resp, httpErr := httpClient.SendRequest("GET", requestURL, nil, nil, nil) 493 494 defer func() { 495 if resp != nil && resp.Body != nil { 496 resp.Body.Close() 497 } 498 }() 499 500 if httpErr != nil { 501 _, errorDumpParseErr := parseErrorDumpFromResponseBody(resp) 502 if errorDumpParseErr != nil { 503 return errorDumpParseErr 504 } 505 return httpErr 506 } else if resp == nil { 507 return errors.New("did not retrieve a HTTP response") 508 } 509 510 bodyText, readErr := ioutil.ReadAll(resp.Body) 511 512 if readErr != nil { 513 return errors.Wrapf(readErr, "HTTP response body could not be read") 514 } 515 516 response, parsingErr := gabs.ParseJSON([]byte(bodyText)) 517 518 if parsingErr != nil { 519 return errors.Wrapf(parsingErr, "HTTP response body could not be parsed as JSON: %v", string(bodyText)) 520 } 521 522 log.Entry(). 523 WithField("repository", config.Repository). 524 Infof("successfully deployed commit %v (previous commit was %v)", response.Path("toCommit").Data().(string), response.Path("fromCommit").Data().(string)) 525 return nil 526 } 527 528 func createRepositoryForDeploy(config *gctsCreateRepositoryOptions, telemetryData *telemetry.CustomData, command command.ExecRunner, httpClient piperhttp.Sender, repositoryConfig []repositoryConfiguration) error { 529 530 cookieJar, cookieErr := cookiejar.New(nil) 531 if cookieErr != nil { 532 return errors.Wrapf(cookieErr, "creating repository on the ABAP system %v failed", config.Host) 533 } 534 clientOptions := piperhttp.ClientOptions{ 535 CookieJar: cookieJar, 536 Username: config.Username, 537 Password: config.Password, 538 MaxRetries: -1, 539 } 540 httpClient.SetOptions(clientOptions) 541 542 type repoData struct { 543 RID string `json:"rid"` 544 Name string `json:"name"` 545 Role string `json:"role"` 546 Type string `json:"type"` 547 VSID string `json:"vsid"` 548 RemoteRepositoryURL string `json:"url"` 549 Config []repositoryConfiguration `json:"config"` 550 } 551 552 type createRequestBody struct { 553 Repository string `json:"repository"` 554 Data repoData `json:"data"` 555 } 556 557 reqBody := createRequestBody{ 558 Repository: config.Repository, 559 Data: repoData{ 560 RID: config.Repository, 561 Name: config.Repository, 562 Role: config.Role, 563 Type: config.Type, 564 VSID: config.VSID, 565 RemoteRepositoryURL: config.RemoteRepositoryURL, 566 Config: repositoryConfig, 567 }, 568 } 569 jsonBody, marshalErr := json.Marshal(reqBody) 570 571 if marshalErr != nil { 572 return errors.Wrapf(marshalErr, "creating repository on the ABAP system %v failed", config.Host) 573 } 574 575 header := make(http.Header) 576 header.Set("Content-Type", "application/json") 577 header.Add("Accept", "application/json") 578 579 url := config.Host + "/sap/bc/cts_abapvcs/repository?sap-client=" + config.Client 580 581 resp, httpErr := httpClient.SendRequest("POST", url, bytes.NewBuffer(jsonBody), header, nil) 582 583 defer func() { 584 if resp != nil && resp.Body != nil { 585 resp.Body.Close() 586 } 587 }() 588 589 if resp == nil { 590 return errors.Errorf("creating repository on the ABAP system %v failed: %v", config.Host, httpErr) 591 } 592 593 if httpErr != nil { 594 response, errorDumpParseErr := parseErrorDumpFromResponseBody(resp) 595 if errorDumpParseErr != nil { 596 return errorDumpParseErr 597 } 598 if resp.StatusCode == 500 { 599 if response.Exception == "Repository already exists" { 600 log.Entry(). 601 WithField("repository", config.Repository). 602 Infof("the repository already exists on the ABAP system %v", config.Host) 603 return nil 604 } 605 } 606 log.Entry().Errorf("a HTTP error occurred! Response body: %v", response) 607 return errors.Wrapf(httpErr, "creating repository on the ABAP system %v failed", config.Host) 608 } 609 610 log.Entry(). 611 WithField("repository", config.Repository). 612 Infof("successfully created the repository on ABAP system %v", config.Host) 613 return nil 614 } 615 616 func getConfigurationMetadata(config *gctsDeployOptions, httpClient piperhttp.Sender) (*configurationMetadataBody, error) { 617 var response configurationMetadataBody 618 log.Entry().Infof("Starting to retrieve configuration metadata from the system") 619 requestURL := config.Host + 620 "/sap/bc/cts_abapvcs/config?sap-client=" + config.Client 621 622 resp, httpErr := httpClient.SendRequest("GET", requestURL, nil, nil, nil) 623 defer func() { 624 if resp != nil && resp.Body != nil { 625 resp.Body.Close() 626 } 627 }() 628 if httpErr != nil { 629 _, errorDumpParseErr := parseErrorDumpFromResponseBody(resp) 630 if errorDumpParseErr != nil { 631 return nil, errorDumpParseErr 632 } 633 log.Entry().Infof("Error while repository Check : %v", httpErr) 634 return &response, httpErr 635 } else if resp == nil { 636 return &response, errors.New("did not retrieve a HTTP response") 637 } 638 639 parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response) 640 if parsingErr != nil { 641 return &response, parsingErr 642 } 643 log.Entry().Infof("System Available for further step processing. The configuration metadata was successfully retrieved.") 644 return &response, nil 645 } 646 647 func splitConfigurationToMap(inputConfigMap map[string]interface{}, configMetadataInSystem configurationMetadataBody) ([]repositoryConfiguration, error) { 648 log.Entry().Infof("Parsing the configurations from the yml file") 649 var configurations []repositoryConfiguration 650 for key, value := range inputConfigMap { 651 foundConfigMetadata, _ := findConfigurationMetadata(key, configMetadataInSystem) 652 configValue := fmt.Sprint(value) 653 if (configMetadata{}) != foundConfigMetadata { 654 if foundConfigMetadata.Datatype == "BOOLEAN" && foundConfigMetadata.Example == "X" { 655 if configValue == "false" || configValue == "" { 656 configValue = "" 657 } else if configValue == "true" || configValue == "X" { 658 configValue = "X" 659 } 660 } 661 } 662 configuration := repositoryConfiguration{ 663 Key: key, 664 Value: configValue, 665 } 666 configurations = append(configurations, configuration) 667 668 } 669 log.Entry().Infof("The Configurations for the repoistory creation are : %v", configurations) 670 return configurations, nil 671 } 672 673 func findConfigurationMetadata(configToFind string, configurationsAvailable configurationMetadataBody) (configMetadata, error) { 674 var configStruct configMetadata 675 for _, config := range configurationsAvailable.Config { 676 if config.Ckey == configToFind { 677 return config, nil 678 } 679 } 680 return configStruct, nil 681 } 682 683 // Error handling for failure responses from the gcts api calls 684 func parseErrorDumpFromResponseBody(responseBody *http.Response) (*errorLogBody, error) { 685 var errorDump errorLogBody 686 parsingErr := piperhttp.ParseHTTPResponseBodyJSON(responseBody, &errorDump) 687 if parsingErr != nil { 688 return &errorDump, parsingErr 689 } 690 for _, errorLogData := range errorDump.ErrorLog { 691 log.Entry().Errorf("Time: %v, User: %v, Section: %v, Action: %v, Severity: %v, Message: %v", 692 errorLogData.Time, errorLogData.User, errorLogData.Section, 693 errorLogData.Action, errorLogData.Severity, errorLogData.Message) 694 for _, protocolErrorData := range errorLogData.Protocol { 695 log.Entry().Errorf("Type: %v", protocolErrorData.Type) 696 for _, protocols := range protocolErrorData.Protocol { 697 if strings.Contains(protocols, "4 ETW000 ") { 698 protocols = strings.ReplaceAll(protocols, "4 ETW000 ", "") 699 } else if strings.Contains(protocols, "4EETW000 ") { 700 protocols = strings.ReplaceAll(protocols, "4EETW000 ", "ERROR: ") 701 } 702 log.Entry().Error(protocols) 703 } 704 } 705 } 706 return &errorDump, nil 707 } 708 709 type repositoryConfiguration struct { 710 Key string `json:"key"` 711 Value string `json:"value"` 712 } 713 714 type getRepositoryResponseBody struct { 715 Result struct { 716 Rid string `json:"rid"` 717 Name string `json:"name"` 718 Role string `json:"role"` 719 Vsid string `json:"vsid"` 720 Status string `json:"status"` 721 Branch string `json:"branch"` 722 Url string `json:"url"` 723 Config []struct { 724 Key string `json:"key"` 725 Value string `json:"value"` 726 Category string `json:"category"` 727 } `json:"config"` 728 Objects int64 `json:"objects"` 729 CurrentCommit string `json:"currentCommit"` 730 Connection string `json:"connection"` 731 } `json:"result"` 732 } 733 734 type setConfigKeyBody struct { 735 Key string `json:"key"` 736 Value string `json:"value"` 737 } 738 739 type switchBranchResponseBody struct { 740 Result struct { 741 FromCommit string `json:"fromCommit"` 742 ToCommit string `json:"ToCommit"` 743 } `json:"result"` 744 Log []struct { 745 Time string `json:"time"` 746 User string `json:"user"` 747 Section string `json:"section"` 748 Action string `json:"Action"` 749 Severity string `json:"Severity"` 750 Message string `json:"Message"` 751 } `json:"log"` 752 } 753 754 type deployCommitToAbapSystemBody struct { 755 Repository string `json:"repository"` 756 Scope string `json:"scope"` 757 Commit string `json:"commit"` 758 Objects []struct { 759 Object string `json:"object"` 760 Type string `json:"type"` 761 User string `json:"user"` 762 Pgmid string `json:"pgmid"` 763 Keys []struct { 764 Tabname string `json:"tabname"` 765 Columns []struct { 766 Key string `json:"key"` 767 Field string `json:"field"` 768 Value string `json:"value"` 769 Type string `json:"type"` 770 Inttype string `json:"inttype"` 771 Length string `json:"length"` 772 } 773 } 774 } `json:"objects"` 775 } 776 777 type configMetadata struct { 778 Ckey string `json:"ckey"` 779 Ctype string `json:"ctype"` 780 Cvisible string `json:"cvisible"` 781 Datatype string `json:"datatype"` 782 DefaultValue string `json:"defaultValue"` 783 Description string `json:"description"` 784 Category string `json:"category"` 785 UiElement string `json:"uiElement"` 786 Example string `json:"example"` 787 } 788 789 type configurationMetadataBody struct { 790 Config []configMetadata `json:"config"` 791 } 792 793 type errorProtocolbody struct { 794 Type string `json:"type"` 795 Protocol []string `json:"protocol"` 796 } 797 798 type errorLog struct { 799 Time int `json:"time"` 800 User string `json:"user"` 801 Section string `json:"section"` 802 Action string `json:"action"` 803 Severity string `json:"severity"` 804 Message string `json:"message"` 805 Protocol []errorProtocolbody `json:"protocol"` 806 } 807 808 type errorLogBody struct { 809 ErrorLog []errorLog `json:"errorLog"` 810 Exception string `json:"exception"` 811 }