Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(notifier,api): add plot configuration #1058

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/moira-alert/moira"
"github.com/moira-alert/moira/plotting"
)

// WebContact is container for web ui contact validation.
Expand Down Expand Up @@ -38,6 +39,7 @@ type Config struct {
MetricsTTL map[moira.ClusterKey]time.Duration
Flags FeatureFlags
Authorization Authorization
PlotCfg plotting.PlotConfig
}

// WebConfig is container for web ui configuration parameters.
Expand Down
2 changes: 1 addition & 1 deletion api/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func NewHandler(
router.Route("/user", user)
router.With(moiramiddle.Triggers(
apiConfig.MetricsTTL,
)).Route("/trigger", triggers(metricSourceProvider, searchIndex))
)).Route("/trigger", triggers(metricSourceProvider, searchIndex, apiConfig.PlotCfg))
router.Route("/tag", tag)
router.Route("/pattern", pattern)
router.Route("/event", event)
Expand Down
31 changes: 17 additions & 14 deletions api/handler/trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,25 @@ import (
"github.com/moira-alert/moira/api/controller"
"github.com/moira-alert/moira/api/dto"
"github.com/moira-alert/moira/api/middleware"
"github.com/moira-alert/moira/plotting"
)

func trigger(router chi.Router) {
router.Use(middleware.TriggerContext)
router.Put("/", updateTrigger)
router.With(middleware.TriggerContext, middleware.Populate(false)).Get("/", getTrigger)
router.Delete("/", removeTrigger)
router.Get("/state", getTriggerState)
router.Route("/throttling", func(router chi.Router) {
router.Get("/", getTriggerThrottling)
router.Delete("/", deleteThrottling)
})
router.Route("/metrics", triggerMetrics)
router.Put("/setMaintenance", setTriggerMaintenance)
router.With(middleware.DateRange("-1hour", "now")).With(middleware.TargetName("t1")).Get("/render", renderTrigger)
router.Get("/dump", triggerDump)
func trigger(plotCfg plotting.PlotConfig) func(chi.Router) {
return func(router chi.Router) {
router.Use(middleware.TriggerContext)
router.Put("/", updateTrigger)
router.With(middleware.TriggerContext, middleware.Populate(false)).Get("/", getTrigger)
router.Delete("/", removeTrigger)
router.Get("/state", getTriggerState)
router.Route("/throttling", func(router chi.Router) {
router.Get("/", getTriggerThrottling)
router.Delete("/", deleteThrottling)
})
router.Route("/metrics", triggerMetrics)
router.Put("/setMaintenance", setTriggerMaintenance)
router.With(middleware.DateRange("-1hour", "now")).With(middleware.TargetName("t1")).Get("/render", renderTrigger(plotCfg))
router.Get("/dump", triggerDump)
}
}

// nolint: gofmt,goimports
Expand Down
62 changes: 33 additions & 29 deletions api/handler/trigger_render.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,36 +36,40 @@ import (
// @failure 404 {object} api.ErrorNotFoundExample "Resource not found"
// @failure 500 {object} api.ErrorInternalServerExample "Internal server error"
// @router /trigger/{triggerID}/render [get]
func renderTrigger(writer http.ResponseWriter, request *http.Request) {
sourceProvider, targetName, from, to, triggerID, fetchRealtimeData, err := getEvaluationParameters(request)
if err != nil {
render.Render(writer, request, api.ErrorInvalidRequest(err)) //nolint
return
}
metricsData, trigger, err := evaluateTargetMetrics(sourceProvider, from, to, triggerID, fetchRealtimeData)
if err != nil {
if trigger == nil {
render.Render(writer, request, api.ErrorNotFound(fmt.Sprintf("trigger with ID = '%s' does not exists", triggerID))) //nolint
} else {
render.Render(writer, request, api.ErrorInternalServer(err)) //nolint
func renderTrigger(plotCfg plotting.PlotConfig) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
sourceProvider, targetName, from, to, triggerID, fetchRealtimeData, err := getEvaluationParameters(r)
if err != nil {
render.Render(w, r, api.ErrorInvalidRequest(err)) //nolint
return
}
return
}

targetMetrics, ok := metricsData[targetName]
if !ok {
render.Render(writer, request, api.ErrorNotFound(fmt.Sprintf("Cannot find target %s", targetName))) //nolint
}
metricsData, trigger, err := evaluateTargetMetrics(sourceProvider, from, to, triggerID, fetchRealtimeData)
if err != nil {
if trigger == nil {
render.Render(w, r, api.ErrorNotFound(fmt.Sprintf("trigger with ID = '%s' does not exists", triggerID))) //nolint
} else {
render.Render(w, r, api.ErrorInternalServer(err)) //nolint
}
return
}

renderable, err := buildRenderable(request, trigger, targetMetrics, targetName)
if err != nil {
render.Render(writer, request, api.ErrorInternalServer(err)) //nolint
return
}
writer.Header().Set("Content-Type", "image/png")
err = renderable.Render(chart.PNG, writer)
if err != nil {
render.Render(writer, request, api.ErrorInternalServer(fmt.Errorf("can not render plot %s", err.Error()))) //nolint
targetMetrics, ok := metricsData[targetName]
if !ok {
render.Render(w, r, api.ErrorNotFound(fmt.Sprintf("Cannot find target %s", targetName))) //nolint
}

renderable, err := buildRenderable(plotCfg, r, trigger, targetMetrics, targetName)
if err != nil {
render.Render(w, r, api.ErrorInternalServer(err)) //nolint
return
}

w.Header().Set("Content-Type", "image/png")
err = renderable.Render(chart.PNG, w)
if err != nil {
render.Render(w, r, api.ErrorInternalServer(fmt.Errorf("can not render plot %w", err))) //nolint
}
}
}

Expand Down Expand Up @@ -113,7 +117,7 @@ func evaluateTargetMetrics(metricSourceProvider *metricSource.SourceProvider, fr
return tts, trigger, err
}

func buildRenderable(request *http.Request, trigger *moira.Trigger, metricsData []metricSource.MetricData, targetName string) (*chart.Chart, error) {
func buildRenderable(plotCfg plotting.PlotConfig, request *http.Request, trigger *moira.Trigger, metricsData []metricSource.MetricData, targetName string) (*chart.Chart, error) {
urlValues, err := url.ParseQuery(request.URL.RawQuery)
if err != nil {
return nil, fmt.Errorf("failed to parse query string: %w", err)
Expand All @@ -126,7 +130,7 @@ func buildRenderable(request *http.Request, trigger *moira.Trigger, metricsData
}

plotTheme := urlValues.Get("theme")
plotTemplate, err := plotting.GetPlotTemplate(plotTheme, location)
plotTemplate, err := plotting.GetPlotTemplate(plotCfg, plotTheme, location)
if err != nil {
return nil, fmt.Errorf("can not initialize plot theme %s", err.Error())
}
Expand Down
15 changes: 11 additions & 4 deletions api/handler/trigger_render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
metricSource "github.com/moira-alert/moira/metric_source"
mock_metric_source "github.com/moira-alert/moira/mock/metric_source"
mock_moira_alert "github.com/moira-alert/moira/mock/moira-alert"
"github.com/moira-alert/moira/plotting"
. "github.com/smartystreets/goconvey/convey"
"go.uber.org/mock/gomock"
)
Expand All @@ -27,6 +28,8 @@ func TestRenderTrigger(t *testing.T) {
responseWriter := httptest.NewRecorder()
mockDb := mock_moira_alert.NewMockDatabase(mockCtrl)

plotConfig := plotting.PlotConfig{}

Convey("with the wrong realtime parameter", func() {
testRequest := httptest.NewRequest(http.MethodGet, "/trigger/triggerID-0000000000001/render?realtime=test", nil)
testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), "triggerID", "triggerID-0000000000001"))
Expand All @@ -35,7 +38,8 @@ func TestRenderTrigger(t *testing.T) {
testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), "from", "-1hour"))
testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), "to", "now"))

