Chore: Change endpoint to check versions in cli (#78008)

This commit is contained in:
Andres Martinez Gotor
2023-11-21 12:46:26 +01:00
committed by GitHub
parent 97c79f2c34
commit f8a6380510
5 changed files with 90 additions and 30 deletions

View File

@@ -79,3 +79,11 @@ type ErrChecksumMismatch struct {
func (e ErrChecksumMismatch) Error() string {
return fmt.Sprintf("expected SHA256 checksum does not match the downloaded archive (%s) - please contact security@grafana.com", e.archiveURL)
}
type ErrCorePlugin struct {
id string
}
func (e ErrCorePlugin) Error() string {
return fmt.Sprintf("plugin %s is a core plugin and cannot be installed separately", e.id)
}

View File

@@ -12,14 +12,15 @@ type PluginArchiveInfo struct {
Checksum string
}
// PluginRepo is (a subset of) the JSON response from /api/plugins/repo/$pluginID
type PluginRepo struct {
Versions []Version `json:"versions"`
// PluginVersions is the JSON response from /api/plugins/$pluginID/versions
type PluginVersions struct {
Versions []Version `json:"items"`
}
type Version struct {
Version string `json:"version"`
Arch map[string]ArchMeta `json:"arch"`
Arch map[string]ArchMeta `json:"packages"`
URL string `json:"url"`
}
type ArchMeta struct {

View File

@@ -5,8 +5,10 @@ import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"path"
"strings"
"github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/log"
@@ -87,7 +89,19 @@ func (m *Manager) PluginVersion(pluginID, version string, compatOpts CompatOpts)
return VersionData{}, errors.New("no system compatibility requirements set")
}
return SelectSystemCompatibleVersion(m.log, versions, pluginID, version, sysCompatOpts)
compatibleVer, err := SelectSystemCompatibleVersion(m.log, versions, pluginID, version, sysCompatOpts)
if err != nil {
return VersionData{}, err
}
isGrafanaCorePlugin := strings.HasPrefix(compatibleVer.URL, "https://github.com/grafana/grafana/tree/main/public/app/plugins/")
_, hasAnyArch := compatibleVer.Arch["any"]
if isGrafanaCorePlugin && hasAnyArch {
// Trying to install a coupled core plugin
return VersionData{}, ErrCorePlugin{id: pluginID}
}
return compatibleVer, nil
}
func (m *Manager) downloadURL(pluginID, version string) string {
@@ -102,19 +116,25 @@ func (m *Manager) grafanaCompatiblePluginVersions(pluginID string, compatOpts Co
return nil, err
}
u.Path = path.Join(u.Path, "repo", pluginID)
u.Path = path.Join(u.Path, pluginID, "versions")
body, err := m.client.SendReq(u, compatOpts)
if err != nil {
return nil, err
}
var v PluginRepo
var v PluginVersions
err = json.Unmarshal(body, &v)
if err != nil {
m.log.Error("Failed to unmarshal plugin repo response", err)
return nil, err
}
if len(v.Versions) == 0 {
// /plugins/{pluginId}/versions returns 200 even if the plugin doesn't exists
// but the response is empty. In this case we return 404.
return nil, newErrResponse4xx(http.StatusNotFound).withMessage("Plugin not found")
}
return v.Versions, nil
}

View File

@@ -21,9 +21,12 @@ const (
func TestGetPluginArchive(t *testing.T) {
tcs := []struct {
name string
sha string
err error
name string
sha string
apiOpSys string
apiArch string
apiUrl string
err error
}{
{
name: "Happy path",
@@ -34,6 +37,18 @@ func TestGetPluginArchive(t *testing.T) {
sha: "1a2b3c",
err: &ErrChecksumMismatch{},
},
{
name: "Core plugin",
sha: "69f698961b6ea651211a187874434821c4727cc22de022e3a7059116d21c75b1",
apiOpSys: "any",
apiUrl: "https://github.com/grafana/grafana/tree/main/public/app/plugins/test",
err: &ErrCorePlugin{},
},
{
name: "Decoupled core plugin",
sha: "69f698961b6ea651211a187874434821c4727cc22de022e3a7059116d21c75b1",
apiUrl: "https://github.com/grafana/grafana/tree/main/public/app/plugins/test",
},
}
pluginZip := createPluginArchive(t)
@@ -57,17 +72,23 @@ func TestGetPluginArchive(t *testing.T) {
grafanaVersion = "10.0.0"
)
srv := mockPluginRepoAPI(t,
srvData{
pluginID: pluginID,
version: version,
opSys: opSys,
arch: arch,
grafanaVersion: grafanaVersion,
sha: tc.sha,
archive: d,
},
)
srvd := srvData{
pluginID: pluginID,
version: version,
opSys: tc.apiOpSys,
arch: tc.apiArch,
url: tc.apiUrl,
grafanaVersion: grafanaVersion,
sha: tc.sha,
archive: d,
}
if srvd.opSys == "" {
srvd.opSys = opSys
}
if srvd.arch == "" && srvd.opSys != "any" {
srvd.arch = arch
}
srv := mockPluginVersionsAPI(t, srvd)
t.Cleanup(srv.Close)
m := NewManager(ManagerCfg{
@@ -125,34 +146,38 @@ type srvData struct {
sha string
grafanaVersion string
archive []byte
url string
}
func mockPluginRepoAPI(t *testing.T, data srvData) *httptest.Server {
func mockPluginVersionsAPI(t *testing.T, data srvData) *httptest.Server {
t.Helper()
mux := http.NewServeMux()
// mock plugin version data
mux.HandleFunc(fmt.Sprintf("/repo/%s", data.pluginID), func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc(fmt.Sprintf("/%s/versions", data.pluginID), func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, data.grafanaVersion, r.Header.Get("grafana-version"))
require.Equal(t, data.opSys, r.Header.Get("grafana-os"))
require.Equal(t, data.arch, r.Header.Get("grafana-arch"))
require.NotNil(t, fmt.Sprintf("grafana %s", data.grafanaVersion), r.Header.Get("User-Agent"))
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
platform := data.opSys
if data.arch != "" {
platform += "-" + data.arch
}
_, _ = w.Write([]byte(fmt.Sprintf(`
{
"versions": [{
"items": [{
"version": "%s",
"arch": {
"%s-%s": {
"packages": {
"%s": {
"sha256": "%s"
}
}
},
"url": "%s"
}]
}
`, data.version, data.opSys, data.arch, data.sha),
`, data.version, platform, data.sha, data.url),
))
})

View File

@@ -9,6 +9,8 @@ import (
type VersionData struct {
Version string
Checksum string
Arch map[string]ArchMeta
URL string
}
// SelectSystemCompatibleVersion selects the most appropriate plugin version based on os + architecture
@@ -33,6 +35,8 @@ func SelectSystemCompatibleVersion(log log.PrettyLogger, versions []Version, plu
return VersionData{
Version: latestForArch.Version,
Checksum: checksum(latestForArch, compatOpts),
Arch: latestForArch.Arch,
URL: latestForArch.URL,
}, nil
}
for _, v := range versions {
@@ -65,6 +69,8 @@ func SelectSystemCompatibleVersion(log log.PrettyLogger, versions []Version, plu
return VersionData{
Version: ver.Version,
Checksum: checksum(ver, compatOpts),
Arch: ver.Arch,
URL: ver.URL,
}, nil
}