mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Rendering: add capabilities check (#44470)
* #44449: add feature check to rendering service * #44449: formatting * #44449: rename feature -> capability (https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/getCapabilities, https://developer.mozilla.org/en-US/docs/Web/API/InputDeviceInfo/getCapabilities) * #44449: refactor * #44449: remove commented code * Update pkg/services/rendering/capabilities.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * #44449: review fixes Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com>
This commit is contained in:
parent
4e37a53a1c
commit
254c59725e
@ -345,6 +345,10 @@ type testRenderService struct {
|
||||
renderErrorImageProvider func(error error) (*rendering.RenderResult, error)
|
||||
}
|
||||
|
||||
func (s *testRenderService) HasCapability(feature rendering.CapabilityName) (rendering.CapabilitySupportRequestResult, error) {
|
||||
return rendering.CapabilitySupportRequestResult{}, nil
|
||||
}
|
||||
|
||||
func (s *testRenderService) IsAvailable() bool {
|
||||
if s.isAvailableProvider != nil {
|
||||
return s.isAvailableProvider()
|
||||
|
55
pkg/services/rendering/capabilities.go
Normal file
55
pkg/services/rendering/capabilities.go
Normal file
@ -0,0 +1,55 @@
|
||||
package rendering
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
)
|
||||
|
||||
type Capability struct {
|
||||
name CapabilityName
|
||||
semverConstraint string
|
||||
}
|
||||
|
||||
type CapabilityName string
|
||||
|
||||
const (
|
||||
ScalingDownImages CapabilityName = "ScalingDownImages"
|
||||
FullHeightImages CapabilityName = "FullHeightImages"
|
||||
)
|
||||
|
||||
var ErrUnknownCapability = errors.New("unknown capability")
|
||||
var ErrInvalidPluginVersion = errors.New("invalid plugin version")
|
||||
|
||||
func (rs *RenderingService) HasCapability(capability CapabilityName) (CapabilitySupportRequestResult, error) {
|
||||
if !rs.IsAvailable() {
|
||||
return CapabilitySupportRequestResult{IsSupported: false, SemverConstraint: ""}, ErrRenderUnavailable
|
||||
}
|
||||
|
||||
var semverConstraint string
|
||||
for i := range rs.capabilities {
|
||||
if rs.capabilities[i].name == capability {
|
||||
semverConstraint = rs.capabilities[i].semverConstraint
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if semverConstraint == "" {
|
||||
return CapabilitySupportRequestResult{}, ErrUnknownCapability
|
||||
}
|
||||
|
||||
compiledSemverConstraint, err := semver.NewConstraint(semverConstraint)
|
||||
if err != nil {
|
||||
rs.log.Error("Failed to parse semver constraint", "constraint", semverConstraint, "capability", capability, "error", err.Error())
|
||||
return CapabilitySupportRequestResult{IsSupported: false, SemverConstraint: semverConstraint}, ErrUnknownCapability
|
||||
}
|
||||
|
||||
imageRendererVersion := rs.Version()
|
||||
compiledImageRendererVersion, err := semver.NewVersion(imageRendererVersion)
|
||||
if err != nil {
|
||||
rs.log.Error("Failed to parse plugin version", "version", imageRendererVersion, "error", err.Error())
|
||||
return CapabilitySupportRequestResult{IsSupported: false, SemverConstraint: semverConstraint}, ErrInvalidPluginVersion
|
||||
}
|
||||
|
||||
return CapabilitySupportRequestResult{IsSupported: compiledSemverConstraint.Check(compiledImageRendererVersion), SemverConstraint: semverConstraint}, nil
|
||||
}
|
136
pkg/services/rendering/capabilities_test.go
Normal file
136
pkg/services/rendering/capabilities_test.go
Normal file
@ -0,0 +1,136 @@
|
||||
package rendering
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type dummyPluginManager struct{}
|
||||
|
||||
func (d *dummyPluginManager) Renderer() *plugins.Plugin {
|
||||
return nil
|
||||
}
|
||||
|
||||
var dummyRendererUrl = "http://dummyurl.com"
|
||||
var testCapabilitySemverConstraint = "> 1.0.0"
|
||||
var testCapabilityName = CapabilityName("TestCap")
|
||||
var testCapabilityNameInvalidSemver = CapabilityName("TestCapInvalidSemver")
|
||||
|
||||
func TestCapabilities(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
rs := &RenderingService{
|
||||
Cfg: cfg,
|
||||
RendererPluginManager: &dummyPluginManager{},
|
||||
log: log.New("test-capabilities-rendering-service"),
|
||||
capabilities: []Capability{
|
||||
{name: testCapabilityName, semverConstraint: testCapabilitySemverConstraint},
|
||||
{name: testCapabilityNameInvalidSemver, semverConstraint: "asfasf"},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
rendererUrl string
|
||||
rendererVersion string
|
||||
capabilityName CapabilityName
|
||||
expectedError error
|
||||
expectedResult CapabilitySupportRequestResult
|
||||
}{
|
||||
{
|
||||
name: "when image-renderer plugin is not available",
|
||||
rendererUrl: "",
|
||||
rendererVersion: "",
|
||||
capabilityName: testCapabilityName,
|
||||
expectedError: ErrRenderUnavailable,
|
||||
expectedResult: CapabilitySupportRequestResult{
|
||||
IsSupported: false,
|
||||
SemverConstraint: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when image-renderer plugin version is not populated",
|
||||
rendererUrl: dummyRendererUrl,
|
||||
rendererVersion: "",
|
||||
capabilityName: testCapabilityName,
|
||||
expectedError: ErrInvalidPluginVersion,
|
||||
expectedResult: CapabilitySupportRequestResult{
|
||||
IsSupported: false,
|
||||
SemverConstraint: testCapabilitySemverConstraint,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when image-renderer plugin version is not valid",
|
||||
rendererUrl: dummyRendererUrl,
|
||||
rendererVersion: "abcd",
|
||||
capabilityName: testCapabilityName,
|
||||
expectedError: ErrInvalidPluginVersion,
|
||||
expectedResult: CapabilitySupportRequestResult{
|
||||
IsSupported: false,
|
||||
SemverConstraint: testCapabilitySemverConstraint,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when image-renderer version does not match target constraint",
|
||||
rendererUrl: dummyRendererUrl,
|
||||
rendererVersion: "1.0.0",
|
||||
capabilityName: testCapabilityName,
|
||||
expectedError: nil,
|
||||
expectedResult: CapabilitySupportRequestResult{
|
||||
IsSupported: false,
|
||||
SemverConstraint: testCapabilitySemverConstraint,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when image-renderer version matches target constraint",
|
||||
rendererUrl: dummyRendererUrl,
|
||||
rendererVersion: "2.0.0",
|
||||
capabilityName: testCapabilityName,
|
||||
expectedError: nil,
|
||||
expectedResult: CapabilitySupportRequestResult{
|
||||
IsSupported: true,
|
||||
SemverConstraint: testCapabilitySemverConstraint,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when capability is unknown",
|
||||
rendererUrl: dummyRendererUrl,
|
||||
rendererVersion: "1.0.0",
|
||||
capabilityName: CapabilityName("unknown"),
|
||||
expectedError: ErrUnknownCapability,
|
||||
expectedResult: CapabilitySupportRequestResult{
|
||||
IsSupported: false,
|
||||
SemverConstraint: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when capability has invalid semver constraint",
|
||||
rendererUrl: dummyRendererUrl,
|
||||
rendererVersion: "1.0.0",
|
||||
capabilityName: testCapabilityNameInvalidSemver,
|
||||
expectedError: ErrUnknownCapability,
|
||||
expectedResult: CapabilitySupportRequestResult{
|
||||
IsSupported: false,
|
||||
SemverConstraint: "asfasf",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rs.Cfg.RendererUrl = tt.rendererUrl
|
||||
rs.version = tt.rendererVersion
|
||||
res, err := rs.HasCapability(tt.capabilityName)
|
||||
|
||||
if tt.expectedError == nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.ErrorIs(t, err, tt.expectedError)
|
||||
}
|
||||
require.Equal(t, tt.expectedResult, res)
|
||||
})
|
||||
}
|
||||
}
|
@ -96,6 +96,11 @@ type Session interface {
|
||||
Dispose(ctx context.Context)
|
||||
}
|
||||
|
||||
type CapabilitySupportRequestResult struct {
|
||||
IsSupported bool
|
||||
SemverConstraint string
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
IsAvailable() bool
|
||||
Version() string
|
||||
@ -103,5 +108,6 @@ type Service interface {
|
||||
RenderCSV(ctx context.Context, opts CSVOpts, session Session) (*RenderCSVResult, error)
|
||||
RenderErrorImage(theme Theme, error error) (*RenderResult, error)
|
||||
GetRenderUser(ctx context.Context, key string) (*RenderUser, bool)
|
||||
HasCapability(capability CapabilityName) (CapabilitySupportRequestResult, error)
|
||||
CreateRenderingSession(ctx context.Context, authOpts AuthOpts, sessionOpts SessionOpts) (Session, error)
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ type RenderingService struct {
|
||||
inProgressCount int32
|
||||
version string
|
||||
versionMutex sync.RWMutex
|
||||
capabilities []Capability
|
||||
|
||||
perRequestRenderKeyProvider renderKeyProvider
|
||||
Cfg *setting.Cfg
|
||||
@ -81,6 +82,16 @@ func ProvideService(cfg *setting.Cfg, remoteCache *remotecache.RemoteCache, rm p
|
||||
log: logger,
|
||||
keyExpiry: 5 * time.Minute,
|
||||
},
|
||||
capabilities: []Capability{
|
||||
{
|
||||
name: FullHeightImages,
|
||||
semverConstraint: ">= 3.4.0",
|
||||
},
|
||||
{
|
||||
name: ScalingDownImages,
|
||||
semverConstraint: ">= 3.4.0",
|
||||
},
|
||||
},
|
||||
Cfg: cfg,
|
||||
RemoteCacheService: remoteCache,
|
||||
RendererPluginManager: rm,
|
||||
|
Loading…
Reference in New Issue
Block a user