renderTrigger(responseWriter, testRequest)
render := renderTrigger(plotConfig)
render(responseWriter, testRequest)

response := responseWriter.Result()
defer response.Body.Close()
Expand Down Expand Up @@ -68,7 +72,8 @@ func TestRenderTrigger(t *testing.T) {
testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), "from", "-1hour"))
testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), "to", "now"))

renderTrigger(responseWriter, testRequest)
render := renderTrigger(plotConfig)
render(responseWriter, testRequest)

response := responseWriter.Result()
defer response.Body.Close()
Expand Down Expand Up @@ -101,7 +106,8 @@ func TestRenderTrigger(t *testing.T) {
testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), "from", "-1hour"))
testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), "to", "now"))

renderTrigger(responseWriter, testRequest)
render := renderTrigger(plotConfig)
render(responseWriter, testRequest)

response := responseWriter.Result()
defer response.Body.Close()
Expand All @@ -122,7 +128,8 @@ func TestRenderTrigger(t *testing.T) {
testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), "from", "-1hour"))
testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), "to", "now"))

renderTrigger(responseWriter, testRequest)
render := renderTrigger(plotConfig)
render(responseWriter, testRequest)

response := responseWriter.Result()
defer response.Body.Close()
Expand Down
5 changes: 3 additions & 2 deletions api/handler/triggers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
metricSource "github.com/moira-alert/moira/metric_source"
"github.com/moira-alert/moira/metric_source/local"
"github.com/moira-alert/moira/metric_source/remote"
"github.com/moira-alert/moira/plotting"

