3737 lines
116 KiB
Go
3737 lines
116 KiB
Go
package pkger
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"math/rand"
|
|
"net/url"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/influxdata/influxdb/v2"
|
|
"github.com/influxdata/influxdb/v2/mock"
|
|
"github.com/influxdata/influxdb/v2/notification"
|
|
icheck "github.com/influxdata/influxdb/v2/notification/check"
|
|
"github.com/influxdata/influxdb/v2/notification/endpoint"
|
|
"github.com/influxdata/influxdb/v2/notification/rule"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap/zaptest"
|
|
)
|
|
|
|
func TestService(t *testing.T) {
|
|
newTestService := func(opts ...ServiceSetterFn) *Service {
|
|
opt := serviceOpt{
|
|
bucketSVC: mock.NewBucketService(),
|
|
checkSVC: mock.NewCheckService(),
|
|
dashSVC: mock.NewDashboardService(),
|
|
labelSVC: mock.NewLabelService(),
|
|
endpointSVC: mock.NewNotificationEndpointService(),
|
|
orgSVC: mock.NewOrganizationService(),
|
|
ruleSVC: mock.NewNotificationRuleStore(),
|
|
store: &fakeStore{
|
|
createFn: func(ctx context.Context, stack Stack) error {
|
|
return nil
|
|
},
|
|
deleteFn: func(ctx context.Context, id influxdb.ID) error {
|
|
return nil
|
|
},
|
|
readFn: func(ctx context.Context, id influxdb.ID) (Stack, error) {
|
|
return Stack{ID: id}, nil
|
|
},
|
|
updateFn: func(ctx context.Context, stack Stack) error {
|
|
return nil
|
|
},
|
|
},
|
|
taskSVC: mock.NewTaskService(),
|
|
teleSVC: mock.NewTelegrafConfigStore(),
|
|
varSVC: mock.NewVariableService(),
|
|
}
|
|
for _, o := range opts {
|
|
o(&opt)
|
|
}
|
|
|
|
applyOpts := []ServiceSetterFn{
|
|
WithStore(opt.store),
|
|
WithBucketSVC(opt.bucketSVC),
|
|
WithCheckSVC(opt.checkSVC),
|
|
WithDashboardSVC(opt.dashSVC),
|
|
WithLabelSVC(opt.labelSVC),
|
|
WithNotificationEndpointSVC(opt.endpointSVC),
|
|
WithNotificationRuleSVC(opt.ruleSVC),
|
|
WithOrganizationService(opt.orgSVC),
|
|
WithSecretSVC(opt.secretSVC),
|
|
WithTaskSVC(opt.taskSVC),
|
|
WithTelegrafSVC(opt.teleSVC),
|
|
WithVariableSVC(opt.varSVC),
|
|
}
|
|
if opt.idGen != nil {
|
|
applyOpts = append(applyOpts, WithIDGenerator(opt.idGen))
|
|
}
|
|
if opt.timeGen != nil {
|
|
applyOpts = append(applyOpts, WithTimeGenerator(opt.timeGen))
|
|
}
|
|
|
|
return NewService(applyOpts...)
|
|
}
|
|
|
|
t.Run("DryRun", func(t *testing.T) {
|
|
type dryRunTestFields struct {
|
|
path string
|
|
kinds []Kind
|
|
skipResources []ActionSkipResource
|
|
assertFn func(*testing.T, PkgImpactSummary)
|
|
}
|
|
|
|
testDryRunActions := func(t *testing.T, fields dryRunTestFields) {
|
|
t.Helper()
|
|
|
|
var skipResOpts []ApplyOptFn
|
|
for _, asr := range fields.skipResources {
|
|
skipResOpts = append(skipResOpts, ApplyWithResourceSkip(asr))
|
|
}
|
|
|
|
testfileRunner(t, fields.path, func(t *testing.T, pkg *Pkg) {
|
|
t.Helper()
|
|
|
|
tests := []struct {
|
|
name string
|
|
applyOpts []ApplyOptFn
|
|
}{
|
|
{
|
|
name: "skip resources",
|
|
applyOpts: skipResOpts,
|
|
},
|
|
}
|
|
|
|
for _, k := range fields.kinds {
|
|
tests = append(tests, struct {
|
|
name string
|
|
applyOpts []ApplyOptFn
|
|
}{
|
|
name: "skip kind " + k.String(),
|
|
applyOpts: []ApplyOptFn{
|
|
ApplyWithKindSkip(ActionSkipKind{
|
|
Kind: k,
|
|
}),
|
|
},
|
|
})
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
t.Helper()
|
|
|
|
svc := newTestService()
|
|
|
|
impact, err := svc.DryRun(
|
|
context.TODO(),
|
|
influxdb.ID(100),
|
|
0,
|
|
append(tt.applyOpts, ApplyWithPkg(pkg))...,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
fields.assertFn(t, impact)
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
}
|
|
|
|
t.Run("buckets", func(t *testing.T) {
|
|
t.Run("single bucket updated", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/bucket.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeBktSVC := mock.NewBucketService()
|
|
fakeBktSVC.FindBucketByNameFn = func(_ context.Context, orgID influxdb.ID, name string) (*influxdb.Bucket, error) {
|
|
if name != "rucket-11" {
|
|
return nil, errors.New("not found")
|
|
}
|
|
return &influxdb.Bucket{
|
|
ID: influxdb.ID(1),
|
|
OrgID: orgID,
|
|
Name: name,
|
|
Description: "old desc",
|
|
RetentionPeriod: 30 * time.Hour,
|
|
}, nil
|
|
}
|
|
svc := newTestService(WithBucketSVC(fakeBktSVC))
|
|
|
|
impact, err := svc.DryRun(context.TODO(), influxdb.ID(100), 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, impact.Diff.Buckets, 2)
|
|
|
|
expected := DiffBucket{
|
|
DiffIdentifier: DiffIdentifier{
|
|
ID: SafeID(1),
|
|
StateStatus: StateStatusExists,
|
|
PkgName: "rucket-11",
|
|
},
|
|
|
|
Old: &DiffBucketValues{
|
|
Name: "rucket-11",
|
|
Description: "old desc",
|
|
RetentionRules: retentionRules{newRetentionRule(30 * time.Hour)},
|
|
},
|
|
New: DiffBucketValues{
|
|
Name: "rucket-11",
|
|
Description: "bucket 1 description",
|
|
RetentionRules: retentionRules{newRetentionRule(time.Hour)},
|
|
},
|
|
}
|
|
assert.Contains(t, impact.Diff.Buckets, expected)
|
|
})
|
|
})
|
|
|
|
t.Run("single bucket new", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/bucket.json", func(t *testing.T, pkg *Pkg) {
|
|
fakeBktSVC := mock.NewBucketService()
|
|
fakeBktSVC.FindBucketByNameFn = func(_ context.Context, orgID influxdb.ID, name string) (*influxdb.Bucket, error) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
svc := newTestService(WithBucketSVC(fakeBktSVC))
|
|
|
|
impact, err := svc.DryRun(context.TODO(), influxdb.ID(100), 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, impact.Diff.Buckets, 2)
|
|
|
|
expected := DiffBucket{
|
|
DiffIdentifier: DiffIdentifier{
|
|
PkgName: "rucket-11",
|
|
StateStatus: StateStatusNew,
|
|
},
|
|
New: DiffBucketValues{
|
|
Name: "rucket-11",
|
|
Description: "bucket 1 description",
|
|
RetentionRules: retentionRules{newRetentionRule(time.Hour)},
|
|
},
|
|
}
|
|
assert.Contains(t, impact.Diff.Buckets, expected)
|
|
})
|
|
})
|
|
|
|
t.Run("with actions applied", func(t *testing.T) {
|
|
testDryRunActions(t, dryRunTestFields{
|
|
path: "testdata/bucket.yml",
|
|
kinds: []Kind{KindBucket},
|
|
skipResources: []ActionSkipResource{
|
|
{
|
|
Kind: KindBucket,
|
|
MetaName: "rucket-22",
|
|
},
|
|
{
|
|
Kind: KindBucket,
|
|
MetaName: "rucket-11",
|
|
},
|
|
},
|
|
assertFn: func(t *testing.T, impact PkgImpactSummary) {
|
|
require.Empty(t, impact.Diff.Buckets)
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("checks", func(t *testing.T) {
|
|
t.Run("mixed update and creates", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/checks.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeCheckSVC := mock.NewCheckService()
|
|
id := influxdb.ID(1)
|
|
existing := &icheck.Deadman{
|
|
Base: icheck.Base{
|
|
ID: id,
|
|
Name: "display name",
|
|
Description: "old desc",
|
|
},
|
|
}
|
|
fakeCheckSVC.FindCheckFn = func(ctx context.Context, f influxdb.CheckFilter) (influxdb.Check, error) {
|
|
if f.Name != nil && *f.Name == "display name" {
|
|
return existing, nil
|
|
}
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
svc := newTestService(WithCheckSVC(fakeCheckSVC))
|
|
|
|
impact, err := svc.DryRun(context.TODO(), influxdb.ID(100), 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
checks := impact.Diff.Checks
|
|
require.Len(t, checks, 2)
|
|
check0 := checks[0]
|
|
assert.True(t, check0.IsNew())
|
|
assert.Equal(t, "check-0", check0.PkgName)
|
|
assert.Zero(t, check0.ID)
|
|
assert.Nil(t, check0.Old)
|
|
|
|
check1 := checks[1]
|
|
assert.False(t, check1.IsNew())
|
|
assert.Equal(t, "check-1", check1.PkgName)
|
|
assert.Equal(t, "display name", check1.New.GetName())
|
|
assert.NotZero(t, check1.ID)
|
|
assert.Equal(t, existing, check1.Old.Check)
|
|
})
|
|
})
|
|
|
|
t.Run("with actions applied", func(t *testing.T) {
|
|
testDryRunActions(t, dryRunTestFields{
|
|
path: "testdata/checks.yml",
|
|
kinds: []Kind{KindCheck, KindCheckDeadman, KindCheckThreshold},
|
|
skipResources: []ActionSkipResource{
|
|
{
|
|
Kind: KindCheck,
|
|
MetaName: "check-0",
|
|
},
|
|
{
|
|
Kind: KindCheck,
|
|
MetaName: "check-1",
|
|
},
|
|
},
|
|
assertFn: func(t *testing.T, impact PkgImpactSummary) {
|
|
require.Empty(t, impact.Diff.Checks)
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("dashboards", func(t *testing.T) {
|
|
t.Run("with actions applied", func(t *testing.T) {
|
|
testDryRunActions(t, dryRunTestFields{
|
|
path: "testdata/dashboard.yml",
|
|
kinds: []Kind{KindDashboard},
|
|
skipResources: []ActionSkipResource{
|
|
{
|
|
Kind: KindDashboard,
|
|
MetaName: "dash-1",
|
|
},
|
|
{
|
|
Kind: KindDashboard,
|
|
MetaName: "dash-2",
|
|
},
|
|
},
|
|
assertFn: func(t *testing.T, impact PkgImpactSummary) {
|
|
require.Empty(t, impact.Diff.Dashboards)
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("labels", func(t *testing.T) {
|
|
t.Run("two labels updated", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/label.json", func(t *testing.T, pkg *Pkg) {
|
|
fakeLabelSVC := mock.NewLabelService()
|
|
fakeLabelSVC.FindLabelsFn = func(_ context.Context, filter influxdb.LabelFilter) ([]*influxdb.Label, error) {
|
|
return []*influxdb.Label{
|
|
{
|
|
ID: influxdb.ID(1),
|
|
Name: filter.Name,
|
|
Properties: map[string]string{
|
|
"color": "old color",
|
|
"description": "old description",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
svc := newTestService(WithLabelSVC(fakeLabelSVC))
|
|
|
|
impact, err := svc.DryRun(context.TODO(), influxdb.ID(100), 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, impact.Diff.Labels, 3)
|
|
|
|
expected := DiffLabel{
|
|
DiffIdentifier: DiffIdentifier{
|
|
ID: SafeID(1),
|
|
StateStatus: StateStatusExists,
|
|
PkgName: "label-1",
|
|
},
|
|
Old: &DiffLabelValues{
|
|
Name: "label-1",
|
|
Color: "old color",
|
|
Description: "old description",
|
|
},
|
|
New: DiffLabelValues{
|
|
Name: "label-1",
|
|
Color: "#FFFFFF",
|
|
Description: "label 1 description",
|
|
},
|
|
}
|
|
assert.Contains(t, impact.Diff.Labels, expected)
|
|
|
|
expected.PkgName = "label-2"
|
|
expected.New.Name = "label-2"
|
|
expected.New.Color = "#000000"
|
|
expected.New.Description = "label 2 description"
|
|
expected.Old.Name = "label-2"
|
|
assert.Contains(t, impact.Diff.Labels, expected)
|
|
})
|
|
})
|
|
|
|
t.Run("two labels created", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/label.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeLabelSVC := mock.NewLabelService()
|
|
fakeLabelSVC.FindLabelsFn = func(_ context.Context, filter influxdb.LabelFilter) ([]*influxdb.Label, error) {
|
|
return nil, errors.New("no labels found")
|
|
}
|
|
svc := newTestService(WithLabelSVC(fakeLabelSVC))
|
|
|
|
impact, err := svc.DryRun(context.TODO(), influxdb.ID(100), 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
labels := impact.Diff.Labels
|
|
require.Len(t, labels, 3)
|
|
|
|
expected := DiffLabel{
|
|
DiffIdentifier: DiffIdentifier{
|
|
PkgName: "label-1",
|
|
StateStatus: StateStatusNew,
|
|
},
|
|
New: DiffLabelValues{
|
|
Name: "label-1",
|
|
Color: "#FFFFFF",
|
|
Description: "label 1 description",
|
|
},
|
|
}
|
|
assert.Contains(t, labels, expected)
|
|
|
|
expected.PkgName = "label-2"
|
|
expected.New.Name = "label-2"
|
|
expected.New.Color = "#000000"
|
|
expected.New.Description = "label 2 description"
|
|
assert.Contains(t, labels, expected)
|
|
})
|
|
})
|
|
|
|
t.Run("with actions applied", func(t *testing.T) {
|
|
testDryRunActions(t, dryRunTestFields{
|
|
path: "testdata/label.yml",
|
|
kinds: []Kind{KindLabel},
|
|
skipResources: []ActionSkipResource{
|
|
{
|
|
Kind: KindLabel,
|
|
MetaName: "label-1",
|
|
},
|
|
{
|
|
Kind: KindLabel,
|
|
MetaName: "label-2",
|
|
},
|
|
{
|
|
Kind: KindLabel,
|
|
MetaName: "label-3",
|
|
},
|
|
},
|
|
assertFn: func(t *testing.T, impact PkgImpactSummary) {
|
|
require.Empty(t, impact.Diff.Labels)
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("notification endpoints", func(t *testing.T) {
|
|
t.Run("mixed update and created", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/notification_endpoint.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeEndpointSVC := mock.NewNotificationEndpointService()
|
|
id := influxdb.ID(1)
|
|
existing := &endpoint.HTTP{
|
|
Base: endpoint.Base{
|
|
ID: &id,
|
|
Name: "http-none-auth-notification-endpoint",
|
|
Description: "old desc",
|
|
Status: influxdb.TaskStatusInactive,
|
|
},
|
|
Method: "POST",
|
|
AuthMethod: "none",
|
|
URL: "https://www.example.com/endpoint/old",
|
|
}
|
|
fakeEndpointSVC.FindNotificationEndpointsF = func(ctx context.Context, f influxdb.NotificationEndpointFilter, opt ...influxdb.FindOptions) ([]influxdb.NotificationEndpoint, int, error) {
|
|
return []influxdb.NotificationEndpoint{existing}, 1, nil
|
|
}
|
|
|
|
svc := newTestService(WithNotificationEndpointSVC(fakeEndpointSVC))
|
|
|
|
impact, err := svc.DryRun(context.TODO(), influxdb.ID(100), 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, impact.Diff.NotificationEndpoints, 5)
|
|
|
|
var (
|
|
newEndpoints []DiffNotificationEndpoint
|
|
existingEndpoints []DiffNotificationEndpoint
|
|
)
|
|
for _, e := range impact.Diff.NotificationEndpoints {
|
|
if e.Old != nil {
|
|
existingEndpoints = append(existingEndpoints, e)
|
|
continue
|
|
}
|
|
newEndpoints = append(newEndpoints, e)
|
|
}
|
|
require.Len(t, newEndpoints, 4)
|
|
require.Len(t, existingEndpoints, 1)
|
|
|
|
expected := DiffNotificationEndpoint{
|
|
DiffIdentifier: DiffIdentifier{
|
|
ID: 1,
|
|
PkgName: "http-none-auth-notification-endpoint",
|
|
StateStatus: StateStatusExists,
|
|
},
|
|
Old: &DiffNotificationEndpointValues{
|
|
NotificationEndpoint: existing,
|
|
},
|
|
New: DiffNotificationEndpointValues{
|
|
NotificationEndpoint: &endpoint.HTTP{
|
|
Base: endpoint.Base{
|
|
ID: &id,
|
|
Name: "http-none-auth-notification-endpoint",
|
|
Description: "http none auth desc",
|
|
Status: influxdb.TaskStatusActive,
|
|
},
|
|
AuthMethod: "none",
|
|
Method: "GET",
|
|
URL: "https://www.example.com/endpoint/noneauth",
|
|
},
|
|
},
|
|
}
|
|
assert.Equal(t, expected, existingEndpoints[0])
|
|
})
|
|
})
|
|
|
|
t.Run("with actions applied", func(t *testing.T) {
|
|
testDryRunActions(t, dryRunTestFields{
|
|
path: "testdata/notification_endpoint.yml",
|
|
kinds: []Kind{
|
|
KindNotificationEndpoint,
|
|
KindNotificationEndpointHTTP,
|
|
KindNotificationEndpointPagerDuty,
|
|
KindNotificationEndpointSlack,
|
|
},
|
|
skipResources: []ActionSkipResource{
|
|
{
|
|
Kind: KindNotificationEndpoint,
|
|
MetaName: "http-none-auth-notification-endpoint",
|
|
},
|
|
{
|
|
Kind: KindNotificationEndpoint,
|
|
MetaName: "http-bearer-auth-notification-endpoint",
|
|
},
|
|
{
|
|
Kind: KindNotificationEndpointHTTP,
|
|
MetaName: "http-basic-auth-notification-endpoint",
|
|
},
|
|
{
|
|
Kind: KindNotificationEndpointSlack,
|
|
MetaName: "slack-notification-endpoint",
|
|
},
|
|
{
|
|
Kind: KindNotificationEndpointPagerDuty,
|
|
MetaName: "pager-duty-notification-endpoint",
|
|
},
|
|
},
|
|
assertFn: func(t *testing.T, impact PkgImpactSummary) {
|
|
require.Empty(t, impact.Diff.NotificationEndpoints)
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("notification rules", func(t *testing.T) {
|
|
t.Run("mixed update and created", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/notification_rule.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeEndpointSVC := mock.NewNotificationEndpointService()
|
|
id := influxdb.ID(1)
|
|
existing := &endpoint.HTTP{
|
|
Base: endpoint.Base{
|
|
ID: &id,
|
|
// This name here matches the endpoint identified in the pkg notification rule
|
|
Name: "endpoint-0",
|
|
Description: "old desc",
|
|
Status: influxdb.TaskStatusInactive,
|
|
},
|
|
Method: "POST",
|
|
AuthMethod: "none",
|
|
URL: "https://www.example.com/endpoint/old",
|
|
}
|
|
fakeEndpointSVC.FindNotificationEndpointsF = func(ctx context.Context, f influxdb.NotificationEndpointFilter, opt ...influxdb.FindOptions) ([]influxdb.NotificationEndpoint, int, error) {
|
|
return []influxdb.NotificationEndpoint{existing}, 1, nil
|
|
}
|
|
|
|
svc := newTestService(WithNotificationEndpointSVC(fakeEndpointSVC))
|
|
|
|
impact, err := svc.DryRun(context.TODO(), influxdb.ID(100), 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, impact.Diff.NotificationRules, 1)
|
|
|
|
actual := impact.Diff.NotificationRules[0].New
|
|
assert.Equal(t, "rule_0", actual.Name)
|
|
assert.Equal(t, "desc_0", actual.Description)
|
|
assert.Equal(t, "slack", actual.EndpointType)
|
|
assert.Equal(t, existing.Name, actual.EndpointName)
|
|
assert.Equal(t, SafeID(*existing.ID), actual.EndpointID)
|
|
assert.Equal(t, (10 * time.Minute).String(), actual.Every)
|
|
assert.Equal(t, (30 * time.Second).String(), actual.Offset)
|
|
|
|
expectedStatusRules := []SummaryStatusRule{
|
|
{CurrentLevel: "CRIT", PreviousLevel: "OK"},
|
|
{CurrentLevel: "WARN"},
|
|
}
|
|
assert.Equal(t, expectedStatusRules, actual.StatusRules)
|
|
|
|
expectedTagRules := []SummaryTagRule{
|
|
{Key: "k1", Value: "v1", Operator: "equal"},
|
|
{Key: "k1", Value: "v2", Operator: "equal"},
|
|
}
|
|
assert.Equal(t, expectedTagRules, actual.TagRules)
|
|
})
|
|
})
|
|
|
|
t.Run("with actions applied", func(t *testing.T) {
|
|
testDryRunActions(t, dryRunTestFields{
|
|
path: "testdata/notification_rule.yml",
|
|
kinds: []Kind{KindNotificationRule},
|
|
skipResources: []ActionSkipResource{
|
|
{
|
|
Kind: KindNotificationRule,
|
|
MetaName: "rule-uuid",
|
|
},
|
|
},
|
|
assertFn: func(t *testing.T, impact PkgImpactSummary) {
|
|
require.Empty(t, impact.Diff.NotificationRules)
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("secrets not returns missing secrets", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/notification_endpoint_secrets.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeSecretSVC := mock.NewSecretService()
|
|
fakeSecretSVC.GetSecretKeysFn = func(ctx context.Context, orgID influxdb.ID) ([]string, error) {
|
|
return []string{"rando-1", "rando-2"}, nil
|
|
}
|
|
svc := newTestService(WithSecretSVC(fakeSecretSVC))
|
|
|
|
impact, err := svc.DryRun(context.TODO(), influxdb.ID(100), 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{"routing-key"}, impact.Summary.MissingSecrets)
|
|
})
|
|
})
|
|
|
|
t.Run("tasks", func(t *testing.T) {
|
|
t.Run("with actions applied", func(t *testing.T) {
|
|
testDryRunActions(t, dryRunTestFields{
|
|
path: "testdata/tasks.yml",
|
|
kinds: []Kind{KindTask},
|
|
skipResources: []ActionSkipResource{
|
|
{
|
|
Kind: KindTask,
|
|
MetaName: "task-uuid",
|
|
},
|
|
{
|
|
Kind: KindTask,
|
|
MetaName: "task-1",
|
|
},
|
|
},
|
|
assertFn: func(t *testing.T, impact PkgImpactSummary) {
|
|
require.Empty(t, impact.Diff.Tasks)
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("telegraf configs", func(t *testing.T) {
|
|
t.Run("with actions applied", func(t *testing.T) {
|
|
testDryRunActions(t, dryRunTestFields{
|
|
path: "testdata/telegraf.yml",
|
|
kinds: []Kind{KindTelegraf},
|
|
skipResources: []ActionSkipResource{
|
|
{
|
|
Kind: KindTelegraf,
|
|
MetaName: "first-tele-config",
|
|
},
|
|
{
|
|
Kind: KindTelegraf,
|
|
MetaName: "tele-2",
|
|
},
|
|
},
|
|
assertFn: func(t *testing.T, impact PkgImpactSummary) {
|
|
require.Empty(t, impact.Diff.Telegrafs)
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("variables", func(t *testing.T) {
|
|
t.Run("mixed update and created", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/variables.json", func(t *testing.T, pkg *Pkg) {
|
|
fakeVarSVC := mock.NewVariableService()
|
|
fakeVarSVC.FindVariablesF = func(_ context.Context, filter influxdb.VariableFilter, opts ...influxdb.FindOptions) ([]*influxdb.Variable, error) {
|
|
return []*influxdb.Variable{
|
|
{
|
|
ID: influxdb.ID(1),
|
|
Name: "var-const-3",
|
|
Description: "old desc",
|
|
},
|
|
}, nil
|
|
}
|
|
svc := newTestService(WithVariableSVC(fakeVarSVC))
|
|
|
|
impact, err := svc.DryRun(context.TODO(), influxdb.ID(100), 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
variables := impact.Diff.Variables
|
|
require.Len(t, variables, 4)
|
|
|
|
expected := DiffVariable{
|
|
DiffIdentifier: DiffIdentifier{
|
|
ID: 1,
|
|
PkgName: "var-const-3",
|
|
StateStatus: StateStatusExists,
|
|
},
|
|
Old: &DiffVariableValues{
|
|
Name: "var-const-3",
|
|
Description: "old desc",
|
|
},
|
|
New: DiffVariableValues{
|
|
Name: "var-const-3",
|
|
Description: "var-const-3 desc",
|
|
Args: &influxdb.VariableArguments{
|
|
Type: "constant",
|
|
Values: influxdb.VariableConstantValues{"first val"},
|
|
},
|
|
},
|
|
}
|
|
assert.Equal(t, expected, variables[0])
|
|
|
|
expected = DiffVariable{
|
|
DiffIdentifier: DiffIdentifier{
|
|
// no ID here since this one would be new
|
|
PkgName: "var-map-4",
|
|
StateStatus: StateStatusNew,
|
|
},
|
|
New: DiffVariableValues{
|
|
Name: "var-map-4",
|
|
Description: "var-map-4 desc",
|
|
Args: &influxdb.VariableArguments{
|
|
Type: "map",
|
|
Values: influxdb.VariableMapValues{"k1": "v1"},
|
|
},
|
|
},
|
|
}
|
|
assert.Equal(t, expected, variables[1])
|
|
})
|
|
})
|
|
|
|
t.Run("with actions applied", func(t *testing.T) {
|
|
testDryRunActions(t, dryRunTestFields{
|
|
path: "testdata/variables.yml",
|
|
kinds: []Kind{KindVariable},
|
|
skipResources: []ActionSkipResource{
|
|
{
|
|
Kind: KindVariable,
|
|
MetaName: "var-query-1",
|
|
},
|
|
{
|
|
Kind: KindVariable,
|
|
MetaName: "var-query-2",
|
|
},
|
|
{
|
|
Kind: KindVariable,
|
|
MetaName: "var-const-3",
|
|
},
|
|
{
|
|
Kind: KindVariable,
|
|
MetaName: "var-map-4",
|
|
},
|
|
},
|
|
assertFn: func(t *testing.T, impact PkgImpactSummary) {
|
|
require.Empty(t, impact.Diff.Variables)
|
|
},
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("Apply", func(t *testing.T) {
|
|
t.Run("buckets", func(t *testing.T) {
|
|
t.Run("successfully creates pkg of buckets", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/bucket.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeBktSVC := mock.NewBucketService()
|
|
fakeBktSVC.CreateBucketFn = func(_ context.Context, b *influxdb.Bucket) error {
|
|
b.ID = influxdb.ID(b.RetentionPeriod)
|
|
return nil
|
|
}
|
|
fakeBktSVC.FindBucketByNameFn = func(_ context.Context, id influxdb.ID, s string) (*influxdb.Bucket, error) {
|
|
// forces the bucket to be created a new
|
|
return nil, errors.New("an error")
|
|
}
|
|
fakeBktSVC.UpdateBucketFn = func(_ context.Context, id influxdb.ID, upd influxdb.BucketUpdate) (*influxdb.Bucket, error) {
|
|
return &influxdb.Bucket{ID: id}, nil
|
|
}
|
|
|
|
svc := newTestService(WithBucketSVC(fakeBktSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.Buckets, 2)
|
|
|
|
expected := SummaryBucket{
|
|
ID: SafeID(time.Hour),
|
|
OrgID: SafeID(orgID),
|
|
PkgName: "rucket-11",
|
|
Name: "rucket-11",
|
|
Description: "bucket 1 description",
|
|
RetentionPeriod: time.Hour,
|
|
LabelAssociations: []SummaryLabel{},
|
|
EnvReferences: []SummaryReference{},
|
|
}
|
|
assert.Contains(t, sum.Buckets, expected)
|
|
})
|
|
})
|
|
|
|
t.Run("will not apply bucket if no changes to be applied", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/bucket.yml", func(t *testing.T, pkg *Pkg) {
|
|
orgID := influxdb.ID(9000)
|
|
|
|
fakeBktSVC := mock.NewBucketService()
|
|
fakeBktSVC.FindBucketByNameFn = func(ctx context.Context, oid influxdb.ID, name string) (*influxdb.Bucket, error) {
|
|
if orgID != oid {
|
|
return nil, errors.New("invalid org id")
|
|
}
|
|
|
|
id := influxdb.ID(3)
|
|
if name == "display name" {
|
|
id = 4
|
|
name = "rucket-22"
|
|
}
|
|
if bkt, ok := pkg.mBuckets[name]; ok {
|
|
return &influxdb.Bucket{
|
|
ID: id,
|
|
OrgID: oid,
|
|
Name: bkt.Name(),
|
|
Description: bkt.Description,
|
|
RetentionPeriod: bkt.RetentionRules.RP(),
|
|
}, nil
|
|
}
|
|
return nil, errors.New("not found")
|
|
}
|
|
fakeBktSVC.UpdateBucketFn = func(_ context.Context, id influxdb.ID, upd influxdb.BucketUpdate) (*influxdb.Bucket, error) {
|
|
return &influxdb.Bucket{ID: id}, nil
|
|
}
|
|
|
|
svc := newTestService(WithBucketSVC(fakeBktSVC))
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.Buckets, 2)
|
|
|
|
expected := SummaryBucket{
|
|
ID: SafeID(3),
|
|
OrgID: SafeID(orgID),
|
|
PkgName: "rucket-11",
|
|
Name: "rucket-11",
|
|
Description: "bucket 1 description",
|
|
RetentionPeriod: time.Hour,
|
|
LabelAssociations: []SummaryLabel{},
|
|
EnvReferences: []SummaryReference{},
|
|
}
|
|
assert.Contains(t, sum.Buckets, expected)
|
|
assert.Zero(t, fakeBktSVC.CreateBucketCalls.Count())
|
|
assert.Zero(t, fakeBktSVC.UpdateBucketCalls.Count())
|
|
})
|
|
})
|
|
|
|
t.Run("rolls back all created buckets on an error", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/bucket.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeBktSVC := mock.NewBucketService()
|
|
fakeBktSVC.FindBucketByNameFn = func(_ context.Context, id influxdb.ID, s string) (*influxdb.Bucket, error) {
|
|
// forces the bucket to be created a new
|
|
return nil, errors.New("an error")
|
|
}
|
|
fakeBktSVC.CreateBucketFn = func(_ context.Context, b *influxdb.Bucket) error {
|
|
if fakeBktSVC.CreateBucketCalls.Count() == 1 {
|
|
return errors.New("blowed up ")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithBucketSVC(fakeBktSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.Error(t, err)
|
|
|
|
assert.GreaterOrEqual(t, fakeBktSVC.DeleteBucketCalls.Count(), 1)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("checks", func(t *testing.T) {
|
|
t.Run("successfully creates pkg of checks", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/checks.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeCheckSVC := mock.NewCheckService()
|
|
fakeCheckSVC.CreateCheckFn = func(ctx context.Context, c influxdb.CheckCreate, id influxdb.ID) error {
|
|
c.SetID(influxdb.ID(fakeCheckSVC.CreateCheckCalls.Count() + 1))
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithCheckSVC(fakeCheckSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.Checks, 2)
|
|
|
|
containsWithID := func(t *testing.T, name string) {
|
|
t.Helper()
|
|
|
|
for _, actualNotification := range sum.Checks {
|
|
actual := actualNotification.Check
|
|
if actual.GetID() == 0 {
|
|
assert.NotZero(t, actual.GetID())
|
|
}
|
|
if actual.GetName() == name {
|
|
return
|
|
}
|
|
}
|
|
assert.Fail(t, "did not find notification by name: "+name)
|
|
}
|
|
|
|
for _, expectedName := range []string{"check-0", "display name"} {
|
|
containsWithID(t, expectedName)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("rolls back all created checks on an error", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/checks.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeCheckSVC := mock.NewCheckService()
|
|
fakeCheckSVC.CreateCheckFn = func(ctx context.Context, c influxdb.CheckCreate, id influxdb.ID) error {
|
|
c.SetID(influxdb.ID(fakeCheckSVC.CreateCheckCalls.Count() + 1))
|
|
if fakeCheckSVC.CreateCheckCalls.Count() == 1 {
|
|
return errors.New("hit that kill count")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithCheckSVC(fakeCheckSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.Error(t, err)
|
|
|
|
assert.GreaterOrEqual(t, fakeCheckSVC.DeleteCheckCalls.Count(), 1)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("labels", func(t *testing.T) {
|
|
t.Run("successfully creates pkg of labels", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/label.json", func(t *testing.T, pkg *Pkg) {
|
|
fakeLabelSVC := mock.NewLabelService()
|
|
fakeLabelSVC.CreateLabelFn = func(_ context.Context, l *influxdb.Label) error {
|
|
i, err := strconv.Atoi(l.Name[len(l.Name)-1:])
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
l.ID = influxdb.ID(i)
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithLabelSVC(fakeLabelSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.Labels, 3)
|
|
|
|
expectedLabel := sumLabelGen("label-1", "label-1", "#FFFFFF", "label 1 description")
|
|
expectedLabel.ID = 1
|
|
expectedLabel.OrgID = SafeID(orgID)
|
|
assert.Contains(t, sum.Labels, expectedLabel)
|
|
|
|
expectedLabel = sumLabelGen("label-2", "label-2", "#000000", "label 2 description")
|
|
expectedLabel.ID = 2
|
|
expectedLabel.OrgID = SafeID(orgID)
|
|
assert.Contains(t, sum.Labels, expectedLabel)
|
|
})
|
|
})
|
|
|
|
t.Run("rolls back all created labels on an error", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/label", func(t *testing.T, pkg *Pkg) {
|
|
fakeLabelSVC := mock.NewLabelService()
|
|
fakeLabelSVC.CreateLabelFn = func(_ context.Context, l *influxdb.Label) error {
|
|
// 3rd/4th label will return the error here, and 2 before should be rolled back
|
|
if fakeLabelSVC.CreateLabelCalls.Count() == 2 {
|
|
return errors.New("blowed up ")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithLabelSVC(fakeLabelSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.Error(t, err)
|
|
|
|
assert.GreaterOrEqual(t, fakeLabelSVC.DeleteLabelCalls.Count(), 1)
|
|
})
|
|
})
|
|
|
|
t.Run("will not apply label if no changes to be applied", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/label.yml", func(t *testing.T, pkg *Pkg) {
|
|
orgID := influxdb.ID(9000)
|
|
|
|
stubExisting := func(name string, id influxdb.ID) *influxdb.Label {
|
|
pkgLabel := pkg.mLabels[name]
|
|
return &influxdb.Label{
|
|
// makes all pkg changes same as they are on the existing
|
|
ID: id,
|
|
OrgID: orgID,
|
|
Name: pkgLabel.Name(),
|
|
Properties: map[string]string{
|
|
"color": pkgLabel.Color,
|
|
"description": pkgLabel.Description,
|
|
},
|
|
}
|
|
}
|
|
stubExisting("label-1", 1)
|
|
stubExisting("label-3", 3)
|
|
|
|
fakeLabelSVC := mock.NewLabelService()
|
|
fakeLabelSVC.FindLabelsFn = func(ctx context.Context, f influxdb.LabelFilter) ([]*influxdb.Label, error) {
|
|
if f.Name != "label-1" && f.Name != "display name" {
|
|
return nil, nil
|
|
}
|
|
id := influxdb.ID(1)
|
|
name := f.Name
|
|
if f.Name == "display name" {
|
|
id = 3
|
|
name = "label-3"
|
|
}
|
|
return []*influxdb.Label{stubExisting(name, id)}, nil
|
|
}
|
|
fakeLabelSVC.CreateLabelFn = func(_ context.Context, l *influxdb.Label) error {
|
|
if l.Name == "label-2" {
|
|
l.ID = 2
|
|
}
|
|
return nil
|
|
}
|
|
fakeLabelSVC.UpdateLabelFn = func(_ context.Context, id influxdb.ID, l influxdb.LabelUpdate) (*influxdb.Label, error) {
|
|
if id == influxdb.ID(3) {
|
|
return nil, errors.New("invalid id provided")
|
|
}
|
|
return &influxdb.Label{ID: id}, nil
|
|
}
|
|
|
|
svc := newTestService(WithLabelSVC(fakeLabelSVC))
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.Labels, 3)
|
|
|
|
expectedLabel := sumLabelGen("label-1", "label-1", "#FFFFFF", "label 1 description")
|
|
expectedLabel.ID = 1
|
|
expectedLabel.OrgID = SafeID(orgID)
|
|
assert.Contains(t, sum.Labels, expectedLabel)
|
|
|
|
expectedLabel = sumLabelGen("label-2", "label-2", "#000000", "label 2 description")
|
|
expectedLabel.ID = 2
|
|
expectedLabel.OrgID = SafeID(orgID)
|
|
assert.Contains(t, sum.Labels, expectedLabel)
|
|
|
|
assert.Equal(t, 1, fakeLabelSVC.CreateLabelCalls.Count()) // only called for second label
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("dashboards", func(t *testing.T) {
|
|
t.Run("successfully creates a dashboard", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/dashboard.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeDashSVC := mock.NewDashboardService()
|
|
fakeDashSVC.CreateDashboardF = func(_ context.Context, d *influxdb.Dashboard) error {
|
|
d.ID = influxdb.ID(1)
|
|
return nil
|
|
}
|
|
fakeDashSVC.UpdateDashboardCellViewF = func(ctx context.Context, dID influxdb.ID, cID influxdb.ID, upd influxdb.ViewUpdate) (*influxdb.View, error) {
|
|
return &influxdb.View{}, nil
|
|
}
|
|
|
|
svc := newTestService(WithDashboardSVC(fakeDashSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
|
|
require.Len(t, sum.Dashboards, 2)
|
|
dash1 := sum.Dashboards[0]
|
|
assert.NotZero(t, dash1.ID)
|
|
assert.NotZero(t, dash1.OrgID)
|
|
assert.Equal(t, "dash-1", dash1.PkgName)
|
|
assert.Equal(t, "display name", dash1.Name)
|
|
require.Len(t, dash1.Charts, 1)
|
|
|
|
dash2 := sum.Dashboards[1]
|
|
assert.NotZero(t, dash2.ID)
|
|
assert.Equal(t, "dash-2", dash2.PkgName)
|
|
assert.Equal(t, "dash-2", dash2.Name)
|
|
require.Empty(t, dash2.Charts)
|
|
})
|
|
})
|
|
|
|
t.Run("rolls back created dashboard on an error", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/dashboard.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeDashSVC := mock.NewDashboardService()
|
|
fakeDashSVC.CreateDashboardF = func(_ context.Context, d *influxdb.Dashboard) error {
|
|
// error out on second dashboard attempted
|
|
if fakeDashSVC.CreateDashboardCalls.Count() == 1 {
|
|
return errors.New("blowed up ")
|
|
}
|
|
d.ID = influxdb.ID(1)
|
|
return nil
|
|
}
|
|
deletedDashs := make(map[influxdb.ID]bool)
|
|
fakeDashSVC.DeleteDashboardF = func(_ context.Context, id influxdb.ID) error {
|
|
deletedDashs[id] = true
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithDashboardSVC(fakeDashSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.Error(t, err)
|
|
|
|
assert.True(t, deletedDashs[1])
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("label mapping", func(t *testing.T) {
|
|
testLabelMappingApplyFn := func(t *testing.T, filename string, numExpected int, settersFn func() []ServiceSetterFn) {
|
|
t.Helper()
|
|
testfileRunner(t, filename, func(t *testing.T, pkg *Pkg) {
|
|
t.Helper()
|
|
|
|
fakeLabelSVC := mock.NewLabelService()
|
|
fakeLabelSVC.CreateLabelFn = func(_ context.Context, l *influxdb.Label) error {
|
|
l.ID = influxdb.ID(rand.Int())
|
|
return nil
|
|
}
|
|
fakeLabelSVC.CreateLabelMappingFn = func(_ context.Context, mapping *influxdb.LabelMapping) error {
|
|
if mapping.ResourceID == 0 {
|
|
return errors.New("did not get a resource ID")
|
|
}
|
|
if mapping.ResourceType == "" {
|
|
return errors.New("did not get a resource type")
|
|
}
|
|
return nil
|
|
}
|
|
svc := newTestService(append(settersFn(),
|
|
WithLabelSVC(fakeLabelSVC),
|
|
WithLogger(zaptest.NewLogger(t)),
|
|
)...)
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, numExpected, fakeLabelSVC.CreateLabelMappingCalls.Count())
|
|
})
|
|
}
|
|
|
|
testLabelMappingRollbackFn := func(t *testing.T, filename string, killCount int, settersFn func() []ServiceSetterFn) {
|
|
t.Helper()
|
|
testfileRunner(t, filename, func(t *testing.T, pkg *Pkg) {
|
|
t.Helper()
|
|
|
|
fakeLabelSVC := mock.NewLabelService()
|
|
fakeLabelSVC.CreateLabelFn = func(_ context.Context, l *influxdb.Label) error {
|
|
l.ID = influxdb.ID(fakeLabelSVC.CreateLabelCalls.Count() + 1)
|
|
return nil
|
|
}
|
|
fakeLabelSVC.CreateLabelMappingFn = func(_ context.Context, mapping *influxdb.LabelMapping) error {
|
|
if mapping.ResourceID == 0 {
|
|
return errors.New("did not get a resource ID")
|
|
}
|
|
if mapping.ResourceType == "" {
|
|
return errors.New("did not get a resource type")
|
|
}
|
|
if fakeLabelSVC.CreateLabelMappingCalls.Count() == killCount {
|
|
return errors.New("hit last label")
|
|
}
|
|
return nil
|
|
}
|
|
svc := newTestService(append(settersFn(),
|
|
WithLabelSVC(fakeLabelSVC),
|
|
WithLogger(zaptest.NewLogger(t)),
|
|
)...)
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.Error(t, err)
|
|
|
|
assert.GreaterOrEqual(t, fakeLabelSVC.DeleteLabelMappingCalls.Count(), killCount)
|
|
})
|
|
}
|
|
|
|
t.Run("maps buckets with labels", func(t *testing.T) {
|
|
bktOpt := func() []ServiceSetterFn {
|
|
fakeBktSVC := mock.NewBucketService()
|
|
fakeBktSVC.CreateBucketFn = func(_ context.Context, b *influxdb.Bucket) error {
|
|
b.ID = influxdb.ID(rand.Int())
|
|
return nil
|
|
}
|
|
fakeBktSVC.FindBucketByNameFn = func(_ context.Context, id influxdb.ID, s string) (*influxdb.Bucket, error) {
|
|
// forces the bucket to be created a new
|
|
return nil, errors.New("an error")
|
|
}
|
|
return []ServiceSetterFn{WithBucketSVC(fakeBktSVC)}
|
|
}
|
|
|
|
t.Run("applies successfully", func(t *testing.T) {
|
|
testLabelMappingApplyFn(t, "testdata/bucket_associates_label.yml", 4, bktOpt)
|
|
})
|
|
|
|
t.Run("deletes new label mappings on error", func(t *testing.T) {
|
|
testLabelMappingRollbackFn(t, "testdata/bucket_associates_label.yml", 2, bktOpt)
|
|
})
|
|
})
|
|
|
|
t.Run("maps checks with labels", func(t *testing.T) {
|
|
opts := func() []ServiceSetterFn {
|
|
fakeCheckSVC := mock.NewCheckService()
|
|
fakeCheckSVC.CreateCheckFn = func(ctx context.Context, c influxdb.CheckCreate, id influxdb.ID) error {
|
|
c.Check.SetID(influxdb.ID(rand.Int()))
|
|
return nil
|
|
}
|
|
fakeCheckSVC.FindCheckFn = func(ctx context.Context, f influxdb.CheckFilter) (influxdb.Check, error) {
|
|
return nil, errors.New("check not found")
|
|
}
|
|
|
|
return []ServiceSetterFn{WithCheckSVC(fakeCheckSVC)}
|
|
}
|
|
|
|
t.Run("applies successfully", func(t *testing.T) {
|
|
testLabelMappingApplyFn(t, "testdata/checks.yml", 2, opts)
|
|
})
|
|
|
|
t.Run("deletes new label mappings on error", func(t *testing.T) {
|
|
testLabelMappingRollbackFn(t, "testdata/checks.yml", 1, opts)
|
|
})
|
|
})
|
|
|
|
t.Run("maps dashboards with labels", func(t *testing.T) {
|
|
opts := func() []ServiceSetterFn {
|
|
fakeDashSVC := mock.NewDashboardService()
|
|
fakeDashSVC.CreateDashboardF = func(_ context.Context, d *influxdb.Dashboard) error {
|
|
d.ID = influxdb.ID(rand.Int())
|
|
return nil
|
|
}
|
|
return []ServiceSetterFn{WithDashboardSVC(fakeDashSVC)}
|
|
}
|
|
|
|
t.Run("applies successfully", func(t *testing.T) {
|
|
testLabelMappingApplyFn(t, "testdata/dashboard_associates_label.yml", 2, opts)
|
|
})
|
|
|
|
t.Run("deletes new label mappings on error", func(t *testing.T) {
|
|
testLabelMappingRollbackFn(t, "testdata/dashboard_associates_label.yml", 1, opts)
|
|
})
|
|
})
|
|
|
|
t.Run("maps notification endpoints with labels", func(t *testing.T) {
|
|
opts := func() []ServiceSetterFn {
|
|
fakeEndpointSVC := mock.NewNotificationEndpointService()
|
|
fakeEndpointSVC.CreateNotificationEndpointF = func(ctx context.Context, nr influxdb.NotificationEndpoint, userID influxdb.ID) error {
|
|
nr.SetID(influxdb.ID(rand.Int()))
|
|
return nil
|
|
}
|
|
return []ServiceSetterFn{WithNotificationEndpointSVC(fakeEndpointSVC)}
|
|
}
|
|
|
|
t.Run("applies successfully", func(t *testing.T) {
|
|
testLabelMappingApplyFn(t, "testdata/notification_endpoint.yml", 5, opts)
|
|
})
|
|
|
|
t.Run("deletes new label mappings on error", func(t *testing.T) {
|
|
testLabelMappingRollbackFn(t, "testdata/notification_endpoint.yml", 3, opts)
|
|
})
|
|
})
|
|
|
|
t.Run("maps notification rules with labels", func(t *testing.T) {
|
|
opts := func() []ServiceSetterFn {
|
|
fakeRuleStore := mock.NewNotificationRuleStore()
|
|
fakeRuleStore.CreateNotificationRuleF = func(ctx context.Context, nr influxdb.NotificationRuleCreate, userID influxdb.ID) error {
|
|
nr.SetID(influxdb.ID(fakeRuleStore.CreateNotificationRuleCalls.Count() + 1))
|
|
return nil
|
|
}
|
|
return []ServiceSetterFn{
|
|
WithNotificationRuleSVC(fakeRuleStore),
|
|
}
|
|
}
|
|
|
|
t.Run("applies successfully", func(t *testing.T) {
|
|
testLabelMappingApplyFn(t, "testdata/notification_rule.yml", 2, opts)
|
|
})
|
|
|
|
t.Run("deletes new label mappings on error", func(t *testing.T) {
|
|
testLabelMappingRollbackFn(t, "testdata/notification_rule.yml", 1, opts)
|
|
})
|
|
})
|
|
|
|
t.Run("maps tasks with labels", func(t *testing.T) {
|
|
opts := func() []ServiceSetterFn {
|
|
fakeTaskSVC := mock.NewTaskService()
|
|
fakeTaskSVC.CreateTaskFn = func(ctx context.Context, tc influxdb.TaskCreate) (*influxdb.Task, error) {
|
|
reg := regexp.MustCompile(`name: "(.+)",`)
|
|
names := reg.FindStringSubmatch(tc.Flux)
|
|
if len(names) < 2 {
|
|
return nil, errors.New("bad flux query provided: " + tc.Flux)
|
|
}
|
|
return &influxdb.Task{
|
|
ID: influxdb.ID(rand.Int()),
|
|
Type: tc.Type,
|
|
OrganizationID: tc.OrganizationID,
|
|
OwnerID: tc.OwnerID,
|
|
Name: names[1],
|
|
Description: tc.Description,
|
|
Status: tc.Status,
|
|
Flux: tc.Flux,
|
|
}, nil
|
|
}
|
|
return []ServiceSetterFn{WithTaskSVC(fakeTaskSVC)}
|
|
}
|
|
|
|
t.Run("applies successfully", func(t *testing.T) {
|
|
testLabelMappingApplyFn(t, "testdata/tasks.yml", 2, opts)
|
|
})
|
|
|
|
t.Run("deletes new label mappings on error", func(t *testing.T) {
|
|
testLabelMappingRollbackFn(t, "testdata/tasks.yml", 1, opts)
|
|
})
|
|
})
|
|
|
|
t.Run("maps telegrafs with labels", func(t *testing.T) {
|
|
opts := func() []ServiceSetterFn {
|
|
fakeTeleSVC := mock.NewTelegrafConfigStore()
|
|
fakeTeleSVC.CreateTelegrafConfigF = func(_ context.Context, cfg *influxdb.TelegrafConfig, _ influxdb.ID) error {
|
|
cfg.ID = influxdb.ID(rand.Int())
|
|
return nil
|
|
}
|
|
return []ServiceSetterFn{WithTelegrafSVC(fakeTeleSVC)}
|
|
}
|
|
|
|
t.Run("applies successfully", func(t *testing.T) {
|
|
testLabelMappingApplyFn(t, "testdata/telegraf.yml", 2, opts)
|
|
})
|
|
|
|
t.Run("deletes new label mappings on error", func(t *testing.T) {
|
|
testLabelMappingRollbackFn(t, "testdata/telegraf.yml", 1, opts)
|
|
})
|
|
})
|
|
|
|
t.Run("maps variables with labels", func(t *testing.T) {
|
|
opt := func() []ServiceSetterFn {
|
|
fakeVarSVC := mock.NewVariableService()
|
|
fakeVarSVC.CreateVariableF = func(_ context.Context, v *influxdb.Variable) error {
|
|
v.ID = influxdb.ID(rand.Int())
|
|
return nil
|
|
}
|
|
return []ServiceSetterFn{WithVariableSVC(fakeVarSVC)}
|
|
}
|
|
|
|
t.Run("applies successfully", func(t *testing.T) {
|
|
testLabelMappingApplyFn(t, "testdata/variable_associates_label.yml", 1, opt)
|
|
})
|
|
|
|
t.Run("deletes new label mappings on error", func(t *testing.T) {
|
|
testLabelMappingRollbackFn(t, "testdata/variable_associates_label.yml", 0, opt)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("notification endpoints", func(t *testing.T) {
|
|
t.Run("successfully creates pkg of endpoints", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/notification_endpoint.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeEndpointSVC := mock.NewNotificationEndpointService()
|
|
fakeEndpointSVC.CreateNotificationEndpointF = func(ctx context.Context, nr influxdb.NotificationEndpoint, userID influxdb.ID) error {
|
|
nr.SetID(influxdb.ID(fakeEndpointSVC.CreateNotificationEndpointCalls.Count() + 1))
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithNotificationEndpointSVC(fakeEndpointSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.NotificationEndpoints, 5)
|
|
|
|
containsWithID := func(t *testing.T, name string) {
|
|
var endpoints []string
|
|
for _, actualNotification := range sum.NotificationEndpoints {
|
|
actual := actualNotification.NotificationEndpoint
|
|
if actual.GetID() == 0 {
|
|
assert.NotZero(t, actual.GetID())
|
|
}
|
|
if actual.GetName() == name {
|
|
return
|
|
}
|
|
endpoints = append(endpoints, fmt.Sprintf("%+v", actual))
|
|
}
|
|
assert.Failf(t, "did not find notification by name: "+name, "endpoints received: %s", endpoints)
|
|
}
|
|
|
|
expectedNames := []string{
|
|
"basic endpoint name",
|
|
"http-bearer-auth-notification-endpoint",
|
|
"http-none-auth-notification-endpoint",
|
|
"pager duty name",
|
|
"slack name",
|
|
}
|
|
for _, expectedName := range expectedNames {
|
|
containsWithID(t, expectedName)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("rolls back all created notifications on an error", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/notification_endpoint.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeEndpointSVC := mock.NewNotificationEndpointService()
|
|
fakeEndpointSVC.CreateNotificationEndpointF = func(ctx context.Context, nr influxdb.NotificationEndpoint, userID influxdb.ID) error {
|
|
nr.SetID(influxdb.ID(fakeEndpointSVC.CreateNotificationEndpointCalls.Count() + 1))
|
|
if fakeEndpointSVC.CreateNotificationEndpointCalls.Count() == 3 {
|
|
return errors.New("hit that kill count")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithNotificationEndpointSVC(fakeEndpointSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.Error(t, err)
|
|
|
|
assert.GreaterOrEqual(t, fakeEndpointSVC.DeleteNotificationEndpointCalls.Count(), 3)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("notification rules", func(t *testing.T) {
|
|
t.Run("successfully creates", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/notification_rule.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeEndpointSVC := mock.NewNotificationEndpointService()
|
|
fakeEndpointSVC.CreateNotificationEndpointF = func(ctx context.Context, nr influxdb.NotificationEndpoint, userID influxdb.ID) error {
|
|
nr.SetID(influxdb.ID(fakeEndpointSVC.CreateNotificationEndpointCalls.Count() + 1))
|
|
return nil
|
|
}
|
|
fakeRuleStore := mock.NewNotificationRuleStore()
|
|
fakeRuleStore.CreateNotificationRuleF = func(ctx context.Context, nr influxdb.NotificationRuleCreate, userID influxdb.ID) error {
|
|
nr.SetID(influxdb.ID(fakeRuleStore.CreateNotificationRuleCalls.Count() + 1))
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(
|
|
WithNotificationEndpointSVC(fakeEndpointSVC),
|
|
WithNotificationRuleSVC(fakeRuleStore),
|
|
)
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.NotificationRules, 1)
|
|
assert.Equal(t, "rule-uuid", sum.NotificationRules[0].PkgName)
|
|
assert.Equal(t, "rule_0", sum.NotificationRules[0].Name)
|
|
assert.Equal(t, "desc_0", sum.NotificationRules[0].Description)
|
|
assert.Equal(t, SafeID(1), sum.NotificationRules[0].EndpointID)
|
|
assert.Equal(t, "endpoint-0", sum.NotificationRules[0].EndpointPkgName)
|
|
assert.Equal(t, "slack", sum.NotificationRules[0].EndpointType)
|
|
})
|
|
})
|
|
|
|
t.Run("rolls back all created notification rules on an error", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/notification_rule.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeRuleStore := mock.NewNotificationRuleStore()
|
|
fakeRuleStore.CreateNotificationRuleF = func(ctx context.Context, nr influxdb.NotificationRuleCreate, userID influxdb.ID) error {
|
|
nr.SetID(influxdb.ID(fakeRuleStore.CreateNotificationRuleCalls.Count() + 1))
|
|
return nil
|
|
}
|
|
fakeRuleStore.DeleteNotificationRuleF = func(ctx context.Context, id influxdb.ID) error {
|
|
if id != 1 {
|
|
return errors.New("wrong id here")
|
|
}
|
|
return nil
|
|
}
|
|
fakeLabelSVC := mock.NewLabelService()
|
|
fakeLabelSVC.CreateLabelFn = func(ctx context.Context, l *influxdb.Label) error {
|
|
l.ID = influxdb.ID(fakeLabelSVC.CreateLabelCalls.Count() + 1)
|
|
return nil
|
|
}
|
|
fakeLabelSVC.CreateLabelMappingFn = func(ctx context.Context, m *influxdb.LabelMapping) error {
|
|
return errors.New("start the rollack")
|
|
}
|
|
|
|
svc := newTestService(
|
|
WithLabelSVC(fakeLabelSVC),
|
|
WithNotificationRuleSVC(fakeRuleStore),
|
|
)
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.Error(t, err)
|
|
|
|
assert.Equal(t, 1, fakeRuleStore.DeleteNotificationRuleCalls.Count())
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("tasks", func(t *testing.T) {
|
|
t.Run("successfuly creates", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/tasks.yml", func(t *testing.T, pkg *Pkg) {
|
|
orgID := influxdb.ID(9000)
|
|
|
|
fakeTaskSVC := mock.NewTaskService()
|
|
fakeTaskSVC.CreateTaskFn = func(ctx context.Context, tc influxdb.TaskCreate) (*influxdb.Task, error) {
|
|
reg := regexp.MustCompile(`name: "(.+)",`)
|
|
names := reg.FindStringSubmatch(tc.Flux)
|
|
if len(names) < 2 {
|
|
return nil, errors.New("bad flux query provided: " + tc.Flux)
|
|
}
|
|
return &influxdb.Task{
|
|
ID: influxdb.ID(fakeTaskSVC.CreateTaskCalls.Count() + 1),
|
|
Type: tc.Type,
|
|
OrganizationID: tc.OrganizationID,
|
|
OwnerID: tc.OwnerID,
|
|
Name: names[1],
|
|
Description: tc.Description,
|
|
Status: tc.Status,
|
|
Flux: tc.Flux,
|
|
}, nil
|
|
}
|
|
|
|
svc := newTestService(WithTaskSVC(fakeTaskSVC))
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.Tasks, 2)
|
|
assert.NotZero(t, sum.Tasks[0].ID)
|
|
assert.Equal(t, "task-1", sum.Tasks[0].PkgName)
|
|
assert.Equal(t, "task-1", sum.Tasks[0].Name)
|
|
assert.Equal(t, "desc_1", sum.Tasks[0].Description)
|
|
|
|
assert.NotZero(t, sum.Tasks[1].ID)
|
|
assert.Equal(t, "task-uuid", sum.Tasks[1].PkgName)
|
|
assert.Equal(t, "task-0", sum.Tasks[1].Name)
|
|
assert.Equal(t, "desc_0", sum.Tasks[1].Description)
|
|
})
|
|
})
|
|
|
|
t.Run("rolls back all created tasks on an error", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/tasks.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeTaskSVC := mock.NewTaskService()
|
|
fakeTaskSVC.CreateTaskFn = func(ctx context.Context, tc influxdb.TaskCreate) (*influxdb.Task, error) {
|
|
if fakeTaskSVC.CreateTaskCalls.Count() == 1 {
|
|
return nil, errors.New("expected error")
|
|
}
|
|
return &influxdb.Task{
|
|
ID: influxdb.ID(fakeTaskSVC.CreateTaskCalls.Count() + 1),
|
|
}, nil
|
|
}
|
|
|
|
svc := newTestService(WithTaskSVC(fakeTaskSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.Error(t, err)
|
|
|
|
assert.Equal(t, 1, fakeTaskSVC.DeleteTaskCalls.Count())
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("telegrafs", func(t *testing.T) {
|
|
t.Run("successfuly creates", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/telegraf.yml", func(t *testing.T, pkg *Pkg) {
|
|
orgID := influxdb.ID(9000)
|
|
|
|
fakeTeleSVC := mock.NewTelegrafConfigStore()
|
|
fakeTeleSVC.CreateTelegrafConfigF = func(_ context.Context, tc *influxdb.TelegrafConfig, userID influxdb.ID) error {
|
|
tc.ID = 1
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithTelegrafSVC(fakeTeleSVC))
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.TelegrafConfigs, 2)
|
|
assert.Equal(t, "display name", sum.TelegrafConfigs[0].TelegrafConfig.Name)
|
|
assert.Equal(t, "desc", sum.TelegrafConfigs[0].TelegrafConfig.Description)
|
|
assert.Equal(t, "tele-2", sum.TelegrafConfigs[1].TelegrafConfig.Name)
|
|
})
|
|
})
|
|
|
|
t.Run("rolls back all created telegrafs on an error", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/telegraf.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeTeleSVC := mock.NewTelegrafConfigStore()
|
|
fakeTeleSVC.CreateTelegrafConfigF = func(_ context.Context, tc *influxdb.TelegrafConfig, userID influxdb.ID) error {
|
|
t.Log("called")
|
|
if fakeTeleSVC.CreateTelegrafConfigCalls.Count() == 1 {
|
|
return errors.New("limit hit")
|
|
}
|
|
tc.ID = influxdb.ID(1)
|
|
return nil
|
|
}
|
|
fakeTeleSVC.DeleteTelegrafConfigF = func(_ context.Context, id influxdb.ID) error {
|
|
if id != 1 {
|
|
return errors.New("wrong id here")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithTelegrafSVC(fakeTeleSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.Error(t, err)
|
|
|
|
assert.Equal(t, 1, fakeTeleSVC.DeleteTelegrafConfigCalls.Count())
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("variables", func(t *testing.T) {
|
|
t.Run("successfully creates pkg of variables", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/variables.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeVarSVC := mock.NewVariableService()
|
|
fakeVarSVC.CreateVariableF = func(_ context.Context, v *influxdb.Variable) error {
|
|
v.ID = influxdb.ID(fakeVarSVC.CreateVariableCalls.Count() + 1)
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithVariableSVC(fakeVarSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.Variables, 4)
|
|
|
|
actual := sum.Variables[0]
|
|
assert.True(t, actual.ID > 0 && actual.ID < 5)
|
|
assert.Equal(t, SafeID(orgID), actual.OrgID)
|
|
assert.Equal(t, "var-const-3", actual.Name)
|
|
assert.Equal(t, "var-const-3 desc", actual.Description)
|
|
require.NotNil(t, actual.Arguments)
|
|
assert.Equal(t, influxdb.VariableConstantValues{"first val"}, actual.Arguments.Values)
|
|
|
|
actual = sum.Variables[2]
|
|
assert.Equal(t, []string{"rucket"}, actual.Selected)
|
|
|
|
for _, actual := range sum.Variables {
|
|
assert.Containsf(t, []SafeID{1, 2, 3, 4}, actual.ID, "actual var: %+v", actual)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("rolls back all created variables on an error", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/variables.yml", func(t *testing.T, pkg *Pkg) {
|
|
fakeVarSVC := mock.NewVariableService()
|
|
fakeVarSVC.CreateVariableF = func(_ context.Context, l *influxdb.Variable) error {
|
|
// 4th variable will return the error here, and 3 before should be rolled back
|
|
if fakeVarSVC.CreateVariableCalls.Count() == 2 {
|
|
return errors.New("blowed up ")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
svc := newTestService(WithVariableSVC(fakeVarSVC))
|
|
|
|
orgID := influxdb.ID(9000)
|
|
|
|
_, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.Error(t, err)
|
|
|
|
assert.GreaterOrEqual(t, fakeVarSVC.DeleteVariableCalls.Count(), 1)
|
|
})
|
|
})
|
|
|
|
t.Run("will not apply variable if no changes to be applied", func(t *testing.T) {
|
|
testfileRunner(t, "testdata/variables.yml", func(t *testing.T, pkg *Pkg) {
|
|
orgID := influxdb.ID(9000)
|
|
|
|
fakeVarSVC := mock.NewVariableService()
|
|
fakeVarSVC.FindVariablesF = func(ctx context.Context, f influxdb.VariableFilter, _ ...influxdb.FindOptions) ([]*influxdb.Variable, error) {
|
|
return []*influxdb.Variable{
|
|
{
|
|
// makes all pkg changes same as they are on the existing
|
|
ID: influxdb.ID(1),
|
|
OrganizationID: orgID,
|
|
Name: pkg.mVariables["var-const-3"].Name(),
|
|
Arguments: &influxdb.VariableArguments{
|
|
Type: "constant",
|
|
Values: influxdb.VariableConstantValues{"first val"},
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
fakeVarSVC.CreateVariableF = func(_ context.Context, l *influxdb.Variable) error {
|
|
if l.Name == "var_const" {
|
|
return errors.New("shouldn't get here")
|
|
}
|
|
return nil
|
|
}
|
|
fakeVarSVC.UpdateVariableF = func(_ context.Context, id influxdb.ID, v *influxdb.VariableUpdate) (*influxdb.Variable, error) {
|
|
if id > influxdb.ID(1) {
|
|
return nil, errors.New("this id should not be updated")
|
|
}
|
|
return &influxdb.Variable{ID: id}, nil
|
|
}
|
|
|
|
svc := newTestService(WithVariableSVC(fakeVarSVC))
|
|
|
|
impact, err := svc.Apply(context.TODO(), orgID, 0, ApplyWithPkg(pkg))
|
|
require.NoError(t, err)
|
|
|
|
sum := impact.Summary
|
|
require.Len(t, sum.Variables, 4)
|
|
expected := sum.Variables[0]
|
|
assert.Equal(t, SafeID(1), expected.ID)
|
|
assert.Equal(t, "var-const-3", expected.Name)
|
|
|
|
assert.Equal(t, 3, fakeVarSVC.CreateVariableCalls.Count()) // only called for last 3 labels
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("CreatePkg", func(t *testing.T) {
|
|
newThresholdBase := func(i int) icheck.Base {
|
|
return icheck.Base{
|
|
ID: influxdb.ID(i),
|
|
TaskID: 300,
|
|
Name: fmt.Sprintf("check_%d", i),
|
|
Description: fmt.Sprintf("desc_%d", i),
|
|
Every: mustDuration(t, time.Minute),
|
|
Offset: mustDuration(t, 15*time.Second),
|
|
Query: influxdb.DashboardQuery{
|
|
Text: `from(bucket: "telegraf") |> range(start: -1m) |> filter(fn: (r) => r._field == "usage_user")`,
|
|
},
|
|
StatusMessageTemplate: "Check: ${ r._check_name } is: ${ r._level }",
|
|
Tags: []influxdb.Tag{
|
|
{Key: "key_1", Value: "val_1"},
|
|
{Key: "key_2", Value: "val_2"},
|
|
},
|
|
}
|
|
}
|
|
|
|
sortLabelsByName := func(labels []SummaryLabel) {
|
|
sort.Slice(labels, func(i, j int) bool {
|
|
return labels[i].Name < labels[j].Name
|
|
})
|
|
}
|
|
|
|
t.Run("with existing resources", func(t *testing.T) {
|
|
encodeAndDecode := func(t *testing.T, pkg *Pkg) *Pkg {
|
|
t.Helper()
|
|
|
|
b, err := pkg.Encode(EncodingJSON)
|
|
require.NoError(t, err)
|
|
|
|
newPkg, err := Parse(EncodingJSON, FromReader(bytes.NewReader(b)))
|
|
require.NoError(t, err)
|
|
|
|
return newPkg
|
|
}
|
|
|
|
t.Run("bucket", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
newName string
|
|
}{
|
|
{
|
|
name: "without new name",
|
|
},
|
|
{
|
|
name: "with new name",
|
|
newName: "new name",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
expected := &influxdb.Bucket{
|
|
ID: 3,
|
|
Name: "bucket name",
|
|
Description: "desc",
|
|
RetentionPeriod: time.Hour,
|
|
}
|
|
|
|
bktSVC := mock.NewBucketService()
|
|
bktSVC.FindBucketByIDFn = func(_ context.Context, id influxdb.ID) (*influxdb.Bucket, error) {
|
|
if id != expected.ID {
|
|
return nil, errors.New("uh ohhh, wrong id here: " + id.String())
|
|
}
|
|
return expected, nil
|
|
}
|
|
|
|
svc := newTestService(WithBucketSVC(bktSVC), WithLabelSVC(mock.NewLabelService()))
|
|
|
|
resToClone := ResourceToClone{
|
|
Kind: KindBucket,
|
|
ID: expected.ID,
|
|
Name: tt.newName,
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
bkts := newPkg.Summary().Buckets
|
|
require.Len(t, bkts, 1)
|
|
|
|
actual := bkts[0]
|
|
expectedName := expected.Name
|
|
if tt.newName != "" {
|
|
expectedName = tt.newName
|
|
}
|
|
assert.Equal(t, expectedName, actual.Name)
|
|
assert.Equal(t, expected.Description, actual.Description)
|
|
assert.Equal(t, expected.RetentionPeriod, actual.RetentionPeriod)
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
|
|
t.Run("checks", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
newName string
|
|
expected influxdb.Check
|
|
}{
|
|
{
|
|
name: "threshold",
|
|
expected: &icheck.Threshold{
|
|
Base: newThresholdBase(0),
|
|
Thresholds: []icheck.ThresholdConfig{
|
|
icheck.Lesser{
|
|
ThresholdConfigBase: icheck.ThresholdConfigBase{
|
|
AllValues: true,
|
|
Level: notification.Critical,
|
|
},
|
|
Value: 20,
|
|
},
|
|
icheck.Greater{
|
|
ThresholdConfigBase: icheck.ThresholdConfigBase{
|
|
AllValues: true,
|
|
Level: notification.Warn,
|
|
},
|
|
Value: 30,
|
|
},
|
|
icheck.Range{
|
|
ThresholdConfigBase: icheck.ThresholdConfigBase{
|
|
AllValues: true,
|
|
Level: notification.Info,
|
|
},
|
|
Within: false, // outside_range
|
|
Min: 10,
|
|
Max: 25,
|
|
},
|
|
icheck.Range{
|
|
ThresholdConfigBase: icheck.ThresholdConfigBase{
|
|
AllValues: true,
|
|
Level: notification.Ok,
|
|
},
|
|
Within: true, // inside_range
|
|
Min: 21,
|
|
Max: 24,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "deadman",
|
|
newName: "new name",
|
|
expected: &icheck.Deadman{
|
|
Base: newThresholdBase(1),
|
|
TimeSince: mustDuration(t, time.Hour),
|
|
StaleTime: mustDuration(t, 5*time.Hour),
|
|
ReportZero: true,
|
|
Level: notification.Critical,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
id := influxdb.ID(1)
|
|
tt.expected.SetID(id)
|
|
|
|
checkSVC := mock.NewCheckService()
|
|
checkSVC.FindCheckByIDFn = func(ctx context.Context, id influxdb.ID) (influxdb.Check, error) {
|
|
if id != tt.expected.GetID() {
|
|
return nil, errors.New("uh ohhh, wrong id here: " + id.String())
|
|
}
|
|
return tt.expected, nil
|
|
}
|
|
|
|
svc := newTestService(WithCheckSVC(checkSVC))
|
|
|
|
resToClone := ResourceToClone{
|
|
Kind: KindCheck,
|
|
ID: tt.expected.GetID(),
|
|
Name: tt.newName,
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
checks := newPkg.Summary().Checks
|
|
require.Len(t, checks, 1)
|
|
|
|
actual := checks[0].Check
|
|
expectedName := tt.expected.GetName()
|
|
if tt.newName != "" {
|
|
expectedName = tt.newName
|
|
}
|
|
assert.Equal(t, expectedName, actual.GetName())
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
|
|
newQuery := func() influxdb.DashboardQuery {
|
|
q := influxdb.DashboardQuery{
|
|
Text: "from(v.bucket) |> count()",
|
|
EditMode: "advanced",
|
|
}
|
|
// TODO: remove this when issue that forced the builder tag to be here to render in UI.
|
|
q.BuilderConfig.Tags = append(q.BuilderConfig.Tags, influxdb.NewBuilderTag("_measurement", "filter", ""))
|
|
return q
|
|
}
|
|
|
|
newAxes := func() map[string]influxdb.Axis {
|
|
return map[string]influxdb.Axis{
|
|
"x": {
|
|
Bounds: []string{},
|
|
Label: "labx",
|
|
Prefix: "pre",
|
|
Suffix: "suf",
|
|
Base: "base",
|
|
Scale: "linear",
|
|
},
|
|
"y": {
|
|
Bounds: []string{},
|
|
Label: "laby",
|
|
Prefix: "pre",
|
|
Suffix: "suf",
|
|
Base: "base",
|
|
Scale: "linear",
|
|
},
|
|
}
|
|
}
|
|
|
|
newColors := func(types ...string) []influxdb.ViewColor {
|
|
var out []influxdb.ViewColor
|
|
for _, t := range types {
|
|
out = append(out, influxdb.ViewColor{
|
|
Type: t,
|
|
Hex: time.Now().Format(time.RFC3339),
|
|
Name: time.Now().Format(time.RFC3339),
|
|
Value: float64(time.Now().Unix()),
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
t.Run("dashboard", func(t *testing.T) {
|
|
t.Run("with single chart", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
newName string
|
|
expectedView influxdb.View
|
|
}{
|
|
{
|
|
name: "gauge",
|
|
newName: "new name",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.GaugeViewProperties{
|
|
Type: influxdb.ViewPropertyTypeGauge,
|
|
DecimalPlaces: influxdb.DecimalPlaces{IsEnforced: true, Digits: 1},
|
|
Note: "a note",
|
|
Prefix: "pre",
|
|
TickPrefix: "true",
|
|
Suffix: "suf",
|
|
TickSuffix: "false",
|
|
Queries: []influxdb.DashboardQuery{newQuery()},
|
|
ShowNoteWhenEmpty: true,
|
|
ViewColors: newColors("min", "max", "threshold"),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "heatmap",
|
|
newName: "new name",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.HeatmapViewProperties{
|
|
Type: influxdb.ViewPropertyTypeHeatMap,
|
|
Note: "a note",
|
|
Queries: []influxdb.DashboardQuery{newQuery()},
|
|
ShowNoteWhenEmpty: true,
|
|
ViewColors: []string{"#8F8AF4", "#8F8AF4", "#8F8AF4"},
|
|
XColumn: "x",
|
|
YColumn: "y",
|
|
XDomain: []float64{0, 10},
|
|
YDomain: []float64{0, 100},
|
|
XAxisLabel: "x_label",
|
|
XPrefix: "x_prefix",
|
|
XSuffix: "x_suffix",
|
|
YAxisLabel: "y_label",
|
|
YPrefix: "y_prefix",
|
|
YSuffix: "y_suffix",
|
|
BinSize: 10,
|
|
TimeFormat: "",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "histogram",
|
|
newName: "new name",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.HistogramViewProperties{
|
|
Type: influxdb.ViewPropertyTypeHistogram,
|
|
Note: "a note",
|
|
Queries: []influxdb.DashboardQuery{newQuery()},
|
|
ShowNoteWhenEmpty: true,
|
|
ViewColors: []influxdb.ViewColor{{Type: "scale", Hex: "#8F8AF4", Value: 0}, {Type: "scale", Hex: "#8F8AF4", Value: 0}, {Type: "scale", Hex: "#8F8AF4", Value: 0}},
|
|
FillColumns: []string{"a", "b"},
|
|
XColumn: "_value",
|
|
XDomain: []float64{0, 10},
|
|
XAxisLabel: "x_label",
|
|
BinCount: 30,
|
|
Position: "stacked",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "scatter",
|
|
newName: "new name",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.ScatterViewProperties{
|
|
Type: influxdb.ViewPropertyTypeScatter,
|
|
Note: "a note",
|
|
Queries: []influxdb.DashboardQuery{newQuery()},
|
|
ShowNoteWhenEmpty: true,
|
|
ViewColors: []string{"#8F8AF4", "#8F8AF4", "#8F8AF4"},
|
|
XColumn: "x",
|
|
YColumn: "y",
|
|
XDomain: []float64{0, 10},
|
|
YDomain: []float64{0, 100},
|
|
XAxisLabel: "x_label",
|
|
XPrefix: "x_prefix",
|
|
XSuffix: "x_suffix",
|
|
YAxisLabel: "y_label",
|
|
YPrefix: "y_prefix",
|
|
YSuffix: "y_suffix",
|
|
TimeFormat: "",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "without new name single stat",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.SingleStatViewProperties{
|
|
Type: influxdb.ViewPropertyTypeSingleStat,
|
|
DecimalPlaces: influxdb.DecimalPlaces{IsEnforced: true, Digits: 1},
|
|
Note: "a note",
|
|
Queries: []influxdb.DashboardQuery{newQuery()},
|
|
Prefix: "pre",
|
|
TickPrefix: "false",
|
|
ShowNoteWhenEmpty: true,
|
|
Suffix: "suf",
|
|
TickSuffix: "true",
|
|
ViewColors: []influxdb.ViewColor{{Type: "text", Hex: "red"}},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "with new name single stat",
|
|
newName: "new name",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.SingleStatViewProperties{
|
|
Type: influxdb.ViewPropertyTypeSingleStat,
|
|
DecimalPlaces: influxdb.DecimalPlaces{IsEnforced: true, Digits: 1},
|
|
Note: "a note",
|
|
Queries: []influxdb.DashboardQuery{newQuery()},
|
|
Prefix: "pre",
|
|
TickPrefix: "false",
|
|
ShowNoteWhenEmpty: true,
|
|
Suffix: "suf",
|
|
TickSuffix: "true",
|
|
ViewColors: []influxdb.ViewColor{{Type: "text", Hex: "red"}},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "single stat plus line",
|
|
newName: "new name",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.LinePlusSingleStatProperties{
|
|
Type: influxdb.ViewPropertyTypeSingleStatPlusLine,
|
|
Axes: newAxes(),
|
|
DecimalPlaces: influxdb.DecimalPlaces{IsEnforced: true, Digits: 1},
|
|
Legend: influxdb.Legend{Type: "type", Orientation: "horizontal"},
|
|
Note: "a note",
|
|
Prefix: "pre",
|
|
Suffix: "suf",
|
|
Queries: []influxdb.DashboardQuery{newQuery()},
|
|
ShadeBelow: true,
|
|
ShowNoteWhenEmpty: true,
|
|
ViewColors: []influxdb.ViewColor{{Type: "text", Hex: "red"}},
|
|
XColumn: "x",
|
|
YColumn: "y",
|
|
Position: "stacked",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "xy",
|
|
newName: "new name",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.XYViewProperties{
|
|
Type: influxdb.ViewPropertyTypeXY,
|
|
Axes: newAxes(),
|
|
Geom: "step",
|
|
Legend: influxdb.Legend{Type: "type", Orientation: "horizontal"},
|
|
Note: "a note",
|
|
Queries: []influxdb.DashboardQuery{newQuery()},
|
|
ShadeBelow: true,
|
|
ShowNoteWhenEmpty: true,
|
|
ViewColors: []influxdb.ViewColor{{Type: "text", Hex: "red"}},
|
|
XColumn: "x",
|
|
YColumn: "y",
|
|
Position: "overlaid",
|
|
TimeFormat: "",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "markdown",
|
|
newName: "new name",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.MarkdownViewProperties{
|
|
Type: influxdb.ViewPropertyTypeMarkdown,
|
|
Note: "a note",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "table",
|
|
newName: "new name",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.TableViewProperties{
|
|
Type: influxdb.ViewPropertyTypeTable,
|
|
Note: "a note",
|
|
ShowNoteWhenEmpty: true,
|
|
Queries: []influxdb.DashboardQuery{newQuery()},
|
|
ViewColors: []influxdb.ViewColor{{Type: "scale", Hex: "#8F8AF4", Value: 0}, {Type: "scale", Hex: "#8F8AF4", Value: 0}, {Type: "scale", Hex: "#8F8AF4", Value: 0}},
|
|
TableOptions: influxdb.TableOptions{
|
|
VerticalTimeAxis: true,
|
|
SortBy: influxdb.RenamableField{
|
|
InternalName: "_time",
|
|
},
|
|
Wrapping: "truncate",
|
|
FixFirstColumn: true,
|
|
},
|
|
FieldOptions: []influxdb.RenamableField{
|
|
{
|
|
InternalName: "_time",
|
|
DisplayName: "time (ms)",
|
|
Visible: true,
|
|
},
|
|
},
|
|
TimeFormat: "YYYY:MM:DD",
|
|
DecimalPlaces: influxdb.DecimalPlaces{
|
|
IsEnforced: true,
|
|
Digits: 1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// validate implementation resolves: https://github.com/influxdata/influxdb/issues/17708
|
|
name: "table converts table options correctly",
|
|
newName: "new name",
|
|
expectedView: influxdb.View{
|
|
ViewContents: influxdb.ViewContents{
|
|
Name: "view name",
|
|
},
|
|
Properties: influxdb.TableViewProperties{
|
|
Type: influxdb.ViewPropertyTypeTable,
|
|
Note: "a note",
|
|
ShowNoteWhenEmpty: true,
|
|
Queries: []influxdb.DashboardQuery{newQuery()},
|
|
ViewColors: []influxdb.ViewColor{{Type: "scale", Hex: "#8F8AF4", Value: 0}, {Type: "scale", Hex: "#8F8AF4", Value: 0}, {Type: "scale", Hex: "#8F8AF4", Value: 0}},
|
|
TableOptions: influxdb.TableOptions{
|
|
VerticalTimeAxis: true,
|
|
SortBy: influxdb.RenamableField{
|
|
InternalName: "_time",
|
|
},
|
|
Wrapping: "truncate",
|
|
},
|
|
FieldOptions: []influxdb.RenamableField{
|
|
{
|
|
InternalName: "_time",
|
|
DisplayName: "time (ms)",
|
|
Visible: true,
|
|
},
|
|
{
|
|
InternalName: "_value",
|
|
DisplayName: "bytes",
|
|
Visible: true,
|
|
},
|
|
},
|
|
TimeFormat: "YYYY:MM:DD",
|
|
DecimalPlaces: influxdb.DecimalPlaces{
|
|
IsEnforced: true,
|
|
Digits: 1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
expectedCell := &influxdb.Cell{
|
|
ID: 5,
|
|
CellProperty: influxdb.CellProperty{X: 1, Y: 2, W: 3, H: 4},
|
|
View: &tt.expectedView,
|
|
}
|
|
expected := &influxdb.Dashboard{
|
|
ID: 3,
|
|
Name: "bucket name",
|
|
Description: "desc",
|
|
Cells: []*influxdb.Cell{expectedCell},
|
|
}
|
|
|
|
dashSVC := mock.NewDashboardService()
|
|
dashSVC.FindDashboardByIDF = func(_ context.Context, id influxdb.ID) (*influxdb.Dashboard, error) {
|
|
if id != expected.ID {
|
|
return nil, errors.New("uh ohhh, wrong id here: " + id.String())
|
|
}
|
|
return expected, nil
|
|
}
|
|
dashSVC.GetDashboardCellViewF = func(_ context.Context, id influxdb.ID, cID influxdb.ID) (*influxdb.View, error) {
|
|
if id == expected.ID && cID == expectedCell.ID {
|
|
return &tt.expectedView, nil
|
|
}
|
|
return nil, errors.New("wrongo ids")
|
|
}
|
|
|
|
svc := newTestService(WithDashboardSVC(dashSVC), WithLabelSVC(mock.NewLabelService()))
|
|
|
|
resToClone := ResourceToClone{
|
|
Kind: KindDashboard,
|
|
ID: expected.ID,
|
|
Name: tt.newName,
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
dashs := newPkg.Summary().Dashboards
|
|
require.Len(t, dashs, 1)
|
|
|
|
actual := dashs[0]
|
|
expectedName := expected.Name
|
|
if tt.newName != "" {
|
|
expectedName = tt.newName
|
|
}
|
|
assert.Equal(t, expectedName, actual.Name)
|
|
assert.Equal(t, expected.Description, actual.Description)
|
|
|
|
require.Len(t, actual.Charts, 1)
|
|
ch := actual.Charts[0]
|
|
assert.Equal(t, int(expectedCell.X), ch.XPosition)
|
|
assert.Equal(t, int(expectedCell.Y), ch.YPosition)
|
|
assert.Equal(t, int(expectedCell.H), ch.Height)
|
|
assert.Equal(t, int(expectedCell.W), ch.Width)
|
|
assert.Equal(t, tt.expectedView.Properties, ch.Properties)
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
|
|
t.Run("handles duplicate dashboard names", func(t *testing.T) {
|
|
dashSVC := mock.NewDashboardService()
|
|
dashSVC.FindDashboardByIDF = func(_ context.Context, id influxdb.ID) (*influxdb.Dashboard, error) {
|
|
return &influxdb.Dashboard{
|
|
ID: id,
|
|
Name: "dash name",
|
|
Description: "desc",
|
|
}, nil
|
|
}
|
|
|
|
svc := newTestService(WithDashboardSVC(dashSVC), WithLabelSVC(mock.NewLabelService()))
|
|
|
|
resourcesToClone := []ResourceToClone{
|
|
{
|
|
Kind: KindDashboard,
|
|
ID: 1,
|
|
},
|
|
{
|
|
Kind: KindDashboard,
|
|
ID: 2,
|
|
},
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resourcesToClone...))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
dashs := newPkg.Summary().Dashboards
|
|
require.Len(t, dashs, len(resourcesToClone))
|
|
|
|
for i := range resourcesToClone {
|
|
actual := dashs[i]
|
|
assert.Equal(t, "dash name", actual.Name)
|
|
assert.Equal(t, "desc", actual.Description)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("label", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
newName string
|
|
}{
|
|
{
|
|
name: "without new name",
|
|
},
|
|
{
|
|
name: "with new name",
|
|
newName: "new name",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
expectedLabel := &influxdb.Label{
|
|
ID: 3,
|
|
Name: "bucket name",
|
|
Properties: map[string]string{
|
|
"description": "desc",
|
|
"color": "red",
|
|
},
|
|
}
|
|
|
|
labelSVC := mock.NewLabelService()
|
|
labelSVC.FindLabelByIDFn = func(_ context.Context, id influxdb.ID) (*influxdb.Label, error) {
|
|
if id != expectedLabel.ID {
|
|
return nil, errors.New("uh ohhh, wrong id here: " + id.String())
|
|
}
|
|
return expectedLabel, nil
|
|
}
|
|
|
|
svc := newTestService(WithLabelSVC(labelSVC))
|
|
|
|
resToClone := ResourceToClone{
|
|
Kind: KindLabel,
|
|
ID: expectedLabel.ID,
|
|
Name: tt.newName,
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
newLabels := newPkg.Summary().Labels
|
|
require.Len(t, newLabels, 1)
|
|
|
|
actual := newLabels[0]
|
|
expectedName := expectedLabel.Name
|
|
if tt.newName != "" {
|
|
expectedName = tt.newName
|
|
}
|
|
assert.Equal(t, expectedName, actual.Name)
|
|
assert.Equal(t, expectedLabel.Properties["color"], actual.Properties.Color)
|
|
assert.Equal(t, expectedLabel.Properties["description"], actual.Properties.Description)
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
|
|
t.Run("notification endpoints", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
newName string
|
|
expected influxdb.NotificationEndpoint
|
|
}{
|
|
{
|
|
name: "pager duty",
|
|
expected: &endpoint.PagerDuty{
|
|
Base: endpoint.Base{
|
|
Name: "pd-endpoint",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusActive,
|
|
},
|
|
ClientURL: "http://example.com",
|
|
RoutingKey: influxdb.SecretField{Key: "-routing-key"},
|
|
},
|
|
},
|
|
{
|
|
name: "pager duty with new name",
|
|
newName: "new name",
|
|
expected: &endpoint.PagerDuty{
|
|
Base: endpoint.Base{
|
|
Name: "pd-endpoint",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusActive,
|
|
},
|
|
ClientURL: "http://example.com",
|
|
RoutingKey: influxdb.SecretField{Key: "-routing-key"},
|
|
},
|
|
},
|
|
{
|
|
name: "slack",
|
|
expected: &endpoint.Slack{
|
|
Base: endpoint.Base{
|
|
Name: "pd-endpoint",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusInactive,
|
|
},
|
|
URL: "http://example.com",
|
|
Token: influxdb.SecretField{Key: "tokne"},
|
|
},
|
|
},
|
|
{
|
|
name: "http basic",
|
|
expected: &endpoint.HTTP{
|
|
Base: endpoint.Base{
|
|
Name: "pd-endpoint",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusInactive,
|
|
},
|
|
AuthMethod: "basic",
|
|
Method: "POST",
|
|
URL: "http://example.com",
|
|
Password: influxdb.SecretField{Key: "password"},
|
|
Username: influxdb.SecretField{Key: "username"},
|
|
},
|
|
},
|
|
{
|
|
name: "http bearer",
|
|
expected: &endpoint.HTTP{
|
|
Base: endpoint.Base{
|
|
Name: "pd-endpoint",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusInactive,
|
|
},
|
|
AuthMethod: "bearer",
|
|
Method: "GET",
|
|
URL: "http://example.com",
|
|
Token: influxdb.SecretField{Key: "token"},
|
|
},
|
|
},
|
|
{
|
|
name: "http none",
|
|
expected: &endpoint.HTTP{
|
|
Base: endpoint.Base{
|
|
Name: "pd-endpoint",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusInactive,
|
|
},
|
|
AuthMethod: "none",
|
|
Method: "GET",
|
|
URL: "http://example.com",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
id := influxdb.ID(1)
|
|
tt.expected.SetID(id)
|
|
|
|
endpointSVC := mock.NewNotificationEndpointService()
|
|
endpointSVC.FindNotificationEndpointByIDF = func(ctx context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) {
|
|
if id != tt.expected.GetID() {
|
|
return nil, errors.New("uh ohhh, wrong id here: " + id.String())
|
|
}
|
|
return tt.expected, nil
|
|
}
|
|
|
|
svc := newTestService(WithNotificationEndpointSVC(endpointSVC))
|
|
|
|
resToClone := ResourceToClone{
|
|
Kind: KindNotificationEndpoint,
|
|
ID: tt.expected.GetID(),
|
|
Name: tt.newName,
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
endpoints := newPkg.Summary().NotificationEndpoints
|
|
require.Len(t, endpoints, 1)
|
|
|
|
actual := endpoints[0].NotificationEndpoint
|
|
expectedName := tt.expected.GetName()
|
|
if tt.newName != "" {
|
|
expectedName = tt.newName
|
|
}
|
|
assert.Equal(t, expectedName, actual.GetName())
|
|
assert.Equal(t, tt.expected.GetDescription(), actual.GetDescription())
|
|
assert.Equal(t, tt.expected.GetStatus(), actual.GetStatus())
|
|
assert.Equal(t, tt.expected.SecretFields(), actual.SecretFields())
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
|
|
t.Run("notification rules", func(t *testing.T) {
|
|
newRuleBase := func(id int) rule.Base {
|
|
return rule.Base{
|
|
ID: influxdb.ID(id),
|
|
Name: "old_name",
|
|
Description: "desc",
|
|
EndpointID: influxdb.ID(id),
|
|
Every: mustDuration(t, time.Hour),
|
|
Offset: mustDuration(t, time.Minute),
|
|
TagRules: []notification.TagRule{
|
|
{Tag: influxdb.Tag{Key: "k1", Value: "v1"}},
|
|
},
|
|
StatusRules: []notification.StatusRule{
|
|
{CurrentLevel: notification.Ok, PreviousLevel: levelPtr(notification.Warn)},
|
|
{CurrentLevel: notification.Critical},
|
|
},
|
|
}
|
|
}
|
|
|
|
t.Run("single rule export", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
newName string
|
|
endpoint influxdb.NotificationEndpoint
|
|
rule influxdb.NotificationRule
|
|
}{
|
|
{
|
|
name: "pager duty",
|
|
newName: "pager_duty_name",
|
|
endpoint: &endpoint.PagerDuty{
|
|
Base: endpoint.Base{
|
|
ID: newTestIDPtr(13),
|
|
Name: "endpoint_0",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusActive,
|
|
},
|
|
ClientURL: "http://example.com",
|
|
RoutingKey: influxdb.SecretField{Key: "-routing-key"},
|
|
},
|
|
rule: &rule.PagerDuty{
|
|
Base: newRuleBase(13),
|
|
MessageTemplate: "Template",
|
|
},
|
|
},
|
|
{
|
|
name: "slack",
|
|
endpoint: &endpoint.Slack{
|
|
Base: endpoint.Base{
|
|
ID: newTestIDPtr(13),
|
|
Name: "endpoint_0",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusInactive,
|
|
},
|
|
URL: "http://example.com",
|
|
Token: influxdb.SecretField{Key: "tokne"},
|
|
},
|
|
rule: &rule.Slack{
|
|
Base: newRuleBase(13),
|
|
Channel: "abc",
|
|
MessageTemplate: "SLACK TEMPlate",
|
|
},
|
|
},
|
|
{
|
|
name: "http none",
|
|
endpoint: &endpoint.HTTP{
|
|
Base: endpoint.Base{
|
|
ID: newTestIDPtr(13),
|
|
Name: "endpoint_0",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusInactive,
|
|
},
|
|
AuthMethod: "none",
|
|
Method: "GET",
|
|
URL: "http://example.com",
|
|
},
|
|
rule: &rule.HTTP{
|
|
Base: newRuleBase(13),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
endpointSVC := mock.NewNotificationEndpointService()
|
|
endpointSVC.FindNotificationEndpointByIDF = func(ctx context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) {
|
|
if id != tt.endpoint.GetID() {
|
|
return nil, errors.New("uh ohhh, wrong id here: " + id.String())
|
|
}
|
|
return tt.endpoint, nil
|
|
}
|
|
ruleSVC := mock.NewNotificationRuleStore()
|
|
ruleSVC.FindNotificationRuleByIDF = func(ctx context.Context, id influxdb.ID) (influxdb.NotificationRule, error) {
|
|
return tt.rule, nil
|
|
}
|
|
|
|
svc := newTestService(
|
|
WithNotificationEndpointSVC(endpointSVC),
|
|
WithNotificationRuleSVC(ruleSVC),
|
|
)
|
|
|
|
resToClone := ResourceToClone{
|
|
Kind: KindNotificationRule,
|
|
ID: tt.rule.GetID(),
|
|
Name: tt.newName,
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
sum := newPkg.Summary()
|
|
require.Len(t, sum.NotificationRules, 1)
|
|
|
|
actualRule := sum.NotificationRules[0]
|
|
assert.Zero(t, actualRule.ID)
|
|
assert.Zero(t, actualRule.EndpointID)
|
|
assert.NotEmpty(t, actualRule.EndpointType)
|
|
assert.NotEmpty(t, actualRule.EndpointPkgName)
|
|
|
|
baseEqual := func(t *testing.T, base rule.Base) {
|
|
t.Helper()
|
|
expectedName := base.Name
|
|
if tt.newName != "" {
|
|
expectedName = tt.newName
|
|
}
|
|
assert.Equal(t, expectedName, actualRule.Name)
|
|
assert.Equal(t, base.Description, actualRule.Description)
|
|
assert.Equal(t, base.Every.TimeDuration().String(), actualRule.Every)
|
|
assert.Equal(t, base.Offset.TimeDuration().String(), actualRule.Offset)
|
|
|
|
for _, sRule := range base.StatusRules {
|
|
expected := SummaryStatusRule{CurrentLevel: sRule.CurrentLevel.String()}
|
|
if sRule.PreviousLevel != nil {
|
|
expected.PreviousLevel = sRule.PreviousLevel.String()
|
|
}
|
|
assert.Contains(t, actualRule.StatusRules, expected)
|
|
}
|
|
for _, tRule := range base.TagRules {
|
|
expected := SummaryTagRule{
|
|
Key: tRule.Key,
|
|
Value: tRule.Value,
|
|
Operator: tRule.Operator.String(),
|
|
}
|
|
assert.Contains(t, actualRule.TagRules, expected)
|
|
}
|
|
}
|
|
|
|
switch p := tt.rule.(type) {
|
|
case *rule.HTTP:
|
|
baseEqual(t, p.Base)
|
|
case *rule.PagerDuty:
|
|
baseEqual(t, p.Base)
|
|
assert.Equal(t, p.MessageTemplate, actualRule.MessageTemplate)
|
|
case *rule.Slack:
|
|
baseEqual(t, p.Base)
|
|
assert.Equal(t, p.MessageTemplate, actualRule.MessageTemplate)
|
|
}
|
|
|
|
require.Len(t, pkg.Summary().NotificationEndpoints, 1)
|
|
|
|
actualEndpoint := pkg.Summary().NotificationEndpoints[0].NotificationEndpoint
|
|
assert.Equal(t, tt.endpoint.GetName(), actualEndpoint.GetName())
|
|
assert.Equal(t, tt.endpoint.GetDescription(), actualEndpoint.GetDescription())
|
|
assert.Equal(t, tt.endpoint.GetStatus(), actualEndpoint.GetStatus())
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
|
|
t.Run("handles rules duplicate names", func(t *testing.T) {
|
|
endpointSVC := mock.NewNotificationEndpointService()
|
|
endpointSVC.FindNotificationEndpointByIDF = func(ctx context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) {
|
|
return &endpoint.HTTP{
|
|
Base: endpoint.Base{
|
|
ID: &id,
|
|
Name: "endpoint_0",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusInactive,
|
|
},
|
|
AuthMethod: "none",
|
|
Method: "GET",
|
|
URL: "http://example.com",
|
|
}, nil
|
|
}
|
|
ruleSVC := mock.NewNotificationRuleStore()
|
|
ruleSVC.FindNotificationRuleByIDF = func(ctx context.Context, id influxdb.ID) (influxdb.NotificationRule, error) {
|
|
return &rule.HTTP{
|
|
Base: newRuleBase(int(id)),
|
|
}, nil
|
|
}
|
|
|
|
svc := newTestService(
|
|
WithNotificationEndpointSVC(endpointSVC),
|
|
WithNotificationRuleSVC(ruleSVC),
|
|
)
|
|
|
|
resourcesToClone := []ResourceToClone{
|
|
{
|
|
Kind: KindNotificationRule,
|
|
ID: 1,
|
|
},
|
|
{
|
|
Kind: KindNotificationRule,
|
|
ID: 2,
|
|
},
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resourcesToClone...))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
sum := newPkg.Summary()
|
|
require.Len(t, sum.NotificationRules, len(resourcesToClone))
|
|
|
|
expectedSameEndpointName := sum.NotificationRules[0].EndpointPkgName
|
|
assert.NotZero(t, expectedSameEndpointName)
|
|
assert.NotEqual(t, "endpoint_0", expectedSameEndpointName)
|
|
|
|
for i := range resourcesToClone {
|
|
actual := sum.NotificationRules[i]
|
|
assert.Equal(t, "old_name", actual.Name)
|
|
assert.Equal(t, "desc", actual.Description)
|
|
assert.Equal(t, expectedSameEndpointName, actual.EndpointPkgName)
|
|
}
|
|
|
|
require.Len(t, sum.NotificationEndpoints, 1)
|
|
assert.Equal(t, "endpoint_0", sum.NotificationEndpoints[0].NotificationEndpoint.GetName())
|
|
})
|
|
})
|
|
|
|
t.Run("tasks", func(t *testing.T) {
|
|
t.Run("single task exports", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
newName string
|
|
task influxdb.Task
|
|
}{
|
|
{
|
|
name: "every offset is set",
|
|
newName: "new name",
|
|
task: influxdb.Task{
|
|
ID: 1,
|
|
Name: "name_9000",
|
|
Every: time.Minute.String(),
|
|
Offset: 10 * time.Second,
|
|
Type: influxdb.TaskSystemType,
|
|
Flux: `option task = { name: "larry" } from(bucket: "rucket") |> yield()`,
|
|
},
|
|
},
|
|
{
|
|
name: "cron is set",
|
|
task: influxdb.Task{
|
|
ID: 1,
|
|
Name: "name_0",
|
|
Cron: "2 * * * *",
|
|
Type: influxdb.TaskSystemType,
|
|
Flux: `option task = { name: "larry" } from(bucket: "rucket") |> yield()`,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
taskSVC := mock.NewTaskService()
|
|
taskSVC.FindTaskByIDFn = func(ctx context.Context, id influxdb.ID) (*influxdb.Task, error) {
|
|
if id != tt.task.ID {
|
|
return nil, errors.New("wrong id provided: " + id.String())
|
|
}
|
|
return &tt.task, nil
|
|
}
|
|
|
|
svc := newTestService(WithTaskSVC(taskSVC))
|
|
|
|
resToClone := ResourceToClone{
|
|
Kind: KindTask,
|
|
ID: tt.task.ID,
|
|
Name: tt.newName,
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
sum := newPkg.Summary()
|
|
|
|
tasks := sum.Tasks
|
|
require.Len(t, tasks, 1)
|
|
|
|
expectedName := tt.task.Name
|
|
if tt.newName != "" {
|
|
expectedName = tt.newName
|
|
}
|
|
actual := tasks[0]
|
|
assert.Equal(t, expectedName, actual.Name)
|
|
assert.Equal(t, tt.task.Cron, actual.Cron)
|
|
assert.Equal(t, tt.task.Description, actual.Description)
|
|
assert.Equal(t, tt.task.Every, actual.Every)
|
|
assert.Equal(t, durToStr(tt.task.Offset), actual.Offset)
|
|
|
|
expectedQuery := `from(bucket: "rucket") |> yield()`
|
|
assert.Equal(t, expectedQuery, actual.Query)
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
|
|
t.Run("handles multiple tasks of same name", func(t *testing.T) {
|
|
taskSVC := mock.NewTaskService()
|
|
taskSVC.FindTaskByIDFn = func(ctx context.Context, id influxdb.ID) (*influxdb.Task, error) {
|
|
return &influxdb.Task{
|
|
ID: id,
|
|
Type: influxdb.TaskSystemType,
|
|
Name: "same name",
|
|
Description: "desc",
|
|
Status: influxdb.TaskStatusActive,
|
|
Flux: `from(bucket: "foo")`,
|
|
Every: "5m0s",
|
|
}, nil
|
|
}
|
|
|
|
svc := newTestService(WithTaskSVC(taskSVC))
|
|
|
|
resourcesToClone := []ResourceToClone{
|
|
{
|
|
Kind: KindTask,
|
|
ID: 1,
|
|
},
|
|
{
|
|
Kind: KindTask,
|
|
ID: 2,
|
|
},
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resourcesToClone...))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
sum := newPkg.Summary()
|
|
|
|
tasks := sum.Tasks
|
|
require.Len(t, tasks, len(resourcesToClone))
|
|
|
|
for _, actual := range sum.Tasks {
|
|
assert.Equal(t, "same name", actual.Name)
|
|
assert.Equal(t, "desc", actual.Description)
|
|
assert.Equal(t, influxdb.Active, actual.Status)
|
|
assert.Equal(t, `from(bucket: "foo")`, actual.Query)
|
|
assert.Equal(t, "5m0s", actual.Every)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("telegraf configs", func(t *testing.T) {
|
|
t.Run("allows for duplicate telegraf names to be exported", func(t *testing.T) {
|
|
teleStore := mock.NewTelegrafConfigStore()
|
|
teleStore.FindTelegrafConfigByIDF = func(ctx context.Context, id influxdb.ID) (*influxdb.TelegrafConfig, error) {
|
|
return &influxdb.TelegrafConfig{
|
|
ID: id,
|
|
OrgID: 9000,
|
|
Name: "same name",
|
|
Description: "desc",
|
|
Config: "some config string",
|
|
}, nil
|
|
}
|
|
|
|
svc := newTestService(WithTelegrafSVC(teleStore))
|
|
|
|
resourcesToClone := []ResourceToClone{
|
|
{
|
|
Kind: KindTelegraf,
|
|
ID: 1,
|
|
},
|
|
{
|
|
Kind: KindTelegraf,
|
|
ID: 2,
|
|
},
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resourcesToClone...))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
sum := newPkg.Summary()
|
|
|
|
teles := sum.TelegrafConfigs
|
|
sort.Slice(teles, func(i, j int) bool {
|
|
return teles[i].TelegrafConfig.Name < teles[j].TelegrafConfig.Name
|
|
})
|
|
require.Len(t, teles, len(resourcesToClone))
|
|
|
|
for i := range resourcesToClone {
|
|
actual := teles[i]
|
|
assert.Equal(t, "same name", actual.TelegrafConfig.Name)
|
|
assert.Equal(t, "desc", actual.TelegrafConfig.Description)
|
|
assert.Equal(t, "some config string", actual.TelegrafConfig.Config)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("variable", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
newName string
|
|
expectedVar influxdb.Variable
|
|
}{
|
|
{
|
|
name: "without new name",
|
|
expectedVar: influxdb.Variable{
|
|
ID: 1,
|
|
Name: "old name",
|
|
Description: "desc",
|
|
Selected: []string{"val"},
|
|
Arguments: &influxdb.VariableArguments{
|
|
Type: "constant",
|
|
Values: influxdb.VariableConstantValues{"val"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "with new name",
|
|
newName: "new name",
|
|
expectedVar: influxdb.Variable{
|
|
ID: 1,
|
|
Name: "old name",
|
|
Selected: []string{"val"},
|
|
Arguments: &influxdb.VariableArguments{
|
|
Type: "constant",
|
|
Values: influxdb.VariableConstantValues{"val"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "with map arg",
|
|
expectedVar: influxdb.Variable{
|
|
ID: 1,
|
|
Name: "old name",
|
|
Selected: []string{"v"},
|
|
Arguments: &influxdb.VariableArguments{
|
|
Type: "map",
|
|
Values: influxdb.VariableMapValues{"k": "v"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "with query arg",
|
|
expectedVar: influxdb.Variable{
|
|
ID: 1,
|
|
Name: "old name",
|
|
Selected: []string{"bucket-foo"},
|
|
Arguments: &influxdb.VariableArguments{
|
|
Type: "query",
|
|
Values: influxdb.VariableQueryValues{
|
|
Query: "buckets()",
|
|
Language: "flux",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
varSVC := mock.NewVariableService()
|
|
varSVC.FindVariableByIDF = func(_ context.Context, id influxdb.ID) (*influxdb.Variable, error) {
|
|
if id != tt.expectedVar.ID {
|
|
return nil, errors.New("uh ohhh, wrong id here: " + id.String())
|
|
}
|
|
return &tt.expectedVar, nil
|
|
}
|
|
|
|
svc := newTestService(WithVariableSVC(varSVC), WithLabelSVC(mock.NewLabelService()))
|
|
|
|
resToClone := ResourceToClone{
|
|
Kind: KindVariable,
|
|
ID: tt.expectedVar.ID,
|
|
Name: tt.newName,
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
newVars := newPkg.Summary().Variables
|
|
require.Len(t, newVars, 1)
|
|
|
|
actual := newVars[0]
|
|
expectedName := tt.expectedVar.Name
|
|
if tt.newName != "" {
|
|
expectedName = tt.newName
|
|
}
|
|
assert.Equal(t, expectedName, actual.Name)
|
|
assert.Equal(t, tt.expectedVar.Description, actual.Description)
|
|
assert.Equal(t, tt.expectedVar.Selected, actual.Selected)
|
|
assert.Equal(t, tt.expectedVar.Arguments, actual.Arguments)
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
|
|
t.Run("includes resource associations", func(t *testing.T) {
|
|
t.Run("single resource with single association", func(t *testing.T) {
|
|
expected := &influxdb.Bucket{
|
|
ID: 3,
|
|
Name: "bucket name",
|
|
Description: "desc",
|
|
RetentionPeriod: time.Hour,
|
|
}
|
|
|
|
bktSVC := mock.NewBucketService()
|
|
bktSVC.FindBucketByIDFn = func(_ context.Context, id influxdb.ID) (*influxdb.Bucket, error) {
|
|
if id != expected.ID {
|
|
return nil, errors.New("uh ohhh, wrong id here: " + id.String())
|
|
}
|
|
return expected, nil
|
|
}
|
|
|
|
labelSVC := mock.NewLabelService()
|
|
labelSVC.FindResourceLabelsFn = func(_ context.Context, f influxdb.LabelMappingFilter) ([]*influxdb.Label, error) {
|
|
if f.ResourceID != expected.ID {
|
|
return nil, errors.New("uh ohs wrong id: " + f.ResourceID.String())
|
|
}
|
|
return []*influxdb.Label{
|
|
{Name: "label_1"},
|
|
}, nil
|
|
}
|
|
|
|
svc := newTestService(WithBucketSVC(bktSVC), WithLabelSVC(labelSVC))
|
|
|
|
resToClone := ResourceToClone{
|
|
Kind: KindBucket,
|
|
ID: expected.ID,
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
sum := newPkg.Summary()
|
|
|
|
bkts := sum.Buckets
|
|
require.Len(t, bkts, 1)
|
|
|
|
actual := bkts[0]
|
|
expectedName := expected.Name
|
|
assert.Equal(t, expectedName, actual.Name)
|
|
assert.Equal(t, expected.Description, actual.Description)
|
|
assert.Equal(t, expected.RetentionPeriod, actual.RetentionPeriod)
|
|
require.Len(t, actual.LabelAssociations, 1)
|
|
assert.Equal(t, "label_1", actual.LabelAssociations[0].Name)
|
|
|
|
labels := sum.Labels
|
|
require.Len(t, labels, 1)
|
|
assert.Equal(t, "label_1", labels[0].Name)
|
|
})
|
|
|
|
t.Run("multiple resources with same associations", func(t *testing.T) {
|
|
bktSVC := mock.NewBucketService()
|
|
bktSVC.FindBucketByIDFn = func(_ context.Context, id influxdb.ID) (*influxdb.Bucket, error) {
|
|
return &influxdb.Bucket{ID: id, Name: strconv.Itoa(int(id))}, nil
|
|
}
|
|
|
|
labelSVC := mock.NewLabelService()
|
|
labelSVC.FindResourceLabelsFn = func(_ context.Context, f influxdb.LabelMappingFilter) ([]*influxdb.Label, error) {
|
|
return []*influxdb.Label{
|
|
{Name: "label_1"},
|
|
{Name: "label_2"},
|
|
}, nil
|
|
}
|
|
|
|
svc := newTestService(WithBucketSVC(bktSVC), WithLabelSVC(labelSVC))
|
|
|
|
resourcesToClone := []ResourceToClone{
|
|
{
|
|
Kind: KindBucket,
|
|
ID: 10,
|
|
},
|
|
{
|
|
Kind: KindBucket,
|
|
ID: 20,
|
|
},
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resourcesToClone...))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
sum := newPkg.Summary()
|
|
|
|
bkts := sum.Buckets
|
|
sort.Slice(bkts, func(i, j int) bool {
|
|
return bkts[i].Name < bkts[j].Name
|
|
})
|
|
require.Len(t, bkts, 2)
|
|
|
|
for i, actual := range bkts {
|
|
sortLabelsByName(actual.LabelAssociations)
|
|
assert.Equal(t, strconv.Itoa((i+1)*10), actual.Name)
|
|
require.Len(t, actual.LabelAssociations, 2)
|
|
assert.Equal(t, "label_1", actual.LabelAssociations[0].Name)
|
|
assert.Equal(t, "label_2", actual.LabelAssociations[1].Name)
|
|
}
|
|
|
|
labels := sum.Labels
|
|
sortLabelsByName(labels)
|
|
require.Len(t, labels, 2)
|
|
assert.Equal(t, "label_1", labels[0].Name)
|
|
assert.Equal(t, "label_2", labels[1].Name)
|
|
})
|
|
|
|
t.Run("labels do not fetch associations", func(t *testing.T) {
|
|
labelSVC := mock.NewLabelService()
|
|
labelSVC.FindLabelByIDFn = func(_ context.Context, id influxdb.ID) (*influxdb.Label, error) {
|
|
return &influxdb.Label{ID: id, Name: "label_1"}, nil
|
|
}
|
|
labelSVC.FindResourceLabelsFn = func(_ context.Context, f influxdb.LabelMappingFilter) ([]*influxdb.Label, error) {
|
|
return nil, errors.New("should not get here")
|
|
}
|
|
|
|
svc := newTestService(WithLabelSVC(labelSVC))
|
|
|
|
resToClone := ResourceToClone{
|
|
Kind: KindLabel,
|
|
ID: 1,
|
|
}
|
|
pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone))
|
|
require.NoError(t, err)
|
|
|
|
newPkg := encodeAndDecode(t, pkg)
|
|
|
|
labels := newPkg.Summary().Labels
|
|
require.Len(t, labels, 1)
|
|
assert.Equal(t, "label_1", labels[0].Name)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("with org id", func(t *testing.T) {
|
|
orgID := influxdb.ID(9000)
|
|
|
|
bktSVC := mock.NewBucketService()
|
|
bktSVC.FindBucketsFn = func(_ context.Context, f influxdb.BucketFilter, opts ...influxdb.FindOptions) ([]*influxdb.Bucket, int, error) {
|
|
if f.OrganizationID == nil || *f.OrganizationID != orgID {
|
|
return nil, 0, errors.New("not suppose to get here")
|
|
}
|
|
return []*influxdb.Bucket{{ID: 1, Name: "bucket"}}, 1, nil
|
|
}
|
|
bktSVC.FindBucketByIDFn = func(_ context.Context, id influxdb.ID) (*influxdb.Bucket, error) {
|
|
if id != 1 {
|
|
return nil, errors.New("wrong id")
|
|
}
|
|
return &influxdb.Bucket{ID: 1, Name: "bucket"}, nil
|
|
}
|
|
|
|
checkSVC := mock.NewCheckService()
|
|
expectedCheck := &icheck.Deadman{
|
|
Base: newThresholdBase(1),
|
|
TimeSince: mustDuration(t, time.Hour),
|
|
StaleTime: mustDuration(t, 5*time.Hour),
|
|
ReportZero: true,
|
|
Level: notification.Critical,
|
|
}
|
|
checkSVC.FindChecksFn = func(ctx context.Context, f influxdb.CheckFilter, _ ...influxdb.FindOptions) ([]influxdb.Check, int, error) {
|
|
if f.OrgID == nil || *f.OrgID != orgID {
|
|
return nil, 0, errors.New("not suppose to get here")
|
|
}
|
|
return []influxdb.Check{expectedCheck}, 1, nil
|
|
}
|
|
checkSVC.FindCheckByIDFn = func(ctx context.Context, id influxdb.ID) (influxdb.Check, error) {
|
|
return expectedCheck, nil
|
|
}
|
|
|
|
dashSVC := mock.NewDashboardService()
|
|
dashSVC.FindDashboardsF = func(_ context.Context, f influxdb.DashboardFilter, _ influxdb.FindOptions) ([]*influxdb.Dashboard, int, error) {
|
|
if f.OrganizationID == nil || *f.OrganizationID != orgID {
|
|
return nil, 0, errors.New("not suppose to get here")
|
|
}
|
|
return []*influxdb.Dashboard{{
|
|
ID: 2,
|
|
Name: "dashboard",
|
|
Cells: []*influxdb.Cell{},
|
|
}}, 1, nil
|
|
}
|
|
dashSVC.FindDashboardByIDF = func(_ context.Context, id influxdb.ID) (*influxdb.Dashboard, error) {
|
|
if id != 2 {
|
|
return nil, errors.New("wrong id")
|
|
}
|
|
return &influxdb.Dashboard{
|
|
ID: 2,
|
|
Name: "dashboard",
|
|
Cells: []*influxdb.Cell{},
|
|
}, nil
|
|
}
|
|
|
|
endpointSVC := mock.NewNotificationEndpointService()
|
|
endpointSVC.FindNotificationEndpointsF = func(ctx context.Context, f influxdb.NotificationEndpointFilter, _ ...influxdb.FindOptions) ([]influxdb.NotificationEndpoint, int, error) {
|
|
id := influxdb.ID(2)
|
|
endpoints := []influxdb.NotificationEndpoint{
|
|
&endpoint.HTTP{
|
|
Base: endpoint.Base{
|
|
ID: &id,
|
|
Name: "http",
|
|
},
|
|
URL: "http://example.com",
|
|
Username: influxdb.SecretField{Key: id.String() + "-username"},
|
|
Password: influxdb.SecretField{Key: id.String() + "-password"},
|
|
AuthMethod: "basic",
|
|
Method: "POST",
|
|
},
|
|
}
|
|
return endpoints, len(endpoints), nil
|
|
}
|
|
endpointSVC.FindNotificationEndpointByIDF = func(ctx context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) {
|
|
return &endpoint.HTTP{
|
|
Base: endpoint.Base{
|
|
ID: &id,
|
|
Name: "http",
|
|
},
|
|
URL: "http://example.com",
|
|
Username: influxdb.SecretField{Key: id.String() + "-username"},
|
|
Password: influxdb.SecretField{Key: id.String() + "-password"},
|
|
AuthMethod: "basic",
|
|
Method: "POST",
|
|
}, nil
|
|
}
|
|
|
|
expectedRule := &rule.HTTP{
|
|
Base: rule.Base{
|
|
ID: 12,
|
|
Name: "rule_0",
|
|
EndpointID: 2,
|
|
Every: mustDuration(t, time.Minute),
|
|
StatusRules: []notification.StatusRule{{CurrentLevel: notification.Critical}},
|
|
},
|
|
}
|
|
ruleSVC := mock.NewNotificationRuleStore()
|
|
ruleSVC.FindNotificationRulesF = func(ctx context.Context, f influxdb.NotificationRuleFilter, _ ...influxdb.FindOptions) ([]influxdb.NotificationRule, int, error) {
|
|
out := []influxdb.NotificationRule{expectedRule}
|
|
return out, len(out), nil
|
|
}
|
|
ruleSVC.FindNotificationRuleByIDF = func(ctx context.Context, id influxdb.ID) (influxdb.NotificationRule, error) {
|
|
return expectedRule, nil
|
|
}
|
|
|
|
labelSVC := mock.NewLabelService()
|
|
labelSVC.FindLabelsFn = func(_ context.Context, f influxdb.LabelFilter) ([]*influxdb.Label, error) {
|
|
if f.OrgID == nil || *f.OrgID != orgID {
|
|
return nil, errors.New("not suppose to get here")
|
|
}
|
|
return []*influxdb.Label{{ID: 3, Name: "label"}}, nil
|
|
}
|
|
labelSVC.FindLabelByIDFn = func(_ context.Context, id influxdb.ID) (*influxdb.Label, error) {
|
|
if id != 3 {
|
|
return nil, errors.New("wrong id")
|
|
}
|
|
return &influxdb.Label{ID: 3, Name: "label"}, nil
|
|
}
|
|
|
|
taskSVC := mock.NewTaskService()
|
|
taskSVC.FindTasksFn = func(ctx context.Context, f influxdb.TaskFilter) ([]*influxdb.Task, int, error) {
|
|
return []*influxdb.Task{
|
|
{ID: 31, Type: influxdb.TaskSystemType},
|
|
{ID: expectedCheck.TaskID, Type: influxdb.TaskSystemType}, // this one should be ignored in the return
|
|
{ID: expectedRule.TaskID, Type: influxdb.TaskSystemType}, // this one should be ignored in the return as well
|
|
{ID: 99}, // this one should be skipped since it is not a system task
|
|
}, 3, nil
|
|
}
|
|
taskSVC.FindTaskByIDFn = func(ctx context.Context, id influxdb.ID) (*influxdb.Task, error) {
|
|
if id != 31 {
|
|
return nil, errors.New("wrong id: " + id.String())
|
|
}
|
|
return &influxdb.Task{
|
|
ID: id,
|
|
Name: "task_0",
|
|
Every: time.Minute.String(),
|
|
Offset: 10 * time.Second,
|
|
Type: influxdb.TaskSystemType,
|
|
Flux: `option task = { name: "larry" } from(bucket: "rucket") |> yield()`,
|
|
}, nil
|
|
}
|
|
|
|
varSVC := mock.NewVariableService()
|
|
varSVC.FindVariablesF = func(_ context.Context, f influxdb.VariableFilter, _ ...influxdb.FindOptions) ([]*influxdb.Variable, error) {
|
|
if f.OrganizationID == nil || *f.OrganizationID != orgID {
|
|
return nil, errors.New("not suppose to get here")
|
|
}
|
|
return []*influxdb.Variable{{ID: 4, Name: "variable"}}, nil
|
|
}
|
|
varSVC.FindVariableByIDF = func(_ context.Context, id influxdb.ID) (*influxdb.Variable, error) {
|
|
if id != 4 {
|
|
return nil, errors.New("wrong id")
|
|
}
|
|
return &influxdb.Variable{ID: 4, Name: "variable"}, nil
|
|
}
|
|
|
|
svc := newTestService(
|
|
WithBucketSVC(bktSVC),
|
|
WithCheckSVC(checkSVC),
|
|
WithDashboardSVC(dashSVC),
|
|
WithLabelSVC(labelSVC),
|
|
WithNotificationEndpointSVC(endpointSVC),
|
|
WithNotificationRuleSVC(ruleSVC),
|
|
WithTaskSVC(taskSVC),
|
|
WithVariableSVC(varSVC),
|
|
)
|
|
|
|
pkg, err := svc.CreatePkg(
|
|
context.TODO(),
|
|
CreateWithAllOrgResources(CreateByOrgIDOpt{
|
|
OrgID: orgID,
|
|
}),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
summary := pkg.Summary()
|
|
bkts := summary.Buckets
|
|
require.Len(t, bkts, 1)
|
|
assert.Equal(t, "bucket", bkts[0].Name)
|
|
|
|
checks := summary.Checks
|
|
require.Len(t, checks, 1)
|
|
assert.Equal(t, expectedCheck.Name, checks[0].Check.GetName())
|
|
|
|
dashs := summary.Dashboards
|
|
require.Len(t, dashs, 1)
|
|
assert.Equal(t, "dashboard", dashs[0].Name)
|
|
|
|
labels := summary.Labels
|
|
require.Len(t, labels, 1)
|
|
assert.Equal(t, "label", labels[0].Name)
|
|
|
|
endpoints := summary.NotificationEndpoints
|
|
require.Len(t, endpoints, 1)
|
|
assert.Equal(t, "http", endpoints[0].NotificationEndpoint.GetName())
|
|
|
|
rules := summary.NotificationRules
|
|
require.Len(t, rules, 1)
|
|
assert.Equal(t, expectedRule.Name, rules[0].Name)
|
|
assert.NotEmpty(t, rules[0].EndpointPkgName)
|
|
|
|
require.Len(t, summary.Tasks, 1)
|
|
task1 := summary.Tasks[0]
|
|
assert.Equal(t, "task_0", task1.Name)
|
|
|
|
vars := summary.Variables
|
|
require.Len(t, vars, 1)
|
|
assert.Equal(t, "variable", vars[0].Name)
|
|
})
|
|
})
|
|
|
|
t.Run("InitStack", func(t *testing.T) {
|
|
safeCreateFn := func(ctx context.Context, stack Stack) error {
|
|
return nil
|
|
}
|
|
|
|
type createFn func(ctx context.Context, stack Stack) error
|
|
|
|
newFakeStore := func(fn createFn) *fakeStore {
|
|
return &fakeStore{
|
|
createFn: fn,
|
|
}
|
|
}
|
|
|
|
now := time.Time{}.Add(10 * 24 * time.Hour)
|
|
|
|
t.Run("when store call is successful", func(t *testing.T) {
|
|
svc := newTestService(
|
|
WithIDGenerator(newFakeIDGen(3)),
|
|
WithTimeGenerator(newTimeGen(now)),
|
|
WithStore(newFakeStore(safeCreateFn)),
|
|
)
|
|
|
|
stack, err := svc.InitStack(context.Background(), 9000, Stack{OrgID: 3333})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, influxdb.ID(3), stack.ID)
|
|
assert.Equal(t, now, stack.CreatedAt)
|
|
assert.Equal(t, now, stack.UpdatedAt)
|
|
})
|
|
|
|
t.Run("handles unexpected error paths", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
expectedErrCode string
|
|
store func() *fakeStore
|
|
orgSVC func() influxdb.OrganizationService
|
|
}{
|
|
{
|
|
name: "unexpected store err",
|
|
expectedErrCode: influxdb.EInternal,
|
|
store: func() *fakeStore {
|
|
return newFakeStore(func(ctx context.Context, stack Stack) error {
|
|
return errors.New("unexpected error")
|
|
})
|
|
},
|
|
},
|
|
{
|
|
name: "unexpected conflict store err",
|
|
expectedErrCode: influxdb.EInternal,
|
|
store: func() *fakeStore {
|
|
return newFakeStore(func(ctx context.Context, stack Stack) error {
|
|
return &influxdb.Error{Code: influxdb.EConflict}
|
|
})
|
|
},
|
|
},
|
|
{
|
|
name: "org does not exist produces conflict error",
|
|
expectedErrCode: influxdb.EConflict,
|
|
store: func() *fakeStore {
|
|
return newFakeStore(safeCreateFn)
|
|
},
|
|
orgSVC: func() influxdb.OrganizationService {
|
|
orgSVC := mock.NewOrganizationService()
|
|
orgSVC.FindOrganizationByIDF = func(ctx context.Context, id influxdb.ID) (*influxdb.Organization, error) {
|
|
return nil, &influxdb.Error{Code: influxdb.ENotFound}
|
|
}
|
|
return orgSVC
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
var orgSVC influxdb.OrganizationService = mock.NewOrganizationService()
|
|
if tt.orgSVC != nil {
|
|
orgSVC = tt.orgSVC()
|
|
}
|
|
|
|
svc := newTestService(
|
|
WithIDGenerator(newFakeIDGen(3)),
|
|
WithTimeGenerator(newTimeGen(now)),
|
|
WithStore(tt.store()),
|
|
WithOrganizationService(orgSVC),
|
|
)
|
|
|
|
_, err := svc.InitStack(context.Background(), 9000, Stack{OrgID: 3333})
|
|
require.Error(t, err)
|
|
assert.Equal(t, tt.expectedErrCode, influxdb.ErrorCode(err))
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("UpdateStack", func(t *testing.T) {
|
|
now := time.Time{}.Add(10 * 24 * time.Hour)
|
|
|
|
t.Run("when updating valid stack", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input StackUpdate
|
|
expected Stack
|
|
}{
|
|
{
|
|
name: "update nothing",
|
|
input: StackUpdate{},
|
|
expected: Stack{},
|
|
},
|
|
{
|
|
name: "update name",
|
|
input: StackUpdate{
|
|
Name: strPtr("name"),
|
|
},
|
|
expected: Stack{
|
|
Name: "name",
|
|
},
|
|
},
|
|
{
|
|
name: "update desc",
|
|
input: StackUpdate{
|
|
Description: strPtr("desc"),
|
|
},
|
|
expected: Stack{
|
|
Description: "desc",
|
|
},
|
|
},
|
|
{
|
|
name: "update URLs",
|
|
input: StackUpdate{
|
|
URLs: []string{"http://example.com"},
|
|
},
|
|
expected: Stack{
|
|
URLs: []string{"http://example.com"},
|
|
},
|
|
},
|
|
{
|
|
name: "update all",
|
|
input: StackUpdate{
|
|
Name: strPtr("name"),
|
|
Description: strPtr("desc"),
|
|
URLs: []string{"http://example.com"},
|
|
},
|
|
expected: Stack{
|
|
Name: "name",
|
|
Description: "desc",
|
|
URLs: []string{"http://example.com"},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
svc := newTestService(
|
|
WithTimeGenerator(newTimeGen(now)),
|
|
WithStore(&fakeStore{
|
|
readFn: func(ctx context.Context, id influxdb.ID) (Stack, error) {
|
|
if id != 33 {
|
|
return Stack{}, errors.New("wrong id: " + id.String())
|
|
}
|
|
return Stack{ID: id, OrgID: 3}, nil
|
|
},
|
|
updateFn: func(ctx context.Context, stack Stack) error {
|
|
return nil
|
|
},
|
|
}),
|
|
)
|
|
|
|
tt.input.ID = 33
|
|
stack, err := svc.UpdateStack(context.Background(), tt.input)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, influxdb.ID(33), stack.ID)
|
|
assert.Equal(t, influxdb.ID(3), stack.OrgID)
|
|
assert.Zero(t, stack.CreatedAt) // should always zero value in these tests
|
|
assert.Equal(t, tt.expected.Name, stack.Name)
|
|
assert.Equal(t, tt.expected.Description, stack.Description)
|
|
assert.Equal(t, tt.expected.URLs, stack.URLs)
|
|
assert.Equal(t, now, stack.UpdatedAt)
|
|
}
|
|
|
|
t.Run(tt.name, fn)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func Test_normalizeRemoteSources(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input []string
|
|
expected []url.URL
|
|
}{
|
|
{
|
|
name: "no urls provided",
|
|
input: []string{"byte stream", "string", ""},
|
|
expected: nil,
|
|
},
|
|
{
|
|
name: "skips valid file url",
|
|
input: []string{"file:///example.com"},
|
|
expected: nil,
|
|
},
|
|
{
|
|
name: "valid http url provided",
|
|
input: []string{"http://example.com"},
|
|
expected: []url.URL{parseURL(t, "http://example.com")},
|
|
},
|
|
{
|
|
name: "valid https url provided",
|
|
input: []string{"https://example.com"},
|
|
expected: []url.URL{parseURL(t, "https://example.com")},
|
|
},
|
|
{
|
|
name: "converts raw github user url to base github",
|
|
input: []string{"https://raw.githubusercontent.com/influxdata/community-templates/master/github/github.yml"},
|
|
expected: []url.URL{
|
|
parseURL(t, "https://github.com/influxdata/community-templates/blob/master/github/github.yml"),
|
|
},
|
|
},
|
|
{
|
|
name: "passes base github link unchanged",
|
|
input: []string{"https://github.com/influxdata/community-templates/blob/master/github/github.yml"},
|
|
expected: []url.URL{
|
|
parseURL(t, "https://github.com/influxdata/community-templates/blob/master/github/github.yml"),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fn := func(t *testing.T) {
|
|
actual := normalizeRemoteSources(tt.input)
|
|
require.Len(t, actual, len(tt.expected))
|
|
for i, expected := range tt.expected {
|
|
assert.Equal(t, expected.String(), actual[i].String())
|
|
}
|
|
}
|
|
t.Run(tt.name, fn)
|
|
}
|
|
}
|
|
|
|
func newTestIDPtr(i int) *influxdb.ID {
|
|
id := influxdb.ID(i)
|
|
return &id
|
|
}
|
|
|
|
func levelPtr(l notification.CheckLevel) *notification.CheckLevel {
|
|
return &l
|
|
}
|
|
|
|
type fakeStore struct {
|
|
createFn func(ctx context.Context, stack Stack) error
|
|
deleteFn func(ctx context.Context, id influxdb.ID) error
|
|
readFn func(ctx context.Context, id influxdb.ID) (Stack, error)
|
|
updateFn func(ctx context.Context, stack Stack) error
|
|
}
|
|
|
|
var _ Store = (*fakeStore)(nil)
|
|
|
|
func (s *fakeStore) CreateStack(ctx context.Context, stack Stack) error {
|
|
if s.createFn != nil {
|
|
return s.createFn(ctx, stack)
|
|
}
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (s *fakeStore) ListStacks(ctx context.Context, orgID influxdb.ID, f ListFilter) ([]Stack, error) {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (s *fakeStore) ReadStackByID(ctx context.Context, id influxdb.ID) (Stack, error) {
|
|
if s.readFn != nil {
|
|
return s.readFn(ctx, id)
|
|
}
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (s *fakeStore) UpdateStack(ctx context.Context, stack Stack) error {
|
|
if s.updateFn != nil {
|
|
return s.updateFn(ctx, stack)
|
|
}
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (s *fakeStore) DeleteStack(ctx context.Context, id influxdb.ID) error {
|
|
if s.deleteFn != nil {
|
|
return s.deleteFn(ctx, id)
|
|
}
|
|
panic("not implemented")
|
|
}
|
|
|
|
type fakeIDGen func() influxdb.ID
|
|
|
|
func newFakeIDGen(id influxdb.ID) fakeIDGen {
|
|
return func() influxdb.ID {
|
|
return id
|
|
}
|
|
}
|
|
|
|
func (f fakeIDGen) ID() influxdb.ID {
|
|
return f()
|
|
}
|
|
|
|
type fakeTimeGen func() time.Time
|
|
|
|
func newTimeGen(t time.Time) fakeTimeGen {
|
|
return func() time.Time {
|
|
return t
|
|
}
|
|
}
|
|
|
|
func (t fakeTimeGen) Now() time.Time {
|
|
return t()
|
|
}
|
|
|
|
func parseURL(t *testing.T, rawAddr string) url.URL {
|
|
t.Helper()
|
|
u, err := url.Parse(rawAddr)
|
|
require.NoError(t, err)
|
|
return *u
|
|
}
|