Previews: refactor (#47728)

* #44449: return standard thumb service even if auth setup fails

* #44449: remove dashboardPreviewsScheduler feature flag

* #44449: externalize dashboardPreviews config

* #44449: disable previews by default

* #44449: rename logger

* #44449: dashboardPreviewsAdmin feature requires dev mode

* #44449: retrigger CII
This commit is contained in:
Artur Wierzbicki 2022-04-25 01:55:10 +04:00 committed by GitHub
parent 70a7b73839
commit 2e599643f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 94 additions and 32 deletions

View File

@ -1176,3 +1176,22 @@ default_baselayer_config =
# Enable or disable loading other base map layers # Enable or disable loading other base map layers
enable_custom_baselayers = true enable_custom_baselayers = true
#################################### Dashboard previews #####################################
[dashboard_previews.crawler]
# Number of dashboards rendered in parallel. Default is 6.
thread_count =
# Timeout passed down to the Image Renderer plugin. It is used in two separate places within a single rendering request:
# First during the initial navigation to the dashboard and then when waiting for all the panels to load. Default is 20s.
# This setting should be expressed as a duration. Examples: 10s (seconds), 1m (minutes).
rendering_timeout =
# Maximum duration of a single crawl. Default is 1h.
# This setting should be expressed as a duration. Examples: 10s (seconds), 1m (minutes).
max_crawl_duration =
# Minimum interval between two subsequent scheduler runs. Default is 12h.
# This setting should be expressed as a duration. Examples: 10s (seconds), 1m (minutes).
scheduler_interval =

View File

@ -22,7 +22,6 @@ export interface FeatureToggles {
serviceAccounts?: boolean; serviceAccounts?: boolean;
database_metrics?: boolean; database_metrics?: boolean;
dashboardPreviews?: boolean; dashboardPreviews?: boolean;
dashboardPreviewsScheduler?: boolean;
dashboardPreviewsAdmin?: boolean; dashboardPreviewsAdmin?: boolean;
['live-config']?: boolean; ['live-config']?: boolean;
['live-pipeline']?: boolean; ['live-pipeline']?: boolean;

View File

@ -39,14 +39,10 @@ var (
State: FeatureStateAlpha, State: FeatureStateAlpha,
}, },
{ {
Name: "dashboardPreviewsScheduler", Name: "dashboardPreviewsAdmin",
Description: "Schedule automatic updates to dashboard previews", Description: "Manage the dashboard previews crawler process from the UI",
State: FeatureStateAlpha, State: FeatureStateAlpha,
}, RequiresDevMode: true,
{
Name: "dashboardPreviewsAdmin",
Description: "Manage the dashboard previews crawler process from the UI",
State: FeatureStateAlpha,
}, },
{ {
Name: "live-config", Name: "live-config",

View File

@ -31,10 +31,6 @@ const (
// Create and show thumbnails for dashboard search results // Create and show thumbnails for dashboard search results
FlagDashboardPreviews = "dashboardPreviews" FlagDashboardPreviews = "dashboardPreviews"
// FlagDashboardPreviewsScheduler
// Schedule automatic updates to dashboard previews
FlagDashboardPreviewsScheduler = "dashboardPreviewsScheduler"
// FlagDashboardPreviewsAdmin // FlagDashboardPreviewsAdmin
// Manage the dashboard previews crawler process from the UI // Manage the dashboard previews crawler process from the UI
FlagDashboardPreviewsAdmin = "dashboardPreviewsAdmin" FlagDashboardPreviewsAdmin = "dashboardPreviewsAdmin"

View File

@ -10,6 +10,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/grafana/grafana/pkg/setting"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
@ -19,8 +20,10 @@ import (
) )
type simpleCrawler struct { type simpleCrawler struct {
renderService rendering.Service renderService rendering.Service
threadCount int threadCount int
concurrentLimit int
renderingTimeout time.Duration
glive *live.GrafanaLive glive *live.GrafanaLive
thumbnailRepo thumbnailRepo thumbnailRepo thumbnailRepo
@ -36,13 +39,17 @@ type simpleCrawler struct {
renderingSessionByOrgId map[int64]rendering.Session renderingSessionByOrgId map[int64]rendering.Session
} }
func newSimpleCrawler(renderService rendering.Service, gl *live.GrafanaLive, repo thumbnailRepo) dashRenderer { func newSimpleCrawler(renderService rendering.Service, gl *live.GrafanaLive, repo thumbnailRepo, cfg *setting.Cfg, settings setting.DashboardPreviewsSettings) dashRenderer {
threadCount := int(settings.CrawlThreadCount)
c := &simpleCrawler{ c := &simpleCrawler{
renderService: renderService, // temporarily increases the concurrentLimit from the 'cfg.RendererConcurrentRequestLimit' to 'cfg.RendererConcurrentRequestLimit + crawlerThreadCount'
threadCount: 6, concurrentLimit: cfg.RendererConcurrentRequestLimit + threadCount,
glive: gl, renderingTimeout: settings.RenderingTimeout,
thumbnailRepo: repo, renderService: renderService,
log: log.New("thumbnails_crawler"), threadCount: threadCount,
glive: gl,
thumbnailRepo: repo,
log: log.New("thumbnails_crawler"),
status: crawlStatus{ status: crawlStatus{
State: initializing, State: initializing,
Complete: 0, Complete: 0,
@ -154,11 +161,11 @@ func (r *simpleCrawler) Run(ctx context.Context, auth CrawlerAuth, mode CrawlerM
r.auth = auth r.auth = auth
r.opts = rendering.Opts{ r.opts = rendering.Opts{
TimeoutOpts: rendering.TimeoutOpts{ TimeoutOpts: rendering.TimeoutOpts{
Timeout: 20 * time.Second, Timeout: r.renderingTimeout,
RequestTimeoutMultiplier: 3, RequestTimeoutMultiplier: 3,
}, },
Theme: theme, Theme: theme,
ConcurrentLimit: 10, ConcurrentLimit: r.concurrentLimit,
} }
r.renderingSessionByOrgId = make(map[int64]rendering.Session) r.renderingSessionByOrgId = make(map[int64]rendering.Session)

View File

@ -51,6 +51,8 @@ type thumbService struct {
crawlLockServiceActionName string crawlLockServiceActionName string
log log.Logger log log.Logger
usageStatsService usagestats.Service usageStatsService usagestats.Service
canRunCrawler bool
settings setting.DashboardPreviewsSettings
} }
type crawlerScheduleOptions struct { type crawlerScheduleOptions struct {
@ -67,30 +69,34 @@ func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, lockS
if !features.IsEnabled(featuremgmt.FlagDashboardPreviews) { if !features.IsEnabled(featuremgmt.FlagDashboardPreviews) {
return &dummyService{} return &dummyService{}
} }
logger := log.New("thumbnails_service") logger := log.New("previews_service")
logger.Info("initialized thumb", "settings", cfg.DashboardPreviews)
thumbnailRepo := newThumbnailRepo(store) thumbnailRepo := newThumbnailRepo(store)
canRunCrawler := true
crawlerAuth, err := authSetupService.Setup(context.Background()) crawlerAuth, err := authSetupService.Setup(context.Background())
if err != nil { if err != nil {
logger.Error("Failed to setup auth for the dashboard previews crawler", "err", err) logger.Error("failed to setup auth for the dashboard previews crawler", "err", err)
return &dummyService{} canRunCrawler = false
} }
t := &thumbService{ t := &thumbService{
usageStatsService: usageStatsService, usageStatsService: usageStatsService,
renderingService: renderService, renderingService: renderService,
renderer: newSimpleCrawler(renderService, gl, thumbnailRepo), renderer: newSimpleCrawler(renderService, gl, thumbnailRepo, cfg, cfg.DashboardPreviews),
thumbnailRepo: thumbnailRepo, thumbnailRepo: thumbnailRepo,
store: store, store: store,
features: features, features: features,
lockService: lockService, lockService: lockService,
crawlLockServiceActionName: "dashboard-crawler", crawlLockServiceActionName: "dashboard-crawler",
log: logger, log: logger,
canRunCrawler: canRunCrawler,
settings: cfg.DashboardPreviews,
scheduleOptions: crawlerScheduleOptions{ scheduleOptions: crawlerScheduleOptions{
tickerInterval: time.Hour, tickerInterval: 5 * time.Minute,
crawlInterval: time.Hour * 12, crawlInterval: cfg.DashboardPreviews.SchedulerInterval,
maxCrawlDuration: time.Hour, maxCrawlDuration: cfg.DashboardPreviews.MaxCrawlDuration,
crawlerMode: CrawlerModeThumbs, crawlerMode: CrawlerModeThumbs,
thumbnailKind: models.ThumbnailKindDefault, thumbnailKind: models.ThumbnailKindDefault,
themes: []models.Theme{models.ThemeDark, models.ThemeLight}, themes: []models.Theme{models.ThemeDark, models.ThemeLight},
@ -401,6 +407,10 @@ func (hs *thumbService) getDashboardId(c *models.ReqContext, uid string) (int64,
} }
func (hs *thumbService) runOnDemandCrawl(parentCtx context.Context, theme models.Theme, mode CrawlerMode, kind models.ThumbnailKind, authOpts rendering.AuthOpts) { func (hs *thumbService) runOnDemandCrawl(parentCtx context.Context, theme models.Theme, mode CrawlerMode, kind models.ThumbnailKind, authOpts rendering.AuthOpts) {
if !hs.canRunCrawler {
return
}
crawlerCtx, cancel := context.WithTimeout(parentCtx, hs.scheduleOptions.maxCrawlDuration) crawlerCtx, cancel := context.WithTimeout(parentCtx, hs.scheduleOptions.maxCrawlDuration)
defer cancel() defer cancel()
@ -435,7 +445,7 @@ func (hs *thumbService) runScheduledCrawl(parentCtx context.Context) {
} }
func (hs *thumbService) Run(ctx context.Context) error { func (hs *thumbService) Run(ctx context.Context) error {
if !hs.features.IsEnabled(featuremgmt.FlagDashboardPreviewsScheduler) { if !hs.canRunCrawler {
return nil return nil
} }

View File

@ -439,6 +439,8 @@ type Cfg struct {
// Query history // Query history
QueryHistoryEnabled bool QueryHistoryEnabled bool
DashboardPreviews DashboardPreviewsSettings
} }
type CommandLineArgs struct { type CommandLineArgs struct {
@ -1001,6 +1003,8 @@ func (cfg *Cfg) Load(args CommandLineArgs) error {
cfg.readDataSourcesSettings() cfg.readDataSourcesSettings()
cfg.DashboardPreviews = readDashboardPreviewsSettings(iniFile)
if VerifyEmailEnabled && !cfg.Smtp.Enabled { if VerifyEmailEnabled && !cfg.Smtp.Enabled {
cfg.Logger.Warn("require_email_validation is enabled but smtp is disabled") cfg.Logger.Warn("require_email_validation is enabled but smtp is disabled")
} }

View File

@ -0,0 +1,31 @@
package setting
import (
"time"
"gopkg.in/ini.v1"
)
type DashboardPreviewsSettings struct {
SchedulerInterval time.Duration
MaxCrawlDuration time.Duration
RenderingTimeout time.Duration
CrawlThreadCount uint32
}
func readDashboardPreviewsSettings(iniFile *ini.File) DashboardPreviewsSettings {
maxThreadCount := uint32(20)
s := DashboardPreviewsSettings{}
previewsCrawlerSection := iniFile.Section("dashboard_previews.crawler")
s.CrawlThreadCount = uint32(previewsCrawlerSection.Key("thread_count").MustUint(6))
if s.CrawlThreadCount > maxThreadCount {
s.CrawlThreadCount = maxThreadCount
}
s.SchedulerInterval = previewsCrawlerSection.Key("scheduler_interval").MustDuration(12 * time.Hour)
s.MaxCrawlDuration = previewsCrawlerSection.Key("max_crawl_duration").MustDuration(1 * time.Hour)
s.RenderingTimeout = previewsCrawlerSection.Key("rendering_timeout").MustDuration(20 * time.Second)
return s
}

View File

@ -6,7 +6,7 @@ import { PREVIEWS_LOCAL_STORAGE_KEY } from '../constants';
export const useShowDashboardPreviews = () => { export const useShowDashboardPreviews = () => {
const previewFeatureEnabled = Boolean(config.featureToggles.dashboardPreviews); const previewFeatureEnabled = Boolean(config.featureToggles.dashboardPreviews);
const [showPreviews, setShowPreviews] = useLocalStorage<boolean>(PREVIEWS_LOCAL_STORAGE_KEY, previewFeatureEnabled); const [showPreviews, setShowPreviews] = useLocalStorage<boolean>(PREVIEWS_LOCAL_STORAGE_KEY, false);
return { showPreviews: Boolean(showPreviews && previewFeatureEnabled), previewFeatureEnabled, setShowPreviews }; return { showPreviews: Boolean(showPreviews && previewFeatureEnabled), previewFeatureEnabled, setShowPreviews };
}; };