mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 05:46:28 +08:00
Plugins: Refactor plugin repository API (#69063)
* support grafana wildcard version * undo go.mod changes * tidy * flesh out tests * refactor * add tests * tidy naming * undo some changes * split interfaces * separation * update new signature * simplify * update var namings * unexport types * introduce opts pattern * reorder test * fix compat checks * middle ground * unexport client * move back * fix tests * inline logger * make client usable * use fake logger * tidy errors * remove unused types * fix test * review fixes * rework compatibility * adjust installer * fix tests * opts => cfg * remove unused var * fix var name
This commit is contained in:
@ -26,7 +26,7 @@ type Client struct {
|
||||
log log.PrettyLogger
|
||||
}
|
||||
|
||||
func newClient(skipTLSVerify bool, logger log.PrettyLogger) *Client {
|
||||
func NewClient(skipTLSVerify bool, logger log.PrettyLogger) *Client {
|
||||
return &Client{
|
||||
httpClient: makeHttpClient(skipTLSVerify, 10*time.Second),
|
||||
httpClientNoTimeout: makeHttpClient(skipTLSVerify, 0),
|
||||
@ -34,7 +34,7 @@ func newClient(skipTLSVerify bool, logger log.PrettyLogger) *Client {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) download(_ context.Context, pluginZipURL, checksum string, compatOpts CompatOpts) (*PluginArchive, error) {
|
||||
func (c *Client) Download(_ context.Context, pluginZipURL, checksum string, compatOpts CompatOpts) (*PluginArchive, error) {
|
||||
// Create temp file for downloading zip file
|
||||
tmpFile, err := os.CreateTemp("", "*.zip")
|
||||
if err != nil {
|
||||
@ -53,7 +53,7 @@ func (c *Client) download(_ context.Context, pluginZipURL, checksum string, comp
|
||||
if err := tmpFile.Close(); err != nil {
|
||||
c.log.Warn("Failed to close file", "err", err)
|
||||
}
|
||||
return nil, fmt.Errorf("%w: failed to download plugin archive (%s)", err, pluginZipURL)
|
||||
return nil, fmt.Errorf("failed to download plugin archive: %w", err)
|
||||
}
|
||||
|
||||
rc, err := zip.OpenReader(tmpFile.Name())
|
||||
@ -61,9 +61,29 @@ func (c *Client) download(_ context.Context, pluginZipURL, checksum string, comp
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PluginArchive{
|
||||
File: rc,
|
||||
}, nil
|
||||
return &PluginArchive{File: rc}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SendReq(url *url.URL, compatOpts CompatOpts) ([]byte, error) {
|
||||
req, err := c.createReq(url, compatOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bodyReader, err := c.handleResp(res, compatOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err = bodyReader.Close(); err != nil {
|
||||
c.log.Warn("Failed to close stream", "err", err)
|
||||
}
|
||||
}()
|
||||
return io.ReadAll(bodyReader)
|
||||
}
|
||||
|
||||
func (c *Client) downloadFile(tmpFile *os.File, pluginURL, checksum string, compatOpts CompatOpts) (err error) {
|
||||
@ -122,8 +142,8 @@ func (c *Client) downloadFile(tmpFile *os.File, pluginURL, checksum string, comp
|
||||
return err
|
||||
}
|
||||
|
||||
// Using no timeout here as some plugins can be bigger and smaller timeout would prevent to download a plugin on
|
||||
// slow network. As this is CLI operation hanging is not a big of an issue as user can just abort.
|
||||
// Using no timeout as some plugin archives make take longer to fetch due to size, network performance, etc.
|
||||
// Note: This is also used as part of the grafana plugin install CLI operation
|
||||
bodyReader, err := c.sendReqNoTimeout(u, compatOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -139,37 +159,15 @@ func (c *Client) downloadFile(tmpFile *os.File, pluginURL, checksum string, comp
|
||||
if _, err = io.Copy(w, io.TeeReader(bodyReader, h)); err != nil {
|
||||
return fmt.Errorf("%v: %w", "failed to compute SHA256 checksum", err)
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
if err = w.Flush(); err != nil {
|
||||
return fmt.Errorf("failed to write to %q: %w", tmpFile.Name(), err)
|
||||
}
|
||||
if len(checksum) > 0 && checksum != fmt.Sprintf("%x", h.Sum(nil)) {
|
||||
return fmt.Errorf("expected SHA256 checksum does not match the downloaded archive (%s) - please contact security@grafana.com", pluginURL)
|
||||
return ErrChecksumMismatch{archiveURL: pluginURL}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) sendReq(url *url.URL, compatOpts CompatOpts) ([]byte, error) {
|
||||
req, err := c.createReq(url, compatOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bodyReader, err := c.handleResp(res, compatOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := bodyReader.Close(); err != nil {
|
||||
c.log.Warn("Failed to close stream", "err", err)
|
||||
}
|
||||
}()
|
||||
return io.ReadAll(bodyReader)
|
||||
}
|
||||
|
||||
func (c *Client) sendReqNoTimeout(url *url.URL, compatOpts CompatOpts) (io.ReadCloser, error) {
|
||||
req, err := c.createReq(url, compatOpts)
|
||||
if err != nil {
|
||||
@ -189,10 +187,18 @@ func (c *Client) createReq(url *url.URL, compatOpts CompatOpts) (*http.Request,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("grafana-version", compatOpts.GrafanaVersion)
|
||||
req.Header.Set("grafana-os", compatOpts.OS)
|
||||
req.Header.Set("grafana-arch", compatOpts.Arch)
|
||||
req.Header.Set("User-Agent", "grafana "+compatOpts.GrafanaVersion)
|
||||
if gVer, exists := compatOpts.GrafanaVersion(); exists {
|
||||
req.Header.Set("grafana-version", gVer)
|
||||
req.Header.Set("User-Agent", "grafana "+gVer)
|
||||
}
|
||||
|
||||
if sysOS, exists := compatOpts.system.OS(); exists {
|
||||
req.Header.Set("grafana-os", sysOS)
|
||||
}
|
||||
|
||||
if sysArch, exists := compatOpts.system.Arch(); exists {
|
||||
req.Header.Set("grafana-arch", sysArch)
|
||||
}
|
||||
|
||||
return req, err
|
||||
}
|
||||
@ -206,7 +212,7 @@ func (c *Client) handleResp(res *http.Response, compatOpts CompatOpts) (io.ReadC
|
||||
}
|
||||
}()
|
||||
if err != nil || len(body) == 0 {
|
||||
return nil, Response4xxError{StatusCode: res.StatusCode}
|
||||
return nil, newErrResponse4xx(res.StatusCode)
|
||||
}
|
||||
var message string
|
||||
var jsonBody map[string]string
|
||||
@ -216,7 +222,8 @@ func (c *Client) handleResp(res *http.Response, compatOpts CompatOpts) (io.ReadC
|
||||
} else {
|
||||
message = jsonBody["message"]
|
||||
}
|
||||
return nil, Response4xxError{StatusCode: res.StatusCode, Message: message, SystemInfo: compatOpts.String()}
|
||||
|
||||
return nil, newErrResponse4xx(res.StatusCode).withMessage(message).withCompatibilityInfo(compatOpts)
|
||||
}
|
||||
|
||||
if res.StatusCode/100 != 2 {
|
||||
@ -227,23 +234,21 @@ func (c *Client) handleResp(res *http.Response, compatOpts CompatOpts) (io.ReadC
|
||||
}
|
||||
|
||||
func makeHttpClient(skipTLSVerify bool, timeout time.Duration) http.Client {
|
||||
tr := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: skipTLSVerify,
|
||||
return http.Client{
|
||||
Timeout: timeout,
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: skipTLSVerify,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return http.Client{
|
||||
Timeout: timeout,
|
||||
Transport: tr,
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user