Skip to content

Commit

Permalink
feat: Improve advanced mode in checker
Browse files Browse the repository at this point in the history
Datatype that stores metrics for check changed to multiple datatypes
that handle different operations on data. Fetcher interface changed to
return new implemented datatypes

Relates #428
  • Loading branch information
litleleprikon committed Dec 20, 2019
1 parent eba6e9a commit 8c277c1
Show file tree
Hide file tree
Showing 128 changed files with 2,682 additions and 1,864 deletions.
3 changes: 1 addition & 2 deletions api/controller/contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,10 @@ func RemoveContact(database moira.Database, contactID string, userLogin string)

// SendTestContactNotification push test notification to verify the correct contact settings
func SendTestContactNotification(dataBase moira.Database, contactID string) *api.ErrorResponse {
var value float64 = 1
eventData := &moira.NotificationEvent{
ContactID: contactID,
Metric: "Test.metric.value",
Value: &value,
Values: map[string]float64{"t1": 1},
OldState: moira.StateTEST,
State: moira.StateTEST,
Timestamp: date.DateParamToEpoch("now", "", time.Now().Add(-24*time.Hour).Unix(), time.UTC),
Expand Down
3 changes: 1 addition & 2 deletions api/controller/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,10 @@ func RemoveSubscription(database moira.Database, subscriptionID string) *api.Err

// SendTestNotification push test notification to verify the correct notification settings
func SendTestNotification(database moira.Database, subscriptionID string) *api.ErrorResponse {
var value float64 = 1
eventData := &moira.NotificationEvent{
SubscriptionID: &subscriptionID,
Metric: "Test.metric.value",
Value: &value,
Values: map[string]float64{"t1": 1},
OldState: moira.StateTEST,
State: moira.StateTEST,
Timestamp: date.DateParamToEpoch("now", "", time.Now().Add(-24*time.Hour).Unix(), time.UTC),
Expand Down
55 changes: 22 additions & 33 deletions api/controller/trigger_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,30 @@ import (
"github.com/moira-alert/moira/api"
"github.com/moira-alert/moira/api/dto"
"github.com/moira-alert/moira/database"
"github.com/moira-alert/moira/metric_source"
metricSource "github.com/moira-alert/moira/metric_source"
)

// GetTriggerEvaluationResult evaluates every target in trigger and returns
// result, separated on main and additional targets metrics
func GetTriggerEvaluationResult(dataBase moira.Database, metricSourceProvider *metricSource.SourceProvider, from, to int64, triggerID string, fetchRealtimeData bool) (*metricSource.TriggerMetricsData, *moira.Trigger, error) {
func GetTriggerEvaluationResult(dataBase moira.Database, metricSourceProvider *metricSource.SourceProvider, from, to int64, triggerID string, fetchRealtimeData bool) (metricSource.FetchedMetrics, *moira.Trigger, error) {
trigger, err := dataBase.GetTrigger(triggerID)
if err != nil {
return nil, nil, err
}
triggerMetrics := metricSource.NewTriggerMetricsData()
triggerMetrics := metricSource.NewFetchedMetricsWithCapacity(0)
metricsSource, err := metricSourceProvider.GetTriggerMetricSource(&trigger)
if err != nil {
return nil, &trigger, err
}
for i, tar := range trigger.Targets {
fetchResult, err := metricsSource.Fetch(tar, from, to, fetchRealtimeData)
for i, target := range trigger.Targets {
i++ // Increase counter to have trigger names start from t1
fetchResult, err := metricsSource.Fetch(target, from, to, fetchRealtimeData)
if err != nil {
return nil, &trigger, err
}
metricData := fetchResult.GetMetricsData()
if i == 0 {
triggerMetrics.Main = metricData
} else {
triggerMetrics.Additional = append(triggerMetrics.Additional, metricData...)
}

triggerMetrics.AddMetrics(i, metricData)
}
return triggerMetrics, &trigger, nil
}
Expand All @@ -57,31 +55,22 @@ func GetTriggerMetrics(dataBase moira.Database, metricSourceProvider *metricSour
}
return nil, api.ErrorInternalServer(err)
}
triggerMetrics := dto.TriggerMetrics{
Main: make(map[string][]*moira.MetricValue),
Additional: make(map[string][]*moira.MetricValue),
}
for _, timeSeries := range tts.Main {
values := make([]*moira.MetricValue, 0)
for i := 0; i < len(timeSeries.Values); i++ {
timestamp := timeSeries.StartTime + int64(i)*timeSeries.StepTime
value := timeSeries.GetTimestampValue(timestamp)
if moira.IsValidFloat64(value) {
values = append(values, &moira.MetricValue{Value: value, Timestamp: timestamp})
}
}
triggerMetrics.Main[timeSeries.Name] = values
}
for _, timeSeries := range tts.Additional {
values := make([]*moira.MetricValue, 0)
for i := 0; i < len(timeSeries.Values); i++ {
timestamp := timeSeries.StartTime + int64(i)*timeSeries.StepTime
value := timeSeries.GetTimestampValue(timestamp)
if moira.IsValidFloat64(value) {
values = append(values, &moira.MetricValue{Value: value, Timestamp: timestamp})
triggerMetrics := make(dto.TriggerMetrics)

for targetName, target := range tts {
targetMetrics := make(map[string][]moira.MetricValue)
for _, timeSeries := range target {
values := make([]moira.MetricValue, 0)
for i, l := 0, len(timeSeries.Values); i < l; i++ {
timestamp := timeSeries.StartTime + int64(i)*timeSeries.StepTime
value := timeSeries.GetTimestampValue(timestamp)
if moira.IsValidFloat64(value) {
values = append(values, moira.MetricValue{Value: value, Timestamp: timestamp})
}
}
targetMetrics[timeSeries.Name] = values
}
triggerMetrics.Additional[timeSeries.Name] = values
triggerMetrics[targetName] = targetMetrics
}
return &triggerMetrics, nil
}
Expand Down
10 changes: 5 additions & 5 deletions api/controller/trigger_metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import (

"github.com/gofrs/uuid"
"github.com/golang/mock/gomock"
"github.com/moira-alert/moira/metric_source"
metricSource "github.com/moira-alert/moira/metric_source"
"github.com/moira-alert/moira/metric_source/remote"
"github.com/moira-alert/moira/mock/metric_source"
mock_metric_source "github.com/moira-alert/moira/mock/metric_source"
. "github.com/smartystreets/goconvey/convey"

"github.com/moira-alert/moira"
"github.com/moira-alert/moira/api"
"github.com/moira-alert/moira/api/dto"
"github.com/moira-alert/moira/database"
"github.com/moira-alert/moira/mock/moira-alert"
mock_moira_alert "github.com/moira-alert/moira/mock/moira-alert"
)

func TestDeleteTriggerMetric(t *testing.T) {
Expand Down Expand Up @@ -297,10 +297,10 @@ func TestGetTriggerMetrics(t *testing.T) {
dataBase.EXPECT().GetTrigger(triggerID).Return(moira.Trigger{ID: triggerID, Targets: []string{pattern}}, nil)
localSource.EXPECT().IsConfigured().Return(true, nil)
localSource.EXPECT().Fetch(pattern, from, until, false).Return(fetchResult, nil)
fetchResult.EXPECT().GetMetricsData().Return([]*metricSource.MetricData{metricSource.MakeMetricData(metric, []float64{0, 1, 2, 3, 4}, retention, from)})
fetchResult.EXPECT().GetMetricsData().Return([]metricSource.MetricData{*metricSource.MakeMetricData(metric, []float64{0, 1, 2, 3, 4}, retention, from)})
triggerMetrics, err := GetTriggerMetrics(dataBase, sourceProvider, from, until, triggerID)
So(err, ShouldBeNil)
So(*triggerMetrics, ShouldResemble, dto.TriggerMetrics{Main: map[string][]*moira.MetricValue{metric: {{Value: 0, Timestamp: 17}, {Value: 1, Timestamp: 27}, {Value: 2, Timestamp: 37}, {Value: 3, Timestamp: 47}, {Value: 4, Timestamp: 57}}}, Additional: make(map[string][]*moira.MetricValue)})
So(*triggerMetrics, ShouldResemble, dto.TriggerMetrics{"t1": map[string][]moira.MetricValue{metric: {{Value: 0, Timestamp: 17}, {Value: 1, Timestamp: 27}, {Value: 2, Timestamp: 37}, {Value: 3, Timestamp: 47}, {Value: 4, Timestamp: 57}}}})
})

Convey("GetTrigger error", t, func() {
Expand Down
7 changes: 2 additions & 5 deletions api/dto/triggers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/moira-alert/moira/api"
"github.com/moira-alert/moira/api/middleware"
"github.com/moira-alert/moira/expression"
"github.com/moira-alert/moira/metric_source"
metricSource "github.com/moira-alert/moira/metric_source"
)

type TriggersList struct {
Expand Down Expand Up @@ -305,10 +305,7 @@ func (*SaveTriggerResponse) Render(w http.ResponseWriter, r *http.Request) error
return nil
}

type TriggerMetrics struct {
Main map[string][]*moira.MetricValue `json:"main"`
Additional map[string][]*moira.MetricValue `json:"additional,omitempty"`
}
type TriggerMetrics map[string]map[string][]moira.MetricValue

func (*TriggerMetrics) Render(w http.ResponseWriter, r *http.Request) error {
return nil
Expand Down
6 changes: 3 additions & 3 deletions api/dto/triggers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
"github.com/moira-alert/moira"
"github.com/moira-alert/moira/api"
"github.com/moira-alert/moira/api/middleware"
"github.com/moira-alert/moira/metric_source"
"github.com/moira-alert/moira/mock/metric_source"
metricSource "github.com/moira-alert/moira/metric_source"
mock_metric_source "github.com/moira-alert/moira/mock/metric_source"

"github.com/golang/mock/gomock"
. "github.com/smartystreets/goconvey/convey"
Expand All @@ -30,7 +30,7 @@ func TestExpressionModeMultipleTargetsWarnValue(t *testing.T) {
localSource.EXPECT().IsConfigured().Return(true, nil).AnyTimes()
localSource.EXPECT().Fetch(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fetchResult, nil).AnyTimes()
fetchResult.EXPECT().GetPatterns().Return(make([]string, 0), nil).AnyTimes()
fetchResult.EXPECT().GetMetricsData().Return([]*metricSource.MetricData{metricSource.MakeMetricData("", []float64{}, 0, 0)}).AnyTimes()
fetchResult.EXPECT().GetMetricsData().Return([]metricSource.MetricData{*metricSource.MakeMetricData("", []float64{}, 0, 0)}).AnyTimes()

request, _ := http.NewRequest("PUT", "/api/trigger", nil)
request.Header.Set("Content-Type", "application/json")
Expand Down
2 changes: 1 addition & 1 deletion api/handler/trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func trigger(router chi.Router) {
})
router.Route("/metrics", triggerMetrics)
router.Put("/setMaintenance", setTriggerMaintenance)
router.With(middleware.DateRange("-1hour", "now")).Get("/render", renderTrigger)
router.With(middleware.DateRange("-1hour", "now")).With(middleware.TargetIndex(0)).Get("/render", renderTrigger)
}

func updateTrigger(writer http.ResponseWriter, request *http.Request) {
Expand Down
36 changes: 21 additions & 15 deletions api/handler/trigger_render.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,28 @@ import (
"github.com/moira-alert/moira/api"
"github.com/moira-alert/moira/api/controller"
"github.com/moira-alert/moira/api/middleware"
"github.com/moira-alert/moira/metric_source"
metricSource "github.com/moira-alert/moira/metric_source"
"github.com/moira-alert/moira/plotting"
)

func renderTrigger(writer http.ResponseWriter, request *http.Request) {
sourceProvider, from, to, triggerID, fetchRealtimeData, err := getEvaluationParameters(request)
sourceProvider, targetName, from, to, triggerID, fetchRealtimeData, err := getEvaluationParameters(request)
if err != nil {
render.Render(writer, request, api.ErrorInvalidRequest(err))
return
}
metricsData, trigger, err := evaluateTriggerMetrics(sourceProvider, from, to, triggerID, fetchRealtimeData)
metricsData, trigger, err := evaluateTargetMetrics(sourceProvider, from, to, triggerID, fetchRealtimeData)
if err != nil {
render.Render(writer, request, api.ErrorInternalServer(err))
return
}
renderable, err := buildRenderable(request, trigger, metricsData)

targetMetrics, ok := metricsData[targetName]
if !ok {
render.Render(writer, request, api.ErrorNotFound(fmt.Sprintf("Cannot find target %s", targetName)))
}

renderable, err := buildRenderable(request, trigger, targetMetrics, targetName)
if err != nil {
render.Render(writer, request, api.ErrorInternalServer(err))
return
Expand All @@ -40,43 +46,43 @@ func renderTrigger(writer http.ResponseWriter, request *http.Request) {
}
}

func getEvaluationParameters(request *http.Request) (sourceProvider *metricSource.SourceProvider, from int64, to int64, triggerID string, fetchRealtimeData bool, err error) {
func getEvaluationParameters(request *http.Request) (sourceProvider *metricSource.SourceProvider, targetName string, from int64, to int64, triggerID string, fetchRealtimeData bool, err error) {
sourceProvider = middleware.GetTriggerTargetsSourceProvider(request)
targetName = middleware.GetTargetName(request)
triggerID = middleware.GetTriggerID(request)
fromStr := middleware.GetFromStr(request)
toStr := middleware.GetToStr(request)
from = date.DateParamToEpoch(fromStr, "UTC", 0, time.UTC)

if from == 0 {
return sourceProvider, 0, 0, "", false, fmt.Errorf("can not parse from: %s", fromStr)
return sourceProvider, "", 0, 0, "", false, fmt.Errorf("can not parse from: %s", fromStr)
}
from -= from % 60
to = date.DateParamToEpoch(toStr, "UTC", 0, time.UTC)
if to == 0 {
return sourceProvider, 0, 0, "", false, fmt.Errorf("can not parse to: %s", fromStr)
return sourceProvider, "", 0, 0, "", false, fmt.Errorf("can not parse to: %s", fromStr)
}
realtime := request.URL.Query().Get("realtime")
if realtime == "" {
return
}
fetchRealtimeData, err = strconv.ParseBool(realtime)
if err != nil {
return sourceProvider, 0, 0, "", false, fmt.Errorf("invalid realtime param: %s", err.Error())
return sourceProvider, "", 0, 0, "", false, fmt.Errorf("invalid realtime param: %s", err.Error())
}
return
}

func evaluateTriggerMetrics(metricSourceProvider *metricSource.SourceProvider, from, to int64, triggerID string, fetchRealtimeData bool) ([]*metricSource.MetricData, *moira.Trigger, error) {
func evaluateTargetMetrics(metricSourceProvider *metricSource.SourceProvider, from, to int64, triggerID string, fetchRealtimeData bool) (metricSource.FetchedMetrics, *moira.Trigger, error) {
tts, trigger, err := controller.GetTriggerEvaluationResult(database, metricSourceProvider, from, to, triggerID, fetchRealtimeData)
if err != nil {
return nil, trigger, err
}
var metricsData = make([]*metricSource.MetricData, 0, len(tts.Main)+len(tts.Additional))
metricsData = append(metricsData, tts.Main...)
metricsData = append(metricsData, tts.Additional...)
return metricsData, trigger, err

return tts, trigger, err
}

func buildRenderable(request *http.Request, trigger *moira.Trigger, metricsData []*metricSource.MetricData) (*chart.Chart, error) {
func buildRenderable(request *http.Request, trigger *moira.Trigger, metricsData []metricSource.MetricData, targetName string) (*chart.Chart, error) {
timezone := request.URL.Query().Get("timezone")
location, err := time.LoadLocation(timezone)
if err != nil {
Expand All @@ -87,7 +93,7 @@ func buildRenderable(request *http.Request, trigger *moira.Trigger, metricsData
if err != nil {
return nil, fmt.Errorf("can not initialize plot theme %s", err.Error())
}
renderable, err := plotTemplate.GetRenderable(trigger, metricsData)
renderable, err := plotTemplate.GetRenderable(targetName, trigger, metricsData)
if err != nil {
return nil, err
}
Expand Down
16 changes: 15 additions & 1 deletion api/middleware/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/go-chi/render"
"github.com/moira-alert/moira"
"github.com/moira-alert/moira/api"
"github.com/moira-alert/moira/metric_source"
metricSource "github.com/moira-alert/moira/metric_source"
)

// DatabaseContext sets to requests context configured database
Expand Down Expand Up @@ -143,3 +143,17 @@ func DateRange(defaultFrom, defaultTo string) func(next http.Handler) http.Handl
})
}
}

// TargetName is a function that gets target name value from query string and places it in context. If query does not have value sets given value.
func TargetName(defaultTargetName string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
targetName := request.URL.Query().Get("target")
if targetName == "" {
targetName = defaultTargetName
}
ctx := context.WithValue(request.Context(), targetNameKey, targetName)
next.ServeHTTP(writer, request.WithContext(ctx))
})
}
}
8 changes: 7 additions & 1 deletion api/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"net/http"

"github.com/moira-alert/moira"
"github.com/moira-alert/moira/metric_source"
metricSource "github.com/moira-alert/moira/metric_source"
)

// ContextKey used as key of api request context values
Expand All @@ -29,6 +29,7 @@ var (
loginKey ContextKey = "login"
timeSeriesNamesKey ContextKey = "timeSeriesNames"
metricSourceProvider ContextKey = "metricSourceProvider"
targetNameKey ContextKey = "target"
)

// GetDatabase gets moira.Database realization from request context
Expand Down Expand Up @@ -96,3 +97,8 @@ func GetTimeSeriesNames(request *http.Request) map[string]bool {
func GetTriggerTargetsSourceProvider(request *http.Request) *metricSource.SourceProvider {
return request.Context().Value(metricSourceProvider).(*metricSource.SourceProvider)
}

// GetTargetName gets target name
func GetTargetName(request *http.Request) string {
return request.Context().Value(targetNameKey).(string)
}
Loading

0 comments on commit 8c277c1

Please sign in to comment.