/
pull_chart_tarball.go
131 lines (102 loc) · 3.38 KB
/
pull_chart_tarball.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
121
122
123
124
125
126
127
128
129
130
131
package helmclient
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"net/url"
"time"
"github.com/giantswarm/backoff"
"github.com/giantswarm/microerror"
"github.com/prometheus/client_golang/prometheus"
"github.com/spf13/afero"
)
// PullChartTarball downloads a tarball from the provided tarball URL,
// returning the file path.
func (c *Client) PullChartTarball(ctx context.Context, tarballURL string) (string, error) {
eventName := "pull_chart_tarball"
t := prometheus.NewTimer(histogram.WithLabelValues(eventName))
defer t.ObserveDuration()
chartTarballPath, err := c.pullChartTarball(ctx, tarballURL)
if err != nil {
errorGauge.WithLabelValues(eventName).Inc()
return "", microerror.Mask(err)
}
return chartTarballPath, nil
}
func (c *Client) pullChartTarball(ctx context.Context, tarballURL string) (string, error) {
req, err := c.newRequest("GET", tarballURL)
if err != nil {
return "", microerror.Mask(err)
}
u, err := url.Parse(tarballURL)
if err != nil {
return "", microerror.Mask(err)
}
// Set host header to prevent 404 responses from GitHub Pages.
req.Host = u.Host
chartTarballPath, err := c.doFile(ctx, req)
if err != nil {
return "", microerror.Mask(err)
}
return chartTarballPath, nil
}
func (c *Client) doFile(ctx context.Context, req *http.Request) (string, error) {
var tmpFileName string
req = req.WithContext(ctx)
o := func() error {
resp, err := c.httpClient.Do(req)
if isNoSuchHostError(err) {
return backoff.Permanent(microerror.Maskf(pullChartFailedError, "no such host %#q", req.Host))
} else if IsPullChartTimeout(err) {
return backoff.Permanent(microerror.Maskf(pullChartTimeoutError, "%#q timeout for %#q", req.Method, req.URL.String()))
} else if err != nil {
return microerror.Mask(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(resp.Body)
if err != nil {
return microerror.Mask(err)
}
// Github Pages 404 produces full HTML page which obscures the logs.
if resp.StatusCode == http.StatusNotFound {
return backoff.Permanent(microerror.Maskf(pullChartNotFoundError, fmt.Sprintf("got StatusCode %d for url %#q", resp.StatusCode, req.URL.String())))
}
// Github Pages 503 produces full HTML page which obscures the logs.
if resp.StatusCode == http.StatusServiceUnavailable {
return backoff.Permanent(microerror.Maskf(pullChartFailedError, fmt.Sprintf("got StatusCode %d for url %#q", resp.StatusCode, req.URL.String())))
}
return microerror.Maskf(executionFailedError, fmt.Sprintf("got StatusCode %d for url %#q with body %s", resp.StatusCode, req.URL.String(), buf.String()))
}
tmpfile, err := afero.TempFile(c.fs, "", "chart-tarball")
if err != nil {
return microerror.Mask(err)
}
defer tmpfile.Close()
_, err = io.Copy(tmpfile, resp.Body)
if err != nil {
return microerror.Mask(err)
}
tmpFileName = tmpfile.Name()
return nil
}
b := backoff.NewMaxRetries(3, 5*time.Second)
n := backoff.NewNotifier(c.logger, ctx)
err := backoff.RetryNotify(o, b, n)
if err != nil {
return "", microerror.Mask(err)
}
return tmpFileName, nil
}
func (c *Client) newRequest(method, url string) (*http.Request, error) {
var buf io.Reader
req, err := http.NewRequest(method, url, buf)
if err != nil {
return nil, microerror.Mask(err)
}
req.Header.Set("Accept", "application/json")
return req, nil
}