Skip to content

Commit 850cddc

Browse files
authored
fix: component download timeout retry (#1389)
1 parent 39add0e commit 850cddc

File tree

5 files changed

+98
-19
lines changed

5 files changed

+98
-19
lines changed

lwcomponent/component.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ func (s State) Install(component *Component, version string) error {
257257
return err
258258
}
259259

260-
err = downloadFile(path, artifact.URL)
260+
err = DownloadFile(path, artifact.URL, 0)
261261
if err != nil {
262262
return errors.Wrap(err, "unable to download component artifact")
263263
}

lwcomponent/component_internal_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,15 @@ type pathTest struct {
125125
}
126126

127127
var pathTests = []pathTest{
128-
pathTest{
128+
{
129129
"NotExists",
130130
Component{
131131
Name: "no-such-component",
132132
Type: "STANDALONE",
133133
},
134134
errors.New("component not found on disk"),
135135
},
136-
pathTest{
136+
{
137137
"Exists",
138138
mockComponent,
139139
nil,
@@ -176,15 +176,15 @@ type isVerifiedTest struct {
176176
}
177177

178178
var isVerifiedTests = []isVerifiedTest{
179-
isVerifiedTest{
179+
{
180180
Name: "NoSignature",
181181
Component: Component{
182182
Name: "lacework-mock-component",
183183
},
184184
Version: "0.1.0",
185185
Error: errors.New("component signature file does not exist"),
186186
},
187-
isVerifiedTest{
187+
{
188188
Name: "Mismatch",
189189
Component: Component{
190190
Name: "lacework-mock-component",
@@ -193,7 +193,7 @@ var isVerifiedTests = []isVerifiedTest{
193193
Signature: base64.StdEncoding.EncodeToString([]byte("blah blah blah")),
194194
Error: errors.New("unable to parse signature"),
195195
},
196-
isVerifiedTest{
196+
{
197197
Name: "Verified",
198198
Component: mockComponent,
199199
Version: "0.1.0\n",
@@ -231,26 +231,26 @@ type runTest struct {
231231
}
232232

233233
var runTests = []runTest{
234-
runTest{
234+
{
235235
Name: "IsNotBinary",
236236
Component: Component{Name: "IsNotBinary"},
237237
Version: "0.1.0",
238238
Error: errors.New("unable to run component: component IsNotBinary is not a binary"),
239239
},
240-
runTest{
240+
{
241241
Name: "IsNotVerified",
242242
Component: Component{Name: "IsNotVerified", Type: "BINARY"},
243243
Version: "0.1.0",
244244
Error: errors.New("unable to run component: component signature file does not exist"),
245245
},
246-
runTest{
246+
{
247247
Name: "OK",
248248
Component: mockComponent,
249249
Version: "0.1.0",
250250
Signature: helloWorldSig,
251251
Error: nil,
252252
},
253-
runTest{
253+
{
254254
Name: "Error",
255255
Component: mockComponent,
256256
Version: "0.1.0",

lwcomponent/http.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,36 @@ import (
2222
"io"
2323
"net/http"
2424
"os"
25+
"time"
26+
)
27+
28+
const (
29+
defaultTimeout = 30 * time.Second
30+
defaultMaxRetry = 2
2531
)
2632

2733
// downloadFile is an internal helper that downloads a file to the provided file path
28-
func downloadFile(filepath string, url string) error {
29-
resp, err := http.Get(url)
34+
func DownloadFile(filepath string, url string, timeout time.Duration) error {
35+
var (
36+
resp *http.Response
37+
err error
38+
_timeout time.Duration = timeout
39+
)
40+
41+
if _timeout == 0 {
42+
_timeout = defaultTimeout
43+
}
44+
45+
client := &http.Client{Timeout: _timeout}
46+
47+
resp, err = client.Get(url)
3048
if err != nil {
31-
return err
49+
for retry := 0; retry < defaultMaxRetry && os.IsTimeout(err); retry++ {
50+
resp, err = client.Get(url)
51+
}
52+
if err != nil {
53+
return err
54+
}
3255
}
3356
defer resp.Body.Close()
3457

lwcomponent/http_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package lwcomponent_test
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"net/http/httptest"
7+
"os"
8+
"testing"
9+
"time"
10+
11+
"github.com/lacework/go-sdk/lwcomponent"
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
func TestDownloadFile(t *testing.T) {
16+
var (
17+
urlPath string = "/lw-cdk-store/catalog/component-example/1.0.0/component-example-linux-amd64.tar.gz"
18+
content string = "CDK component"
19+
)
20+
21+
file, err := os.CreateTemp("", "lwcomponent-downloadFile")
22+
assert.Nil(t, err)
23+
defer file.Close()
24+
25+
mux := http.NewServeMux()
26+
27+
server := httptest.NewServer(mux)
28+
defer server.Close()
29+
30+
mux.HandleFunc(urlPath, func(w http.ResponseWriter, r *http.Request) {
31+
if assert.Equal(t, "GET", r.Method, "Get() should be a GET method") {
32+
fmt.Fprint(w, content)
33+
}
34+
})
35+
36+
t.Run("happy path", func(t *testing.T) {
37+
err = lwcomponent.DownloadFile(file.Name(), fmt.Sprintf("%s%s", server.URL, urlPath), 0)
38+
assert.Nil(t, err)
39+
40+
buf, err := os.ReadFile(file.Name())
41+
assert.Nil(t, err)
42+
assert.Equal(t, content, string(buf))
43+
})
44+
45+
t.Run("timeout error", func(t *testing.T) {
46+
err = lwcomponent.DownloadFile(file.Name(), fmt.Sprintf("%s%s", server.URL, urlPath), 1*time.Microsecond)
47+
assert.NotNil(t, err)
48+
assert.True(t, os.IsTimeout(err))
49+
})
50+
51+
t.Run("non-timeout error", func(t *testing.T) {
52+
err = lwcomponent.DownloadFile(file.Name(), "", 0)
53+
assert.NotNil(t, err)
54+
assert.False(t, os.IsTimeout(err))
55+
})
56+
}

lwcomponent/minisign_internal_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,43 +35,43 @@ type verifySignatureTest struct {
3535
}
3636

3737
var verifySignatureTests = []verifySignatureTest{
38-
verifySignatureTest{
38+
{
3939
Name: "UnableToParseSignature",
4040
Error: errors.New("unable to parse signature"),
4141
},
42-
verifySignatureTest{
42+
{
4343
Name: "InvalidTrustedComment",
4444
Signature: []byte(`untrusted comment:
4545
RWQnyHnTKz1RiRl+vm7dNpp7Jrt+qLANsu9m+TaHSgVyJO9Ldeo5ZPw/GQJu1fJZBuCrsMVW5KbuB6dEPzQvi8ct1zJfMkgVsgs=
4646
trusted comment: RWQnyHnTKz1RidqMl45Ou5XbJCroV7zohE1jpbigTUGXyQY94AF5uo4v.1.dW50cnVzdGVkIGNvbW1lbnQ6IApSV1RjbVljdjlQMHlNcExyVnA3V3lIdmx2RU02VlhJbFhzUGE0QVZVL1MraHlwUCtQdHBFWEVvck5MS3ZReTJSV0QxTmNKNzNlU1NMYUE2TnpLNnBvczlpb2RhbENMczhOZzg9CnRydXN0ZWQgY29tbWVudDogCm1iMmpJRVZBUTNPZlIvL0pRcjUxVzdQR3FrWDhGbWM1YUFKcCt0Q3JXOHZmWmZ6YnVMM2ZvejdtQmV6MHpQdlBldmQxU0FrUklmSW1EMkdjSkVlUEJ3PT0=
4747
cJwh7XDctGR8dpc1NEJnPGU5IXNvMNXa6hdMA0BBZvvnsKQHeVVEkMc7zo72iFsHEKRxe+AmXkryNgVi7Gg0DQ==`),
4848
Error: errors.New("invalid signature trusted comment"),
4949
},
50-
verifySignatureTest{
50+
{
5151
Name: "UnableToParseRootSignature",
5252
Signature: []byte(`untrusted comment:
5353
RWQnyHnTKz1RiRl+vm7dNpp7Jrt+qLANsu9m+TaHSgVyJO9Ldeo5ZPw/GQJu1fJZBuCrsMVW5KbuB6dEPzQvi8ct1zJfMkgVsgs=
5454
trusted comment: 1.RWQnyHnTz1RidqMl45Ou5XbJCroV7zohE1jpbigTUGXyQY94AF5uo4v.1.W50cnVzdGVkIGNvbW1lbnQ6IApSV1RjbVljdjlQMHlNcExyVnA3V3lIdmx2RU02VlhJbFhzUGE0QVZVL1MraHlwUCtQdHBFWEVvck5MS3ZReTJSV0QxTmNKNzNlU1NMYUE2TnpLNnBvczlpb2RhbENMczhOZzg9CnRydXN0ZWQgY29tbWVudDogCm1iMmpJRVZBUTNPZlIvL0pRcjUxVzdQR3FrWDhGbWM1YUFKcCt0Q3JXOHZmWmZ6YnVMM2ZvejdtQmV6MHpQdlBldmQxU0FrUklmSW1EMkdjSkVlUEJ3PT0=
5555
cJwh7XDctGR8dpc1NEJnPGU5IXNvMNXa6hdMA0BBZvvnsKQHeVVEkMc7zo72iFsHEKRxe+AmXkryNgVi7Gg0DQ==`),
5656
Error: errors.New("unable to parse root signature from trusted comment: illegal base64 data at input byte 303"),
5757
},
58-
verifySignatureTest{
58+
{
5959
Name: "InvalidRootSignatureOverSigningKey",
6060
Signature: []byte(`untrusted comment:
6161
RWQnyHnTKz1RiRl+vm7dNpp7Jrt+qLANsu9m+TaHSgVyJO9Ldeo5ZPw/GQJu1fJZBuCrsMVW5KbuB6dEPzQvi8ct1zJfMkgVsgs=
6262
trusted comment: 1.RWQnyHnTz1RidqMl45Ou5XbJCroV7zohE1jpbigTUGXyQY94AF5uo4v.1.dW50cnVzdGVkIGNvbW1lbnQ6IApSV1RjbVljdjlQMHlNcExyVnA3V3lIdmx2RU02VlhJbFhzUGE0QVZVL1MraHlwUCtQdHBFWEVvck5MS3ZReTJSV0QxTmNKNzNlU1NMYUE2TnpLNnBvczlpb2RhbENMczhOZzg9CnRydXN0ZWQgY29tbWVudDogCm1iMmpJRVZBUTNPZlIvL0pRcjUxVzdQR3FrWDhGbWM1YUFKcCt0Q3JXOHZmWmZ6YnVMM2ZvejdtQmV6MHpQdlBldmQxU0FrUklmSW1EMkdjSkVlUEJ3PT0=
6363
cJwh7XDctGR8dpc1NEJnPGU5IXNvMNXa6hdMA0BBZvvnsKQHeVVEkMc7zo72iFsHEKRxe+AmXkryNgVi7Gg0DQ==`),
6464
Error: errors.New("invalid root signature over signing key"),
6565
},
66-
verifySignatureTest{
66+
{
6767
Name: "InvalidSignatureOverComponent",
6868
Signature: []byte(`untrusted comment:
6969
RWQnyHnTKz1RiRl+vm7dNpp7Jrt+qLANsu9m+TaHSgVyJO9Ldeo5ZPw/GQJu1fJZBuCrsMVW5KbuB6dEPzQvi8ct1zJfMkgVsgs=
7070
trusted comment: 1.RWQnyHnTKz1RidqMl45Ou5XbJCroV7zohE1jpbigTUGXyQY94AF5uo4v.1.dW50cnVzdGVkIGNvbW1lbnQ6IApSV1RjbVljdjlQMHlNcExyVnA3V3lIdmx2RU02VlhJbFhzUGE0QVZVL1MraHlwUCtQdHBFWEVvck5MS3ZReTJSV0QxTmNKNzNlU1NMYUE2TnpLNnBvczlpb2RhbENMczhOZzg9CnRydXN0ZWQgY29tbWVudDogCm1iMmpJRVZBUTNPZlIvL0pRcjUxVzdQR3FrWDhGbWM1YUFKcCt0Q3JXOHZmWmZ6YnVMM2ZvejdtQmV6MHpQdlBldmQxU0FrUklmSW1EMkdjSkVlUEJ3PT0=
7171
cJwh7XDctGR8dpc1NEJnPGU5IXNvMNXa6hdMA0BBZvvnsKQHeVVEkMc7zo72iFsHEKRxe+AmXkryNgVi7Gg0DQ==`),
7272
Error: errors.New("invalid signature over component"),
7373
},
74-
verifySignatureTest{
74+
{
7575
Name: "OK",
7676
Message: helloWorld,
7777
Signature: helloWorldSigDecoded,

0 commit comments

Comments
 (0)