mirror of
https://github.com/grafana/grafana.git
synced 2024-12-02 05:29:42 -06:00
a025109647
* 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>
213 lines
6.2 KiB
Go
213 lines
6.2 KiB
Go
package rendering
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/models"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
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) {
|
|
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) {
|
|
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 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
|
|
theme models.Theme
|
|
expected string
|
|
}{
|
|
{
|
|
name: "Light theme returns light image",
|
|
theme: models.ThemeLight,
|
|
expected: path + "/public/img/rendering_limit_light.png",
|
|
},
|
|
{
|
|
name: "Dark theme returns dark image",
|
|
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(), opts, nil)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.expected, result.FilePath)
|
|
})
|
|
}
|
|
}
|
|
|
|
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)
|
|
})
|
|
}
|