Skip to content

Commit 5d0427a

Browse files
refactor(v2): refactor client to have better code organization (#267)
* build(deps): bump golang.org/x/net Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20220111093109-d55c255bac03 to 0.7.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/commits/v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * refactor(v2): refactor v2 client to use Requester interface instead of RequestFunc * refactor(v2): add sysdig client tests * refactor(v2): add error from response tests * refactor(v2): rename sysdigrequest receiver --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1 parent 63000c8 commit 5d0427a

File tree

8 files changed

+294
-139
lines changed

8 files changed

+294
-139
lines changed

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ require (
7070
github.com/vmihailenco/tagparser v0.1.1 // indirect
7171
github.com/zclconf/go-cty v1.11.0 // indirect
7272
golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 // indirect
73-
golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect
73+
golang.org/x/net v0.7.0 // indirect
7474
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
75-
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
76-
golang.org/x/text v0.3.8 // indirect
75+
golang.org/x/sys v0.5.0 // indirect
76+
golang.org/x/text v0.7.0 // indirect
7777
google.golang.org/appengine v1.6.7 // indirect
7878
google.golang.org/genproto v0.0.0-20220111164026-67b88f271998 // indirect
7979
google.golang.org/grpc v1.48.0 // indirect

go.sum

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,8 +1072,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
10721072
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
10731073
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
10741074
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
1075-
golang.org/x/net v0.0.0-20220111093109-d55c255bac03 h1:0FB83qp0AzVJm+0wcIlauAjJ+tNdh7jLuacRYCIVv7s=
1076-
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
1075+
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
1076+
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
10771077
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
10781078
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
10791079
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1209,10 +1209,10 @@ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBc
12091209
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
12101210
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
12111211
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1212-
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
1213-
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1214-
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
1212+
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
1213+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
12151214
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
1215+
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
12161216
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
12171217
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
12181218
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1222,8 +1222,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
12221222
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
12231223
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
12241224
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
1225-
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
1226-
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
1225+
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
1226+
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
12271227
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
12281228
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
12291229
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

sysdig/internal/client/v2/client.go

Lines changed: 43 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"crypto/tls"
77
"encoding/json"
88
"errors"
9-
"fmt"
109
"github.com/hashicorp/go-retryablehttp"
1110
"github.com/jmespath/go-jmespath"
1211
"github.com/spf13/cast"
@@ -17,6 +16,12 @@ import (
1716
"strings"
1817
)
1918

19+
const (
20+
AuthorizationHeader = "Authorization"
21+
ContentTypeHeader = "Content-Type"
22+
ContentTypeJSON = "application/json"
23+
)
24+
2025
type Common interface {
2126
TeamInterface
2227
}
@@ -29,80 +34,54 @@ type Secure interface {
2934
Common
3035
}
3136

32-
type RequestFunc func(ctx context.Context, method string, url string, payload io.Reader) (*http.Response, error)
33-
34-
type Client struct {
35-
config *config
36-
httpClient *http.Client
37-
DoRequest RequestFunc
38-
}
39-
40-
type config struct {
41-
url string
42-
token string
43-
insecure bool
44-
extraHeaders map[string]string
37+
type Requester interface {
38+
Request(ctx context.Context, method string, url string, payload io.Reader) (*http.Response, error)
4539
}
4640

47-
type ClientOption func(c *config)
48-
49-
func WithURL(url string) ClientOption {
50-
return func(c *config) {
51-
c.url = url
52-
}
41+
type Client struct {
42+
config *config
43+
requester Requester
5344
}
5445

55-
func WithToken(token string) ClientOption {
56-
return func(c *config) {
57-
c.token = token
46+
func (client *Client) ErrorFromResponse(response *http.Response) error {
47+
var data interface{}
48+
err := json.NewDecoder(response.Body).Decode(&data)
49+
if err != nil {
50+
return errors.New(response.Status)
5851
}
59-
}
6052

61-
func WithInsecure(insecure bool) ClientOption {
62-
return func(c *config) {
63-
c.insecure = insecure
53+
search, err := jmespath.Search("[message, errors[].[reason, message]][][] | join(', ', @)", data)
54+
if err != nil {
55+
return errors.New(response.Status)
6456
}
65-
}
6657

67-
func WithExtraHeaders(headers map[string]string) ClientOption {
68-
return func(c *config) {
69-
c.extraHeaders = headers
58+
if searchArray, ok := search.([]interface{}); ok {
59+
return errors.New(strings.Join(cast.ToStringSlice(searchArray), ", "))
7060
}
71-
}
72-
73-
func NewMonitor(opts ...ClientOption) Monitor {
74-
return newClient(opts...)
75-
}
7661

77-
func NewSecure(opts ...ClientOption) Secure {
78-
return newClient(opts...)
62+
return errors.New(cast.ToString(search))
7963
}
8064

81-
func newClient(opts ...ClientOption) *Client {
82-
cfg := &config{}
83-
for _, opt := range opts {
84-
opt(cfg)
85-
}
86-
87-
httpClient := retryablehttp.NewClient()
88-
transport := http.DefaultTransport.(*http.Transport).Clone()
89-
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: cfg.insecure}
90-
httpClient.HTTPClient = &http.Client{Transport: transport}
65+
func Unmarshal[T any](data io.ReadCloser) (T, error) {
66+
var result T
9167

92-
c := &Client{
93-
config: cfg,
94-
httpClient: httpClient.StandardClient(),
68+
body, err := io.ReadAll(data)
69+
if err != nil {
70+
return result, err
9571
}
9672

97-
// default to sysdig request
98-
c.DoRequest = c.doSysdigRequest
73+
err = json.Unmarshal(body, &result)
74+
return result, err
75+
}
9976

100-
return c
77+
func Marshal[T any](data T) (io.Reader, error) {
78+
payload, err := json.Marshal(data)
79+
return bytes.NewBuffer(payload), err
10180
}
10281

103-
func (client *Client) request(request *http.Request) (*http.Response, error) {
104-
if client.config.extraHeaders != nil {
105-
for key, value := range client.config.extraHeaders {
82+
func request(httpClient *http.Client, cfg *config, request *http.Request) (*http.Response, error) {
83+
if cfg.extraHeaders != nil {
84+
for key, value := range cfg.extraHeaders {
10685
request.Header.Set(key, value)
10786
}
10887
}
@@ -113,7 +92,7 @@ func (client *Client) request(request *http.Request) (*http.Response, error) {
11392
}
11493

11594
log.Printf("[DEBUG] %s", string(out))
116-
response, err := client.httpClient.Do(request)
95+
response, err := httpClient.Do(request)
11796
if err != nil {
11897
log.Println(err.Error())
11998
return response, err
@@ -127,45 +106,10 @@ func (client *Client) request(request *http.Request) (*http.Response, error) {
127106
return response, err
128107
}
129108

130-
func (client *Client) doSysdigRequest(ctx context.Context, method string, url string, payload io.Reader) (*http.Response, error) {
131-
request, err := http.NewRequest(method, url, payload)
132-
if err != nil {
133-
return nil, err
134-
}
135-
136-
request = request.WithContext(ctx)
137-
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.config.token))
138-
request.Header.Set("Content-Type", "application/json")
139-
140-
return client.request(request)
141-
}
142-
143-
func (client *Client) ErrorFromResponse(response *http.Response) error {
144-
var data interface{}
145-
err := json.NewDecoder(response.Body).Decode(&data)
146-
if err != nil {
147-
return errors.New(response.Status)
148-
}
149-
150-
search, err := jmespath.Search("[message, errors[].[reason, message]][][] | join(', ', @)", data)
151-
if err != nil {
152-
return errors.New(response.Status)
153-
}
154-
155-
if searchArray, ok := search.([]interface{}); ok {
156-
return errors.New(strings.Join(cast.ToStringSlice(searchArray), ", "))
157-
}
158-
159-
return errors.New(cast.ToString(search))
160-
}
161-
162-
func Unmarshal[T any](body []byte) (T, error) {
163-
var result T
164-
err := json.Unmarshal(body, &result)
165-
return result, err
166-
}
167-
168-
func Marshal[T any](data T) (io.Reader, error) {
169-
payload, err := json.Marshal(data)
170-
return bytes.NewBuffer(payload), err
109+
func newHTTPClient(cfg *config) *http.Client {
110+
httpClient := retryablehttp.NewClient()
111+
transport := http.DefaultTransport.(*http.Transport).Clone()
112+
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: cfg.insecure}
113+
httpClient.HTTPClient = &http.Client{Transport: transport}
114+
return httpClient.StandardClient()
171115
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package v2
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"net/http"
7+
"strings"
8+
"testing"
9+
)
10+
11+
func TestMarshal(t *testing.T) {
12+
t.Parallel()
13+
type foo struct {
14+
Number int `json:"number"`
15+
}
16+
17+
given := &foo{Number: 15}
18+
expected := `{"number":15}`
19+
20+
data, err := Marshal(given)
21+
if err != nil {
22+
t.Errorf("failed to marshal %v", err)
23+
}
24+
25+
buf := &strings.Builder{}
26+
_, err = io.Copy(buf, data)
27+
if err != nil {
28+
t.Errorf("failed to populate buffer, err: %v", err)
29+
}
30+
31+
marshaled := buf.String()
32+
if marshaled != expected {
33+
t.Errorf("expected %v, got %v", expected, marshaled)
34+
}
35+
}
36+
37+
func TestUnmarshal(t *testing.T) {
38+
t.Parallel()
39+
type foo struct {
40+
Number int `json:"number"`
41+
}
42+
given := `{"number":15}`
43+
expected := foo{Number: 15}
44+
45+
unmarshalled, err := Unmarshal[foo](io.NopCloser(strings.NewReader(given)))
46+
if err != nil {
47+
t.Errorf("got error while unmarshaling, err: %v", err)
48+
}
49+
50+
if expected != unmarshalled {
51+
t.Errorf("expected %v, got %v", expected, unmarshalled)
52+
}
53+
}
54+
55+
func TestClient_ErrorFromResponse(t *testing.T) {
56+
type Error struct {
57+
Reason string `json:"reason"`
58+
Message string `json:"message"`
59+
}
60+
61+
type Errors struct {
62+
Errors []Error `json:"errors"`
63+
}
64+
65+
given := Errors{
66+
Errors: []Error{
67+
{
68+
Reason: "error1",
69+
Message: "message1",
70+
},
71+
{
72+
Reason: "error2",
73+
Message: "message2",
74+
},
75+
},
76+
}
77+
expected := "error1, message1, error2, message2"
78+
c := Client{}
79+
payload, err := json.Marshal(given)
80+
if err != nil {
81+
t.Errorf("failed to marshal errors, %v", err)
82+
}
83+
84+
resp := &http.Response{
85+
Body: io.NopCloser(strings.NewReader(string(payload))),
86+
}
87+
err = c.ErrorFromResponse(resp)
88+
if err.Error() != expected {
89+
t.Errorf("expected err %v, got %v", expected, err)
90+
}
91+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package v2
2+
3+
type config struct {
4+
url string
5+
token string
6+
insecure bool
7+
extraHeaders map[string]string
8+
}
9+
10+
type ClientOption func(c *config)
11+
12+
func WithURL(url string) ClientOption {
13+
return func(c *config) {
14+
c.url = url
15+
}
16+
}
17+
18+
func WithToken(token string) ClientOption {
19+
return func(c *config) {
20+
c.token = token
21+
}
22+
}
23+
24+
func WithInsecure(insecure bool) ClientOption {
25+
return func(c *config) {
26+
c.insecure = insecure
27+
}
28+
}
29+
30+
func WithExtraHeaders(headers map[string]string) ClientOption {
31+
return func(c *config) {
32+
c.extraHeaders = headers
33+
}
34+
}
35+
36+
func configure(opts ...ClientOption) *config {
37+
cfg := &config{}
38+
for _, opt := range opts {
39+
opt(cfg)
40+
}
41+
return cfg
42+
}

0 commit comments

Comments
 (0)