grafana/pkg/plugins/models.go
Jack Westbrook 3c3cf2eee9
Plugins Catalog: Install and show the latest compatible version of a plugin (#41003)
* fix(catalog): prefer rendering installed version over latest version

* feat(catalog): signify installed version in version history

* feat(catalog): introduce installedVersion and latestVersion

* refactor(catalog): use latestVersion for installation, simplify plugindetails header logic

* refactor(catalog): clean up installedVersion and latestVersion

* feat(catalog): use table-layout so versions list table has consistent column widths

* test(catalog): update failing tests

* removed the need of having a latest version in the plugin catalog type root level.

* fixed flaky test depending on what locale it was being running with.

* added missing test to verify version for a remote plugin.

* fixed version in header.

* preventing the UI from break if no versions are available.

* fixed failing test due to missing mock data.

* added todo as a reminder.

* refactor(catalog): prefer grafana plugin icons over gcom notfound images

* refactor(Plugins/Admin): change constant name

* refactor(Plugins/Admin): add comment to make condition easier to understand

* chore: update go modules

* feat(Backend/Plugins): add "dependencies" field to `PluginListItem`

* feat(Plugins/Admin): show the grafana dependency for the installed version

* refactor(Plugins/Admin): use the local version of links

* refactor(Plugins/Admin): prefer the local version for `.type`

* refactor(Plugins/ADmin): prefer the local `.description` field

* fix(Plugins/Admin): fix tests

* test(plugins/api): update the expected response for the `api/plugins` tests

* chore(Plugins/Admin): add todo comments to check preferation of remote/local values

* feat(backend/api): always send the grafana version as a header when proxying to GCOM

* feat(plugins/admin): use the `isCompatible` flag to get the latest compatible version

* feat(plugins/admin): show the latest compatible version in the versions list

* fix(plugins/admin): show the grafana dependency for the latest compatible version

* fix(plugins/admin): update the version list when installing/uninstalling a plugin

* test(plugins/admin): add some test-cases for the latest-compatible-version

* fix(plugins/admin): show the grafana dependency for the installed version (if installed)

* feat(plugins/backend): add the `dependencies.grafanaDependency` property to the plugin object

* test(plugins/backend): fix tests by adjusting expected response json

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
2021-11-12 11:07:12 +01:00

214 lines
5.0 KiB
Go

package plugins
import (
"errors"
"fmt"
"github.com/grafana/grafana/pkg/models"
)
const (
TypeDashboard = "dashboard"
)
var (
ErrInstallCorePlugin = errors.New("cannot install a Core plugin")
ErrUninstallCorePlugin = errors.New("cannot uninstall a Core plugin")
ErrUninstallOutsideOfPluginDir = errors.New("cannot uninstall a plugin outside")
ErrPluginNotInstalled = errors.New("plugin is not installed")
)
type NotFoundError struct {
PluginID string
}
func (e NotFoundError) Error() string {
return fmt.Sprintf("plugin with ID '%s' not found", e.PluginID)
}
type DuplicateError struct {
PluginID string
ExistingPluginDir string
}
func (e DuplicateError) Error() string {
return fmt.Sprintf("plugin with ID '%s' already exists in '%s'", e.PluginID, e.ExistingPluginDir)
}
func (e DuplicateError) Is(err error) bool {
// nolint:errorlint
_, ok := err.(DuplicateError)
return ok
}
type SignatureError struct {
PluginID string `json:"pluginId"`
SignatureStatus SignatureStatus `json:"status"`
}
func (e SignatureError) Error() string {
switch e.SignatureStatus {
case SignatureInvalid:
return fmt.Sprintf("plugin '%s' has an invalid signature", e.PluginID)
case SignatureModified:
return fmt.Sprintf("plugin '%s' has an modified signature", e.PluginID)
case SignatureUnsigned:
return fmt.Sprintf("plugin '%s' has no signature", e.PluginID)
case SignatureInternal, SignatureValid:
return ""
}
return fmt.Sprintf("plugin '%s' has an unknown signature state", e.PluginID)
}
func (e SignatureError) AsErrorCode() ErrorCode {
switch e.SignatureStatus {
case SignatureInvalid:
return signatureInvalid
case SignatureModified:
return signatureModified
case SignatureUnsigned:
return signatureMissing
case SignatureInternal, SignatureValid:
return ""
}
return ""
}
type Dependencies struct {
GrafanaDependency string `json:"grafanaDependency"`
GrafanaVersion string `json:"grafanaVersion"`
Plugins []Dependency `json:"plugins"`
}
type Includes struct {
Name string `json:"name"`
Path string `json:"path"`
Type string `json:"type"`
Component string `json:"component"`
Role models.RoleType `json:"role"`
AddToNav bool `json:"addToNav"`
DefaultNav bool `json:"defaultNav"`
Slug string `json:"slug"`
Icon string `json:"icon"`
UID string `json:"uid"`
ID string `json:"-"`
}
func (e Includes) GetSlugOrUIDLink() string {
if len(e.UID) > 0 {
return "/d/" + e.UID
} else {
return "/dashboard/db/" + e.Slug
}
}
type Dependency struct {
ID string `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Version string `json:"version"`
}
type BuildInfo struct {
Time int64 `json:"time,omitempty"`
Repo string `json:"repo,omitempty"`
Branch string `json:"branch,omitempty"`
Hash string `json:"hash,omitempty"`
}
type Info struct {
Author InfoLink `json:"author"`
Description string `json:"description"`
Links []InfoLink `json:"links"`
Logos Logos `json:"logos"`
Build BuildInfo `json:"build"`
Screenshots []Screenshots `json:"screenshots"`
Version string `json:"version"`
Updated string `json:"updated"`
}
type InfoLink struct {
Name string `json:"name"`
URL string `json:"url"`
}
type Logos struct {
Small string `json:"small"`
Large string `json:"large"`
}
type Screenshots struct {
Name string `json:"name"`
Path string `json:"path"`
}
type StaticRoute struct {
PluginID string
Directory string
}
type SignatureStatus string
func (ss SignatureStatus) IsValid() bool {
return ss == SignatureValid
}
func (ss SignatureStatus) IsInternal() bool {
return ss == SignatureInternal
}
const (
SignatureInternal SignatureStatus = "internal" // core plugin, no signature
SignatureValid SignatureStatus = "valid" // signed and accurate MANIFEST
SignatureInvalid SignatureStatus = "invalid" // invalid signature
SignatureModified SignatureStatus = "modified" // valid signature, but content mismatch
SignatureUnsigned SignatureStatus = "unsigned" // no MANIFEST file
)
type ReleaseState string
const (
AlphaRelease ReleaseState = "alpha"
)
type SignatureType string
const (
GrafanaSignature SignatureType = "grafana"
PrivateSignature SignatureType = "private"
)
type PluginFiles map[string]struct{}
type Signature struct {
Status SignatureStatus
Type SignatureType
SigningOrg string
Files PluginFiles
}
type PluginMetaDTO struct {
JSONData
Signature SignatureStatus `json:"signature"`
Module string `json:"module"`
BaseURL string `json:"baseUrl"`
}
const (
signatureMissing ErrorCode = "signatureMissing"
signatureModified ErrorCode = "signatureModified"
signatureInvalid ErrorCode = "signatureInvalid"
)
type ErrorCode string
type Error struct {
ErrorCode `json:"errorCode"`
PluginID string `json:"pluginId,omitempty"`
}