Chore: Remove provisional APIVersion from plugin info (#89831)

This commit is contained in:
Andres Martinez Gotor 2024-07-01 10:53:16 +02:00 committed by GitHub
parent 55ba32bda7
commit a22c1ae424
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 20 additions and 157 deletions

View File

@ -582,11 +582,6 @@
}, },
"required": ["extensionPointId", "title", "type"] "required": ["extensionPointId", "title", "type"]
} }
},
"apiVersion": {
"type": "string",
"description": "[internal only] The API version for the plugin. Used for Datasource API servers. This metadata is temporary and will be removed in the future.",
"pattern": "^v([\\d]+)(?:(alpha|beta)([\\d]+))?$"
} }
} }
} }

View File

@ -28,7 +28,6 @@ type PluginSetting struct {
SignatureType plugins.SignatureType `json:"signatureType"` SignatureType plugins.SignatureType `json:"signatureType"`
SignatureOrg string `json:"signatureOrg"` SignatureOrg string `json:"signatureOrg"`
AngularDetected bool `json:"angularDetected"` AngularDetected bool `json:"angularDetected"`
APIVersion string `json:"apiVersion"`
} }
type PluginListItem struct { type PluginListItem struct {
@ -50,7 +49,6 @@ type PluginListItem struct {
AccessControl accesscontrol.Metadata `json:"accessControl,omitempty"` AccessControl accesscontrol.Metadata `json:"accessControl,omitempty"`
AngularDetected bool `json:"angularDetected"` AngularDetected bool `json:"angularDetected"`
IAM *pfs.IAM `json:"iam,omitempty"` IAM *pfs.IAM `json:"iam,omitempty"`
APIVersion string `json:"apiVersion"`
} }
type PluginList []PluginListItem type PluginList []PluginListItem

View File

@ -142,7 +142,6 @@ func (hs *HTTPServer) GetPluginList(c *contextmodel.ReqContext) response.Respons
SignatureOrg: pluginDef.SignatureOrg, SignatureOrg: pluginDef.SignatureOrg,
AccessControl: pluginsMetadata[pluginDef.ID], AccessControl: pluginsMetadata[pluginDef.ID],
AngularDetected: pluginDef.Angular.Detected, AngularDetected: pluginDef.Angular.Detected,
APIVersion: pluginDef.APIVersion,
} }
if hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagExternalServiceAccounts) { if hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagExternalServiceAccounts) {
@ -209,7 +208,6 @@ func (hs *HTTPServer) GetPluginSettingByID(c *contextmodel.ReqContext) response.
SignatureOrg: plugin.SignatureOrg, SignatureOrg: plugin.SignatureOrg,
SecureJsonFields: map[string]bool{}, SecureJsonFields: map[string]bool{},
AngularDetected: plugin.Angular.Detected, AngularDetected: plugin.Angular.Detected,
APIVersion: plugin.APIVersion,
} }
if plugin.IsApp() { if plugin.IsApp() {

View File

@ -3,8 +3,6 @@ package validation
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"regexp"
"slices" "slices"
"time" "time"
@ -117,36 +115,3 @@ func (a *AngularDetector) Validate(ctx context.Context, p *plugins.Plugin) error
p.Angular.HideDeprecation = slices.Contains(a.cfg.HideAngularDeprecation, p.ID) p.Angular.HideDeprecation = slices.Contains(a.cfg.HideAngularDeprecation, p.ID)
return nil return nil
} }
// APIVersionValidation implements a ValidateFunc for validating plugin API versions.
type APIVersionValidation struct {
}
// APIVersionValidationStep returns a new ValidateFunc for validating plugin signatures.
func APIVersionValidationStep() ValidateFunc {
sv := &APIVersionValidation{}
return sv.Validate
}
// Validate validates the plugin signature. If a signature error is encountered, the error is recorded with the
// pluginerrs.ErrorTracker.
func (v *APIVersionValidation) Validate(ctx context.Context, p *plugins.Plugin) error {
if p.APIVersion != "" {
if !p.Backend {
return fmt.Errorf("plugin %s has an API version but is not a backend plugin", p.ID)
}
// Eventually, all backend plugins will be supported
if p.Type != plugins.TypeDataSource {
return fmt.Errorf("plugin %s has an API version but is not a datasource plugin", p.ID)
}
m, err := regexp.MatchString(`^v([\d]+)(?:(alpha|beta)([\d]+))?$`, p.APIVersion)
if err != nil {
return fmt.Errorf("failed to verify apiVersion %s: %v", p.APIVersion, err)
}
if !m {
return fmt.Errorf("plugin %s has an invalid API version %s", p.ID, p.APIVersion)
}
}
return nil
}

View File

