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 }