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

update templates #536

Merged
merged 1 commit into from
Oct 27, 2020
Merged
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
20 changes: 20 additions & 0 deletions api/dto/triggers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"strconv"
"time"

"github.com/moira-alert/moira/templating"

"github.com/moira-alert/moira"
"github.com/moira-alert/moira/api"
"github.com/moira-alert/moira/api/middleware"
Expand Down Expand Up @@ -174,10 +176,12 @@ func (trigger *Trigger) Bind(request *http.Request) error {
return api.ErrInvalidRequestContent{ValidationError: fmt.Errorf("pattern \"*\" is not allowed to use")}
}
}

middleware.SetTimeSeriesNames(request, metricsDataNames)
if _, err := triggerExpression.Evaluate(); err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -312,6 +316,22 @@ func (*Trigger) Render(w http.ResponseWriter, r *http.Request) error {
return nil
}

func (trigger *Trigger) PopulatedDescription(events moira.NotificationEvents) error {
if trigger.Desc == nil {
return nil
}

templatingEvents := moira.NotificationEventsToTemplatingEvents(events)
description, err := templating.Populate(trigger.Name, *trigger.Desc, templatingEvents)
if err != nil {
return fmt.Errorf("you have an error in your Go template: %v", err)
}

*trigger.Desc = description

return nil
}

type TriggerCheck struct {
*moira.CheckData
TriggerID string `json:"trigger_id"`
Expand Down
43 changes: 24 additions & 19 deletions api/handler/trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/go-chi/chi"
"github.com/go-chi/render"
"github.com/moira-alert/moira"
"github.com/moira-alert/moira/metric_source/local"
"github.com/moira-alert/moira/metric_source/remote"

Expand Down Expand Up @@ -54,16 +53,11 @@ func updateTrigger(writer http.ResponseWriter, request *http.Request) {
render.Render(writer, request, api.ErrorInternalServer(err)) //nolint
}

return
}

