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