@ -1,75 +0,0 @@
package validation
import (
"context"
"testing"
"github.com/grafana/grafana/pkg/plugins"
"github.com/stretchr/testify/require"
)
func TestAPIVersionValidation(t *testing.T) {
s := APIVersionValidationStep()
tests := []struct {
name string
plugin *plugins.Plugin
err bool
}{
{
name: "valid plugin",
plugin: &plugins.Plugin{
JSONData: plugins.JSONData{
Backend: true,
Type: plugins.TypeDataSource,
APIVersion: "v0alpha1",
},
},
err: false,
},
{
name: "invalid plugin - not backend",
plugin: &plugins.Plugin{
JSONData: plugins.JSONData{
Backend: false,
Type: plugins.TypeDataSource,
APIVersion: "v0alpha1",
},
},
err: true,
},
{
name: "invalid plugin - not datasource",
plugin: &plugins.Plugin{
JSONData: plugins.JSONData{
Backend: true,
Type: plugins.TypeApp,
APIVersion: "v0alpha1",
},
},
err: true,
},
{
name: "invalid plugin - invalid API version",
plugin: &plugins.Plugin{
JSONData: plugins.JSONData{
Backend: true,
Type: plugins.TypeDataSource,
APIVersion: "invalid",
},
},
err: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := s(context.Background(), tt.plugin)
if tt.err {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}

View File

@ -128,9 +128,6 @@ type JSONData struct {
// App Service Auth Registration // App Service Auth Registration
IAM *pfs.IAM `json:"iam,omitempty"` IAM *pfs.IAM `json:"iam,omitempty"`
// API Version: Temporary field while plugins don't expose a OpenAPI schema
APIVersion string `json:"apiVersion,omitempty"`
} }
func ReadPluginJSON(reader io.Reader) (JSONData, error) { func ReadPluginJSON(reader io.Reader) (JSONData, error) {

View File

@ -322,10 +322,7 @@ func (s *Service) prepareInstanceSettings(ctx context.Context, settings *backend
} }
// When the APIVersion is set, the client must also implement AdmissionHandler // When the APIVersion is set, the client must also implement AdmissionHandler
if p.APIVersion == "" { if settings.APIVersion == "" {
if settings.APIVersion != "" {
return nil, fmt.Errorf("invalid request apiVersion (datasource does not have one configured)")
}
return settings, nil // NOOP return settings, nil // NOOP
} }
@ -367,7 +364,7 @@ func (s *Service) prepareInstanceSettings(ctx context.Context, settings *backend
if err != nil { if err != nil {
if errors.Is(err, plugins.ErrMethodNotImplemented) { if errors.Is(err, plugins.ErrMethodNotImplemented) {
return nil, errutil.Internal("plugin.unimplemented"). return nil, errutil.Internal("plugin.unimplemented").
Errorf("plugin (%s) with apiVersion=%s must implement ValidateAdmission", p.ID, p.APIVersion) Errorf("plugin (%s) with apiVersion=%s must implement ValidateAdmission", p.ID, settings.APIVersion)
} }
return nil, err return nil, err
} }
@ -388,7 +385,7 @@ func (s *Service) prepareInstanceSettings(ctx context.Context, settings *backend
if err != nil { if err != nil {
if errors.Is(err, plugins.ErrMethodNotImplemented) { if errors.Is(err, plugins.ErrMethodNotImplemented) {
return nil, errutil.Internal("plugin.unimplemented"). return nil, errutil.Internal("plugin.unimplemented").
Errorf("plugin (%s) with apiVersion=%s must implement MutateAdmission", p.ID, p.APIVersion) Errorf("plugin (%s) with apiVersion=%s must implement MutateAdmission", p.ID, settings.APIVersion)
} }
return nil, err return nil, err
} }

View File

@ -110,10 +110,9 @@ func TestService_AddDataSource(t *testing.T) {
dsService.pluginStore = &pluginstore.FakePluginStore{ dsService.pluginStore = &pluginstore.FakePluginStore{
PluginList: []pluginstore.Plugin{{ PluginList: []pluginstore.Plugin{{
JSONData: plugins.JSONData{ JSONData: plugins.JSONData{
ID: "test", ID: "test",
Type: plugins.TypeDataSource, Type: plugins.TypeDataSource,
Name: "test", Name: "test",
APIVersion: "v0alpha1", // When a value exists in plugin.json, the callbacks will be executed
}, },
}}, }},
} }
@ -150,10 +149,9 @@ func TestService_AddDataSource(t *testing.T) {
dsService.pluginStore = &pluginstore.FakePluginStore{ dsService.pluginStore = &pluginstore.FakePluginStore{
PluginList: []pluginstore.Plugin{{ PluginList: []pluginstore.Plugin{{
JSONData: plugins.JSONData{ JSONData: plugins.JSONData{
ID: "test", ID: "test",
Type: plugins.TypeDataSource, Type: plugins.TypeDataSource,
Name: "test", Name: "test",
APIVersion: "v0alpha1", // When a value exists in plugin.json, the callbacks will be executed
}, },
}}, }},
} }
@ -200,10 +198,9 @@ func TestService_AddDataSource(t *testing.T) {
dsService.pluginStore = &pluginstore.FakePluginStore{ dsService.pluginStore = &pluginstore.FakePluginStore{
PluginList: []pluginstore.Plugin{{ PluginList: []pluginstore.Plugin{{
JSONData: plugins.JSONData{ JSONData: plugins.JSONData{
ID: "test", ID: "test",
Type: plugins.TypeDataSource, Type: plugins.TypeDataSource,
Name: "test", Name: "test",
APIVersion: "v0alpha1", // When a value exists in plugin.json, the callbacks will be executed
}, },
}}, }},
} }
@ -491,10 +488,9 @@ func TestService_UpdateDataSource(t *testing.T) {
dsService.pluginStore = &pluginstore.FakePluginStore{ dsService.pluginStore = &pluginstore.FakePluginStore{
PluginList: []pluginstore.Plugin{{ PluginList: []pluginstore.Plugin{{
JSONData: plugins.JSONData{ JSONData: plugins.JSONData{
ID: "test", ID: "test",
Type: plugins.TypeDataSource, Type: plugins.TypeDataSource,
Name: "test", Name: "test",
APIVersion: "v0alpha1", // When a value exists in plugin.json, the callbacks will be executed
}, },
}}, }},
} }

View File

@ -54,7 +54,6 @@ func ProvideValidationStage(cfg *config.PluginManagementCfg, sv signature.Valida
SignatureValidationStep(sv), SignatureValidationStep(sv),
validation.ModuleJSValidationStep(), validation.ModuleJSValidationStep(),
validation.AngularDetectionStep(cfg, ai), validation.AngularDetectionStep(cfg, ai),
validation.APIVersionValidationStep(),
}, },
}) })
} }

View File

@ -43,7 +43,6 @@ func (p *BaseProvider) GetBasePluginContext(ctx context.Context, plugin pluginst
pCtx := backend.PluginContext{ pCtx := backend.PluginContext{
PluginID: plugin.ID, PluginID: plugin.ID,
PluginVersion: plugin.Info.Version, PluginVersion: plugin.Info.Version,
APIVersion: plugin.APIVersion,
} }
if user != nil && !user.IsNil() { if user != nil && !user.IsNil() {
pCtx.OrgID = user.GetOrgID() pCtx.OrgID = user.GetOrgID()

View File

@ -26,17 +26,15 @@ import (
func TestGet(t *testing.T) { func TestGet(t *testing.T) {
const ( const (
pluginID = "plugin-id" pluginID = "plugin-id"
alias = "alias" alias = "alias"
apiVersion = "v0alpha1"
) )
preg := registry.NewInMemory() preg := registry.NewInMemory()
require.NoError(t, preg.Add(context.Background(), &plugins.Plugin{ require.NoError(t, preg.Add(context.Background(), &plugins.Plugin{
JSONData: plugins.JSONData{ JSONData: plugins.JSONData{
ID: pluginID, ID: pluginID,
AliasIDs: []string{alias}, AliasIDs: []string{alias},
APIVersion: apiVersion,
}, },
})) }))
@ -61,7 +59,6 @@ func TestGet(t *testing.T) {
pCtx, err := pcp.Get(context.Background(), tc.input, identity, identity.OrgID) pCtx, err := pcp.Get(context.Background(), tc.input, identity, identity.OrgID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, pluginID, pCtx.PluginID) require.Equal(t, pluginID, pCtx.PluginID)
require.Equal(t, apiVersion, pCtx.APIVersion)
require.NotNil(t, pCtx.GrafanaConfig) require.NotNil(t, pCtx.GrafanaConfig)
}) })
@ -75,7 +72,6 @@ func TestGet(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, pluginID, pCtx.PluginID) require.Equal(t, pluginID, pCtx.PluginID)
require.Equal(t, apiVersion, pCtx.APIVersion)
require.NotNil(t, pCtx.GrafanaConfig) require.NotNil(t, pCtx.GrafanaConfig)
}) })
}) })

View File

@ -1671,8 +1671,7 @@
"signature": "internal", "signature": "internal",
"signatureType": "", "signatureType": "",
"signatureOrg": "", "signatureOrg": "",
"angularDetected": false, "angularDetected": false
"apiVersion": "v0alpha1"
}, },
{ {
"name": "Text", "name": "Text",

View File

@ -10,7 +10,6 @@
"alerting": true, "alerting": true,
"annotations": true, "annotations": true,
"backend": true, "backend": true,
"apiVersion": "v0alpha1",
"queryOptions": { "queryOptions": {
"minInterval": true, "minInterval": true,