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  }