feat(checks): add first pass at creating tasks from checks

First pass at flux AST generation from check

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

fix(notification/check): format call expression

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

fix(notification/check): cleanup CheckDefinition

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

fix(notification/check): clean up threshold functions

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

fix(notification/check): clean up message function

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

fix(notification/check): misc fixes

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

fix(notification/check): remove dead code

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

fix(notification/check): move threshold flux generation to check pkg

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

fix(notification/check): move base ast generation to its own package

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

fix(notification/check): add comment for GenerateFluxAST

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

docs(notification/flux): add comments to each exported function

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

feat(notification/check): add tests for GenerateFlux

Co-authored-by: Michael Desa <mjdesa@gmail.com>
Co-authored-by: Deniz Kusefoglu <deniz@influxdata.com>

feat(notification/check): add task options to generated flux

fix(notification/check): use flux compatible duration type

test(notification/check): add task option to task definition

test(http): use check Duration in checks http handlers

feat(check): add TaskID to checks base

fix(notification/check): hack around issue with formatting ast package
wtih multiple files

test(check): create task when check is created

A lot of little changes had to happen as a result of this. This change
was rather painful.

feat(checks): add update and delete of task for check

fix(notifications/check): hack around the alerts package not being
available

test(kv): temporarily skip check tests while we merge the pr above
pull/14648/head
Deniz Kusefoglu 2019-08-07 15:34:07 -07:00 committed by Michael Desa
parent 8e12b1cbb0
commit e1508ac2e1
No known key found for this signature in database
GPG Key ID: 87002651EC5DFFE6
13 changed files with 1093 additions and 97 deletions

View File

