mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Upload error image when image renderer unavailable (#23713)
When Include image is enabled for an alert notification channel, but there's no image renderer available/installed when sending notification an error image will be uploaded/attached explaining that you need to install the Grafana Image Renderer plugin. Ref #13802 Co-Authored-By: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
parent
98df2ec745
commit
58de0dabd4
@ -53,25 +53,19 @@ func (n *notificationService) SendIfNeeded(evalCtx *EvalContext) error {
|
||||
}
|
||||
|
||||
if notifierStates.ShouldUploadImage() {
|
||||
if n.renderService.IsAvailable() {
|
||||
// Create a copy of EvalContext and give it a new, shorter, timeout context to upload the image
|
||||
uploadEvalCtx := *evalCtx
|
||||
timeout := setting.AlertingNotificationTimeout / 2
|
||||
var uploadCtxCancel func()
|
||||
uploadEvalCtx.Ctx, uploadCtxCancel = context.WithTimeout(evalCtx.Ctx, timeout)
|
||||
// Create a copy of EvalContext and give it a new, shorter, timeout context to upload the image
|
||||
uploadEvalCtx := *evalCtx
|
||||
timeout := setting.AlertingNotificationTimeout / 2
|
||||
var uploadCtxCancel func()
|
||||
uploadEvalCtx.Ctx, uploadCtxCancel = context.WithTimeout(evalCtx.Ctx, timeout)
|
||||
|
||||
// Try to upload the image without consuming all the time allocated for EvalContext
|
||||
if err = n.renderAndUploadImage(&uploadEvalCtx, timeout); err != nil {
|
||||
n.log.Error("Failed to render and upload alert panel image.", "ruleId", uploadEvalCtx.Rule.ID, "error", err)
|
||||
}
|
||||
uploadCtxCancel()
|
||||
evalCtx.ImageOnDiskPath = uploadEvalCtx.ImageOnDiskPath
|
||||
evalCtx.ImagePublicURL = uploadEvalCtx.ImagePublicURL
|
||||
} else {
|
||||
n.log.Warn("Could not render image for alert notification, no image renderer found/installed. " +
|
||||
"For image rendering support please install the grafana-image-renderer plugin. " +
|
||||
"Read more at https://grafana.com/docs/grafana/latest/administration/image_rendering/")
|
||||
// Try to upload the image without consuming all the time allocated for EvalContext
|
||||
if err = n.renderAndUploadImage(&uploadEvalCtx, timeout); err != nil {
|
||||
n.log.Error("Failed to render and upload alert panel image.", "ruleId", uploadEvalCtx.Rule.ID, "error", err)
|
||||
}
|
||||
uploadCtxCancel()
|
||||
evalCtx.ImageOnDiskPath = uploadEvalCtx.ImageOnDiskPath
|
||||
evalCtx.ImagePublicURL = uploadEvalCtx.ImagePublicURL
|
||||
}
|
||||
|
||||
return n.sendNotifications(evalCtx, notifierStates)
|
||||
@ -174,6 +168,7 @@ func (n *notificationService) renderAndUploadImage(evalCtx *EvalContext, timeout
|
||||
n.log.Debug("Rendered alert panel image", "ruleId", evalCtx.Rule.ID, "path", result.FilePath, "took", took)
|
||||
|
||||
evalCtx.ImageOnDiskPath = result.FilePath
|
||||
|
||||
n.log.Debug("Uploading alert panel image to external image store", "ruleId", evalCtx.Rule.ID, "path", evalCtx.ImageOnDiskPath)
|
||||
|
||||
start = time.Now()
|
||||
|
@ -39,13 +39,13 @@ func TestNotificationService(t *testing.T) {
|
||||
require.Truef(t, evalCtx.Ctx.Value(notificationSent{}).(bool), "expected notification to be sent, but wasn't")
|
||||
})
|
||||
|
||||
notificationServiceScenario(t, "Given alert rule with upload image enabled but no renderer available should not render and upload image, but send notification", evalCtx, true, func(scenarioCtx *scenarioContext) {
|
||||
notificationServiceScenario(t, "Given alert rule with upload image enabled but no renderer available should render and upload unavailable image and send notification", evalCtx, true, func(scenarioCtx *scenarioContext) {
|
||||
scenarioCtx.rendererAvailable = false
|
||||
err := scenarioCtx.notificationService.SendIfNeeded(evalCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equalf(t, 0, scenarioCtx.renderCount, "expected render to not be called, but it was")
|
||||
require.Equalf(t, 0, scenarioCtx.imageUploadCount, "expected image to not be uploaded, but it was")
|
||||
require.Equalf(t, 1, scenarioCtx.renderCount, "expected render to be called, but it wasn't")
|
||||
require.Equalf(t, 1, scenarioCtx.imageUploadCount, "expected image to be uploaded, but it wasn't")
|
||||
require.Truef(t, evalCtx.Ctx.Value(notificationSent{}).(bool), "expected notification to be sent, but wasn't")
|
||||
})
|
||||
|
||||
|
@ -121,6 +121,14 @@ func (rs *RenderingService) RenderErrorImage(err error) (*RenderResult, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (rs *RenderingService) renderUnavailableImage() *RenderResult {
|
||||
imgPath := "public/img/rendering_plugin_not_installed.png"
|
||||
|
||||
return &RenderResult{
|
||||
FilePath: filepath.Join(setting.HomePath, imgPath),
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *RenderingService) Render(ctx context.Context, opts Opts) (*RenderResult, error) {
|
||||
if rs.inProgressCount > opts.ConcurrentLimit {
|
||||
return &RenderResult{
|
||||
@ -128,8 +136,11 @@ func (rs *RenderingService) Render(ctx context.Context, opts Opts) (*RenderResul
|
||||
}, nil
|
||||
}
|
||||
|
||||
if rs.renderAction == nil {
|
||||
return nil, fmt.Errorf("no renderer found")
|
||||
if !rs.IsAvailable() {
|
||||
rs.log.Warn("Could not render image, no image renderer found/installed. " +
|
||||
"For image rendering support please install the grafana-image-renderer plugin. " +
|
||||
"Read more at https://grafana.com/docs/grafana/latest/administration/image_rendering/")
|
||||
return rs.renderUnavailableImage(), nil
|
||||
}
|
||||
|
||||
rs.log.Info("Rendering", "path", opts.Path)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 8.9 KiB |
Loading…
Reference in New Issue
Block a user