github.com/jaylevin/jenkins-library@v1.230.4/cmd/abapEnvironmentCreateTag.go (about)

     1  package cmd
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http/cookiejar"
     8  	"strings"
     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 abapEnvironmentCreateTag(config abapEnvironmentCreateTagOptions, telemetryData *telemetry.CustomData) {
    20  
    21  	c := command.Command{}
    22  
    23  	c.Stdout(log.Writer())
    24  	c.Stderr(log.Writer())
    25  
    26  	var autils = abaputils.AbapUtils{
    27  		Exec: &c,
    28  	}
    29  
    30  	client := piperhttp.Client{}
    31  
    32  	if err := runAbapEnvironmentCreateTag(&config, telemetryData, &autils, &client); err != nil {
    33  		log.Entry().WithError(err).Fatal("step execution failed")
    34  	}
    35  }
    36  
    37  func runAbapEnvironmentCreateTag(config *abapEnvironmentCreateTagOptions, telemetryData *telemetry.CustomData, com abaputils.Communication, client piperhttp.Sender) error {
    38  
    39  	connectionDetails, errorGetInfo := com.GetAbapCommunicationArrangementInfo(convertTagConfig(config), "")
    40  	if errorGetInfo != nil {
    41  		return errors.Wrap(errorGetInfo, "Parameters for the ABAP Connection not available")
    42  	}
    43  
    44  	// Configuring the HTTP Client and CookieJar
    45  	cookieJar, errorCookieJar := cookiejar.New(nil)
    46  	if errorCookieJar != nil {
    47  		return errors.Wrap(errorCookieJar, "Could not create a Cookie Jar")
    48  	}
    49  
    50  	client.SetOptions(piperhttp.ClientOptions{
    51  		MaxRequestDuration: 180 * time.Second,
    52  		CookieJar:          cookieJar,
    53  		Username:           connectionDetails.User,
    54  		Password:           connectionDetails.Password,
    55  	})
    56  
    57  	backlog, errorPrepare := prepareBacklog(config)
    58  	if errorPrepare != nil {
    59  		return fmt.Errorf("Something failed during the tag creation: %w", errorPrepare)
    60  	}
    61  
    62  	return createTags(backlog, telemetryData, connectionDetails, client, com)
    63  }
    64  
    65  func createTags(backlog []CreateTagBacklog, telemetryData *telemetry.CustomData, con abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, com abaputils.Communication) (err error) {
    66  
    67  	connection := con
    68  	connection.XCsrfToken = "fetch"
    69  	connection.URL = con.URL + "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Tags"
    70  	resp, err := abaputils.GetHTTPResponse("HEAD", connection, nil, client)
    71  	if err != nil {
    72  		return abaputils.HandleHTTPError(resp, err, "Authentication on the ABAP system failed", con)
    73  	}
    74  	defer resp.Body.Close()
    75  
    76  	log.Entry().WithField("StatusCode", resp.Status).WithField("ABAP Endpoint", connection.URL).Debug("Authentication on the ABAP system successful")
    77  	connection.XCsrfToken = resp.Header.Get("X-Csrf-Token")
    78  
    79  	errorOccurred := false
    80  	for _, item := range backlog {
    81  		err = createTagsForSingleItem(item, telemetryData, connection, client, com)
    82  		if err != nil {
    83  			errorOccurred = true
    84  		}
    85  	}
    86  
    87  	if errorOccurred {
    88  		message := "At least one tag has not been created"
    89  		log.Entry().Errorf(message)
    90  		return errors.New(message)
    91  	}
    92  	return nil
    93  
    94  }
    95  
    96  func createTagsForSingleItem(item CreateTagBacklog, telemetryData *telemetry.CustomData, con abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, com abaputils.Communication) (err error) {
    97  
    98  	errorOccurred := false
    99  	for index := range item.tags {
   100  		err = createSingleTag(item, index, telemetryData, con, client, com)
   101  		if err != nil {
   102  			errorOccurred = true
   103  		}
   104  	}
   105  	if errorOccurred {
   106  		message := "At least one tag has not been created"
   107  		err = errors.New(message)
   108  	}
   109  	return err
   110  }
   111  
   112  func createSingleTag(item CreateTagBacklog, index int, telemetryData *telemetry.CustomData, con abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, com abaputils.Communication) (err error) {
   113  
   114  	requestBodyStruct := CreateTagBody{RepositoryName: item.repositoryName, CommitID: item.commitID, Tag: item.tags[index].tagName, Description: item.tags[index].tagDescription}
   115  	requestBodyJson, err := json.Marshal(&requestBodyStruct)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	log.Entry().Debugf("Request body: %s", requestBodyJson)
   121  	resp, err := abaputils.GetHTTPResponse("POST", con, requestBodyJson, client)
   122  	if err != nil {
   123  		errorMessage := "Could not create tag " + requestBodyStruct.Tag + " for repository " + requestBodyStruct.RepositoryName + " with commitID " + requestBodyStruct.CommitID
   124  		err = abaputils.HandleHTTPError(resp, err, errorMessage, con)
   125  		return err
   126  	}
   127  	defer resp.Body.Close()
   128  
   129  	// Parse response
   130  	var createTagResponse CreateTagResponse
   131  	var abapResp map[string]*json.RawMessage
   132  	bodyText, _ := ioutil.ReadAll(resp.Body)
   133  
   134  	if err = json.Unmarshal(bodyText, &abapResp); err != nil {
   135  		return err
   136  	}
   137  	if err = json.Unmarshal(*abapResp["d"], &createTagResponse); err != nil {
   138  		return err
   139  	}
   140  
   141  	con.URL = con.Host + "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull(guid'" + createTagResponse.UUID + "')"
   142  	err = checkStatus(con, client, com)
   143  
   144  	if err == nil {
   145  		log.Entry().Info("Created tag " + requestBodyStruct.Tag + " for repository " + requestBodyStruct.RepositoryName + " with commitID " + requestBodyStruct.CommitID)
   146  	} else {
   147  		log.Entry().Error("NOT created: Tag " + requestBodyStruct.Tag + " for repository " + requestBodyStruct.RepositoryName + " with commitID " + requestBodyStruct.CommitID)
   148  	}
   149  
   150  	return err
   151  }
   152  
   153  func checkStatus(con abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, com abaputils.Communication) (err error) {
   154  	status := "R"
   155  	pollIntervall := com.GetPollIntervall()
   156  	count := 0
   157  	for {
   158  		count += 1
   159  		entity, _, err := abaputils.GetStatus("Could not create Tag", con, client)
   160  		if err != nil {
   161  			return err
   162  		}
   163  		status = entity.Status
   164  		if status != "R" {
   165  			if status == "E" {
   166  				err = errors.New("Could not create Tag")
   167  			}
   168  			return err
   169  		}
   170  		if count >= 200 {
   171  			return errors.New("Could not create Tag (Timeout)")
   172  		}
   173  		time.Sleep(pollIntervall)
   174  	}
   175  }
   176  
   177  func prepareBacklog(config *abapEnvironmentCreateTagOptions) (backlog []CreateTagBacklog, err error) {
   178  
   179  	if config.Repositories != "" && config.RepositoryName != "" {
   180  		return nil, errors.New("Configuring the parameter repositories and the parameter repositoryName at the same time is not allowed")
   181  	}
   182  
   183  	if config.RepositoryName != "" && config.CommitID != "" {
   184  		backlog = append(backlog, CreateTagBacklog{repositoryName: config.RepositoryName, commitID: config.CommitID})
   185  	}
   186  
   187  	if config.Repositories != "" {
   188  		descriptor, err := abaputils.ReadAddonDescriptor(config.Repositories) //config.Repositories should contain a file name
   189  		if err != nil {
   190  			return nil, err
   191  		}
   192  		for _, repo := range descriptor.Repositories {
   193  			backlogInstance := CreateTagBacklog{repositoryName: repo.Name, commitID: repo.CommitID}
   194  			if config.GenerateTagForAddonComponentVersion && repo.VersionYAML != "" {
   195  				tag := Tag{tagName: "v" + repo.VersionYAML, tagDescription: "Generated by the ABAP Environment Pipeline"}
   196  				backlogInstance.tags = append(backlogInstance.tags, tag)
   197  			}
   198  			backlog = append(backlog, backlogInstance)
   199  		}
   200  		if config.GenerateTagForAddonProductVersion {
   201  			if descriptor.AddonProduct != "" && descriptor.AddonVersionYAML != "" {
   202  				addonProductDash := strings.Replace(descriptor.AddonProduct, "/", "-", 2)
   203  				backlog = addTagToList(backlog, addonProductDash+"-"+descriptor.AddonVersionYAML, "Generated by the ABAP Environment Pipeline")
   204  			} else {
   205  				log.Entry().WithField("generateTagForAddonProductVersion", config.GenerateTagForAddonProductVersion).WithField("AddonProduct", descriptor.AddonProduct).WithField("AddonVersion", descriptor.AddonVersionYAML).Infof("Not all required values are provided to create an addon product version tag")
   206  			}
   207  		}
   208  	}
   209  	if config.TagName != "" {
   210  		backlog = addTagToList(backlog, config.TagName, config.TagDescription)
   211  	}
   212  	return backlog, nil
   213  }
   214  
   215  func addTagToList(backlog []CreateTagBacklog, tag string, description string) []CreateTagBacklog {
   216  
   217  	for i, item := range backlog {
   218  		tag := Tag{tagName: tag, tagDescription: description}
   219  		backlog[i].tags = append(item.tags, tag)
   220  	}
   221  	return backlog
   222  }
   223  
   224  func convertTagConfig(config *abapEnvironmentCreateTagOptions) abaputils.AbapEnvironmentOptions {
   225  	subOptions := abaputils.AbapEnvironmentOptions{}
   226  
   227  	subOptions.CfAPIEndpoint = config.CfAPIEndpoint
   228  	subOptions.CfServiceInstance = config.CfServiceInstance
   229  	subOptions.CfServiceKeyName = config.CfServiceKeyName
   230  	subOptions.CfOrg = config.CfOrg
   231  	subOptions.CfSpace = config.CfSpace
   232  	subOptions.Host = config.Host
   233  	subOptions.Password = config.Password
   234  	subOptions.Username = config.Username
   235  
   236  	return subOptions
   237  }
   238  
   239  type CreateTagBacklog struct {
   240  	repositoryName string
   241  	commitID       string
   242  	tags           []Tag
   243  }
   244  
   245  type Tag struct {
   246  	tagName        string
   247  	tagDescription string
   248  }
   249  
   250  type CreateTagBody struct {
   251  	RepositoryName string `json:"sc_name"`
   252  	CommitID       string `json:"commit_id"`
   253  	Tag            string `json:"tag_name"`
   254  	Description    string `json:"tag_description"`
   255  }
   256  
   257  type CreateTagResponse struct {
   258  	UUID string `json:"uuid"`
   259  }