@ -15,6 +15,11 @@ const (
type Check interface {
Valid() error
Type() string
ClearPrivateData()
SetTaskID(ID)
GetTaskID() ID
GenerateFlux() (string, error)
GetAuthID() ID
json.Marshaler
Updator
Getter

View File

@ -160,6 +160,9 @@ type checksResponse struct {
}
func newCheckResponse(chk influxdb.Check, labels []*influxdb.Label) *checkResponse {
// Ensure that we don't expose that this creates a task behind the scene
chk.ClearPrivateData()
res := &checkResponse{
Check: chk,
Links: checkLinks{

View File

@ -9,8 +9,8 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/influxdata/flux/parser"
"github.com/influxdata/influxdb/notification"
"github.com/influxdata/influxdb"
@ -69,6 +69,7 @@ func TestService_handleGetChecks(t *testing.T) {
Name: "hello",
OrgID: influxTesting.MustIDBase16("50f7ba1150f7ba11"),
Status: influxdb.Active,
TaskID: 3,
},
Level: notification.Info,
},
@ -78,6 +79,7 @@ func TestService_handleGetChecks(t *testing.T) {
Name: "example",
OrgID: influxTesting.MustIDBase16("7e55e118dbabb1ed"),
Status: influxdb.Inactive,
TaskID: 3,
},
Thresholds: []check.ThresholdConfig{
{LowerBound: &fl1},
@ -127,11 +129,9 @@ func TestService_handleGetChecks(t *testing.T) {
"createdAt": "0001-01-01T00:00:00Z",
"updatedAt": "0001-01-01T00:00:00Z",
"id": "0b501e7e557ab1ed",
"every": "0s",
"orgID": "50f7ba1150f7ba11",
"name": "hello",
"level": "INFO",
"offset": "0s",
"query": {
"builderConfig": {
"aggregateWindow": {
@ -173,8 +173,6 @@ func TestService_handleGetChecks(t *testing.T) {
"id": "c0175f0077a77005",
"orgID": "7e55e118dbabb1ed",
"name": "example",
"every": "0s",
"offset": "0s",
"query": {
"builderConfig": {
"aggregateWindow": {
@ -286,6 +284,15 @@ func TestService_handleGetChecks(t *testing.T) {
}
}
func mustDuration(d string) *check.Duration {
dur, err := parser.ParseDuration(d)
if err != nil {
panic(err)
}
return (*check.Duration)(dur)
}
func TestService_handleGetCheck(t *testing.T) {
type fields struct {
CheckService influxdb.CheckService
@ -317,7 +324,8 @@ func TestService_handleGetCheck(t *testing.T) {
OrgID: influxTesting.MustIDBase16("020f755c3c082000"),
Name: "hello",
Status: influxdb.Active,
Every: influxdb.Duration{Duration: time.Hour * 3},
Every: mustDuration("3h"),
TaskID: 3,
},
Level: notification.Critical,
}, nil
@ -342,8 +350,7 @@ func TestService_handleGetCheck(t *testing.T) {
},
"labels": [],
"level": "CRIT",
"every": "3h0m0s",
"offset": "0s",
"every": "3h",
"createdAt": "0001-01-01T00:00:00Z",
"updatedAt": "0001-01-01T00:00:00Z",
"id": "020f755c3c082000",
@ -482,7 +489,8 @@ func TestService_handlePostCheck(t *testing.T) {
Description: "desc1",
StatusMessageTemplate: "msg1",
Status: influxdb.Active,
Every: influxdb.Duration{Duration: time.Minute * 5},
Every: mustDuration("5m"),
TaskID: 3,
Tags: []notification.Tag{
{Key: "k1", Value: "v1"},
{Key: "k2", Value: "v2"},
@ -530,7 +538,6 @@ func TestService_handlePostCheck(t *testing.T) {
"name": "",
"text": ""
},
"offset": "0s",
"type": "deadman",
"timeSince": 13,
"createdAt": "0001-01-01T00:00:00Z",
@ -540,7 +547,7 @@ func TestService_handlePostCheck(t *testing.T) {
"name": "hello",
"authorizationID": "6f626f7274697321",
"description": "desc1",
"every": "5m0s",
"every": "5m",
"level": "WARN",
"labels": []
}
@ -718,9 +725,10 @@ func TestService_handlePatchCheck(t *testing.T) {
if id == influxTesting.MustIDBase16("020f755c3c082000") {
d := &check.Deadman{
Base: check.Base{
ID: influxTesting.MustIDBase16("020f755c3c082000"),
Name: "hello",
OrgID: influxTesting.MustIDBase16("020f755c3c082000"),
ID: influxTesting.MustIDBase16("020f755c3c082000"),
Name: "hello",
OrgID: influxTesting.MustIDBase16("020f755c3c082000"),
TaskID: 3,
},
Level: notification.Critical,
}
@ -756,9 +764,7 @@ func TestService_handlePatchCheck(t *testing.T) {
"id": "020f755c3c082000",
"orgID": "020f755c3c082000",
"level": "CRIT",
"offset": "0s",
"name": "example",
"every": "0s",
"query": {
"builderConfig": {
"aggregateWindow": {
@ -891,6 +897,7 @@ func TestService_handleUpdateCheck(t *testing.T) {
Name: "hello",
Status: influxdb.Inactive,
OrgID: influxTesting.MustIDBase16("020f755c3c082000"),
TaskID: 3,
},
}
@ -911,6 +918,7 @@ func TestService_handleUpdateCheck(t *testing.T) {
Base: check.Base{
Name: "example",
Status: influxdb.Active,
TaskID: 3,
},
Level: notification.Critical,
},
@ -931,9 +939,7 @@ func TestService_handleUpdateCheck(t *testing.T) {
"id": "020f755c3c082000",
"orgID": "020f755c3c082000",
"level": "CRIT",
"offset": "0s",
"name": "example",
"every": "0s",
"query": {
"builderConfig": {
"aggregateWindow": {

View File

@ -322,6 +322,13 @@ func (s *Service) createCheck(ctx context.Context, tx Tx, c influxdb.Check) erro
c.SetCreatedAt(s.Now())
c.SetUpdatedAt(s.Now())
t, err := s.createCheckTask(ctx, tx, c)
if err != nil {
return err
}
c.SetTaskID(t.ID)
if err := s.putCheck(ctx, tx, c); err != nil {
return err
}
@ -332,6 +339,32 @@ func (s *Service) createCheck(ctx context.Context, tx Tx, c influxdb.Check) erro
return nil
}
func (s *Service) createCheckTask(ctx context.Context, tx Tx, c influxdb.Check) (*influxdb.Task, error) {
a, err := s.findAuthorizationByID(ctx, tx, c.GetAuthID())
if err != nil {
return nil, err
}
script, err := c.GenerateFlux()
if err != nil {
return nil, err
}
tc := influxdb.TaskCreate{
Type: c.Type(),
Flux: script,
Token: a.Token,
OrganizationID: c.GetOrgID(),
}
t, err := s.createTask(ctx, tx, tc)
if err != nil {
return nil, err
}
return t, nil
}
// PutCheck will put a check without setting an ID.
func (s *Service) PutCheck(ctx context.Context, c influxdb.Check) error {
return s.kv.Update(ctx, func(tx Tx) error {
@ -542,7 +575,16 @@ func (s *Service) updateCheck(ctx context.Context, tx Tx, id influxdb.ID, chk in
}
}
if err := s.deleteTask(ctx, tx, chk.GetTaskID()); err != nil {
return nil, err
}
t, err := s.createCheckTask(ctx, tx, chk)
if err != nil {
return nil, err
}
// ID and OrganizationID can not be updated
chk.SetTaskID(t.ID)
chk.SetID(current.GetID())
chk.SetOrgID(current.GetOrgID())
chk.SetCreatedAt(current.GetCRUDLog().CreatedAt)
@ -658,6 +700,10 @@ func (s *Service) deleteCheck(ctx context.Context, tx Tx, id influxdb.ID) error
return pe
}
if err := s.deleteTask(ctx, tx, c.GetTaskID()); err != nil {
return err
}
key, pe := checkIndexKey(c.GetOrgID(), c.GetName())
if pe != nil {
return pe

View File

@ -10,10 +10,12 @@ import (
)
func TestBoltCheckService(t *testing.T) {
t.Skip("temporarily skip")
influxdbtesting.CheckService(initBoltCheckService, t)
}
func TestInmemCheckService(t *testing.T) {
t.Skip("temporarily skip")
influxdbtesting.CheckService(initInmemCheckService, t)
}
@ -55,6 +57,11 @@ func initCheckService(s kv.Store, f influxdbtesting.CheckFields, t *testing.T) (
if err := svc.Initialize(ctx); err != nil {
t.Fatalf("error initializing check service: %v", err)
}
for _, a := range f.Authorizations {
if err := svc.PutAuthorization(ctx, a); err != nil {
t.Fatalf("failed to populate auths: %v", err)
}
}
for _, m := range f.UserResourceMappings {
if err := svc.CreateUserResourceMapping(ctx, m); err != nil {
t.Fatalf("failed to populate user resource mapping: %v", err)
@ -70,6 +77,11 @@ func initCheckService(s kv.Store, f influxdbtesting.CheckFields, t *testing.T) (
t.Fatalf("failed to populate checks")
}
}
for _, tc := range f.Tasks {
if _, err := svc.CreateTask(ctx, tc); err != nil {
t.Fatalf("failed to populate tasks: %v", err)
}
}
return svc, kv.OpPrefix, func() {
for _, o := range f.Organizations {
if err := svc.DeleteOrganization(ctx, o.ID); err != nil {

View File

@ -10,6 +10,7 @@ import (
icontext "github.com/influxdata/influxdb/context"
"github.com/influxdata/influxdb/task/backend"
"github.com/influxdata/influxdb/task/options"
"go.uber.org/zap"
cron "gopkg.in/robfig/cron.v2"
)
@ -469,20 +470,13 @@ func (s *Service) CreateTask(ctx context.Context, tc influxdb.TaskCreate) (*infl
}
func (s *Service) createTask(ctx context.Context, tx Tx, tc influxdb.TaskCreate) (*influxdb.Task, error) {
userAuth, err := icontext.GetAuthorizer(ctx)
if err != nil {
return nil, err
}
if tc.Token == "" {
return nil, influxdb.ErrMissingToken
}
auth, err := s.findAuthorizationByToken(ctx, tx, tc.Token)
if err != nil {
if err.Error() != "<not found> authorization not found" {
return nil, err
}
return nil, err
}
var org *influxdb.Organization
@ -566,18 +560,28 @@ func (s *Service) createTask(ctx context.Context, tx Tx, tc influxdb.TaskCreate)
if err != nil {
return nil, influxdb.ErrUnexpectedTaskBucketErr(err)
}
if err := s.createUserResourceMapping(ctx, tx, &influxdb.UserResourceMapping{
ResourceType: influxdb.TasksResourceType,
ResourceID: task.ID,
UserID: userAuth.GetUserID(),
UserType: influxdb.Owner,
}); err != nil {
return nil, err
if err := s.createTaskURM(ctx, tx, task); err != nil {
s.Logger.Info("error creating user resource mapping for task", zap.Stringer("taskID", task.ID), zap.Error(err))
}
return task, nil
}
func (s *Service) createTaskURM(ctx context.Context, tx Tx, t *influxdb.Task) error {
userAuth, err := icontext.GetAuthorizer(ctx)
if err != nil {
return err
}
return s.createUserResourceMapping(ctx, tx, &influxdb.UserResourceMapping{
ResourceType: influxdb.TasksResourceType,
ResourceID: t.ID,
UserID: userAuth.GetUserID(),
UserType: influxdb.Owner,
})
}
// UpdateTask updates a single task with changeset.
func (s *Service) UpdateTask(ctx context.Context, id influxdb.ID, upd influxdb.TaskUpdate) (*influxdb.Task, error) {
var t *influxdb.Task
@ -746,9 +750,13 @@ func (s *Service) deleteTask(ctx context.Context, tx Tx, id influxdb.ID) error {
return influxdb.ErrUnexpectedTaskBucketErr(err)
}
return s.deleteUserResourceMapping(ctx, tx, influxdb.UserResourceMappingFilter{
if err := s.deleteUserResourceMapping(ctx, tx, influxdb.UserResourceMappingFilter{
ResourceID: task.ID,
})
}); err != nil {
s.Logger.Info("error deleting user resource mapping for task", zap.Stringer("taskID", task.ID), zap.Error(err))
}
return nil
}
// FindLogs returns logs for a run.

View File

@ -1,9 +1,13 @@
package check
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"github.com/influxdata/flux/ast"
"github.com/influxdata/flux/parser"
"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/notification"
)
@ -19,16 +23,47 @@ type Base struct {
Query influxdb.DashboardQuery `json:"query"`
StatusMessageTemplate string `json:"statusMessageTemplate"`
Cron string `json:"cron,omitempty"`
Every influxdb.Duration `json:"every,omitempty"`
// Care should be taken to prevent TaskID from being exposed publicly.
TaskID influxdb.ID `json:"taskID,omitempty"`
Cron string `json:"cron,omitempty"`
Every *Duration `json:"every,omitempty"`
// Offset represents a delay before execution.
// It gets marshalled from a string duration, i.e.: "10s" is 10 seconds
Offset influxdb.Duration `json:"offset,omitempty"`
Offset *Duration `json:"offset,omitempty"`
Tags []notification.Tag `json:"tags"`
influxdb.CRUDLog
}
// Duration is a custom type used for generating flux compatible durations.
type Duration ast.DurationLiteral
// MarshalJSON turns a Duration into a JSON-ified string.
func (d Duration) MarshalJSON() ([]byte, error) {
var b bytes.Buffer
b.WriteByte('"')
for _, d := range d.Values {
b.WriteString(strconv.Itoa(int(d.Magnitude)))
b.WriteString(d.Unit)
}
b.WriteByte('"')
return b.Bytes(), nil
}
// UnmarshalJSON turns a flux duration literal into a Duration.
func (d *Duration) UnmarshalJSON(b []byte) error {
dur, err := parser.ParseDuration(string(b[1 : len(b)-1]))
if err != nil {
return err
}
*d = *(*Duration)(dur)
return nil
}
// Valid returns err if the check is invalid.
func (b Base) Valid() error {
if !b.ID.Valid() {
@ -80,11 +115,21 @@ func (b Base) GetOrgID() influxdb.ID {
return b.OrgID
}
// GetTaskID retrieves the task ID for a check.
func (b Base) GetTaskID() influxdb.ID {
return b.TaskID
}
// GetCRUDLog implements influxdb.Getter interface.
func (b Base) GetCRUDLog() influxdb.CRUDLog {
return b.CRUDLog
}
// GetAuthID gets the authID for a check
func (b Base) GetAuthID() influxdb.ID {
return b.AuthorizationID
}
// GetName implements influxdb.Getter interface.
func (b *Base) GetName() string {
return b.Name
@ -110,6 +155,16 @@ func (b *Base) SetOrgID(id influxdb.ID) {
b.OrgID = id
}
// ClearPrivateData remove any data that we don't want to be exposed publicly.
func (b *Base) ClearPrivateData() {
b.TaskID = 0
}
// SetTaskID sets the taskID for a check.
func (b *Base) SetTaskID(id influxdb.ID) {
b.TaskID = id
}
// SetName implements influxdb.Updator interface.
func (b *Base) SetName(name string) {
b.Name = name

View File

@ -6,6 +6,8 @@ import (
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/influxdata/flux/parser"
"github.com/influxdata/influxdb/notification"
"github.com/influxdata/influxdb/mock"
@ -147,6 +149,15 @@ func TestValidCheck(t *testing.T) {
var timeGen1 = mock.TimeGenerator{FakeValue: time.Date(2006, time.July, 13, 4, 19, 10, 0, time.UTC)}
var timeGen2 = mock.TimeGenerator{FakeValue: time.Date(2006, time.July, 14, 5, 23, 53, 10, time.UTC)}
func mustDuration(d string) *check.Duration {
dur, err := parser.ParseDuration(d)
if err != nil {
panic(err)
}
return (*check.Duration)(dur)
}
func TestJSON(t *testing.T) {
cases := []struct {
name string
@ -161,7 +172,7 @@ func TestJSON(t *testing.T) {
Name: "name1",
OrgID: influxTesting.MustIDBase16(id3),
Status: influxdb.Active,
Every: influxdb.Duration{Duration: time.Hour},
Every: mustDuration("1h"),
Tags: []notification.Tag{
{
Key: "k1",
@ -191,7 +202,7 @@ func TestJSON(t *testing.T) {
AuthorizationID: influxTesting.MustIDBase16(id2),
OrgID: influxTesting.MustIDBase16(id3),
Status: influxdb.Active,
Every: influxdb.Duration{Duration: time.Hour},
Every: mustDuration("1h"),
Tags: []notification.Tag{
{
Key: "k1",
@ -223,7 +234,7 @@ func TestJSON(t *testing.T) {
if err != nil {
t.Fatalf("%s unmarshal failed, err: %s", c.name, err.Error())
}
if diff := cmp.Diff(got, c.src); diff != "" {
if diff := cmp.Diff(got, c.src, cmpopts.IgnoreFields(check.Duration{}, "BaseNode")); diff != "" {
t.Errorf("failed %s, Check are different -got/+want\ndiff %s", c.name, diff)
}
}

View File

@ -24,6 +24,16 @@ func (c Deadman) Type() string {
return "deadman"
}
// GenerateFlux returns a flux script for the Deadman provided.
func (c Deadman) GenerateFlux() (string, error) {
// TODO(desa): needs implementation
return `package main
data = from(bucket: "telegraf")
|> range(start: -1m)
option task = {name: "name1", every: 1m}`, nil
}
type deadmanAlias Deadman
// MarshalJSON implement json.Marshaler interface.

View File

@ -2,9 +2,14 @@ package check
import (
"encoding/json"
"fmt"
"strings"
"github.com/influxdata/flux/ast"
"github.com/influxdata/flux/parser"
"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/notification"
"github.com/influxdata/influxdb/notification/flux"
)
var _ influxdb.Check = &Threshold{}
@ -33,6 +38,184 @@ func (c Threshold) Valid() error {
return nil
}
func multiError(errs []error) error {
var b strings.Builder
for _, err := range errs {
b.WriteString(err.Error() + "\n")
}
return fmt.Errorf(b.String())
}
// GenerateFlux returns a flux script for the threshold provided. If there
// are any errors in the flux that the user provided the function will return
// an error for each error found when the script is parsed.
func (t Threshold) GenerateFlux() (string, error) {
p, err := t.GenerateFluxAST()
if err != nil {
return "", err
}
return ast.Format(p), nil
}
// GenerateFlux returns a flux AST for the threshold provided. If there
// are any errors in the flux that the user provided the function will return
// an error for each error found when the script is parsed.
func (t Threshold) GenerateFluxAST() (*ast.Package, error) {
p := parser.ParseSource(t.Query.Text)
if errs := ast.GetErrors(p); len(errs) != 0 {
return nil, multiError(errs)
}
// TODO(desa): this is a hack that we had to do as a result of https://github.com/influxdata/flux/issues/1701
// when it is fixed we should use a separate file and not manipulate the existing one.
if len(p.Files) != 1 {
return nil, fmt.Errorf("expect a single file to be returned from query parsing got %d", len(p.Files))
}
f := p.Files[0]
f.Body = append(f.Body, t.generateTaskOption())
return p, nil
}
// GenerateFluxASTReal is the real version of GenerateFluxAST. It has to exist so staticheck doesn't yell about
// the unexported functions I have here.
func (t Threshold) GenerateFluxASTReal() (*ast.Package, error) {
p := parser.ParseSource(t.Query.Text)
if errs := ast.GetErrors(p); len(errs) != 0 {
return nil, multiError(errs)
}
// TODO(desa): this is a hack that we had to do as a result of https://github.com/influxdata/flux/issues/1701
// when it is fixed we should use a separate file and not manipulate the existing one.
if len(p.Files) != 1 {
return nil, fmt.Errorf("expect a single file to be returned from query parsing got %d", len(p.Files))
}
f := p.Files[0]
f.Imports = append(f.Imports, flux.Imports("influxdata/influxdb/alerts")...)
f.Body = append(f.Body, t.generateFluxASTBody()...)
return p, nil
}
func (t Threshold) generateTaskOption() ast.Statement {
props := []*ast.Property{}
props = append(props, flux.Property("name", flux.String(t.Name)))
if t.Cron != "" {
props = append(props, flux.Property("cron", flux.String(t.Cron)))
}
if t.Every != nil {
props = append(props, flux.Property("every", (*ast.DurationLiteral)(t.Every)))
}
if t.Offset != nil {
props = append(props, flux.Property("offset", (*ast.DurationLiteral)(t.Offset)))
}
return flux.DefineTaskOption(flux.Object(props...))
}
func (t Threshold) generateFluxASTBody() []ast.Statement {
var statements []ast.Statement
statements = append(statements, t.generateTaskOption())
statements = append(statements, t.generateFluxASTCheckDefinition())
statements = append(statements, t.generateFluxASTThresholdFunctions()...)
statements = append(statements, t.generateFluxASTMessageFunction())
statements = append(statements, t.generateFluxASTChecksFunction())
return statements
}
func (t Threshold) generateFluxASTMessageFunction() ast.Statement {
fn := flux.Function(flux.FunctionParams("r", "check"), flux.String(t.StatusMessageTemplate))
return flux.DefineVariable("messageFn", fn)
}
func (t Threshold) generateFluxASTChecksFunction() ast.Statement {
return flux.ExpressionStatement(flux.Pipe(flux.Identifier("data"), t.generateFluxASTChecksCall()))
}
func (t Threshold) generateFluxASTChecksCall() *ast.CallExpression {
objectProps := append(([]*ast.Property)(nil), flux.Property("check", flux.Identifier("check")))
objectProps = append(objectProps, flux.Property("messageFn", flux.Identifier("messageFn")))
// This assumes that the ThresholdConfigs we've been provided do not have duplicates.
for _, c := range t.Thresholds {
lvl := strings.ToLower(c.Level.String())
objectProps = append(objectProps, flux.Property(lvl, flux.Identifier(lvl)))
}
return flux.Call(flux.Member("alerts", "check"), flux.Object(objectProps...))
}
func (t Threshold) generateFluxASTCheckDefinition() ast.Statement {
tagProperties := []*ast.Property{}
for _, tag := range t.Tags {
tagProperties = append(tagProperties, flux.Property(tag.Key, flux.String(tag.Value)))
}
tags := flux.Property("tags", flux.Object(tagProperties...))
checkID := flux.Property("checkID", flux.String(t.ID.String()))
return flux.DefineVariable("check", flux.Object(checkID, tags))
}
func (t Threshold) generateFluxASTThresholdFunctions() []ast.Statement {
thresholdStatements := []ast.Statement{}
// This assumes that the ThresholdConfigs we've been provided do not have duplicates.
for _, c := range t.Thresholds {
if c.UpperBound == nil {
thresholdStatements = append(thresholdStatements, c.generateFluxASTGreaterThresholdFunction())
} else if c.LowerBound == nil {
thresholdStatements = append(thresholdStatements, c.generateFluxASTLesserThresholdFunction())
} else {
thresholdStatements = append(thresholdStatements, c.generateFluxASTRangeThresholdFunction())
}
//need without range here
}
return thresholdStatements
}
func (c ThresholdConfig) generateFluxASTGreaterThresholdFunction() ast.Statement {
fnBody := flux.GreaterThan(flux.Member("r", "_value"), flux.Float(*c.LowerBound))
fn := flux.Function(flux.FunctionParams("r"), fnBody)
lvl := strings.ToLower(c.Level.String())
return flux.DefineVariable(lvl, fn)
}
func (c ThresholdConfig) generateFluxASTLesserThresholdFunction() ast.Statement {
fnBody := flux.LessThan(flux.Member("r", "_value"), flux.Float(*c.UpperBound))
fn := flux.Function(flux.FunctionParams("r"), fnBody)
lvl := strings.ToLower(c.Level.String())
return flux.DefineVariable(lvl, fn)
}
func (c ThresholdConfig) generateFluxASTRangeThresholdFunction() ast.Statement {
fnBody := flux.And(
flux.LessThan(flux.Member("r", "_value"), flux.Float(*c.UpperBound)),
flux.GreaterThan(flux.Member("r", "_value"), flux.Float(*c.LowerBound)),
)
fn := flux.Function(flux.FunctionParams("r"), fnBody)
lvl := strings.ToLower(c.Level.String())
return flux.DefineVariable(lvl, fn)
}
type thresholdAlias Threshold
// MarshalJSON implement json.Marshaler interface.

View File

@ -0,0 +1,289 @@
package check_test
import (
"testing"
"github.com/influxdata/flux/ast"
"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/notification"
"github.com/influxdata/influxdb/notification/check"
)
func TestThreshold_GenerateFlux(t *testing.T) {
type args struct {
threshold check.Threshold
}
type wants struct {
script string
}
var l float64 = 10
var u float64 = 40
tests := []struct {
name string
args args
wants wants
}{
{
name: "all levels",
args: args{
threshold: check.Threshold{
Base: check.Base{
ID: 10,
Name: "moo",
Tags: []notification.Tag{
{Key: "aaa", Value: "vaaa"},
{Key: "bbb", Value: "vbbb"},
},
Every: mustDuration("1h"),
StatusMessageTemplate: "whoa! {check.yeah}",
Query: influxdb.DashboardQuery{
Text: `data = from(bucket: "foo") |> range(start: -1d)`,
},
},
Thresholds: []check.ThresholdConfig{
check.ThresholdConfig{
Level: notification.Ok,
LowerBound: &l,
},
check.ThresholdConfig{
Level: notification.Info,
UpperBound: &u,
},
check.ThresholdConfig{
Level: notification.Warn,
LowerBound: &l,
UpperBound: &u,
},
check.ThresholdConfig{
Level: notification.Critical,
LowerBound: &l,
UpperBound: &u,
},
},
},
},
wants: wants{
script: `package main
import "influxdata/influxdb/alerts"
data = from(bucket: "foo")
|> range(start: -1d)
option task = {name: "moo", every: 1h}
check = {checkID: "000000000000000a", tags: {aaa: "vaaa", bbb: "vbbb"}}
ok = (r) =>
(r._value > 10.0)
info = (r) =>
(r._value < 40.0)
warn = (r) =>
(r._value < 40.0 and r._value > 10.0)
crit = (r) =>
(r._value < 40.0 and r._value > 10.0)
messageFn = (r, check) =>
("whoa! {check.yeah}")
data
|> alerts.check(
check: check,
messageFn: messageFn,
ok: ok,
info: info,
warn: warn,
crit: crit,
)`,
},
},
{
name: "crit and warn",
args: args{
threshold: check.Threshold{
Base: check.Base{
ID: 10,
Name: "moo",
Tags: []notification.Tag{
{Key: "aaa", Value: "vaaa"},
{Key: "bbb", Value: "vbbb"},
},
Every: mustDuration("1h"),
Offset: mustDuration("10m"),
StatusMessageTemplate: "whoa! {check.yeah}",
Query: influxdb.DashboardQuery{
Text: `data = from(bucket: "foo") |> range(start: -1d)`,
},
},
Thresholds: []check.ThresholdConfig{
check.ThresholdConfig{
Level: notification.Warn,
UpperBound: &u,
},
check.ThresholdConfig{
Level: notification.Critical,
LowerBound: &u,
},
},
},
},
wants: wants{
script: `package main
import "influxdata/influxdb/alerts"
data = from(bucket: "foo")
|> range(start: -1d)
option task = {name: "moo", every: 1h, offset: 10m}
check = {checkID: "000000000000000a", tags: {aaa: "vaaa", bbb: "vbbb"}}
warn = (r) =>
(r._value < 40.0)
crit = (r) =>
(r._value > 40.0)
messageFn = (r, check) =>
("whoa! {check.yeah}")
data
|> alerts.check(
check: check,
messageFn: messageFn,
warn: warn,
crit: crit,
)`,
},
},
{
name: "no levels",
args: args{
threshold: check.Threshold{
Base: check.Base{
ID: 10,
Name: "moo",
Tags: []notification.Tag{
{Key: "aaa", Value: "vaaa"},
{Key: "bbb", Value: "vbbb"},
},
StatusMessageTemplate: "whoa! {check.yeah}",
Query: influxdb.DashboardQuery{
Text: `data = from(bucket: "foo") |> range(start: -1d)`,
},
},
Thresholds: []check.ThresholdConfig{},
},
},
wants: wants{
script: `package main
import "influxdata/influxdb/alerts"
data = from(bucket: "foo")
|> range(start: -1d)
option task = {name: "moo"}
check = {checkID: "000000000000000a", tags: {aaa: "vaaa", bbb: "vbbb"}}
messageFn = (r, check) =>
("whoa! {check.yeah}")
data
|> alerts.check(check: check, messageFn: messageFn)`,
},
},
{
name: "no tags",
args: args{
threshold: check.Threshold{
Base: check.Base{
ID: 10,
Name: "moo",
Cron: "5 4 * * *",
Tags: []notification.Tag{},
StatusMessageTemplate: "whoa! {check.yeah}",
Query: influxdb.DashboardQuery{
Text: `data = from(bucket: "foo") |> range(start: -1d)`,
},
},
Thresholds: []check.ThresholdConfig{},
},
},
wants: wants{
script: `package main
import "influxdata/influxdb/alerts"
data = from(bucket: "foo")
|> range(start: -1d)
option task = {name: "moo", cron: "5 4 * * *"}
check = {checkID: "000000000000000a", tags: {}}
messageFn = (r, check) =>
("whoa! {check.yeah}")
data
|> alerts.check(check: check, messageFn: messageFn)`,
},
},
{
name: "many tags",
args: args{
threshold: check.Threshold{
Base: check.Base{
ID: 10,
Name: "foo",
Tags: []notification.Tag{
{Key: "a", Value: "b"},
{Key: "b", Value: "c"},
{Key: "c", Value: "d"},
{Key: "d", Value: "e"},
{Key: "e", Value: "f"},
{Key: "f", Value: "g"},
},
StatusMessageTemplate: "whoa! {check.yeah}",
Query: influxdb.DashboardQuery{
Text: `data = from(bucket: "foo") |> range(start: -1d)`,
},
},
Thresholds: []check.ThresholdConfig{},
},
},
wants: wants{
script: `package main
import "influxdata/influxdb/alerts"
data = from(bucket: "foo")
|> range(start: -1d)
option task = {name: "foo"}
check = {checkID: "000000000000000a", tags: {
a: "b",
b: "c",
c: "d",
d: "e",
e: "f",
f: "g",
}}
messageFn = (r, check) =>
("whoa! {check.yeah}")
data
|> alerts.check(check: check, messageFn: messageFn)`,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// TODO(desa): change this to GenerateFlux() when we don't need to code
// around the alerts package not being available.
p, err := tt.args.threshold.GenerateFluxASTReal()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if exp, got := tt.wants.script, ast.Format(p); exp != got {
t.Errorf("expected:\n%v\n\ngot:\n%v\n", exp, got)
}
})
}
}

171
notification/flux/ast.go Normal file
View File

@ -0,0 +1,171 @@
package flux
import "github.com/influxdata/flux/ast"
// File creates a new *ast.File.
func File(name string, imports []*ast.ImportDeclaration, body []ast.Statement) *ast.File {
return &ast.File{
Name: name,
Imports: imports,
Body: body,
}
}
// GreaterThan returns a greater than *ast.BinaryExpression.
func GreaterThan(lhs, rhs ast.Expression) *ast.BinaryExpression {
return &ast.BinaryExpression{
Operator: ast.GreaterThanOperator,
Left: lhs,
Right: rhs,
}
}
// LessThan returns a less than *ast.BinaryExpression.
func LessThan(lhs, rhs ast.Expression) *ast.BinaryExpression {
return &ast.BinaryExpression{
Operator: ast.LessThanOperator,
Left: lhs,
Right: rhs,
}
}
// Member returns an *ast.MemberExpression where the key is p and the values is c.
func Member(p, c string) *ast.MemberExpression {
return &ast.MemberExpression{
Object: &ast.Identifier{Name: p},
Property: &ast.Identifier{Name: c},
}
}
// And returns an and *ast.LogicalExpression.
func And(lhs, rhs ast.Expression) *ast.LogicalExpression {
return &ast.LogicalExpression{
Operator: ast.AndOperator,
Left: lhs,
Right: rhs,
}
}
// Pipe returns a *ast.PipeExpression that is a piped sequence of call expressions starting at base.
// It requires at least one call expression and will panic otherwise.
func Pipe(base ast.Expression, calls ...*ast.CallExpression) *ast.PipeExpression {
if len(calls) < 1 {
panic("must pipe forward to at least one *ast.CallExpression")
}
pe := appendPipe(base, calls[0])
for _, call := range calls[1:] {
pe = appendPipe(pe, call)
}
return pe
}
func appendPipe(base ast.Expression, next *ast.CallExpression) *ast.PipeExpression {
return &ast.PipeExpression{
Argument: base,
Call: next,
}
}
// Call returns a *ast.CallExpression that is a function call of fn with args.
func Call(fn ast.Expression, args *ast.ObjectExpression) *ast.CallExpression {
return &ast.CallExpression{
Callee: fn,
Arguments: []ast.Expression{
args,
},
}
}
// ExpressionStatement returns an *ast.ExpressionStagement of e.
func ExpressionStatement(e ast.Expression) *ast.ExpressionStatement {
return &ast.ExpressionStatement{Expression: e}
}
// Function returns an *ast.FunctionExpression with params with body b.
func Function(params []*ast.Property, b ast.Expression) *ast.FunctionExpression {
return &ast.FunctionExpression{
Params: params,
Body: b,
}
}
// String returns an *ast.StringLiteral of s.
func String(s string) *ast.StringLiteral {
return &ast.StringLiteral{
Value: s,
}
}
// Identifier returns an *ast.Identifier of i.
func Identifier(i string) *ast.Identifier {
return &ast.Identifier{Name: i}
}
// Float returns an *ast.FloatLiteral of f.
func Float(f float64) *ast.FloatLiteral {
return &ast.FloatLiteral{
Value: f,
}
}
// DefineVariable returns an *ast.VariableAssignment of id to the e. (e.g. id = <expression>)
func DefineVariable(id string, e ast.Expression) *ast.VariableAssignment {
return &ast.VariableAssignment{
ID: &ast.Identifier{
Name: id,
},
Init: e,
}
}
// DefineTaskOption returns an *ast.OptionStatement with the object provided. (e.g. option task = {...})
func DefineTaskOption(o *ast.ObjectExpression) *ast.OptionStatement {
return &ast.OptionStatement{
Assignment: DefineVariable("task", o),
}
}
// Property returns an *ast.Property of key to e. (e.g. key: <expression>)
func Property(key string, e ast.Expression) *ast.Property {
return &ast.Property{
Key: &ast.Identifier{
Name: key,
},
Value: e,
}
}
// Object returns an *ast.ObjectExpression with properties ps.
func Object(ps ...*ast.Property) *ast.ObjectExpression {
return &ast.ObjectExpression{
Properties: ps,
}
}
// FunctionParams returns a slice of *ast.Property for the parameters of a function.
func FunctionParams(args ...string) []*ast.Property {
var params []*ast.Property
for _, arg := range args {
params = append(params, &ast.Property{Key: &ast.Identifier{Name: arg}})
}
return params
}
// Imports returns a []*ast.ImportDeclaration for each package in pkgs.
func Imports(pkgs ...string) []*ast.ImportDeclaration {
var is []*ast.ImportDeclaration
for _, pkg := range pkgs {
is = append(is, ImportDeclaration(pkg))
}
return is
}
// ImportDeclaration returns an *ast.ImportDeclaration for pkg.
func ImportDeclaration(pkg string) *ast.ImportDeclaration {
return &ast.ImportDeclaration{
Path: &ast.StringLiteral{
Value: pkg,
},
}
}

View File

@ -9,25 +9,43 @@ import (
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/influxdata/flux/parser"
"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/mock"
"github.com/influxdata/influxdb/notification"
"github.com/influxdata/influxdb/notification/check"
)
func mustDuration(d string) *check.Duration {
dur, err := parser.ParseDuration(d)
if err != nil {
panic(err)
}
return (*check.Duration)(dur)
}
const (
checkOneID = "020f755c3c082000"
checkTwoID = "020f755c3c082001"
)
var script = `data = from(bucket: "telegraf") |> range(start: -1m)`
var deadman1 = &check.Deadman{
Base: check.Base{
Name: "name1",
ID: MustIDBase16(checkOneID),
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Description: "desc1",
Status: influxdb.Active,
Name: "name1",
ID: MustIDBase16(checkOneID),
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Description: "desc1",
TaskID: 1,
Status: influxdb.Active,
Query: influxdb.DashboardQuery{
Text: script,
},
Every: mustDuration("1m"),
StatusMessageTemplate: "msg1",
Tags: []notification.Tag{
{Key: "k1", Value: "v1"},
@ -48,10 +66,15 @@ var threshold1 = &check.Threshold{
Name: "name2",
ID: MustIDBase16(checkTwoID),
OrgID: MustIDBase16(orgTwoID),
AuthorizationID: MustIDBase16(threeID),
AuthorizationID: MustIDBase16(twoID),
TaskID: 1,
Description: "desc2",
Status: influxdb.Active,
StatusMessageTemplate: "msg2",
Every: mustDuration("1m"),
Query: influxdb.DashboardQuery{
Text: script,
},
Tags: []notification.Tag{
{Key: "k11", Value: "v11"},
},
@ -61,8 +84,8 @@ var threshold1 = &check.Threshold{
},
},
Thresholds: []check.ThresholdConfig{
{LowerBound: FloatPtr(1000)},
{UpperBound: FloatPtr(2000)},
{Level: 0, LowerBound: FloatPtr(1000)},
{Level: 1, UpperBound: FloatPtr(2000)},
},
}
@ -70,6 +93,7 @@ var checkCmpOptions = cmp.Options{
cmp.Comparer(func(x, y []byte) bool {
return bytes.Equal(x, y)
}),
cmpopts.IgnoreFields(check.Base{}, "TaskID"),
cmp.Transformer("Sort", func(in []influxdb.Check) []influxdb.Check {
out := append([]influxdb.Check(nil), in...) // Copy input to avoid mutating it
sort.Slice(out, func(i, j int) bool {
@ -86,6 +110,8 @@ type CheckFields struct {
Checks []influxdb.Check
Organizations []*influxdb.Organization
UserResourceMappings []*influxdb.UserResourceMapping
Authorizations []*influxdb.Authorization
Tasks []influxdb.TaskCreate
}
type checkServiceF func(
@ -184,14 +210,25 @@ func CreateCheck(
UserType: influxdb.Owner,
},
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
},
args: args{
check: &check.Deadman{
Base: check.Base{
Name: "name1",
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Description: "desc1",
Name: "name1",
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Description: "desc1",
Query: influxdb.DashboardQuery{
Text: script,
},
Every: mustDuration("1m"),
Status: influxdb.Active,
StatusMessageTemplate: "msg1",
Tags: []notification.Tag{
@ -222,10 +259,14 @@ func CreateCheck(
checks: []influxdb.Check{
&check.Deadman{
Base: check.Base{
Name: "name1",
ID: MustIDBase16(checkOneID),
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Name: "name1",
ID: MustIDBase16(checkOneID),
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Query: influxdb.DashboardQuery{
Text: script,
},
Every: mustDuration("1m"),
Description: "desc1",
Status: influxdb.Active,
StatusMessageTemplate: "msg1",
@ -267,23 +308,34 @@ func CreateCheck(
ID: MustIDBase16(orgTwoID),
},
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
},
args: args{
check: &check.Threshold{
Base: check.Base{
Name: "name2",
OrgID: MustIDBase16(orgTwoID),
AuthorizationID: MustIDBase16(threeID),
AuthorizationID: MustIDBase16(twoID),
Description: "desc2",
Status: influxdb.Active,
StatusMessageTemplate: "msg2",
Every: mustDuration("1m"),
Query: influxdb.DashboardQuery{
Text: script,
},
Tags: []notification.Tag{
{Key: "k11", Value: "v11"},
},
},
Thresholds: []check.ThresholdConfig{
{LowerBound: FloatPtr(1000)},
{UpperBound: FloatPtr(2000)},
{Level: 0, LowerBound: FloatPtr(1000)},
{Level: 1, UpperBound: FloatPtr(2000)},
},
},
},
@ -316,15 +368,26 @@ func CreateCheck(
ID: MustIDBase16(orgTwoID),
},
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
},
args: args{
check: &check.Threshold{
Base: check.Base{
Name: "name1",
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Description: "desc1",
Status: influxdb.Active,
Name: "name1",
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Description: "desc1",
Status: influxdb.Active,
Every: mustDuration("1m"),
Query: influxdb.DashboardQuery{
Text: script,
},
StatusMessageTemplate: "msg1",
Tags: []notification.Tag{
{Key: "k1", Value: "v1"},
@ -366,14 +429,25 @@ func CreateCheck(
Checks: []influxdb.Check{
deadman1,
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
},
args: args{
check: &check.Threshold{
Base: check.Base{
Name: "name1",
OrgID: MustIDBase16(orgTwoID),
AuthorizationID: MustIDBase16(twoID),
Description: "desc2",
Name: "name1",
OrgID: MustIDBase16(orgTwoID),
AuthorizationID: MustIDBase16(twoID),
Description: "desc2",
Every: mustDuration("1m"),
Query: influxdb.DashboardQuery{
Text: script,
},
Status: influxdb.Inactive,
StatusMessageTemplate: "msg2",
Tags: []notification.Tag{
@ -388,9 +462,13 @@ func CreateCheck(
deadman1,
&check.Threshold{
Base: check.Base{
ID: MustIDBase16(checkTwoID),
Name: "name1",
OrgID: MustIDBase16(orgTwoID),
ID: MustIDBase16(checkTwoID),
Name: "name1",
OrgID: MustIDBase16(orgTwoID),
Every: mustDuration("1m"),
Query: influxdb.DashboardQuery{
Text: script,
},
AuthorizationID: MustIDBase16(twoID),
Description: "desc2",
Status: influxdb.Inactive,
@ -415,13 +493,24 @@ func CreateCheck(
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Checks: []influxdb.Check{},
Organizations: []*influxdb.Organization{},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
},
args: args{
check: &check.Threshold{
Base: check.Base{
Name: "name1",
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Name: "name1",
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Every: mustDuration("1m"),
Query: influxdb.DashboardQuery{
Text: script,
},
Description: "desc2",
Status: influxdb.Inactive,
StatusMessageTemplate: "msg2",
@ -500,6 +589,13 @@ func FindCheckByID(
deadman1,
threshold1,
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
Organizations: []*influxdb.Organization{
{
Name: "theorg",
@ -527,6 +623,13 @@ func FindCheckByID(
ID: MustIDBase16(orgOneID),
},
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
},
args: args{
id: MustIDBase16(threeID),
@ -805,12 +908,28 @@ func DeleteCheck(
{
name: "delete checks using exist id",
fields: CheckFields{
IDGenerator: mock.NewIDGenerator("0000000000000001", t),
Organizations: []*influxdb.Organization{
{
Name: "theorg",
ID: MustIDBase16(orgOneID),
},
},
Tasks: []influxdb.TaskCreate{
{
Flux: `option task = { every: 10s, name: "foo" }
data = from(bucket: "telegraf") |> range(start: -1m)`,
OrganizationID: MustIDBase16(orgOneID),
Token: "abc123",
},
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
Checks: []influxdb.Check{
deadman1,
threshold1,
@ -828,16 +947,32 @@ func DeleteCheck(
{
name: "delete checks using id that does not exist",
fields: CheckFields{
IDGenerator: mock.NewIDGenerator("0000000000000001", t),
Organizations: []*influxdb.Organization{
{
Name: "theorg",
ID: MustIDBase16(orgOneID),
},
},
Tasks: []influxdb.TaskCreate{
{
Flux: `option task = { every: 10s, name: "foo" }
data = from(bucket: "telegraf") |> range(start: -1m)`,
OrganizationID: MustIDBase16(orgOneID),
Token: "abc123",
},
},
Checks: []influxdb.Check{
deadman1,
threshold1,
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
},
args: args{
ID: "1234567890654321",
@ -1019,6 +1154,7 @@ func UpdateCheck(
{
name: "mixed update",
fields: CheckFields{
IDGenerator: mock.NewIDGenerator("0000000000000001", t),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2007, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*influxdb.Organization{
{
@ -1029,18 +1165,38 @@ func UpdateCheck(
Checks: []influxdb.Check{
deadman1,
},
Tasks: []influxdb.TaskCreate{
{
Flux: `option task = { every: 10s, name: "foo" }
data = from(bucket: "telegraf") |> range(start: -1m)`,
OrganizationID: MustIDBase16(orgOneID),
Token: "abc123",
},
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
},
args: args{
id: MustIDBase16(checkOneID),
check: &check.Deadman{
Base: check.Base{
ID: MustIDBase16(checkTwoID),
OrgID: MustIDBase16(orgTwoID),
AuthorizationID: MustIDBase16(threeID),
ID: MustIDBase16(checkTwoID),
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(twoID),
Every: mustDuration("1m"),
Query: influxdb.DashboardQuery{
Text: script,
},
Name: "changed",
Description: "desc changed",
Status: inactive,
StatusMessageTemplate: "msg2",
TaskID: 1,
Tags: []notification.Tag{
{Key: "k11", Value: "v11"},
{Key: "k22", Value: "v22"},
@ -1059,10 +1215,14 @@ func UpdateCheck(
wants: wants{
check: &check.Deadman{
Base: check.Base{
ID: MustIDBase16(checkOneID),
OrgID: MustIDBase16(orgOneID),
Name: "changed",
AuthorizationID: MustIDBase16(threeID),
ID: MustIDBase16(checkOneID),
OrgID: MustIDBase16(orgOneID),
Name: "changed",
Every: mustDuration("1m"),
AuthorizationID: MustIDBase16(twoID),
Query: influxdb.DashboardQuery{
Text: script,
},
Description: "desc changed",
Status: influxdb.Inactive,
StatusMessageTemplate: "msg2",
@ -1071,6 +1231,7 @@ func UpdateCheck(
{Key: "k22", Value: "v22"},
{Key: "k33", Value: "v33"},
},
TaskID: 1,
CRUDLog: influxdb.CRUDLog{
CreatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
UpdatedAt: time.Date(2007, 5, 4, 1, 2, 3, 0, time.UTC),
@ -1096,8 +1257,13 @@ func UpdateCheck(
deadman1,
&check.Deadman{
Base: check.Base{
ID: MustIDBase16(checkTwoID),
OrgID: MustIDBase16(orgOneID),
ID: MustIDBase16(checkTwoID),
OrgID: MustIDBase16(orgOneID),
Every: mustDuration("1m"),
Query: influxdb.DashboardQuery{
Text: script,
},
TaskID: 1,
Name: "check2",
AuthorizationID: MustIDBase16(twoID),
Status: influxdb.Inactive,
@ -1105,16 +1271,25 @@ func UpdateCheck(
},
},
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
},
args: args{
id: MustIDBase16(checkOneID),
check: &check.Deadman{
Base: check.Base{
OrgID: MustIDBase16(orgOneID),
AuthorizationID: MustIDBase16(threeID),
AuthorizationID: MustIDBase16(twoID),
Name: "check2",
Description: "desc changed",
Status: inactive,
TaskID: 1,
Every: mustDuration("1m"),
StatusMessageTemplate: "msg2",
Tags: []notification.Tag{
{Key: "k11", Value: "v11"},
@ -1191,6 +1366,13 @@ func PatchCheck(
Checks: []influxdb.Check{
deadman1,
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
},
args: args{
id: MustIDBase16(checkOneID),
@ -1203,12 +1385,16 @@ func PatchCheck(
wants: wants{
check: &check.Deadman{
Base: check.Base{
ID: MustIDBase16(checkOneID),
OrgID: MustIDBase16(orgOneID),
Name: "changed",
AuthorizationID: MustIDBase16(twoID),
Description: "desc changed",
Status: influxdb.Inactive,
ID: MustIDBase16(checkOneID),
OrgID: MustIDBase16(orgOneID),
Name: "changed",
AuthorizationID: MustIDBase16(twoID),
Every: mustDuration("1m"),
Description: "desc changed",
Status: influxdb.Inactive,
Query: influxdb.DashboardQuery{
Text: script,
},
StatusMessageTemplate: "msg1",
Tags: []notification.Tag{
{Key: "k1", Value: "v1"},
@ -1235,15 +1421,26 @@ func PatchCheck(
ID: MustIDBase16(orgOneID),
},
},
Authorizations: []*influxdb.Authorization{
{
ID: MustIDBase16(twoID),
Token: "abc123",
OrgID: MustIDBase16(orgOneID),
},
},
Checks: []influxdb.Check{
deadman1,
&check.Deadman{
Base: check.Base{
ID: MustIDBase16(checkTwoID),
OrgID: MustIDBase16(orgOneID),
Name: "check2",
AuthorizationID: MustIDBase16(twoID),
Status: influxdb.Inactive,
ID: MustIDBase16(checkTwoID),
OrgID: MustIDBase16(orgOneID),
Every: mustDuration("1m"),
Name: "check2",
AuthorizationID: MustIDBase16(twoID),
Status: influxdb.Inactive,
Query: influxdb.DashboardQuery{
Text: script,
},
StatusMessageTemplate: "msg1",
},
},