github.com/xgoffin/jenkins-library@v1.154.0/cmd/abapEnvironmentPullGitRepo.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 abapEnvironmentPullGitRepo(options abapEnvironmentPullGitRepoOptions, _ *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 := runAbapEnvironmentPullGitRepo(&options, &autils, &client)
    35  	if err != nil {
    36  		log.Entry().WithError(err).Fatal("step execution failed")
    37  	}
    38  }
    39  
    40  func runAbapEnvironmentPullGitRepo(options *abapEnvironmentPullGitRepoOptions, com abaputils.Communication, client piperhttp.Sender) (err error) {
    41  
    42  	subOptions := convertPullConfig(options)
    43  
    44  	// Determine the host, user and password, either via the input parameters or via a cloud foundry service key
    45  	connectionDetails, err := com.GetAbapCommunicationArrangementInfo(subOptions, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull")
    46  	if err != nil {
    47  		return errors.Wrap(err, "Parameters for the ABAP Connection not available")
    48  	}
    49  
    50  	cookieJar, err := cookiejar.New(nil)
    51  	if err != nil {
    52  		return errors.Wrap(err, "Could not create a Cookie Jar")
    53  	}
    54  	clientOptions := piperhttp.ClientOptions{
    55  		MaxRequestDuration: 180 * time.Second,
    56  		CookieJar:          cookieJar,
    57  		Username:           connectionDetails.User,
    58  		Password:           connectionDetails.Password,
    59  	}
    60  	client.SetOptions(clientOptions)
    61  	pollIntervall := com.GetPollIntervall()
    62  
    63  	repositories := []abaputils.Repository{}
    64  	err = checkPullRepositoryConfiguration(*options)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	repositories, err = abaputils.GetRepositories(&abaputils.RepositoriesConfig{RepositoryNames: options.RepositoryNames, Repositories: options.Repositories})
    69  	handleIgnoreCommit(repositories, options.IgnoreCommit)
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	err = pullRepositories(repositories, connectionDetails, client, pollIntervall)
    75  	return err
    76  
    77  }
    78  
    79  func pullRepositories(repositories []abaputils.Repository, pullConnectionDetails abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, pollIntervall time.Duration) (err error) {
    80  	log.Entry().Infof("Start pulling %v repositories", len(repositories))
    81  	for _, repo := range repositories {
    82  		err = handlePull(repo, pullConnectionDetails, client, pollIntervall)
    83  		if err != nil {
    84  			break
    85  		}
    86  	}
    87  	if err == nil {
    88  		finishPullLogs()
    89  	}
    90  	return err
    91  }
    92  
    93  func handlePull(repo abaputils.Repository, pullConnectionDetails abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, pollIntervall time.Duration) (err error) {
    94  
    95  	logString := repo.GetPullLogString()
    96  	errorString := "Pull of the " + logString + " failed on the ABAP system"
    97  
    98  	log.Entry().Info("-------------------------")
    99  	log.Entry().Info("Start pulling the " + logString)
   100  	log.Entry().Info("-------------------------")
   101  
   102  	uriConnectionDetails, err := triggerPull(repo, pullConnectionDetails, client)
   103  	if err != nil {
   104  		return errors.Wrapf(err, errorString)
   105  	}
   106  
   107  	// Polling the status of the repository import on the ABAP Environment system
   108  	status, errorPollEntity := abaputils.PollEntity(repo.Name, uriConnectionDetails, client, pollIntervall)
   109  	if errorPollEntity != nil {
   110  		return errors.Wrapf(errorPollEntity, errorString)
   111  	}
   112  	if status == "E" {
   113  		return errors.New(errorString)
   114  	}
   115  	log.Entry().Info(repo.Name + " was pulled successfully")
   116  	return err
   117  }
   118  
   119  func triggerPull(repo abaputils.Repository, pullConnectionDetails abaputils.ConnectionDetailsHTTP, client piperhttp.Sender) (abaputils.ConnectionDetailsHTTP, error) {
   120  
   121  	uriConnectionDetails := pullConnectionDetails
   122  	uriConnectionDetails.URL = ""
   123  	pullConnectionDetails.XCsrfToken = "fetch"
   124  
   125  	// Loging into the ABAP System - getting the x-csrf-token and cookies
   126  	resp, err := abaputils.GetHTTPResponse("HEAD", pullConnectionDetails, nil, client)
   127  	if err != nil {
   128  		err = abaputils.HandleHTTPError(resp, err, "Authentication on the ABAP system failed", pullConnectionDetails)
   129  		return uriConnectionDetails, err
   130  	}
   131  	defer resp.Body.Close()
   132  
   133  	log.Entry().WithField("StatusCode", resp.Status).WithField("ABAP Endpoint", pullConnectionDetails.URL).Debug("Authentication on the ABAP system successful")
   134  	uriConnectionDetails.XCsrfToken = resp.Header.Get("X-Csrf-Token")
   135  	pullConnectionDetails.XCsrfToken = uriConnectionDetails.XCsrfToken
   136  
   137  	// Trigger the Pull of a Repository
   138  	if repo.Name == "" {
   139  		return uriConnectionDetails, errors.New("An empty string was passed for the parameter 'repositoryName'")
   140  	}
   141  
   142  	jsonBody := []byte(repo.GetPullRequestBody())
   143  	resp, err = abaputils.GetHTTPResponse("POST", pullConnectionDetails, jsonBody, client)
   144  	if err != nil {
   145  		err = abaputils.HandleHTTPError(resp, err, "Could not pull the "+repo.GetPullLogString(), uriConnectionDetails)
   146  		return uriConnectionDetails, err
   147  	}
   148  	defer resp.Body.Close()
   149  	log.Entry().WithField("StatusCode", resp.Status).WithField("repositoryName", repo.Name).WithField("commitID", repo.CommitID).WithField("Tag", repo.Tag).Debug("Triggered Pull of repository / software component")
   150  
   151  	// Parse Response
   152  	var body abaputils.PullEntity
   153  	var abapResp map[string]*json.RawMessage
   154  	bodyText, errRead := ioutil.ReadAll(resp.Body)
   155  	if errRead != nil {
   156  		return uriConnectionDetails, err
   157  	}
   158  	json.Unmarshal(bodyText, &abapResp)
   159  	json.Unmarshal(*abapResp["d"], &body)
   160  	if reflect.DeepEqual(abaputils.PullEntity{}, body) {
   161  		log.Entry().WithField("StatusCode", resp.Status).WithField("repositoryName", repo.Name).WithField("commitID", repo.CommitID).WithField("Tag", repo.Tag).Error("Could not pull the repository / software component")
   162  		err := errors.New("Request to ABAP System not successful")
   163  		return uriConnectionDetails, err
   164  	}
   165  
   166  	uriConnectionDetails.URL = body.Metadata.URI
   167  	return uriConnectionDetails, nil
   168  }
   169  
   170  func checkPullRepositoryConfiguration(options abapEnvironmentPullGitRepoOptions) error {
   171  	if len(options.RepositoryNames) > 0 && options.Repositories != "" {
   172  		log.Entry().Info("It seems like you have specified repositories directly via the configuration parameter 'repositoryNames' as well as in the dedicated repositories configuration file. Please note that in this case both configurations will be handled and pulled.")
   173  	}
   174  	if len(options.RepositoryNames) == 0 && options.Repositories == "" {
   175  		return fmt.Errorf("Checking configuration failed: %w", errors.New("You have not specified any repository configuration to be pulled into the ABAP Environment System. Please make sure that you specified the repositories that should be pulled either in a dedicated file or via the parameter 'repositoryNames'. For more information please read the User documentation"))
   176  	}
   177  	return nil
   178  }
   179  
   180  func finishPullLogs() {
   181  	log.Entry().Info("-------------------------")
   182  	log.Entry().Info("All repositories were pulled successfully")
   183  }
   184  
   185  func convertPullConfig(config *abapEnvironmentPullGitRepoOptions) abaputils.AbapEnvironmentOptions {
   186  	subOptions := abaputils.AbapEnvironmentOptions{}
   187  
   188  	subOptions.CfAPIEndpoint = config.CfAPIEndpoint
   189  	subOptions.CfServiceInstance = config.CfServiceInstance
   190  	subOptions.CfServiceKeyName = config.CfServiceKeyName
   191  	subOptions.CfOrg = config.CfOrg
   192  	subOptions.CfSpace = config.CfSpace
   193  	subOptions.Host = config.Host
   194  	subOptions.Password = config.Password
   195  	subOptions.Username = config.Username
   196  	return subOptions
   197  }
   198  
   199  func handleIgnoreCommit(repositories []abaputils.Repository, ignoreCommit bool) {
   200  	for i := range repositories {
   201  		if ignoreCommit {
   202  			repositories[i].CommitID = ""
   203  		}
   204  	}
   205  }