github.com/jaylevin/jenkins-library@v1.230.4/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  }