The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/pkg/services/rendering/rendering_test.go

242 lines
7.1 KiB

package rendering
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/log"
Dash previews: populate crawler queue from SQL query (#44083) * add SQL migrations * dashboard previews from sql: poc * added todos * refactor: use the same enums where possible * use useEffect, always return json * added todo * refactor + delete files after use * refactor + fix manual thumbnail upload * refactor: move all interactions with sqlStore to thumbnail repo * refactor: remove file operations in thumb crawler/service * refactor: fix dashboard_thumbs sql store * refactor: extracted thumbnail fetching/updating to a hook * refactor: store thumbnails in redux store * refactor: store thumbnails in redux store * refactor: private'd repo methods * removed redux storage, saving images as blobs * allow for configurable rendering timeouts * added 1) query for dashboards with stale thumbnails, 2) command for marking thumbnails as stale * use sql-based queue in crawler * ui for marking thumbnails as stale * replaced `stale` boolean prop with `state` enum * introduce rendering session * compilation errors * fix crawler stop button * rename thumbnail state frozen to locked * #44449: fix merge conflicts * #44449: remove thumb methods from `Store` interface * #44449: clean filepath, defer file closing * #44449: fix rendering.Theme cyclic import * #44449: linting * #44449: linting * #44449: mutex'd crawlerStatus access * #44449: added integration tests for `sqlstore.dashboard_thumbs` * #44449: added comments to explain the `ThumbnailState` enum * #44449: use os.ReadFile rather then os.Open * #44449: always enable dashboardPreviews feature during integration tests * #44449: remove sleep time, adjust number of threads * #44449: review fix: add `orgId` to `DashboardThumbnailMeta` * #44449: review fix: automatic parsing of thumbnailState * #44449: lint fixes * #44449: review fix: prefer `WithDbSession` over `WithTransactionalDbSession` * #44449: review fix: add a comment explaining source of the filepath * #44449: review fix: added filepath validation * #44449: review fixes https://github.com/grafana/grafana/pull/45063/files @fzambia Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Alexander Emelin <frvzmb@gmail.com>
3 years ago
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
func TestGetUrl(t *testing.T) {
path := "render/d-solo/5SdHCadmz/panel-tests-graph?orgId=1&from=1587390211965&to=1587393811965&panelId=5&width=1000&height=500&tz=Europe%2FStockholm"
cfg := setting.NewCfg()
rs := &RenderingService{
Cfg: cfg,
}
t.Run("When renderer and callback url configured should return callback url plus path", func(t *testing.T) {
rs.Cfg.RendererUrl = "http://localhost:8081/render"
rs.Cfg.RendererCallbackUrl = "http://public-grafana.com/"
url := rs.getURL(path)
require.Equal(t, rs.Cfg.RendererCallbackUrl+path+"&render=1", url)
})
t.Run("When renderer url not configured", func(t *testing.T) {
rs.Cfg.RendererUrl = ""
rs.domain = "localhost"
rs.Cfg.HTTPPort = "3000"
t.Run("And protocol HTTP configured should return expected path", func(t *testing.T) {
rs.Cfg.ServeFromSubPath = false
rs.Cfg.AppSubURL = ""
rs.Cfg.Protocol = setting.HTTPScheme
url := rs.getURL(path)
require.Equal(t, "http://localhost:3000/"+path+"&render=1", url)
t.Run("And serve from sub path should return expected path", func(t *testing.T) {
rs.Cfg.ServeFromSubPath = true
rs.Cfg.AppSubURL = "/grafana"
url := rs.getURL(path)
require.Equal(t, "http://localhost:3000/grafana/"+path+"&render=1", url)
})
})
t.Run("And protocol HTTPS configured should return expected path", func(t *testing.T) {
rs.Cfg.ServeFromSubPath = false
rs.Cfg.AppSubURL = ""
rs.Cfg.Protocol = setting.HTTPSScheme
url := rs.getURL(path)
require.Equal(t, "https://localhost:3000/"+path+"&render=1", url)
})
t.Run("And protocol HTTP2 configured should return expected path", func(t *testing.T) {
rs.Cfg.ServeFromSubPath = false
rs.Cfg.AppSubURL = ""
rs.Cfg.Protocol = setting.HTTP2Scheme
url := rs.getURL(path)
require.Equal(t, "https://localhost:3000/"+path+"&render=1", url)
})
})
}
func TestRenderErrorImage(t *testing.T) {
path, err := filepath.Abs("../../../")
require.NoError(t, err)
rs := RenderingService{
Cfg: &setting.Cfg{
HomePath: path,
},
}
t.Run("No theme set returns error image with dark theme", func(t *testing.T) {
result, err := rs.RenderErrorImage("", nil)
require.NoError(t, err)
assert.Equal(t, result.FilePath, path+"/public/img/rendering_error_dark.png")
})
t.Run("Timeout error returns timeout error image", func(t *testing.T) {
Dash previews: populate crawler queue from SQL query (#44083) * add SQL migrations * dashboard previews from sql: poc * added todos * refactor: use the same enums where possible * use useEffect, always return json * added todo * refactor + delete files after use * refactor + fix manual thumbnail upload * refactor: move all interactions with sqlStore to thumbnail repo * refactor: remove file operations in thumb crawler/service * refactor: fix dashboard_thumbs sql store * refactor: extracted thumbnail fetching/updating to a hook * refactor: store thumbnails in redux store * refactor: store thumbnails in redux store * refactor: private'd repo methods * removed redux storage, saving images as blobs * allow for configurable rendering timeouts * added 1) query for dashboards with stale thumbnails, 2) command for marking thumbnails as stale * use sql-based queue in crawler * ui for marking thumbnails as stale * replaced `stale` boolean prop with `state` enum * introduce rendering session * compilation errors * fix crawler stop button * rename thumbnail state frozen to locked * #44449: fix merge conflicts * #44449: remove thumb methods from `Store` interface * #44449: clean filepath, defer file closing * #44449: fix rendering.Theme cyclic import * #44449: linting * #44449: linting * #44449: mutex'd crawlerStatus access * #44449: added integration tests for `sqlstore.dashboard_thumbs` * #44449: added comments to explain the `ThumbnailState` enum * #44449: use os.ReadFile rather then os.Open * #44449: always enable dashboardPreviews feature during integration tests * #44449: remove sleep time, adjust number of threads * #44449: review fix: add `orgId` to `DashboardThumbnailMeta` * #44449: review fix: automatic parsing of thumbnailState * #44449: lint fixes * #44449: review fix: prefer `WithDbSession` over `WithTransactionalDbSession` * #44449: review fix: add a comment explaining source of the filepath * #44449: review fix: added filepath validation * #44449: review fixes https://github.com/grafana/grafana/pull/45063/files @fzambia Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Alexander Emelin <frvzmb@gmail.com>
3 years ago
result, err := rs.RenderErrorImage(models.ThemeLight, ErrTimeout)
require.NoError(t, err)
assert.Equal(t, result.FilePath, path+"/public/img/rendering_timeout_light.png")
})
t.Run("Generic error returns error image", func(t *testing.T) {
Dash previews: populate crawler queue from SQL query (#44083) * add SQL migrations * dashboard previews from sql: poc * added todos * refactor: use the same enums where possible * use useEffect, always return json * added todo * refactor + delete files after use * refactor + fix manual thumbnail upload * refactor: move all interactions with sqlStore to thumbnail repo * refactor: remove file operations in thumb crawler/service * refactor: fix dashboard_thumbs sql store * refactor: extracted thumbnail fetching/updating to a hook * refactor: store thumbnails in redux store * refactor: store thumbnails in redux store * refactor: private'd repo methods * removed redux storage, saving images as blobs * allow for configurable rendering timeouts * added 1) query for dashboards with stale thumbnails, 2) command for marking thumbnails as stale * use sql-based queue in crawler * ui for marking thumbnails as stale * replaced `stale` boolean prop with `state` enum * introduce rendering session * compilation errors * fix crawler stop button * rename thumbnail state frozen to locked * #44449: fix merge conflicts * #44449: remove thumb methods from `Store` interface * #44449: clean filepath, defer file closing * #44449: fix rendering.Theme cyclic import * #44449: linting * #44449: linting * #44449: mutex'd crawlerStatus access * #44449: added integration tests for `sqlstore.dashboard_thumbs` * #44449: added comments to explain the `ThumbnailState` enum * #44449: use os.ReadFile rather then os.Open * #44449: always enable dashboardPreviews feature during integration tests * #44449: remove sleep time, adjust number of threads * #44449: review fix: add `orgId` to `DashboardThumbnailMeta` * #44449: review fix: automatic parsing of thumbnailState * #44449: lint fixes * #44449: review fix: prefer `WithDbSession` over `WithTransactionalDbSession` * #44449: review fix: add a comment explaining source of the filepath * #44449: review fix: added filepath validation * #44449: review fixes https://github.com/grafana/grafana/pull/45063/files @fzambia Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Alexander Emelin <frvzmb@gmail.com>
3 years ago
result, err := rs.RenderErrorImage(models.ThemeLight, errors.New("an error"))
require.NoError(t, err)
assert.Equal(t, result.FilePath, path+"/public/img/rendering_error_light.png")
})
t.Run("Unknown image path returns error", func(t *testing.T) {
result, err := rs.RenderErrorImage("abc", errors.New("random error"))
assert.Error(t, err)
assert.Nil(t, result)
})
}
func TestRenderUnavailableError(t *testing.T) {
rs := RenderingService{
Cfg: &setting.Cfg{},
log: log.New("test"),
RendererPluginManager: &dummyPluginManager{},
}
opts := Opts{ErrorOpts: ErrorOpts{ErrorRenderUnavailable: true}}
result, err := rs.Render(context.Background(), RenderPNG, opts, nil)
assert.Equal(t, ErrRenderUnavailable, err)
assert.Nil(t, result)
}
func TestRenderLimitImage(t *testing.T) {
path, err := filepath.Abs("../../../")
require.NoError(t, err)
rs := RenderingService{
Cfg: &setting.Cfg{
HomePath: path,
},
inProgressCount: 2,
log: log.New("test"),
}
tests := []struct {
name string
Dash previews: populate crawler queue from SQL query (#44083) * add SQL migrations * dashboard previews from sql: poc * added todos * refactor: use the same enums where possible * use useEffect, always return json * added todo * refactor + delete files after use * refactor + fix manual thumbnail upload * refactor: move all interactions with sqlStore to thumbnail repo * refactor: remove file operations in thumb crawler/service * refactor: fix dashboard_thumbs sql store * refactor: extracted thumbnail fetching/updating to a hook * refactor: store thumbnails in redux store * refactor: store thumbnails in redux store * refactor: private'd repo methods * removed redux storage, saving images as blobs * allow for configurable rendering timeouts * added 1) query for dashboards with stale thumbnails, 2) command for marking thumbnails as stale * use sql-based queue in crawler * ui for marking thumbnails as stale * replaced `stale` boolean prop with `state` enum * introduce rendering session * compilation errors * fix crawler stop button * rename thumbnail state frozen to locked * #44449: fix merge conflicts * #44449: remove thumb methods from `Store` interface * #44449: clean filepath, defer file closing * #44449: fix rendering.Theme cyclic import * #44449: linting * #44449: linting * #44449: mutex'd crawlerStatus access * #44449: added integration tests for `sqlstore.dashboard_thumbs` * #44449: added comments to explain the `ThumbnailState` enum * #44449: use os.ReadFile rather then os.Open * #44449: always enable dashboardPreviews feature during integration tests * #44449: remove sleep time, adjust number of threads * #44449: review fix: add `orgId` to `DashboardThumbnailMeta` * #44449: review fix: automatic parsing of thumbnailState * #44449: lint fixes * #44449: review fix: prefer `WithDbSession` over `WithTransactionalDbSession` * #44449: review fix: add a comment explaining source of the filepath * #44449: review fix: added filepath validation * #44449: review fixes https://github.com/grafana/grafana/pull/45063/files @fzambia Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Alexander Emelin <frvzmb@gmail.com>
3 years ago
theme models.Theme
expected string
}{
{
name: "Light theme returns light image",
Dash previews: populate crawler queue from SQL query (#44083) * add SQL migrations * dashboard previews from sql: poc * added todos * refactor: use the same enums where possible * use useEffect, always return json * added todo * refactor + delete files after use * refactor + fix manual thumbnail upload * refactor: move all interactions with sqlStore to thumbnail repo * refactor: remove file operations in thumb crawler/service * refactor: fix dashboard_thumbs sql store * refactor: extracted thumbnail fetching/updating to a hook * refactor: store thumbnails in redux store * refactor: store thumbnails in redux store * refactor: private'd repo methods * removed redux storage, saving images as blobs * allow for configurable rendering timeouts * added 1) query for dashboards with stale thumbnails, 2) command for marking thumbnails as stale * use sql-based queue in crawler * ui for marking thumbnails as stale * replaced `stale` boolean prop with `state` enum * introduce rendering session * compilation errors * fix crawler stop button * rename thumbnail state frozen to locked * #44449: fix merge conflicts * #44449: remove thumb methods from `Store` interface * #44449: clean filepath, defer file closing * #44449: fix rendering.Theme cyclic import * #44449: linting * #44449: linting * #44449: mutex'd crawlerStatus access * #44449: added integration tests for `sqlstore.dashboard_thumbs` * #44449: added comments to explain the `ThumbnailState` enum * #44449: use os.ReadFile rather then os.Open * #44449: always enable dashboardPreviews feature during integration tests * #44449: remove sleep time, adjust number of threads * #44449: review fix: add `orgId` to `DashboardThumbnailMeta` * #44449: review fix: automatic parsing of thumbnailState * #44449: lint fixes * #44449: review fix: prefer `WithDbSession` over `WithTransactionalDbSession` * #44449: review fix: add a comment explaining source of the filepath * #44449: review fix: added filepath validation * #44449: review fixes https://github.com/grafana/grafana/pull/45063/files @fzambia Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Alexander Emelin <frvzmb@gmail.com>
3 years ago
theme: models.ThemeLight,
expected: path + "/public/img/rendering_limit_light.png",
},
{
name: "Dark theme returns dark image",
Dash previews: populate crawler queue from SQL query (#44083) * add SQL migrations * dashboard previews from sql: poc * added todos * refactor: use the same enums where possible * use useEffect, always return json * added todo * refactor + delete files after use * refactor + fix manual thumbnail upload * refactor: move all interactions with sqlStore to thumbnail repo * refactor: remove file operations in thumb crawler/service * refactor: fix dashboard_thumbs sql store * refactor: extracted thumbnail fetching/updating to a hook * refactor: store thumbnails in redux store * refactor: store thumbnails in redux store * refactor: private'd repo methods * removed redux storage, saving images as blobs * allow for configurable rendering timeouts * added 1) query for dashboards with stale thumbnails, 2) command for marking thumbnails as stale * use sql-based queue in crawler * ui for marking thumbnails as stale * replaced `stale` boolean prop with `state` enum * introduce rendering session * compilation errors * fix crawler stop button * rename thumbnail state frozen to locked * #44449: fix merge conflicts * #44449: remove thumb methods from `Store` interface * #44449: clean filepath, defer file closing * #44449: fix rendering.Theme cyclic import * #44449: linting * #44449: linting * #44449: mutex'd crawlerStatus access * #44449: added integration tests for `sqlstore.dashboard_thumbs` * #44449: added comments to explain the `ThumbnailState` enum * #44449: use os.ReadFile rather then os.Open * #44449: always enable dashboardPreviews feature during integration tests * #44449: remove sleep time, adjust number of threads * #44449: review fix: add `orgId` to `DashboardThumbnailMeta` * #44449: review fix: automatic parsing of thumbnailState * #44449: lint fixes * #44449: review fix: prefer `WithDbSession` over `WithTransactionalDbSession` * #44449: review fix: add a comment explaining source of the filepath * #44449: review fix: added filepath validation * #44449: review fixes https://github.com/grafana/grafana/pull/45063/files @fzambia Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Alexander Emelin <frvzmb@gmail.com>
3 years ago
theme: models.ThemeDark,
expected: path + "/public/img/rendering_limit_dark.png",
},
{
name: "No theme returns dark image",
theme: "",
expected: path + "/public/img/rendering_limit_dark.png",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
opts := Opts{Theme: tc.theme, ConcurrentLimit: 1}
result, err := rs.Render(context.Background(), RenderPNG, opts, nil)
assert.NoError(t, err)
assert.Equal(t, tc.expected, result.FilePath)
})
}
}
func TestRenderLimitImageError(t *testing.T) {
rs := RenderingService{
Cfg: &setting.Cfg{},
inProgressCount: 2,
log: log.New("test"),
}
opts := Opts{
ErrorOpts: ErrorOpts{ErrorConcurrentLimitReached: true},
ConcurrentLimit: 1,
Theme: models.ThemeDark,
}
result, err := rs.Render(context.Background(), RenderPNG, opts, nil)
assert.Equal(t, ErrConcurrentLimitReached, err)
assert.Nil(t, result)
}
func TestRenderingServiceGetRemotePluginVersion(t *testing.T) {
cfg := setting.NewCfg()
rs := &RenderingService{
Cfg: cfg,
log: log.New("rendering-test"),
}
t.Run("When renderer responds with correct version should return that version", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte("{\"version\":\"2.7.1828\"}"))
require.NoError(t, err)
}))
defer server.Close()
rs.Cfg.RendererUrl = server.URL + "/render"
version, err := rs.getRemotePluginVersion()
require.NoError(t, err)
require.Equal(t, "2.7.1828", version)
})
t.Run("When renderer responds with 404 should assume a valid but old version", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
defer server.Close()
rs.Cfg.RendererUrl = server.URL + "/render"
version, err := rs.getRemotePluginVersion()
require.NoError(t, err)
require.Equal(t, version, "1.0.0")
})
t.Run("When renderer responds with 500 should retry until success", func(t *testing.T) {
tries := uint(0)
ctx, cancel := context.WithCancel(context.Background())
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
tries++
if tries < remoteVersionFetchRetries {
w.WriteHeader(http.StatusInternalServerError)
} else {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte("{\"version\":\"3.1.4159\"}"))
require.NoError(t, err)
cancel()
}
}))
defer server.Close()
rs.Cfg.RendererUrl = server.URL + "/render"
remoteVersionFetchInterval = time.Millisecond
remoteVersionFetchRetries = 5
go func() {
require.NoError(t, rs.Run(ctx))
}()
require.Eventually(t, func() bool { return rs.Version() == "3.1.4159" }, time.Second, time.Millisecond)
})
}