mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-12 10:55:20 +02:00
Retry capabilities for HTTP requests + enablement for Checkmarx step (#2346)
This commit is contained in:
parent
a70933bbd4
commit
3c7712f2ee
@ -26,6 +26,8 @@ import (
|
|||||||
|
|
||||||
func checkmarxExecuteScan(config checkmarxExecuteScanOptions, telemetryData *telemetry.CustomData, influx *checkmarxExecuteScanInflux) {
|
func checkmarxExecuteScan(config checkmarxExecuteScanOptions, telemetryData *telemetry.CustomData, influx *checkmarxExecuteScanInflux) {
|
||||||
client := &piperHttp.Client{}
|
client := &piperHttp.Client{}
|
||||||
|
options := piperHttp.ClientOptions{MaxRetries: config.MaxRetries}
|
||||||
|
client.SetOptions(options)
|
||||||
sys, err := checkmarx.NewSystemInstance(client, config.ServerURL, config.Username, config.Password)
|
sys, err := checkmarx.NewSystemInstance(client, config.ServerURL, config.Username, config.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Entry().WithError(err).Fatalf("Failed to create Checkmarx client talking to URL %v", config.ServerURL)
|
log.Entry().WithError(err).Fatalf("Failed to create Checkmarx client talking to URL %v", config.ServerURL)
|
||||||
|
@ -22,6 +22,7 @@ type checkmarxExecuteScanOptions struct {
|
|||||||
FullScansScheduled bool `json:"fullScansScheduled,omitempty"`
|
FullScansScheduled bool `json:"fullScansScheduled,omitempty"`
|
||||||
GeneratePdfReport bool `json:"generatePdfReport,omitempty"`
|
GeneratePdfReport bool `json:"generatePdfReport,omitempty"`
|
||||||
Incremental bool `json:"incremental,omitempty"`
|
Incremental bool `json:"incremental,omitempty"`
|
||||||
|
MaxRetries int `json:"maxRetries,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
Preset string `json:"preset,omitempty"`
|
Preset string `json:"preset,omitempty"`
|
||||||
ProjectName string `json:"projectName,omitempty"`
|
ProjectName string `json:"projectName,omitempty"`
|
||||||
@ -235,6 +236,7 @@ func addCheckmarxExecuteScanFlags(cmd *cobra.Command, stepConfig *checkmarxExecu
|
|||||||
cmd.Flags().BoolVar(&stepConfig.FullScansScheduled, "fullScansScheduled", true, "Whether full scans are to be scheduled or not. Should be used in relation with `incremental` and `fullScanCycle`")
|
cmd.Flags().BoolVar(&stepConfig.FullScansScheduled, "fullScansScheduled", true, "Whether full scans are to be scheduled or not. Should be used in relation with `incremental` and `fullScanCycle`")
|
||||||
cmd.Flags().BoolVar(&stepConfig.GeneratePdfReport, "generatePdfReport", true, "Whether to generate a PDF report of the analysis results or not")
|
cmd.Flags().BoolVar(&stepConfig.GeneratePdfReport, "generatePdfReport", true, "Whether to generate a PDF report of the analysis results or not")
|
||||||
cmd.Flags().BoolVar(&stepConfig.Incremental, "incremental", true, "Whether incremental scans are to be applied which optimizes the scan time but might reduce detection capabilities. Therefore full scans are still required from time to time and should be scheduled via `fullScansScheduled` and `fullScanCycle`")
|
cmd.Flags().BoolVar(&stepConfig.Incremental, "incremental", true, "Whether incremental scans are to be applied which optimizes the scan time but might reduce detection capabilities. Therefore full scans are still required from time to time and should be scheduled via `fullScansScheduled` and `fullScanCycle`")
|
||||||
|
cmd.Flags().IntVar(&stepConfig.MaxRetries, "maxRetries", 3, "Maximum number of HTTP request retries upon intermittend connetion interrupts")
|
||||||
cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "The password to authenticate")
|
cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "The password to authenticate")
|
||||||
cmd.Flags().StringVar(&stepConfig.Preset, "preset", os.Getenv("PIPER_preset"), "The preset to use for scanning, if not set explicitly the step will attempt to look up the project's setting based on the availability of `checkmarxCredentialsId`")
|
cmd.Flags().StringVar(&stepConfig.Preset, "preset", os.Getenv("PIPER_preset"), "The preset to use for scanning, if not set explicitly the step will attempt to look up the project's setting based on the availability of `checkmarxCredentialsId`")
|
||||||
cmd.Flags().StringVar(&stepConfig.ProjectName, "projectName", os.Getenv("PIPER_projectName"), "The name of the Checkmarx project to scan into")
|
cmd.Flags().StringVar(&stepConfig.ProjectName, "projectName", os.Getenv("PIPER_projectName"), "The name of the Checkmarx project to scan into")
|
||||||
@ -316,6 +318,14 @@ func checkmarxExecuteScanMetadata() config.StepData {
|
|||||||
Mandatory: false,
|
Mandatory: false,
|
||||||
Aliases: []config.Alias{},
|
Aliases: []config.Alias{},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "maxRetries",
|
||||||
|
ResourceRef: []config.ResourceReference{},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "int",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "password",
|
Name: "password",
|
||||||
ResourceRef: []config.ResourceReference{
|
ResourceRef: []config.ResourceReference{
|
||||||
|
2
go.mod
2
go.mod
@ -32,7 +32,7 @@ require (
|
|||||||
github.com/google/uuid v1.1.2
|
github.com/google/uuid v1.1.2
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.0 // indirect
|
github.com/hashicorp/go-multierror v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.7 // indirect
|
github.com/hashicorp/go-retryablehttp v0.6.7
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||||
github.com/hashicorp/vault/api v1.0.4
|
github.com/hashicorp/vault/api v1.0.4
|
||||||
github.com/huandu/xstrings v1.3.2 // indirect
|
github.com/huandu/xstrings v1.3.2 // indirect
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/SAP/jenkins-library/pkg/log"
|
"github.com/SAP/jenkins-library/pkg/log"
|
||||||
|
"github.com/hashicorp/go-retryablehttp"
|
||||||
"github.com/motemen/go-nuts/roundtime"
|
"github.com/motemen/go-nuts/roundtime"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -25,6 +26,7 @@ import (
|
|||||||
// Client defines an http client object
|
// Client defines an http client object
|
||||||
type Client struct {
|
type Client struct {
|
||||||
maxRequestDuration time.Duration
|
maxRequestDuration time.Duration
|
||||||
|
maxRetries int
|
||||||
transportTimeout time.Duration
|
transportTimeout time.Duration
|
||||||
transportSkipVerification bool
|
transportSkipVerification bool
|
||||||
username string
|
username string
|
||||||
@ -43,6 +45,7 @@ type ClientOptions struct {
|
|||||||
// for the request will be enforced. This should only be used if the
|
// for the request will be enforced. This should only be used if the
|
||||||
// length of the request bodies is known.
|
// length of the request bodies is known.
|
||||||
MaxRequestDuration time.Duration
|
MaxRequestDuration time.Duration
|
||||||
|
MaxRetries int
|
||||||
// TransportTimeout defaults to 3 minutes, if not specified. It is
|
// TransportTimeout defaults to 3 minutes, if not specified. It is
|
||||||
// used for the transport layer and duration of handshakes and such.
|
// used for the transport layer and duration of handshakes and such.
|
||||||
TransportTimeout time.Duration
|
TransportTimeout time.Duration
|
||||||
@ -196,6 +199,7 @@ func (c *Client) SetOptions(options ClientOptions) {
|
|||||||
c.username = options.Username
|
c.username = options.Username
|
||||||
c.password = options.Password
|
c.password = options.Password
|
||||||
c.token = options.Token
|
c.token = options.Token
|
||||||
|
c.maxRetries = options.MaxRetries
|
||||||
|
|
||||||
if options.Logger != nil {
|
if options.Logger != nil {
|
||||||
c.logger = options.Logger
|
c.logger = options.Logger
|
||||||
@ -224,14 +228,26 @@ func (c *Client) initialize() *http.Client {
|
|||||||
doLogRequestBodyOnDebug: c.doLogRequestBodyOnDebug,
|
doLogRequestBodyOnDebug: c.doLogRequestBodyOnDebug,
|
||||||
doLogResponseBodyOnDebug: c.doLogResponseBodyOnDebug,
|
doLogResponseBodyOnDebug: c.doLogResponseBodyOnDebug,
|
||||||
}
|
}
|
||||||
var httpClient = &http.Client{
|
|
||||||
Timeout: c.maxRequestDuration,
|
var httpClient *http.Client
|
||||||
Transport: transport,
|
if c.maxRetries > 0 {
|
||||||
Jar: c.cookieJar,
|
retryClient := retryablehttp.NewClient()
|
||||||
|
retryClient.HTTPClient.Timeout = c.maxRequestDuration
|
||||||
|
retryClient.HTTPClient.Jar = c.cookieJar
|
||||||
|
retryClient.HTTPClient.Transport = transport
|
||||||
|
retryClient.RetryMax = c.maxRetries
|
||||||
|
httpClient = retryClient.StandardClient()
|
||||||
|
} else {
|
||||||
|
httpClient = &http.Client{}
|
||||||
|
httpClient.Timeout = c.maxRequestDuration
|
||||||
|
httpClient.Jar = c.cookieJar
|
||||||
|
httpClient.Transport = transport
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.transportSkipVerification {
|
if c.transportSkipVerification {
|
||||||
c.logger.Debugf("TLS verification disabled")
|
c.logger.Debugf("TLS verification disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Debugf("Transport timeout: %v, max request duration: %v", c.transportTimeout, c.maxRequestDuration)
|
c.logger.Debugf("Transport timeout: %v, max request duration: %v", c.transportTimeout, c.maxRequestDuration)
|
||||||
|
|
||||||
return httpClient
|
return httpClient
|
||||||
|
@ -303,6 +303,32 @@ func TestTransportSkipVerification(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMaxRetries(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
client Client
|
||||||
|
countedCalls int
|
||||||
|
}{
|
||||||
|
{client: Client{maxRetries: 0}, countedCalls: 1},
|
||||||
|
{client: Client{maxRetries: 2}, countedCalls: 3},
|
||||||
|
{client: Client{maxRetries: 3}, countedCalls: 4},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
// init
|
||||||
|
count := 0
|
||||||
|
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
count++
|
||||||
|
w.WriteHeader(500)
|
||||||
|
}))
|
||||||
|
defer svr.Close()
|
||||||
|
// test
|
||||||
|
_, err := testCase.client.SendRequest(http.MethodGet, svr.URL, &bytes.Buffer{}, nil, nil)
|
||||||
|
// assert
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, testCase.countedCalls, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseHTTPResponseBodyJSON(t *testing.T) {
|
func TestParseHTTPResponseBodyJSON(t *testing.T) {
|
||||||
|
|
||||||
type myJSONStruct struct {
|
type myJSONStruct struct {
|
||||||
|
@ -72,6 +72,14 @@ spec:
|
|||||||
- STAGES
|
- STAGES
|
||||||
- STEPS
|
- STEPS
|
||||||
default: true
|
default: true
|
||||||
|
- name: maxRetries
|
||||||
|
type: int
|
||||||
|
description: Maximum number of HTTP request retries upon intermittend connetion interrupts
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
default: 3
|
||||||
- name: password
|
- name: password
|
||||||
type: string
|
type: string
|
||||||
description: The password to authenticate
|
description: The password to authenticate
|
||||||
|
Loading…
Reference in New Issue
Block a user