github.com/SAP/jenkins-library@v1.362.0/cmd/integrationArtifactTransport.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"math/rand"
     9  	"net/http"
    10  	"time"
    11  
    12  	"github.com/Jeffail/gabs/v2"
    13  	"github.com/SAP/jenkins-library/pkg/apim"
    14  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    15  	"github.com/SAP/jenkins-library/pkg/log"
    16  	"github.com/SAP/jenkins-library/pkg/telemetry"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  func integrationArtifactTransport(config integrationArtifactTransportOptions, telemetryData *telemetry.CustomData) {
    21  	httpClient := &piperhttp.Client{}
    22  	err := runIntegrationArtifactTransport(&config, telemetryData, httpClient)
    23  	if err != nil {
    24  		log.Entry().WithError(err).Fatal("step execution failed")
    25  	}
    26  }
    27  
    28  func runIntegrationArtifactTransport(config *integrationArtifactTransportOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender) error {
    29  	apimData := apim.Bundle{APIServiceKey: config.CasServiceKey, Client: httpClient}
    30  	err := apim.Utils.InitAPIM(&apimData)
    31  	if err != nil {
    32  		return err
    33  	}
    34  	return CreateIntegrationArtifactTransportRequest(config, apimData)
    35  }
    36  
    37  // CreateIntegrationArtifactTransportRequest - Create a transport request for Integration Package
    38  func CreateIntegrationArtifactTransportRequest(config *integrationArtifactTransportOptions, apistruct apim.Bundle) error {
    39  	httpMethod := http.MethodPost
    40  	httpClient := apistruct.Client
    41  	createTransportRequestURL := fmt.Sprintf("%s/v1/contentResources/export", apistruct.Host)
    42  	header := make(http.Header)
    43  	header.Add("content-type", "application/json")
    44  	payload, jsonError := GetCPITransportReqPayload(config)
    45  	if jsonError != nil {
    46  		return errors.Wrapf(jsonError, "Failed to get json payload for file %v, failed with error", config.IntegrationPackageID)
    47  	}
    48  
    49  	createTransportRequestResp, httpErr := httpClient.SendRequest(httpMethod, createTransportRequestURL, payload, header, nil)
    50  
    51  	if httpErr != nil {
    52  		return errors.Wrapf(httpErr, "HTTP %v request to %v failed with error", httpMethod, createTransportRequestURL)
    53  	}
    54  
    55  	if createTransportRequestResp != nil && createTransportRequestResp.Body != nil {
    56  		defer createTransportRequestResp.Body.Close()
    57  	}
    58  
    59  	if createTransportRequestResp == nil {
    60  		return errors.Errorf("did not retrieve a HTTP response")
    61  	}
    62  
    63  	if createTransportRequestResp.StatusCode == http.StatusAccepted {
    64  		log.Entry().
    65  			WithField("IntegrationPackageID", config.IntegrationPackageID).
    66  			Info("successfully created the integration package transport request")
    67  
    68  		bodyText, readErr := io.ReadAll(createTransportRequestResp.Body)
    69  		if readErr != nil {
    70  			return errors.Wrap(readErr, "HTTP response body could not be read")
    71  		}
    72  		jsonResponse, parsingErr := gabs.ParseJSON([]byte(bodyText))
    73  		if parsingErr != nil {
    74  			return errors.Wrapf(parsingErr, "HTTP response body could not be parsed as JSON: %v", string(bodyText))
    75  		}
    76  		processId := jsonResponse.Path("processId").Data().(string)
    77  
    78  		if processId != "" {
    79  			error := pollTransportStatus(processId, retryCount, config, httpClient, apistruct.Host)
    80  			return error
    81  		}
    82  		return errors.New("Invalid process id")
    83  	}
    84  	responseBody, readErr := io.ReadAll(createTransportRequestResp.Body)
    85  
    86  	if readErr != nil {
    87  		return errors.Wrapf(readErr, "HTTP response body could not be read, response status code: %v", createTransportRequestResp.StatusCode)
    88  	}
    89  	log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code : %v", string(responseBody), createTransportRequestResp.StatusCode)
    90  	return errors.Errorf("integration flow deployment failed, response Status code: %v", createTransportRequestResp.StatusCode)
    91  }
    92  
    93  // pollTransportStatus - Poll the integration package transport processing, return status or error details
    94  func pollTransportStatus(processId string, remainingRetries int, config *integrationArtifactTransportOptions, httpClient piperhttp.Sender, apiHost string) error {
    95  
    96  	if remainingRetries <= 0 {
    97  		return errors.New("failed to start integration artifact after retrying several times")
    98  	}
    99  	transportStatus, err := getIntegrationTransportProcessingStatus(config, httpClient, apiHost, processId)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	//with specific delay between each retry
   105  	if (transportStatus == "RUNNING") || (transportStatus == "INITIAL") {
   106  		// Calling Sleep method
   107  		sleepTime := int(retryCount * 3)
   108  		time.Sleep(time.Duration(sleepTime) * time.Second)
   109  		remainingRetries--
   110  		return pollTransportStatus(processId, retryCount, config, httpClient, apiHost)
   111  	}
   112  
   113  	//if artifact transport completed, then just return
   114  	if transportStatus == "FINISHED" {
   115  		return nil
   116  	}
   117  
   118  	//if error return immediately with error details
   119  	if transportStatus == "ERROR" || transportStatus == "ABORTED" {
   120  		resp, err := getIntegrationTransportError(config, httpClient, apiHost, processId)
   121  		if err != nil {
   122  			return err
   123  		}
   124  		return errors.New(resp)
   125  	}
   126  	return nil
   127  }
   128  
   129  // GetJSONPayload -return http payload as byte array
   130  func GetCPITransportReqPayload(config *integrationArtifactTransportOptions) (*bytes.Buffer, error) {
   131  	jsonObj := gabs.New()
   132  	jsonObj.Set(rand.Intn(5000), "id")
   133  	jsonObj.Set("MonitoringTeam", "requestor")
   134  	jsonObj.Set("1.0.0", "version")
   135  	jsonObj.Set("TransportManagementService", "exportMode")
   136  	jsonObj.Set("MTAR", "exportMediaType")
   137  	jsonObj.Set("Integration Artifact transport request for TransportManagementService", "description")
   138  	jsonResourceObj := gabs.New()
   139  	jsonResourceObj.Set(config.IntegrationPackageID, "id")
   140  	jsonResourceObj.Set(config.ResourceID, "resourceID")
   141  	jsonResourceObj.Set("d9c3fe08ceeb47a2991e53049f2ed766", "contentType")
   142  	jsonResourceObj.Set("package", "subType")
   143  	jsonResourceObj.Set(config.Name, "name")
   144  	jsonResourceObj.Set("CloudIntegration", "type")
   145  	jsonResourceObj.Set(config.Version, "version")
   146  	jsonObj.ArrayAppend(jsonResourceObj, "contentResources")
   147  
   148  	jsonBody, jsonErr := json.Marshal(jsonObj)
   149  
   150  	if jsonErr != nil {
   151  		return nil, errors.Wrapf(jsonErr, "Transport request payload is invalid for integration package artifact %q", config.IntegrationPackageID)
   152  	}
   153  	return bytes.NewBuffer(jsonBody), nil
   154  }
   155  
   156  // getIntegrationTransportProcessingStatus - Get integration package transport request processing Status
   157  func getIntegrationTransportProcessingStatus(config *integrationArtifactTransportOptions, httpClient piperhttp.Sender, apiHost string, processId string) (string, error) {
   158  	httpMethod := "GET"
   159  	header := make(http.Header)
   160  	header.Add("content-type", "application/json")
   161  	header.Add("Accept", "application/json")
   162  	transportProcStatusURL := fmt.Sprintf("%s/v1/operations/%s", apiHost, processId)
   163  	transportProcStatusResp, httpErr := httpClient.SendRequest(httpMethod, transportProcStatusURL, nil, header, nil)
   164  
   165  	if transportProcStatusResp != nil && transportProcStatusResp.Body != nil {
   166  		defer transportProcStatusResp.Body.Close()
   167  	}
   168  
   169  	if transportProcStatusResp == nil {
   170  		return "", errors.Errorf("did not retrieve a HTTP response: %v", httpErr)
   171  	}
   172  
   173  	if (transportProcStatusResp.StatusCode == http.StatusOK) || (transportProcStatusResp.StatusCode == http.StatusAccepted) {
   174  		log.Entry().
   175  			WithField("IntegrationPackageID", config.IntegrationPackageID).
   176  			Info("successfully processed the integration package transport response status")
   177  
   178  		bodyText, readErr := io.ReadAll(transportProcStatusResp.Body)
   179  		if readErr != nil {
   180  			return "", errors.Wrapf(readErr, "HTTP response body could not be read, response status code: %v", transportProcStatusResp.StatusCode)
   181  		}
   182  		jsonResponse, parsingErr := gabs.ParseJSON([]byte(bodyText))
   183  		if parsingErr != nil {
   184  			return "", errors.Wrapf(parsingErr, "HTTP response body could not be parsed as JSON: %v", string(bodyText))
   185  		}
   186  		contentTransporStatus := jsonResponse.Path("state").Data().(string)
   187  		return contentTransporStatus, nil
   188  	}
   189  	if httpErr != nil {
   190  		return getHTTPErrorMessage(httpErr, transportProcStatusResp, httpMethod, transportProcStatusURL)
   191  	}
   192  	return "", errors.Errorf("failed to get transport request processing status, response Status code: %v", transportProcStatusResp.StatusCode)
   193  }
   194  
   195  // getTransportError - Get integration package transport failures error details
   196  func getIntegrationTransportError(config *integrationArtifactTransportOptions, httpClient piperhttp.Sender, apiHost string, processId string) (string, error) {
   197  	httpMethod := "GET"
   198  	header := make(http.Header)
   199  	header.Add("content-type", "application/json")
   200  	errorStatusURL := fmt.Sprintf("%s/v1/operations/%s/logs", apiHost, processId)
   201  	errorStatusResp, httpErr := httpClient.SendRequest(httpMethod, errorStatusURL, nil, header, nil)
   202  
   203  	if errorStatusResp != nil && errorStatusResp.Body != nil {
   204  		defer errorStatusResp.Body.Close()
   205  	}
   206  
   207  	if errorStatusResp == nil {
   208  		return "", errors.Errorf("did not retrieve a HTTP response: %v", httpErr)
   209  	}
   210  
   211  	if errorStatusResp.StatusCode == http.StatusOK {
   212  		log.Entry().
   213  			WithField("IntegrationPackageId", config.IntegrationPackageID).
   214  			Info("Successfully retrieved deployment failures error details")
   215  		responseBody, readErr := io.ReadAll(errorStatusResp.Body)
   216  		if readErr != nil {
   217  			return "", errors.Wrapf(readErr, "HTTP response body could not be read, response status code: %v", errorStatusResp.StatusCode)
   218  		}
   219  		log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code: %v", string(responseBody), errorStatusResp.StatusCode)
   220  		errorDetails := string(responseBody)
   221  		return errorDetails, nil
   222  	}
   223  	if httpErr != nil {
   224  		return getHTTPErrorMessage(httpErr, errorStatusResp, httpMethod, errorStatusURL)
   225  	}
   226  	return "", errors.Errorf("failed to get Integration Package transport error details, response Status code: %v", errorStatusResp.StatusCode)
   227  }