mirror of https://github.com/grafana/grafana
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>pull/44613/head
parent
4e37a53a1c
commit
254c59725e
@ -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 |
||||
} |
@ -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) |
||||
}) |
||||
} |
||||
} |
Loading…
Reference in new issue