github.com/xgoffin/jenkins-library@v1.154.0/cmd/integrationArtifactUpload.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 b64 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "io/ioutil" 9 "net/http" 10 11 "github.com/Jeffail/gabs/v2" 12 "github.com/SAP/jenkins-library/pkg/command" 13 "github.com/SAP/jenkins-library/pkg/cpi" 14 piperhttp "github.com/SAP/jenkins-library/pkg/http" 15 "github.com/SAP/jenkins-library/pkg/log" 16 "github.com/SAP/jenkins-library/pkg/piperutils" 17 "github.com/SAP/jenkins-library/pkg/telemetry" 18 "github.com/pkg/errors" 19 ) 20 21 type integrationArtifactUploadUtils interface { 22 command.ExecRunner 23 24 // Add more methods here, or embed additional interfaces, or remove/replace as required. 25 // The integrationArtifactUploadUtils interface should be descriptive of your runtime dependencies, 26 // i.e. include everything you need to be able to mock in tests. 27 // Unit tests shall be executable in parallel (not depend on global state), and don't (re-)test dependencies. 28 } 29 30 type integrationArtifactUploadUtilsBundle struct { 31 *command.Command 32 33 // Embed more structs as necessary to implement methods or interfaces you add to integrationArtifactUploadUtils. 34 // Structs embedded in this way must each have a unique set of methods attached. 35 // If there is no struct which implements the method you need, attach the method to 36 // integrationArtifactUploadUtilsBundle and forward to the implementation of the dependency. 37 } 38 39 func newIntegrationArtifactUploadUtils() integrationArtifactUploadUtils { 40 utils := integrationArtifactUploadUtilsBundle{ 41 Command: &command.Command{}, 42 } 43 // Reroute command output to logging framework 44 utils.Stdout(log.Writer()) 45 utils.Stderr(log.Writer()) 46 return &utils 47 } 48 49 func integrationArtifactUpload(config integrationArtifactUploadOptions, telemetryData *telemetry.CustomData) { 50 // Utils can be used wherever the command.ExecRunner interface is expected. 51 // It can also be used for example as a mavenExecRunner. 52 httpClient := &piperhttp.Client{} 53 fileUtils := &piperutils.Files{} 54 // For HTTP calls import piperhttp "github.com/SAP/jenkins-library/pkg/http" 55 // and use a &piperhttp.Client{} in a custom system 56 // Example: step checkmarxExecuteScan.go 57 58 // Error situations should be bubbled up until they reach the line below which will then stop execution 59 // through the log.Entry().Fatal() call leading to an os.Exit(1) in the end. 60 err := runIntegrationArtifactUpload(&config, telemetryData, fileUtils, httpClient) 61 if err != nil { 62 log.Entry().WithError(err).Fatal("step execution failed") 63 } 64 } 65 66 func runIntegrationArtifactUpload(config *integrationArtifactUploadOptions, telemetryData *telemetry.CustomData, fileUtils piperutils.FileUtils, httpClient piperhttp.Sender) error { 67 68 serviceKey, err := cpi.ReadCpiServiceKey(config.APIServiceKey) 69 if err != nil { 70 return err 71 } 72 73 clientOptions := piperhttp.ClientOptions{} 74 header := make(http.Header) 75 header.Add("Accept", "application/json") 76 iFlowStatusServiceURL := fmt.Sprintf("%s/api/v1/IntegrationDesigntimeArtifacts(Id='%s',Version='%s')", serviceKey.OAuth.Host, config.IntegrationFlowID, "Active") 77 tokenParameters := cpi.TokenParameters{TokenURL: serviceKey.OAuth.OAuthTokenProviderURL, Username: serviceKey.OAuth.ClientID, Password: serviceKey.OAuth.ClientSecret, Client: httpClient} 78 token, err := cpi.CommonUtils.GetBearerToken(tokenParameters) 79 if err != nil { 80 return errors.Wrap(err, "failed to fetch Bearer Token") 81 } 82 clientOptions.Token = fmt.Sprintf("Bearer %s", token) 83 httpClient.SetOptions(clientOptions) 84 httpMethod := "GET" 85 86 //Check availability of integration artefact in CPI design time 87 iFlowStatusResp, httpErr := httpClient.SendRequest(httpMethod, iFlowStatusServiceURL, nil, header, nil) 88 89 if iFlowStatusResp != nil && iFlowStatusResp.Body != nil { 90 defer iFlowStatusResp.Body.Close() 91 } 92 if iFlowStatusResp.StatusCode == 200 { 93 return UpdateIntegrationArtifact(config, httpClient, fileUtils, serviceKey.OAuth.Host) 94 } else if httpErr != nil && iFlowStatusResp.StatusCode == 404 { 95 return UploadIntegrationArtifact(config, httpClient, fileUtils, serviceKey.OAuth.Host) 96 } 97 98 if iFlowStatusResp == nil { 99 return errors.Errorf("did not retrieve a HTTP response: %v", httpErr) 100 } 101 102 if httpErr != nil { 103 responseBody, readErr := ioutil.ReadAll(iFlowStatusResp.Body) 104 if readErr != nil { 105 return errors.Wrapf(readErr, "HTTP response body could not be read, Response status code: %v", iFlowStatusResp.StatusCode) 106 } 107 log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code: %v", responseBody, iFlowStatusResp.StatusCode) 108 return errors.Wrapf(httpErr, "HTTP %v request to %v failed with error: %v", httpMethod, iFlowStatusServiceURL, string(responseBody)) 109 } 110 return errors.Errorf("Failed to check integration flow availability, Response Status code: %v", iFlowStatusResp.StatusCode) 111 } 112 113 //UploadIntegrationArtifact - Upload new integration artifact 114 func UploadIntegrationArtifact(config *integrationArtifactUploadOptions, httpClient piperhttp.Sender, fileUtils piperutils.FileUtils, apiHost string) error { 115 httpMethod := "POST" 116 uploadIflowStatusURL := fmt.Sprintf("%s/api/v1/IntegrationDesigntimeArtifacts", apiHost) 117 header := make(http.Header) 118 header.Add("content-type", "application/json") 119 payload, jsonError := GetJSONPayloadAsByteArray(config, "create", fileUtils) 120 if jsonError != nil { 121 return errors.Wrapf(jsonError, "Failed to get json payload for file %v, failed with error", config.FilePath) 122 } 123 124 uploadIflowStatusResp, httpErr := httpClient.SendRequest(httpMethod, uploadIflowStatusURL, payload, header, nil) 125 126 if uploadIflowStatusResp != nil && uploadIflowStatusResp.Body != nil { 127 defer uploadIflowStatusResp.Body.Close() 128 } 129 130 if uploadIflowStatusResp == nil { 131 return errors.Errorf("did not retrieve a HTTP response: %v", httpErr) 132 } 133 134 if uploadIflowStatusResp.StatusCode == http.StatusCreated { 135 log.Entry(). 136 WithField("IntegrationFlowID", config.IntegrationFlowID). 137 Info("Successfully created integration flow artefact in CPI designtime") 138 return nil 139 } 140 if httpErr != nil { 141 responseBody, readErr := ioutil.ReadAll(uploadIflowStatusResp.Body) 142 if readErr != nil { 143 return errors.Wrapf(readErr, "HTTP response body could not be read, Response status code: %v", uploadIflowStatusResp.StatusCode) 144 } 145 log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code: %v", responseBody, uploadIflowStatusResp.StatusCode) 146 return errors.Wrapf(httpErr, "HTTP %v request to %v failed with error: %v", httpMethod, uploadIflowStatusURL, string(responseBody)) 147 } 148 return errors.Errorf("Failed to create Integration Flow artefact, Response Status code: %v", uploadIflowStatusResp.StatusCode) 149 } 150 151 //UpdateIntegrationArtifact - Update existing integration artifact 152 func UpdateIntegrationArtifact(config *integrationArtifactUploadOptions, httpClient piperhttp.Sender, fileUtils piperutils.FileUtils, apiHost string) error { 153 httpMethod := "PUT" 154 header := make(http.Header) 155 header.Add("content-type", "application/json") 156 updateIflowStatusURL := fmt.Sprintf("%s/api/v1/IntegrationDesigntimeArtifacts(Id='%s',Version='%s')", apiHost, config.IntegrationFlowID, "Active") 157 payload, jsonError := GetJSONPayloadAsByteArray(config, "update", fileUtils) 158 if jsonError != nil { 159 return errors.Wrapf(jsonError, "Failed to get json payload for file %v, failed with error", config.FilePath) 160 } 161 updateIflowStatusResp, httpErr := httpClient.SendRequest(httpMethod, updateIflowStatusURL, payload, header, nil) 162 163 if updateIflowStatusResp != nil && updateIflowStatusResp.Body != nil { 164 defer updateIflowStatusResp.Body.Close() 165 } 166 167 if updateIflowStatusResp == nil { 168 return errors.Errorf("did not retrieve a HTTP response: %v", httpErr) 169 } 170 171 if updateIflowStatusResp.StatusCode == http.StatusOK { 172 log.Entry(). 173 WithField("IntegrationFlowID", config.IntegrationFlowID). 174 Info("Successfully updated integration flow artefact in CPI designtime") 175 return nil 176 } 177 if httpErr != nil { 178 responseBody, readErr := ioutil.ReadAll(updateIflowStatusResp.Body) 179 if readErr != nil { 180 return errors.Wrapf(readErr, "HTTP response body could not be read, Response status code: %v", updateIflowStatusResp.StatusCode) 181 } 182 log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code: %v", string(responseBody), updateIflowStatusResp.StatusCode) 183 return errors.Wrapf(httpErr, "HTTP %v request to %v failed with error: %v", httpMethod, updateIflowStatusURL, string(responseBody)) 184 } 185 return errors.Errorf("Failed to update Integration Flow artefact, Response Status code: %v", updateIflowStatusResp.StatusCode) 186 } 187 188 //GetJSONPayloadAsByteArray -return http payload as byte array 189 func GetJSONPayloadAsByteArray(config *integrationArtifactUploadOptions, mode string, fileUtils piperutils.FileUtils) (*bytes.Buffer, error) { 190 fileContent, readError := fileUtils.FileRead(config.FilePath) 191 if readError != nil { 192 return nil, errors.Wrapf(readError, "Error reading file") 193 } 194 jsonObj := gabs.New() 195 if mode == "create" { 196 jsonObj.Set(config.IntegrationFlowName, "Name") 197 jsonObj.Set(config.IntegrationFlowID, "Id") 198 jsonObj.Set(config.PackageID, "PackageId") 199 jsonObj.Set(b64.StdEncoding.EncodeToString(fileContent), "ArtifactContent") 200 } else if mode == "update" { 201 jsonObj.Set(config.IntegrationFlowName, "Name") 202 jsonObj.Set(b64.StdEncoding.EncodeToString(fileContent), "ArtifactContent") 203 } else { 204 return nil, fmt.Errorf("Unkown node: '%s'", mode) 205 } 206 207 jsonBody, jsonErr := json.Marshal(jsonObj) 208 209 if jsonErr != nil { 210 return nil, errors.Wrapf(jsonErr, "json payload is invalid for integration flow artifact %q", config.IntegrationFlowID) 211 } 212 return bytes.NewBuffer(jsonBody), nil 213 }