-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathvulcanclient.go
120 lines (105 loc) · 3.01 KB
/
vulcanclient.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
Copyright 2020 Adevinta
*/
package crontinuous
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"github.com/cenkalti/backoff"
"github.com/sirupsen/logrus"
)
const (
createScanURL = "%s/v1/teams/%s/scans"
sendReportURL = "%s/v1/teams/%s/report/digest"
bearerHeaderTemplate = "Bearer %s"
)
// ScanRequest contains the payload to send to the API scan endpoint.
type ScanRequest struct {
ProgramID string `json:"program_id"`
ScheduledTime time.Time `json:"scheduled_time"`
RequestedBy string `json:"requested_by"`
}
// VulcanClient provides functionality for interacting with the vulcan-api.
type VulcanClient struct {
VulcanAPI string
VulcanUser string
VulcanToken string
Log *logrus.Logger
}
// CreateScan creates a scan by calling vulcan-api
func (c *VulcanClient) CreateScan(scanID, teamID string) error {
scanMsg := ScanRequest{
ProgramID: scanID,
ScheduledTime: time.Now(),
RequestedBy: c.VulcanUser,
}
url := fmt.Sprintf(createScanURL, c.VulcanAPI, teamID)
retries := 0
operation := func() error {
err := c.performReq(http.MethodPost, url, scanMsg)
if err != nil {
c.Log.WithError(err).WithField("job", teamID).Errorf("unable to create scan, retry: %d", retries)
}
retries += 1
return err
}
return backoff.Retry(operation, backoff.NewExponentialBackOff())
}
// SendReport triggers a report sending operation by calling vulcan-api.
func (c *VulcanClient) SendReport(teamID string) error {
url := fmt.Sprintf(sendReportURL, c.VulcanAPI, teamID)
retries := 0
operation := func() error {
err := c.performReq(http.MethodPost, url, nil)
if err != nil {
c.Log.WithError(err).WithField("job", teamID).Errorf("unable to send report, retry: %d", retries)
}
retries += 1
return err
}
return backoff.Retry(operation, backoff.NewExponentialBackOff())
}
func (c *VulcanClient) performReq(httpMethod, url string, payload interface{}) error {
content, err := json.Marshal(payload)
if err != nil {
return &backoff.PermanentError{Err: err}
}
req, err := http.NewRequest(httpMethod, url, bytes.NewReader(content))
if err != nil {
return &backoff.PermanentError{Err: err}
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", fmt.Sprintf(bearerHeaderTemplate, c.VulcanToken))
resp, err := http.DefaultClient.Do(req)
if err != nil {
// This is the only error that can be
// related to network issues, so don't
// return a PermanentError in this case
// so retries can be applied.
return err
}
defer resp.Body.Close() // nolint
if resp.StatusCode != http.StatusCreated {
var content string
b, err := io.ReadAll(resp.Body)
if err == nil {
content = string(b)
}
err = fmt.Errorf("response status %s. Content: %s", resp.Status, content)
if resp.StatusCode >= 500 {
// If HTTP communication was successful
// but an error was produced in the server,
// return non permanent err so retries
// are applied.
return err
}
return &backoff.PermanentError{
Err: err,
}
}
return nil
}