if trigger.Desc != nil {
triggerData := moira.TriggerData{Desc: *trigger.Desc, Name: trigger.Name}
if _, err := triggerData.GetPopulatedDescription(moira.NotificationEvents{}); err != nil {
render.Render(writer, request, api.ErrorRender( //nolint
fmt.Errorf("You have an error in your Go template: %v", err)))
return
if err := checkingTemplateFilling(request, *trigger); err != nil {
render.Render(writer, request, err) //nolint
}

return
}

timeSeriesNames := middleware.GetTimeSeriesNames(request)
Expand Down Expand Up @@ -92,28 +86,39 @@ func getTrigger(writer http.ResponseWriter, request *http.Request) {
if triggerID == "testlog" {
panic("Test for multi line logs")
}

trigger, err := controller.GetTrigger(database, triggerID)
if err != nil {
render.Render(writer, request, err) //nolint
return
}

if needToPopulate := middleware.GetPopulated(request); needToPopulate && trigger.Desc != nil {
triggerData := moira.TriggerData{Desc: *trigger.Desc, Name: trigger.Name}

eventsList, err := controller.GetTriggerEvents(database, triggerID, 0, 3)
if err != nil {
render.Render(writer, request, err) //nolint
}

*trigger.Desc, _ = triggerData.GetPopulatedDescription(eventsList.List)
if err := checkingTemplateFilling(request, *trigger); err != nil {
middleware.GetLoggerEntry(request).Warning(err)
}

if err := render.Render(writer, request, trigger); err != nil {
render.Render(writer, request, api.ErrorRender(err)) //nolint
}
}

func checkingTemplateFilling(request *http.Request, trigger dto.Trigger) *api.ErrorResponse {
if !middleware.GetPopulated(request) {
return nil
}

eventsList, err := controller.GetTriggerEvents(database, trigger.ID, 0, 3)
if err != nil {
return err
}

if err := trigger.PopulatedDescription(eventsList.List); err != nil {
return api.ErrorRender(err)
}

return nil
}

func getTriggerState(writer http.ResponseWriter, request *http.Request) {
triggerID := middleware.GetTriggerID(request)
triggerState, err := controller.GetTriggerLastCheck(database, triggerID)
Expand Down
9 changes: 9 additions & 0 deletions api/handler/triggers.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ func createTrigger(writer http.ResponseWriter, request *http.Request) {
}
return
}

if trigger.Desc != nil {
err := trigger.PopulatedDescription(moira.NotificationEvents{{}})
if err != nil {
render.Render(writer, request, api.ErrorRender(err)) //nolint
return
}
}

timeSeriesNames := middleware.GetTimeSeriesNames(request)
response, err := controller.CreateTrigger(database, &trigger.TriggerModel, timeSeriesNames)
if err != nil {
Expand Down
66 changes: 24 additions & 42 deletions datatypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package moira
import (
"bytes"
"fmt"
"html/template"
"math"
"sort"
"strconv"
"strings"
"time"

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

const (
Expand Down Expand Up @@ -106,21 +107,31 @@ func (event *NotificationEvent) CreateMessage(location *time.Location) string {
// NotificationEvents represents slice of NotificationEvent
type NotificationEvents []NotificationEvent

type templateData struct {
Trigger templateTrigger
Events []templateEvent
}
func (trigger *TriggerData) PopulatedDescription(events NotificationEvents) error {
description, err := templating.Populate(trigger.Name, trigger.Desc, NotificationEventsToTemplatingEvents(events))
if err != nil {
description = "Your description is using the wrong template. Since we were unable to populate your template with " +
"data, we return it so you can parse it.\n\n" + trigger.Desc
}

type templateEvent struct {
Metric string
MetricElements []string
Timestamp int64
Value *float64
State State
trigger.Desc = description

return err
}

type templateTrigger struct {
Name string `json:"name"`
func NotificationEventsToTemplatingEvents(events NotificationEvents) []templating.Event {
templatingEvents := make([]templating.Event, 0, len(events))
for _, event := range events {
templatingEvents = append(templatingEvents, templating.Event{
Metric: event.Metric,
MetricElements: strings.Split(event.Metric, "."),
Timestamp: event.Timestamp,
State: string(event.State),
Value: event.Value,
})
}

return templatingEvents
}

// TriggerData represents trigger object
Expand All @@ -135,35 +146,6 @@ type TriggerData struct {
Tags []string `json:"__notifier_trigger_tags"`
}

func (trigger TriggerData) GetPopulatedDescription(events NotificationEvents) (string, error) {
buffer := new(bytes.Buffer)
templateEvents := make([]templateEvent, 0, len(events))

for _, data := range events {
event := templateEvent{
Metric: data.Metric,
MetricElements: strings.Split(data.Metric, "."),
Timestamp: data.Timestamp,
State: data.State,
Value: data.Value,
}

templateEvents = append(templateEvents, event)
}

dataToExecute := templateData{
Trigger: templateTrigger{Name: trigger.Name},
Events: templateEvents,
}

triggerTemplate := template.Must(template.New("populate-description").Parse(trigger.Desc))
if err := triggerTemplate.Execute(buffer, dataToExecute); err != nil {
return "", err
}

return buffer.String(), nil
}

// GetTriggerURI gets frontUri and returns triggerUrl, returns empty string on selfcheck and test notifications
func (trigger TriggerData) GetTriggerURI(frontURI string) string {
if trigger.ID != "" {
Expand Down
42 changes: 0 additions & 42 deletions datatypes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,48 +246,6 @@ func TestTriggerData_GetTags(t *testing.T) {
})
}

func TestTriggerData_TemplateDescription(t *testing.T) {
Convey("Test templates", t, func() {
var trigger = TriggerData{Name: "TestName"}
trigger.Desc = "\n" +
"Trigger name: {{.Trigger.Name}}\n" +
"{{range $v := .Events }}\n" +
"Metric: {{$v.Metric}}\n" +
"MetricElements: {{$v.MetricElements}}\n" +
"Timestamp: {{$v.Timestamp}}\n" +
"Value: {{$v.Value}}\n" +
"State: {{$v.State}}\n" +
"{{end}}\n" +
"https://grafana.yourhost.com/some-dashboard{{ range $i, $v := .Events }}{{ if ne $i 0 }}&{{ else }}?{{ end }}var-host={{ $v.Metric }}{{ end }}\n"

var data = NotificationEvents{{Metric: "1"}, {Metric: "2"}}

Convey("Test nil data", func() {
expected, err := trigger.GetPopulatedDescription(nil)
So(err, ShouldBeNil)
So(`
Trigger name: TestName

https://grafana.yourhost.com/some-dashboard
`, ShouldResemble, expected)
})

Convey("Test data", func() {
expected, err := trigger.GetPopulatedDescription(data)
So(err, ShouldBeNil)
So("\nTrigger name: TestName\n\nMetric: 1\nMetricElements: [1]\nTimestamp: 0\nValue: <nil>\nState: \n\nMetric: 2\nMetricElements: [2]\nTimestamp: 0\nValue: <nil>\nState: \n\nhttps://grafana.yourhost.com/some-dashboard?var-host=1&var-host=2\n", ShouldResemble, expected)
})

Convey("Test description without templates", func() {
anotherText := "Another text"
trigger.Desc = anotherText
expected, err := trigger.GetPopulatedDescription(data)
So(err, ShouldBeNil)
So(anotherText, ShouldEqual, expected)
})
})
}

func TestScheduledNotification_GetKey(t *testing.T) {
Convey("Get key", t, func() {
notification := ScheduledNotification{
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@ require (
gopkg.in/yaml.v2 v2.3.0
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
honnef.co/go/tools v0.0.1-2020.1.5 // indirect
)
)
2 changes: 1 addition & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -956,4 +956,4 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
10 changes: 8 additions & 2 deletions notifier/notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,13 @@ func (notifier *StandardNotifier) resend(pkg *NotificationPackage, reason string
}

func (notifier *StandardNotifier) runSender(sender moira.Sender, ch chan NotificationPackage) {
defer func() {
if err := recover(); err != nil {
notifier.logger.Warningf("Panic notifier: %v, ", err)
}
}()
defer notifier.waitGroup.Done()

for pkg := range ch {
plots, err := notifier.buildNotificationPackagePlots(pkg)
if err != nil {
Expand All @@ -167,9 +173,9 @@ func (notifier *StandardNotifier) runSender(sender moira.Sender, ch chan Notific
}
}

pkg.Trigger.Desc, err = pkg.Trigger.GetPopulatedDescription(pkg.Events)
err = pkg.Trigger.PopulatedDescription(pkg.Events)
if err != nil {
notifier.logger.Errorf("Error populate description: %v", err)
notifier.logger.Warningf("Error populate description:\n%v", err)
}

err = sender.SendEvents(pkg.Events, pkg.Contact, pkg.Trigger, plots, pkg.Throttled)
Expand Down
Loading