1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00
sap-jenkins-library/cmd/integrationArtifactTriggerIntegrationTest.go
Oliver Feldmann 92a782a6c3
[Integration Suite] Store response in CPE (#4277)
* Store response body in CPE

* Also store response headers in CPE
2023-03-27 14:34:59 +00:00

176 lines
7.1 KiB
Go

package cmd
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/cpi"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/pkg/errors"
)
type integrationArtifactTriggerIntegrationTestUtils interface {
command.ExecRunner
FileExists(filename string) (bool, error)
// Add more methods here, or embed additional interfaces, or remove/replace as required.
// The integrationArtifactTriggerIntegrationTestUtils interface should be descriptive of your runtime dependencies,
// i.e. include everything you need to be able to mock in tests.
// Unit tests shall be executable in parallel (not depend on global state), and don't (re-)test dependencies.
}
type integrationArtifactTriggerIntegrationTestUtilsBundle struct {
*command.Command
*piperutils.Files
// Embed more structs as necessary to implement methods or interfaces you add to integrationArtifactTriggerIntegrationTestUtils.
// Structs embedded in this way must each have a unique set of methods attached.
// If there is no struct which implements the method you need, attach the method to
// integrationArtifactTriggerIntegrationTestUtilsBundle and forward to the implementation of the dependency.
}
func newIntegrationArtifactTriggerIntegrationTestUtils() integrationArtifactTriggerIntegrationTestUtils {
utils := integrationArtifactTriggerIntegrationTestUtilsBundle{
Command: &command.Command{},
Files: &piperutils.Files{},
}
// Reroute command output to logging framework
utils.Stdout(log.Writer())
utils.Stderr(log.Writer())
return &utils
}
func integrationArtifactTriggerIntegrationTest(config integrationArtifactTriggerIntegrationTestOptions, telemetryData *telemetry.CustomData, commonPipelineEnvironment *integrationArtifactTriggerIntegrationTestCommonPipelineEnvironment) {
// Utils can be used wherever the command.ExecRunner interface is expected.
// It can also be used for example as a mavenExecRunner.
utils := newIntegrationArtifactTriggerIntegrationTestUtils()
httpClient := &piperhttp.Client{}
// For HTTP calls import piperhttp "github.com/SAP/jenkins-library/pkg/http"
// and use a &piperhttp.Client{} in a custom system
// Example: step checkmarxExecuteScan.go
// Error situations should be bubbled up until they reach the line below which will then stop execution
// through the log.Entry().Fatal() call leading to an os.Exit(1) in the end.
err := runIntegrationArtifactTriggerIntegrationTest(&config, utils, httpClient, commonPipelineEnvironment)
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
}
func runIntegrationArtifactTriggerIntegrationTest(config *integrationArtifactTriggerIntegrationTestOptions, utils integrationArtifactTriggerIntegrationTestUtils, httpClient piperhttp.Sender, commonPipelineEnvironment *integrationArtifactTriggerIntegrationTestCommonPipelineEnvironment) error {
var getServiceEndpointCommonPipelineEnvironment integrationArtifactGetServiceEndpointCommonPipelineEnvironment
var serviceEndpointUrl string
if len(config.IntegrationFlowServiceEndpointURL) > 0 {
serviceEndpointUrl = config.IntegrationFlowServiceEndpointURL
} else {
serviceEndpointUrl = getServiceEndpointCommonPipelineEnvironment.custom.integrationFlowServiceEndpoint
if len(serviceEndpointUrl) == 0 {
log.SetErrorCategory(log.ErrorConfiguration)
return fmt.Errorf("IFlowServiceEndpointURL not set")
}
}
log.Entry().Info("The Service URL : ", serviceEndpointUrl)
// Here we trigger the iFlow Service Endpoint.
IFlowErr := callIFlowURL(config, utils, httpClient, serviceEndpointUrl, commonPipelineEnvironment)
if IFlowErr != nil {
log.SetErrorCategory(log.ErrorService)
return fmt.Errorf("failed to execute iFlow: %w", IFlowErr)
}
return nil
}
func callIFlowURL(
config *integrationArtifactTriggerIntegrationTestOptions,
utils integrationArtifactTriggerIntegrationTestUtils,
httpIFlowClient piperhttp.Sender,
serviceEndpointUrl string,
commonPipelineEnvironment *integrationArtifactTriggerIntegrationTestCommonPipelineEnvironment) error {
var fileBody []byte
var httpMethod string
var header http.Header
if len(config.MessageBodyPath) > 0 {
if len(config.ContentType) == 0 {
log.SetErrorCategory(log.ErrorConfiguration)
return fmt.Errorf("message body file %s given, but no ContentType", config.MessageBodyPath)
}
exists, err := utils.FileExists(config.MessageBodyPath)
if err != nil {
log.SetErrorCategory(log.ErrorUndefined)
// Always wrap non-descriptive errors to enrich them with context for when they appear in the log:
return fmt.Errorf("failed to check message body file %s: %w", config.MessageBodyPath, err)
}
if !exists {
log.SetErrorCategory(log.ErrorConfiguration)
return fmt.Errorf("message body file %s configured, but not found", config.MessageBodyPath)
}
var fileErr error
fileBody, fileErr = ioutil.ReadFile(config.MessageBodyPath)
if fileErr != nil {
log.SetErrorCategory(log.ErrorUndefined)
return fmt.Errorf("failed to read file %s: %w", config.MessageBodyPath, fileErr)
}
httpMethod = "POST"
header = make(http.Header)
header.Add("Content-Type", config.ContentType)
} else {
httpMethod = "GET"
}
serviceKey, err := cpi.ReadCpiServiceKey(config.IntegrationFlowServiceKey)
if err != nil {
return err
}
clientOptions := piperhttp.ClientOptions{}
tokenParameters := cpi.TokenParameters{TokenURL: serviceKey.OAuth.OAuthTokenProviderURL, Username: serviceKey.OAuth.ClientID, Password: serviceKey.OAuth.ClientSecret, Client: httpIFlowClient}
token, err := cpi.CommonUtils.GetBearerToken(tokenParameters)
if err != nil {
return errors.Wrap(err, "failed to fetch Bearer Token")
}
clientOptions.Token = fmt.Sprintf("Bearer %s", token)
clientOptions.MaxRetries = -1
httpIFlowClient.SetOptions(clientOptions)
iFlowResp, httpErr := httpIFlowClient.SendRequest(httpMethod, serviceEndpointUrl, bytes.NewBuffer(fileBody), header, nil)
if httpErr != nil {
return errors.Wrapf(httpErr, "HTTP %q request to %q failed with error", httpMethod, serviceEndpointUrl)
}
if iFlowResp == nil {
return errors.Errorf("did not retrieve any HTTP response")
}
if iFlowResp.StatusCode < 400 {
log.Entry().
WithField(config.IntegrationFlowID, serviceEndpointUrl).
Infof("successfully triggered %s with status code %d", serviceEndpointUrl, iFlowResp.StatusCode)
bodyText, readErr := ioutil.ReadAll(iFlowResp.Body)
if readErr != nil {
log.Entry().Warnf("HTTP response body could not be read. Error: %s", readErr.Error())
} else if len(bodyText) > 0 {
commonPipelineEnvironment.custom.integrationFlowTriggerIntegrationTestResponseBody = string(bodyText)
}
headersJson, err := json.Marshal(iFlowResp.Header)
if err != nil {
log.Entry().Warnf("HTTP response headers could not be marshalled. Error: %s", err.Error())
} else {
commonPipelineEnvironment.custom.integrationFlowTriggerIntegrationTestResponseHeaders = string(headersJson)
}
} else {
return fmt.Errorf("request %s failed with response code %d", serviceEndpointUrl, iFlowResp.StatusCode)
}
return nil
}