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:
committed by
GitHub
parent
98df2ec745
commit
58de0dabd4
@@ -53,25 +53,19 @@ func (n *notificationService) SendIfNeeded(evalCtx *EvalContext) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if notifierStates.ShouldUploadImage() {
|
if notifierStates.ShouldUploadImage() {
|
||||||
if n.renderService.IsAvailable() {
|
// Create a copy of EvalContext and give it a new, shorter, timeout context to upload the image
|
||||||
// Create a copy of EvalContext and give it a new, shorter, timeout context to upload the image
|
uploadEvalCtx := *evalCtx
|
||||||
uploadEvalCtx := *evalCtx
|
timeout := setting.AlertingNotificationTimeout / 2
|
||||||
timeout := setting.AlertingNotificationTimeout / 2
|
var uploadCtxCancel func()
|
||||||
var uploadCtxCancel func()
|
uploadEvalCtx.Ctx, uploadCtxCancel = context.WithTimeout(evalCtx.Ctx, timeout)
|
||||||
uploadEvalCtx.Ctx, uploadCtxCancel = context.WithTimeout(evalCtx.Ctx, timeout)
|
|
||||||
|
|
||||||
// Try to upload the image without consuming all the time allocated for EvalContext
|
// Try to upload the image without consuming all the time allocated for EvalContext
|
||||||
if err = n.renderAndUploadImage(&uploadEvalCtx, timeout); err != nil {
|
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)
|
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/")
|
|
||||||
}
|
}
|
||||||
|
uploadCtxCancel()
|
||||||
|
evalCtx.ImageOnDiskPath = uploadEvalCtx.ImageOnDiskPath
|
||||||
|
evalCtx.ImagePublicURL = uploadEvalCtx.ImagePublicURL
|
||||||
}
|
}
|
||||||
|
|
||||||
return n.sendNotifications(evalCtx, notifierStates)
|
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)
|
n.log.Debug("Rendered alert panel image", "ruleId", evalCtx.Rule.ID, "path", result.FilePath, "took", took)
|
||||||
|
|
||||||
evalCtx.ImageOnDiskPath = result.FilePath
|
evalCtx.ImageOnDiskPath = result.FilePath
|
||||||
|
|
||||||
n.log.Debug("Uploading alert panel image to external image store", "ruleId", evalCtx.Rule.ID, "path", evalCtx.ImageOnDiskPath)
|
n.log.Debug("Uploading alert panel image to external image store", "ruleId", evalCtx.Rule.ID, "path", evalCtx.ImageOnDiskPath)
|
||||||
|
|
||||||
start = time.Now()
|
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")
|
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
|
scenarioCtx.rendererAvailable = false
|
||||||
err := scenarioCtx.notificationService.SendIfNeeded(evalCtx)
|
err := scenarioCtx.notificationService.SendIfNeeded(evalCtx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equalf(t, 0, scenarioCtx.renderCount, "expected render to not be called, but it was")
|
require.Equalf(t, 1, scenarioCtx.renderCount, "expected render to be called, but it wasn't")
|
||||||
require.Equalf(t, 0, scenarioCtx.imageUploadCount, "expected image to not be uploaded, but it was")
|
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")
|
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
|
}, 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) {
|
func (rs *RenderingService) Render(ctx context.Context, opts Opts) (*RenderResult, error) {
|
||||||
if rs.inProgressCount > opts.ConcurrentLimit {
|
if rs.inProgressCount > opts.ConcurrentLimit {
|
||||||
return &RenderResult{
|
return &RenderResult{
|
||||||
@@ -128,8 +136,11 @@ func (rs *RenderingService) Render(ctx context.Context, opts Opts) (*RenderResul
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if rs.renderAction == nil {
|
if !rs.IsAvailable() {
|
||||||
return nil, fmt.Errorf("no renderer found")
|
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)
|
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 |
Reference in New Issue
Block a user