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