"github.com/moira-alert/moira/api"
"github.com/moira-alert/moira/api/controller"
Expand All @@ -23,7 +24,7 @@ import (
"github.com/moira-alert/moira/expression"
)

func triggers(metricSourceProvider *metricSource.SourceProvider, searcher moira.Searcher) func(chi.Router) {
func triggers(metricSourceProvider *metricSource.SourceProvider, searcher moira.Searcher, plotCfg plotting.PlotConfig) func(chi.Router) {
return func(router chi.Router) {
router.Use(middleware.MetricSourceProvider(metricSourceProvider))
router.Use(middleware.SearchIndexContext(searcher))
Expand All @@ -33,7 +34,7 @@ func triggers(metricSourceProvider *metricSource.SourceProvider, searcher moira.

router.Put("/", createTrigger)
router.Put("/check", triggerCheck)
router.Route("/{triggerId}", trigger)
router.Route("/{triggerId}", trigger(plotCfg))
router.With(middleware.Paginate(0, 10)).With(middleware.Pager(false, "")).Get("/search", searchTriggers)
router.With(middleware.Pager(false, "")).Delete("/search/pager", deletePager)
// ToDo: DEPRECATED method. Remove in Moira 2.6
Expand Down
10 changes: 10 additions & 0 deletions cmd/api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ type apiConfig struct {
EnableCORS bool `yaml:"enable_cors"`
// Authorization contains authorization configuration.
Authorization authorization `yaml:"authorization"`
// PlotCfg sets the configuration for the plots, such as size.
PlotCfg cmd.PlotConfig `yaml:"plot"`
}

type authorization struct {
Expand Down Expand Up @@ -116,6 +118,7 @@ func (config *apiConfig) getSettings(
MetricsTTL: metricsTTL,
Flags: flags,
Authorization: config.Authorization.toApiConfig(webConfig),
PlotCfg: config.PlotCfg.GetSettings(),
}
}

Expand Down Expand Up @@ -217,6 +220,13 @@ func getDefault() config {
API: apiConfig{
Listen: ":8081",
EnableCORS: false,
PlotCfg: cmd.PlotConfig{
Width: 800,
Height: 400,
YAxisSecondaryCfg: cmd.YAxisSecondaryConfig{
EnablePrettyTicks: false,
},
},
},
Web: webConfig{
RemoteAllowed: false,
Expand Down
17 changes: 17 additions & 0 deletions cmd/api/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ import (
. "github.com/smartystreets/goconvey/convey"
)

var defaultPlotCfg = cmd.PlotConfig{
Width: 800,
Height: 400,
YAxisSecondaryCfg: cmd.YAxisSecondaryConfig{
EnablePrettyTicks: false,
},
}

func Test_apiConfig_getSettings(t *testing.T) {
Convey("Settings successfully filled", t, func() {
metricTTLs := map[moira.ClusterKey]time.Duration{
Expand All @@ -30,6 +38,7 @@ func Test_apiConfig_getSettings(t *testing.T) {
apiConf := apiConfig{
Listen: "0000",
EnableCORS: true,
PlotCfg: defaultPlotCfg,
}

expectedResult := &api.Config{
Expand All @@ -43,6 +52,7 @@ func Test_apiConfig_getSettings(t *testing.T) {
"test": {},
},
},
PlotCfg: defaultPlotCfg.GetSettings(),
}

result := apiConf.getSettings(metricTTLs, api.FeatureFlags{IsReadonlyEnabled: true}, webConfig)
Expand Down Expand Up @@ -88,6 +98,13 @@ func Test_webConfig_getDefault(t *testing.T) {
API: apiConfig{
Listen: ":8081",
EnableCORS: false,
PlotCfg: cmd.PlotConfig{
Width: 800,
Height: 400,
YAxisSecondaryCfg: cmd.YAxisSecondaryConfig{
EnablePrettyTicks: false,
},
},
},
Web: webConfig{
RemoteAllowed: false,
Expand Down
24 changes: 24 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/moira-alert/moira"
"github.com/moira-alert/moira/metrics"
"github.com/moira-alert/moira/plotting"

"github.com/moira-alert/moira/image_store/s3"
prometheusRemoteSource "github.com/moira-alert/moira/metric_source/prometheus"
Expand Down Expand Up @@ -120,6 +121,29 @@ func (notificationConfig *NotificationConfig) GetSettings() redis.NotificationCo
}
}

// PlotConfig sets the configuration for the plots, such as size.
type PlotConfig struct {
Width int `yaml:"width"`
Height int `yaml:"height"`
YAxisSecondaryCfg YAxisSecondaryConfig `yaml:"y_axis_secondary"`
}

// YAxisSecondaryConfig defines the setting for the secondary y-axis.
type YAxisSecondaryConfig struct {
EnablePrettyTicks bool `yaml:"enable_pretty_ticks"`
}

// GetSettings returns plot configuration.
func (pcfg PlotConfig) GetSettings() plotting.PlotConfig {
return plotting.PlotConfig{
Width: pcfg.Width,
Height: pcfg.Height,
YAxisSecondaryCfg: plotting.YAxisSecondaryConfig{
EnablePrettyTicks: pcfg.YAxisSecondaryCfg.EnablePrettyTicks,
},
}
}

// GraphiteConfig is graphite metrics config structure that initialises at the start of moira.
type GraphiteConfig struct {
// If true, graphite sender will be enabled.
Expand Down
10 changes: 10 additions & 0 deletions cmd/notifier/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type notifierConfig struct {
MaxFailAttemptToSendAvailable int `yaml:"max_fail_attempt_to_send_available"`
// Specify log level by entities
SetLogLevel setLogLevelConfig `yaml:"set_log_level"`
// PlotCfg sets the configuration for the plots, such as size
PlotCfg cmd.PlotConfig `yaml:"plot"`
}

type selfStateConfig struct {
Expand Down Expand Up @@ -117,6 +119,13 @@ func getDefault() config {
Timezone: "UTC",
ReadBatchSize: int(notifier.NotificationsLimitUnlimited),
MaxFailAttemptToSendAvailable: 3,
PlotCfg: cmd.PlotConfig{
Width: 800,
Height: 400,
YAxisSecondaryCfg: cmd.YAxisSecondaryConfig{
EnablePrettyTicks: false,
},
},
},
Telemetry: cmd.TelemetryConfig{
Listen: ":8093",
Expand Down Expand Up @@ -203,6 +212,7 @@ func (config *notifierConfig) getSettings(logger moira.Logger) notifier.Config {
MaxFailAttemptToSendAvailable: config.MaxFailAttemptToSendAvailable,
LogContactsToLevel: contacts,
LogSubscriptionsToLevel: subscriptions,
PlotCfg: config.PlotCfg.GetSettings(),
}
}

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/prometheus/client_golang v1.14.0
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
github.com/rs/cors v1.9.0
github.com/rs/cors v1.11.0
github.com/rs/zerolog v1.29.0
github.com/russross/blackfriday/v2 v2.1.0
github.com/slack-go/slack v0.12.1
Expand Down Expand Up @@ -158,7 +158,7 @@ require (
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
golang.org/x/image v0.13.0 // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.17.0 // indirect
golang.org/x/sys v0.21.0 // indirect
Expand Down
Loading
Loading