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

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  
     9  	"github.com/SAP/jenkins-library/pkg/command"
    10  	"github.com/SAP/jenkins-library/pkg/cpi"
    11  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    12  	"github.com/SAP/jenkins-library/pkg/log"
    13  	"github.com/SAP/jenkins-library/pkg/piperutils"
    14  	"github.com/SAP/jenkins-library/pkg/telemetry"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  type integrationArtifactTriggerIntegrationTestUtils interface {
    19  	command.ExecRunner
    20  
    21  	FileExists(filename string) (bool, error)
    22  
    23  	// Add more methods here, or embed additional interfaces, or remove/replace as required.
    24  	// The integrationArtifactTriggerIntegrationTestUtils interface should be descriptive of your runtime dependencies,
    25  	// i.e. include everything you need to be able to mock in tests.
    26  	// Unit tests shall be executable in parallel (not depend on global state), and don't (re-)test dependencies.
    27  }
    28  
    29  type integrationArtifactTriggerIntegrationTestUtilsBundle struct {
    30  	*command.Command
    31  	*piperutils.Files
    32  
    33  	// Embed more structs as necessary to implement methods or interfaces you add to integrationArtifactTriggerIntegrationTestUtils.
    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  	// integrationArtifactTriggerIntegrationTestUtilsBundle and forward to the implementation of the dependency.
    37  }
    38  
    39  func newIntegrationArtifactTriggerIntegrationTestUtils() integrationArtifactTriggerIntegrationTestUtils {
    40  	utils := integrationArtifactTriggerIntegrationTestUtilsBundle{
    41  		Command: &command.Command{},
    42  		Files:   &piperutils.Files{},
    43  	}
    44  	// Reroute command output to logging framework
    45  	utils.Stdout(log.Writer())
    46  	utils.Stderr(log.Writer())
    47  	return &utils
    48  }
    49  
    50  func integrationArtifactTriggerIntegrationTest(config integrationArtifactTriggerIntegrationTestOptions, telemetryData *telemetry.CustomData) {
    51  	// Utils can be used wherever the command.ExecRunner interface is expected.
    52  	// It can also be used for example as a mavenExecRunner.
    53  	utils := newIntegrationArtifactTriggerIntegrationTestUtils()
    54  	httpClient := &piperhttp.Client{}
    55  	// For HTTP calls import  piperhttp "github.com/SAP/jenkins-library/pkg/http"
    56  	// and use a  &piperhttp.Client{} in a custom system
    57  	// Example: step checkmarxExecuteScan.go
    58  
    59  	// Error situations should be bubbled up until they reach the line below which will then stop execution
    60  	// through the log.Entry().Fatal() call leading to an os.Exit(1) in the end.
    61  	err := runIntegrationArtifactTriggerIntegrationTest(&config, telemetryData, utils, httpClient)
    62  	if err != nil {
    63  		log.Entry().WithError(err).Fatal("step execution failed")
    64  	}
    65  }
    66  
    67  func runIntegrationArtifactTriggerIntegrationTest(config *integrationArtifactTriggerIntegrationTestOptions, telemetryData *telemetry.CustomData, utils integrationArtifactTriggerIntegrationTestUtils, httpClient piperhttp.Sender) error {
    68  	var commonPipelineEnvironment integrationArtifactGetServiceEndpointCommonPipelineEnvironment
    69  	var serviceEndpointUrl string
    70  	if len(config.IntegrationFlowServiceEndpointURL) > 0 {
    71  		serviceEndpointUrl = config.IntegrationFlowServiceEndpointURL
    72  	} else {
    73  		serviceEndpointUrl = commonPipelineEnvironment.custom.integrationFlowServiceEndpoint
    74  		if len(serviceEndpointUrl) == 0 {
    75  			log.SetErrorCategory(log.ErrorConfiguration)
    76  			return fmt.Errorf("IFlowServiceEndpointURL not set")
    77  		}
    78  	}
    79  	log.Entry().Info("The Service URL : ", serviceEndpointUrl)
    80  
    81  	// Here we trigger the iFlow Service Endpoint.
    82  	IFlowErr := callIFlowURL(config, telemetryData, utils, httpClient, serviceEndpointUrl)
    83  	if IFlowErr != nil {
    84  		log.SetErrorCategory(log.ErrorService)
    85  		return fmt.Errorf("failed to execute iFlow: %w", IFlowErr)
    86  	}
    87  
    88  	return nil
    89  }
    90  
    91  func callIFlowURL(config *integrationArtifactTriggerIntegrationTestOptions, telemetryData *telemetry.CustomData, utils integrationArtifactTriggerIntegrationTestUtils, httpIFlowClient piperhttp.Sender, serviceEndpointUrl string) error {
    92  
    93  	var fileBody []byte
    94  	var httpMethod string
    95  	var header http.Header
    96  	if len(config.MessageBodyPath) > 0 {
    97  		if len(config.ContentType) == 0 {
    98  			log.SetErrorCategory(log.ErrorConfiguration)
    99  			return fmt.Errorf("message body file %s given, but no ContentType", config.MessageBodyPath)
   100  		}
   101  		exists, err := utils.FileExists(config.MessageBodyPath)
   102  		if err != nil {
   103  			log.SetErrorCategory(log.ErrorUndefined)
   104  			// Always wrap non-descriptive errors to enrich them with context for when they appear in the log:
   105  			return fmt.Errorf("failed to check message body file %s: %w", config.MessageBodyPath, err)
   106  		}
   107  		if !exists {
   108  			log.SetErrorCategory(log.ErrorConfiguration)
   109  			return fmt.Errorf("message body file %s configured, but not found", config.MessageBodyPath)
   110  		}
   111  
   112  		var fileErr error
   113  		fileBody, fileErr = ioutil.ReadFile(config.MessageBodyPath)
   114  		if fileErr != nil {
   115  			log.SetErrorCategory(log.ErrorUndefined)
   116  			return fmt.Errorf("failed to read file %s: %w", config.MessageBodyPath, fileErr)
   117  		}
   118  		httpMethod = "POST"
   119  		header = make(http.Header)
   120  		header.Add("Content-Type", config.ContentType)
   121  	} else {
   122  		httpMethod = "GET"
   123  	}
   124  
   125  	serviceKey, err := cpi.ReadCpiServiceKey(config.IntegrationFlowServiceKey)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	clientOptions := piperhttp.ClientOptions{}
   130  	tokenParameters := cpi.TokenParameters{TokenURL: serviceKey.OAuth.OAuthTokenProviderURL, Username: serviceKey.OAuth.ClientID, Password: serviceKey.OAuth.ClientSecret, Client: httpIFlowClient}
   131  	token, err := cpi.CommonUtils.GetBearerToken(tokenParameters)
   132  	if err != nil {
   133  		return errors.Wrap(err, "failed to fetch Bearer Token")
   134  	}
   135  	clientOptions.Token = fmt.Sprintf("Bearer %s", token)
   136  	clientOptions.MaxRetries = -1
   137  	httpIFlowClient.SetOptions(clientOptions)
   138  	iFlowResp, httpErr := httpIFlowClient.SendRequest(httpMethod, serviceEndpointUrl, bytes.NewBuffer(fileBody), header, nil)
   139  
   140  	if httpErr != nil {
   141  		return errors.Wrapf(httpErr, "HTTP %q request to %q failed with error", httpMethod, serviceEndpointUrl)
   142  	}
   143  
   144  	if iFlowResp == nil {
   145  		return errors.Errorf("did not retrieve any HTTP response")
   146  	}
   147  
   148  	if iFlowResp.StatusCode < 400 {
   149  		log.Entry().
   150  			WithField(config.IntegrationFlowID, serviceEndpointUrl).
   151  			Infof("successfully triggered %s with status code %d", serviceEndpointUrl, iFlowResp.StatusCode)
   152  	} else {
   153  		return fmt.Errorf("request %s failed with response code %d", serviceEndpointUrl, iFlowResp.StatusCode)
   154  	}
   155  
   156  	return nil
   157  }