diff --git a/bolt/bbolt.go b/bolt/bbolt.go index 1ca11901564..2eacbd4819b 100644 --- a/bolt/bbolt.go +++ b/bolt/bbolt.go @@ -108,6 +108,11 @@ func (c *Client) initialize(ctx context.Context) error { return err } + // Always create Telegraf Config bucket. + if err := c.initializeTelegraf(ctx, tx); err != nil { + return err + } + // Always create Source bucket. if err := c.initializeSources(ctx, tx); err != nil { return err diff --git a/bolt/telegraf.go b/bolt/telegraf.go new file mode 100644 index 00000000000..feed8ffc53c --- /dev/null +++ b/bolt/telegraf.go @@ -0,0 +1,240 @@ +package bolt + +import ( + "context" + "encoding/json" + "fmt" + "time" + + bolt "github.com/coreos/bbolt" + "github.com/influxdata/platform" +) + +var ( + telegrafBucket = []byte("telegrafv1") +) + +var _ platform.TelegrafConfigStore = new(Client) + +func (c *Client) initializeTelegraf(ctx context.Context, tx *bolt.Tx) error { + if _, err := tx.CreateBucketIfNotExists(telegrafBucket); err != nil { + return err + } + return nil +} + +// FindTelegrafConfigByID returns a single telegraf config by ID. +func (c *Client) FindTelegrafConfigByID(ctx context.Context, id platform.ID) (tc *platform.TelegrafConfig, err error) { + op := "bolt/find telegraf config by id" + err = c.db.View(func(tx *bolt.Tx) error { + var pErr *platform.Error + tc, pErr = c.findTelegrafConfigByID(ctx, tx, id) + if pErr != nil { + pErr.Op = op + err = pErr + } + return err + }) + return tc, err +} + +func (c *Client) findTelegrafConfigByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.TelegrafConfig, *platform.Error) { + encID, err := id.Encode() + if err != nil { + return nil, &platform.Error{ + Code: platform.EEmptyValue, + Err: err, + } + } + d := tx.Bucket(telegrafBucket).Get(encID) + if d == nil { + return nil, &platform.Error{ + Code: platform.ENotFound, + Msg: fmt.Sprintf("telegraf config with ID %v not found", id), + } + } + tc := new(platform.TelegrafConfig) + err = json.Unmarshal(d, tc) + if err != nil { + return nil, &platform.Error{ + Err: err, + } + } + return tc, nil +} + +// FindTelegrafConfig returns the first telegraf config that matches filter. +func (c *Client) FindTelegrafConfig(ctx context.Context, filter platform.UserResourceMappingFilter) (*platform.TelegrafConfig, error) { + op := "bolt/find telegraf config" + tcs, n, err := c.FindTelegrafConfigs(ctx, filter) + if err != nil { + return nil, err + } + if n > 0 { + return tcs[0], nil + } + return nil, &platform.Error{ + Code: platform.ENotFound, + Op: op, + } +} + +func (c *Client) findTelegrafConfigs(ctx context.Context, tx *bolt.Tx, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) ([]*platform.TelegrafConfig, int, *platform.Error) { + tcs := make([]*platform.TelegrafConfig, 0) + m, err := c.findUserResourceMappings(ctx, tx, filter) + if err != nil { + return nil, 0, &platform.Error{ + Err: err, + } + } + if len(m) == 0 { + return nil, 0, &platform.Error{ + Code: platform.ENotFound, + } + } + for _, item := range m { + tc, err := c.findTelegrafConfigByID(ctx, tx, item.ResourceID) + if err != nil { + return nil, 0, &platform.Error{ + // return internal error, for any mapping issue + Err: err, + } + } + tcs = append(tcs, tc) + } + if len(tcs) == 0 { + return nil, 0, &platform.Error{ + Msg: "inconsistent user resource mapping and telegraf config", + } + } + return tcs, len(tcs), nil +} + +// FindTelegrafConfigs returns a list of telegraf configs that match filter and the total count of matching telegraf configs. +// Additional options provide pagination & sorting. +func (c *Client) FindTelegrafConfigs(ctx context.Context, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) (tcs []*platform.TelegrafConfig, n int, err error) { + op := "bolt/find telegraf configs" + err = c.db.View(func(tx *bolt.Tx) error { + var pErr *platform.Error + tcs, n, pErr = c.findTelegrafConfigs(ctx, tx, filter) + if pErr != nil { + pErr.Op = op + return pErr + } + return nil + }) + return tcs, len(tcs), err +} + +func (c *Client) putTelegrafConfig(ctx context.Context, tx *bolt.Tx, tc *platform.TelegrafConfig) *platform.Error { + v, err := json.Marshal(tc) + if err != nil { + return &platform.Error{ + Err: err, + } + } + encodedID, err := tc.ID.Encode() + if err != nil { + return &platform.Error{ + Code: platform.EEmptyValue, + Err: err, + } + } + err = tx.Bucket(telegrafBucket).Put(encodedID, v) + if err != nil { + return &platform.Error{ + Err: err, + } + } + return nil +} + +// CreateTelegrafConfig creates a new telegraf config and sets b.ID with the new identifier. +func (c *Client) CreateTelegrafConfig(ctx context.Context, tc *platform.TelegrafConfig, userID platform.ID, now time.Time) error { + op := "bolt/create telegraf config" + return c.db.Update(func(tx *bolt.Tx) error { + tc.ID = c.IDGenerator.ID() + err := c.createUserResourceMapping(ctx, tx, &platform.UserResourceMapping{ + ResourceID: tc.ID, + UserID: userID, + UserType: platform.Owner, + ResourceType: platform.TelegrafResourceType, + }) + if err != nil { + return err + } + tc.Created = now + tc.LastMod = now + tc.LastModBy = userID + pErr := c.putTelegrafConfig(ctx, tx, tc) + if pErr != nil { + pErr.Op = op + err = pErr + } + return err + }) +} + +// UpdateTelegrafConfig updates a single telegraf config. +// Returns the new telegraf config after update. +func (c *Client) UpdateTelegrafConfig(ctx context.Context, id platform.ID, tc *platform.TelegrafConfig, userID platform.ID, now time.Time) (*platform.TelegrafConfig, error) { + op := "bolt/update telegraf config" + err := c.db.Update(func(tx *bolt.Tx) (err error) { + _, pErr := c.findTelegrafConfigByID(ctx, tx, id) + if pErr != nil { + pErr.Op = op + err = pErr + } + tc.ID = id + tc.LastMod = now + tc.LastModBy = userID + pErr = c.putTelegrafConfig(ctx, tx, tc) + if pErr != nil { + pErr.Op = op + err = pErr + } + return err + }) + return tc, err +} + +// DeleteTelegrafConfig removes a telegraf config by ID. +func (c *Client) DeleteTelegrafConfig(ctx context.Context, id platform.ID) error { + op := "bolt/delete telegraf config" + err := c.db.Update(func(tx *bolt.Tx) error { + encodedID, err := id.Encode() + if err != nil { + return &platform.Error{ + Code: platform.EEmptyValue, + Err: err, + } + } + err = tx.Bucket(telegrafBucket).Delete(encodedID) + if err != nil { + return err + } + return c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{ + ResourceID: id, + ResourceType: platform.TelegrafResourceType, + }) + }) + if err != nil { + err = &platform.Error{ + Code: platform.ErrorCode(err), + Op: op, + Err: err, + } + } + return err +} + +// PutTelegrafConfig put a telegraf config to storage +func (c *Client) PutTelegrafConfig(ctx context.Context, tc *platform.TelegrafConfig) error { + return c.db.Update(func(tx *bolt.Tx) (err error) { + pErr := c.putTelegrafConfig(ctx, tx, tc) + if pErr != nil { + err = pErr + } + return nil + }) +} diff --git a/bolt/telegraf_test.go b/bolt/telegraf_test.go new file mode 100644 index 00000000000..a1b55f088fc --- /dev/null +++ b/bolt/telegraf_test.go @@ -0,0 +1,40 @@ +package bolt_test + +import ( + "context" + "testing" + + "github.com/influxdata/platform" + platformtesting "github.com/influxdata/platform/testing" +) + +func initTelegrafConfigStore(f platformtesting.TelegrafConfigFields, t *testing.T) (platform.TelegrafConfigStore, func()) { + c, closeFn, err := NewTestClient() + if err != nil { + t.Fatalf("failed to create new bolt client: %v", err) + } + c.IDGenerator = f.IDGenerator + ctx := context.TODO() + for _, tc := range f.TelegrafConfigs { + if err := c.PutTelegrafConfig(ctx, tc); err != nil { + t.Fatalf("failed to populate telegraf config: %s", err.Error()) + } + } + for _, m := range f.UserResourceMappings { + if err := c.CreateUserResourceMapping(ctx, m); err != nil { + t.Fatalf("failed to populate user resource mapping") + } + } + return c, func() { + defer closeFn() + for _, tc := range f.TelegrafConfigs { + if err := c.DeleteTelegrafConfig(ctx, tc.ID); err != nil { + t.Logf("failed to remove telegraf config: %v", err) + } + } + } +} + +func TestTelegrafConfigStore(t *testing.T) { + platformtesting.TelegrafConfigStore(initTelegrafConfigStore, t) +} diff --git a/bolt/user_resource_mapping.go b/bolt/user_resource_mapping.go index 73e1f551e0f..26dfba387db 100644 --- a/bolt/user_resource_mapping.go +++ b/bolt/user_resource_mapping.go @@ -89,28 +89,32 @@ func (c *Client) findUserResourceMapping(ctx context.Context, tx *bolt.Tx, resou func (c *Client) CreateUserResourceMapping(ctx context.Context, m *platform.UserResourceMapping) error { return c.db.Update(func(tx *bolt.Tx) error { - unique := c.uniqueUserResourceMapping(ctx, tx, m) + return c.createUserResourceMapping(ctx, tx, m) + }) +} - if !unique { - return fmt.Errorf("mapping for user %s already exists", m.UserID.String()) - } +func (c *Client) createUserResourceMapping(ctx context.Context, tx *bolt.Tx, m *platform.UserResourceMapping) error { + unique := c.uniqueUserResourceMapping(ctx, tx, m) - v, err := json.Marshal(m) - if err != nil { - return err - } + if !unique { + return fmt.Errorf("mapping for user %s already exists", m.UserID.String()) + } - key, err := userResourceKey(m) - if err != nil { - return err - } + v, err := json.Marshal(m) + if err != nil { + return err + } - if err := tx.Bucket(userResourceMappingBucket).Put(key, v); err != nil { - return err - } + key, err := userResourceKey(m) + if err != nil { + return err + } - return nil - }) + if err := tx.Bucket(userResourceMappingBucket).Put(key, v); err != nil { + return err + } + + return nil } func userResourceKey(m *platform.UserResourceMapping) ([]byte, error) { @@ -175,3 +179,20 @@ func (c *Client) deleteUserResourceMapping(ctx context.Context, tx *bolt.Tx, res return tx.Bucket(userResourceMappingBucket).Delete(key) } + +func (c *Client) deleteUserResourceMappings(ctx context.Context, tx *bolt.Tx, filter platform.UserResourceMappingFilter) error { + ms, err := c.findUserResourceMappings(ctx, tx, filter) + if err != nil { + return err + } + for _, m := range ms { + key, err := userResourceKey(m) + if err != nil { + return err + } + if err = tx.Bucket(userResourceMappingBucket).Delete(key); err != nil { + return err + } + } + return nil +} diff --git a/inmem/service.go b/inmem/service.go index 6783bda2aa9..0a7dabe0b79 100644 --- a/inmem/service.go +++ b/inmem/service.go @@ -20,6 +20,7 @@ type Service struct { dbrpMappingKV sync.Map userResourceMappingKV sync.Map scraperTargetKV sync.Map + telegrafConfigKV sync.Map TokenGenerator platform.TokenGenerator IDGenerator platform.IDGenerator diff --git a/inmem/telegraf.go b/inmem/telegraf.go new file mode 100644 index 00000000000..29973f087aa --- /dev/null +++ b/inmem/telegraf.go @@ -0,0 +1,191 @@ +package inmem + +import ( + "context" + "fmt" + "time" + + "github.com/influxdata/platform" +) + +var _ platform.TelegrafConfigStore = new(Service) + +// FindTelegrafConfigByID returns a single telegraf config by ID. +func (s *Service) FindTelegrafConfigByID(ctx context.Context, id platform.ID) (tc *platform.TelegrafConfig, err error) { + op := "inmem/find telegraf config by id" + var pErr *platform.Error + tc, pErr = s.findTelegrafConfigByID(ctx, id) + if pErr != nil { + pErr.Op = op + err = pErr + } + return tc, err +} + +func (s *Service) findTelegrafConfigByID(ctx context.Context, id platform.ID) (*platform.TelegrafConfig, *platform.Error) { + if !id.Valid() { + return nil, &platform.Error{ + Code: platform.EEmptyValue, + Err: platform.ErrInvalidID, + } + } + result, found := s.telegrafConfigKV.Load(id) + if !found { + return nil, &platform.Error{ + Code: platform.ENotFound, + Msg: fmt.Sprintf("telegraf config with ID %v not found", id), + } + } + tc := new(platform.TelegrafConfig) + *tc = result.(platform.TelegrafConfig) + return tc, nil +} + +// FindTelegrafConfig returns the first telegraf config that matches filter. +func (s *Service) FindTelegrafConfig(ctx context.Context, filter platform.UserResourceMappingFilter) (*platform.TelegrafConfig, error) { + op := "inmem/find telegraf config" + tcs, n, err := s.FindTelegrafConfigs(ctx, filter) + if err != nil { + return nil, err + } + if n > 0 { + return tcs[0], nil + } + return nil, &platform.Error{ + Code: platform.ENotFound, + Op: op, + } +} + +func (s *Service) findTelegrafConfigs(ctx context.Context, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) ([]*platform.TelegrafConfig, int, *platform.Error) { + tcs := make([]*platform.TelegrafConfig, 0) + m, _, err := s.FindUserResourceMappings(ctx, filter) + if err != nil { + return nil, 0, &platform.Error{ + Err: err, + } + } + if len(m) == 0 { + return nil, 0, &platform.Error{ + Code: platform.ENotFound, + } + } + for _, item := range m { + tc, err := s.findTelegrafConfigByID(ctx, item.ResourceID) + if err != nil { + return nil, 0, &platform.Error{ + // return internal error, for any mapping issue + Err: err, + } + } + tcs = append(tcs, tc) + } + if len(tcs) == 0 { + return nil, 0, &platform.Error{ + Msg: "inconsistent user resource mapping and telegraf config", + } + } + return tcs, len(tcs), nil +} + +// FindTelegrafConfigs returns a list of telegraf configs that match filter and the total count of matching telegraf configs. +// Additional options provide pagination & sorting. +func (s *Service) FindTelegrafConfigs(ctx context.Context, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) (tcs []*platform.TelegrafConfig, n int, err error) { + op := "inmem/find telegraf configs" + var pErr *platform.Error + tcs, n, pErr = s.findTelegrafConfigs(ctx, filter) + if pErr != nil { + pErr.Op = op + err = pErr + } + return tcs, len(tcs), err +} + +func (s *Service) putTelegrafConfig(ctx context.Context, tc *platform.TelegrafConfig) *platform.Error { + if !tc.ID.Valid() { + return &platform.Error{ + Code: platform.EEmptyValue, + Err: platform.ErrInvalidID, + } + } + s.telegrafConfigKV.Store(tc.ID, *tc) + return nil +} + +// CreateTelegrafConfig creates a new telegraf config and sets b.ID with the new identifier. +func (s *Service) CreateTelegrafConfig(ctx context.Context, tc *platform.TelegrafConfig, userID platform.ID, now time.Time) error { + op := "inmem/create telegraf config" + tc.ID = s.IDGenerator.ID() + err := s.CreateUserResourceMapping(ctx, &platform.UserResourceMapping{ + ResourceID: tc.ID, + UserID: userID, + UserType: platform.Owner, + ResourceType: platform.TelegrafResourceType, + }) + if err != nil { + return err + } + tc.Created = now + tc.LastMod = now + tc.LastModBy = userID + pErr := s.putTelegrafConfig(ctx, tc) + if pErr != nil { + pErr.Op = op + err = pErr + } + return err + +} + +// UpdateTelegrafConfig updates a single telegraf config. +// Returns the new telegraf config after update. +func (s *Service) UpdateTelegrafConfig(ctx context.Context, id platform.ID, tc *platform.TelegrafConfig, userID platform.ID, now time.Time) (*platform.TelegrafConfig, error) { + var err error + op := "inmem/update telegraf config" + _, pErr := s.findTelegrafConfigByID(ctx, id) + if pErr != nil { + pErr.Op = op + err = pErr + } + tc.ID = id + tc.LastMod = now + tc.LastModBy = userID + pErr = s.putTelegrafConfig(ctx, tc) + if pErr != nil { + pErr.Op = op + err = pErr + } + + return tc, err +} + +// DeleteTelegrafConfig removes a telegraf config by ID. +func (s *Service) DeleteTelegrafConfig(ctx context.Context, id platform.ID) error { + op := "inmem/delete telegraf config" + var err error + if !id.Valid() { + return &platform.Error{ + Op: op, + Code: platform.EEmptyValue, + Err: platform.ErrInvalidID, + } + } + if _, pErr := s.findTelegrafConfigByID(ctx, id); pErr != nil { + return pErr + } + s.telegrafConfigKV.Delete(id) + + err = s.deleteUserResourceMapping(ctx, platform.UserResourceMappingFilter{ + ResourceID: id, + ResourceType: platform.TelegrafResourceType, + }) + + if err != nil { + return &platform.Error{ + Code: platform.ErrorCode(err), + Op: op, + Err: err, + } + } + return nil +} diff --git a/inmem/telegraf_test.go b/inmem/telegraf_test.go new file mode 100644 index 00000000000..67de9df3aa7 --- /dev/null +++ b/inmem/telegraf_test.go @@ -0,0 +1,30 @@ +package inmem + +import ( + "context" + "testing" + + "github.com/influxdata/platform" + platformtesting "github.com/influxdata/platform/testing" +) + +func initTelegrafStore(f platformtesting.TelegrafConfigFields, t *testing.T) (platform.TelegrafConfigStore, func()) { + s := NewService() + s.IDGenerator = f.IDGenerator + ctx := context.Background() + for _, m := range f.UserResourceMappings { + if err := s.PutUserResourceMapping(ctx, m); err != nil { + t.Fatalf("failed to populate user resource mapping") + } + } + for _, tc := range f.TelegrafConfigs { + if err := s.putTelegrafConfig(ctx, tc); err != nil { + t.Fatalf("failed to populate telegraf configs") + } + } + return s, func() {} +} + +func TestTelegrafStore(t *testing.T) { + platformtesting.TelegrafConfigStore(initTelegrafStore, t) +} diff --git a/inmem/user_resource_mapping_service.go b/inmem/user_resource_mapping_service.go index a1f7d2e64a9..664030f75fc 100644 --- a/inmem/user_resource_mapping_service.go +++ b/inmem/user_resource_mapping_service.go @@ -75,27 +75,29 @@ func (s *Service) FindUserResourceMappings(ctx context.Context, filter platform. return true } + good := true + // Filter by UserID - if filter.UserID.Valid() && filter.UserID == mapping.UserID { - return true + if filter.UserID.Valid() && filter.UserID != mapping.UserID { + good = false } // Filter by ResourceID - if filter.ResourceID.Valid() && filter.ResourceID == mapping.ResourceID { - return true + if filter.ResourceID.Valid() && filter.ResourceID != mapping.ResourceID { + good = false } // Filter by user type - if filter.UserType == mapping.UserType { - return true + if filter.UserType != "" && filter.UserType != mapping.UserType { + good = false } // Filter by resource type - if filter.ResourceType == mapping.ResourceType { - return true + if filter.ResourceType != "" && filter.ResourceType != mapping.ResourceType { + good = false } - return false + return good } mappings, err := s.filterUserResourceMappings(ctx, filterFunc) @@ -135,3 +137,15 @@ func (s *Service) DeleteUserResourceMapping(ctx context.Context, resourceID, use s.userResourceMappingKV.Delete(encodeUserResourceMappingKey(resourceID, userID)) return nil } + +func (s *Service) deleteUserResourceMapping(ctx context.Context, filter platform.UserResourceMappingFilter) error { + mappings, _, err := s.FindUserResourceMappings(ctx, filter) + if mappings == nil && err != nil { + return err + } + for _, m := range mappings { + s.userResourceMappingKV.Delete(encodeUserResourceMappingKey(m.ResourceID, m.UserID)) + } + + return nil +} diff --git a/telegraf.go b/telegraf.go index 720b75f0e97..0c229293b56 100644 --- a/telegraf.go +++ b/telegraf.go @@ -13,6 +13,10 @@ import ( // TelegrafConfigStore represents a service for managing telegraf config data. type TelegrafConfigStore interface { + // UserResourceMappingService must be part of all TelegrafConfigStore service, + // for create, search, delete. + UserResourceMappingService + // FindTelegrafConfigByID returns a single telegraf config by ID. FindTelegrafConfigByID(ctx context.Context, id ID) (*TelegrafConfig, error) diff --git a/testing/telegraf.go b/testing/telegraf.go new file mode 100644 index 00000000000..9b37390e13a --- /dev/null +++ b/testing/telegraf.go @@ -0,0 +1,1494 @@ +package testing + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "sort" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/influxdata/platform" + "github.com/influxdata/platform/mock" + "github.com/influxdata/platform/telegraf/plugins/inputs" + "github.com/influxdata/platform/telegraf/plugins/outputs" +) + +// TelegrafConfigFields includes prepopulated data for mapping tests. +type TelegrafConfigFields struct { + IDGenerator platform.IDGenerator + TelegrafConfigs []*platform.TelegrafConfig + UserResourceMappings []*platform.UserResourceMapping +} + +var telegrafCmpOptions = cmp.Options{ + cmp.Comparer(func(a, b *platform.TelegrafConfig) bool { + x, _ := json.Marshal(a) + y, _ := json.Marshal(b) + return bytes.Equal(x, y) + }), + cmp.Transformer("Sort", func(in []*platform.TelegrafConfig) []*platform.TelegrafConfig { + out := append([]*platform.TelegrafConfig(nil), in...) + sort.Slice(out, func(i, j int) bool { + return out[i].ID > out[j].ID + }) + return out + }), +} + +var userResourceMappingCmpOptions = cmp.Options{ + cmp.Comparer(func(x, y []byte) bool { + return bytes.Equal(x, y) + }), + cmp.Transformer("Sort", func(in []*platform.UserResourceMapping) []*platform.UserResourceMapping { + out := append([]*platform.UserResourceMapping(nil), in...) + sort.Slice(out, func(i, j int) bool { + return out[i].ResourceID.String() > out[j].ResourceID.String() + }) + return out + }), +} + +// TelegrafConfigStore tests all the service functions. +func TelegrafConfigStore( + init func(TelegrafConfigFields, *testing.T) (platform.TelegrafConfigStore, func()), t *testing.T, +) { + tests := []struct { + name string + fn func(init func(TelegrafConfigFields, *testing.T) (platform.TelegrafConfigStore, func()), + t *testing.T) + }{ + { + name: "CreateTelegrafConfig", + fn: CreateTelegrafConfig, + }, + { + name: "FindTelegrafConfigByID", + fn: FindTelegrafConfigByID, + }, + { + name: "FindTelegrafConfig", + fn: FindTelegrafConfig, + }, + { + name: "FindTelegrafConfigs", + fn: FindTelegrafConfigs, + }, + { + name: "UpdateTelegrafConfig", + fn: UpdateTelegrafConfig, + }, + { + name: "DeleteTelegrafConfig", + fn: DeleteTelegrafConfig, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.fn(init, t) + }) + } +} + +// CreateTelegrafConfig testing. +func CreateTelegrafConfig( + init func(TelegrafConfigFields, *testing.T) (platform.TelegrafConfigStore, func()), + t *testing.T, +) { + type args struct { + telegrafConfig *platform.TelegrafConfig + now time.Time + userID platform.ID + } + type wants struct { + err error + telegrafs []*platform.TelegrafConfig + userResourceMapping []*platform.UserResourceMapping + } + + now := time.Now().Round(0) + + tests := []struct { + name string + fields TelegrafConfigFields + args args + wants wants + }{ + { + name: "create telegraf config with empty set", + fields: TelegrafConfigFields{ + IDGenerator: &loopIDGenerator{s: []string{oneID, twoID}}, + TelegrafConfigs: []*platform.TelegrafConfig{}, + UserResourceMappings: []*platform.UserResourceMapping{}, + }, + args: args{ + now: now, + userID: MustIDBase16(threeID), + telegrafConfig: &platform.TelegrafConfig{ + Name: "name1", + Agent: platform.TelegrafAgentConfig{ + Interval: 1000, + }, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.CPUStats{}, + }, + { + Comment: "comment2", + Config: &outputs.InfluxDBV2{ + URLs: []string{"localhost/9999"}, + Token: "token1", + Organization: "org1", + Bucket: "bucket1", + }, + }, + }, + }, + }, + wants: wants{ + userResourceMapping: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + }, + }, + telegrafs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "name1", + Agent: platform.TelegrafAgentConfig{ + Interval: 1000, + }, + Created: now, + LastModBy: MustIDBase16(threeID), + LastMod: now, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.CPUStats{}, + }, + { + Comment: "comment2", + Config: &outputs.InfluxDBV2{ + URLs: []string{"localhost/9999"}, + Token: "token1", + Organization: "org1", + Bucket: "bucket1", + }, + }, + }, + }, + }, + }, + }, + { + name: "basic create telegraf config", + fields: TelegrafConfigFields{ + IDGenerator: mock.NewIDGenerator(twoID, t), + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + Agent: platform.TelegrafAgentConfig{ + Interval: 4000, + }, + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + UserResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Member, + }, + }, + }, + args: args{ + now: now, + userID: MustIDBase16(threeID), + telegrafConfig: &platform.TelegrafConfig{ + Name: "name2", + Agent: platform.TelegrafAgentConfig{ + Interval: 1001, + }, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment2", + Config: &inputs.CPUStats{}, + }, + { + Comment: "comment3", + Config: &outputs.InfluxDBV2{ + URLs: []string{"localhost/9999"}, + Token: "token3", + Organization: "org3", + Bucket: "bucket3", + }, + }, + }, + }, + }, + wants: wants{ + telegrafs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + Agent: platform.TelegrafAgentConfig{ + Interval: 4000, + }, + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.MemStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "name2", + Agent: platform.TelegrafAgentConfig{ + Interval: 1001, + }, + Created: now, + LastMod: now, + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment2", + Config: &inputs.CPUStats{}, + }, + { + Comment: "comment3", + Config: &outputs.InfluxDBV2{ + URLs: []string{"localhost/9999"}, + Token: "token3", + Organization: "org3", + Bucket: "bucket3", + }, + }, + }, + }, + }, + userResourceMapping: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Member, + }, + { + ResourceID: MustIDBase16(twoID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s, done := init(tt.fields, t) + defer done() + ctx := context.Background() + err := s.CreateTelegrafConfig(ctx, tt.args.telegrafConfig, tt.args.userID, tt.args.now) + if (err != nil) != (tt.wants.err != nil) { + t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) + } + if tt.wants.err == nil && !tt.args.telegrafConfig.ID.Valid() { + t.Fatalf("telegraf config ID not set from CreateTelegrafConfig") + } + + if err != nil && tt.wants.err != nil { + if platform.ErrorCode(err) != platform.ErrorCode(tt.wants.err) { + t.Fatalf("expected error messages to match '%v' got '%v'", platform.ErrorCode(tt.wants.err), platform.ErrorCode(err)) + } + } + + tcs, _, err := s.FindTelegrafConfigs(ctx, platform.UserResourceMappingFilter{ + UserID: MustIDBase16(threeID), + ResourceType: platform.TelegrafResourceType, + }) + if err != nil { + t.Fatalf("failed to retrieve telegraf configs: %v", err) + } + if diff := cmp.Diff(tcs, tt.wants.telegrafs, telegrafCmpOptions...); diff != "" { + t.Errorf("telegraf configs are different -got/+want\ndiff %s", diff) + } + + urms, _, err := s.FindUserResourceMappings(ctx, platform.UserResourceMappingFilter{ + UserID: tt.args.userID, + ResourceType: platform.TelegrafResourceType, + }) + if err != nil { + t.Fatalf("failed to retrieve user resource mappings: %v", err) + } + if diff := cmp.Diff(urms, tt.wants.userResourceMapping, userResourceMappingCmpOptions...); diff != "" { + t.Errorf("user resource mappings are different -got/+want\ndiff %s", diff) + } + }) + } +} + +// FindTelegrafConfigByID testing. +func FindTelegrafConfigByID( + init func(TelegrafConfigFields, *testing.T) (platform.TelegrafConfigStore, func()), + t *testing.T, +) { + type args struct { + id platform.ID + } + type wants struct { + err error + telegrafConfig *platform.TelegrafConfig + } + + tests := []struct { + name string + fields TelegrafConfigFields + args args + wants wants + }{ + { + name: "bad id", + fields: TelegrafConfigFields{ + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + id: platform.ID(0), + }, + wants: wants{ + err: &platform.Error{ + Code: platform.EEmptyValue, + Err: platform.ErrInvalidID, + }, + }, + }, + { + name: "not found", + fields: TelegrafConfigFields{ + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + id: MustIDBase16(threeID), + }, + wants: wants{ + err: &platform.Error{ + Code: platform.ENotFound, + Msg: fmt.Sprintf("telegraf config with ID %v not found", MustIDBase16(threeID)), + }, + }, + }, + { + name: "basic find telegraf config by id", + fields: TelegrafConfigFields{ + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + id: MustIDBase16(twoID), + }, + wants: wants{ + telegrafConfig: &platform.TelegrafConfig{ + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s, done := init(tt.fields, t) + defer done() + ctx := context.Background() + + tc, err := s.FindTelegrafConfigByID(ctx, tt.args.id) + if (err != nil) != (tt.wants.err != nil) { + t.Fatalf("expected errors to be equal '%v' got '%v'", tt.wants.err, err) + } + + if err != nil && tt.wants.err != nil { + if platform.ErrorCode(err) != platform.ErrorCode(tt.wants.err) { + t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) + } + } + if diff := cmp.Diff(tc, tt.wants.telegrafConfig, telegrafCmpOptions...); diff != "" { + t.Errorf("telegraf configs are different -got/+want\ndiff %s", diff) + } + }) + } +} + +// FindTelegrafConfig testing +func FindTelegrafConfig( + init func(TelegrafConfigFields, *testing.T) (platform.TelegrafConfigStore, func()), + t *testing.T, +) { + type args struct { + filter platform.UserResourceMappingFilter + } + + type wants struct { + telegrafConfig *platform.TelegrafConfig + err error + } + tests := []struct { + name string + fields TelegrafConfigFields + args args + wants wants + }{ + { + name: "find telegraf config", + fields: TelegrafConfigFields{ + UserResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + }, + { + ResourceID: MustIDBase16(twoID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Member, + }, + }, + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + filter: platform.UserResourceMappingFilter{ + UserID: MustIDBase16(threeID), + ResourceType: platform.TelegrafResourceType, + UserType: platform.Member, + }, + }, + wants: wants{ + telegrafConfig: &platform.TelegrafConfig{ + + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + { + name: "find nothing", + fields: TelegrafConfigFields{ + UserResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + }, + { + ResourceID: MustIDBase16(twoID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Member, + }, + }, + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + filter: platform.UserResourceMappingFilter{ + UserID: MustIDBase16(fourID), + ResourceType: platform.TelegrafResourceType, + }, + }, + wants: wants{ + err: &platform.Error{ + Code: platform.ENotFound, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s, done := init(tt.fields, t) + defer done() + ctx := context.Background() + + tc, err := s.FindTelegrafConfig(ctx, tt.args.filter) + if err != nil && tt.wants.err == nil { + t.Fatalf("expected errors to be nil got '%v'", err) + } + + if err != nil && tt.wants.err != nil { + if platform.ErrorCode(err) != platform.ErrorCode(tt.wants.err) { + t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) + } + } + if diff := cmp.Diff(tc, tt.wants.telegrafConfig, telegrafCmpOptions...); diff != "" { + t.Errorf("telegraf configs are different -got/+want\ndiff %s", diff) + } + }) + } +} + +// FindTelegrafConfigs testing +func FindTelegrafConfigs( + init func(TelegrafConfigFields, *testing.T) (platform.TelegrafConfigStore, func()), + t *testing.T, +) { + type args struct { + filter platform.UserResourceMappingFilter + } + + type wants struct { + telegrafConfigs []*platform.TelegrafConfig + err error + } + tests := []struct { + name string + fields TelegrafConfigFields + args args + wants wants + }{ + { + name: "find all telegraf configs", + fields: TelegrafConfigFields{ + UserResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + }, + { + ResourceID: MustIDBase16(twoID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Member, + }, + }, + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + filter: platform.UserResourceMappingFilter{ + UserID: MustIDBase16(threeID), + ResourceType: platform.TelegrafResourceType, + }, + }, + wants: wants{ + telegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + }, + { + name: "find owners only", + fields: TelegrafConfigFields{ + UserResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + }, + { + ResourceID: MustIDBase16(twoID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Member, + }, + }, + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + filter: platform.UserResourceMappingFilter{ + UserID: MustIDBase16(threeID), + ResourceType: platform.TelegrafResourceType, + UserType: platform.Owner, + }, + }, + wants: wants{ + telegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + }, + }, + }, + { + name: "find nothing", + fields: TelegrafConfigFields{ + UserResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + }, + { + ResourceID: MustIDBase16(twoID), + ResourceType: platform.TelegrafResourceType, + UserID: MustIDBase16(threeID), + UserType: platform.Member, + }, + }, + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + filter: platform.UserResourceMappingFilter{ + UserID: MustIDBase16(fourID), + ResourceType: platform.TelegrafResourceType, + }, + }, + wants: wants{ + err: &platform.Error{ + Code: platform.ENotFound, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s, done := init(tt.fields, t) + defer done() + ctx := context.Background() + + tcs, n, err := s.FindTelegrafConfigs(ctx, tt.args.filter) + if err != nil && tt.wants.err == nil { + t.Fatalf("expected errors to be nil got '%v'", err) + } + + if err != nil && tt.wants.err != nil { + if platform.ErrorCode(err) != platform.ErrorCode(tt.wants.err) { + t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) + } + } + if n != len(tt.wants.telegrafConfigs) { + t.Fatalf("telegraf configs length is different got %d, want %d", n, len(tt.wants.telegrafConfigs)) + } + + if diff := cmp.Diff(tcs, tt.wants.telegrafConfigs, telegrafCmpOptions...); diff != "" { + t.Errorf("telegraf configs are different -got/+want\ndiff %s", diff) + } + }) + } +} + +// UpdateTelegrafConfig testing. +func UpdateTelegrafConfig( + init func(TelegrafConfigFields, *testing.T) (platform.TelegrafConfigStore, func()), + t *testing.T, +) { + now := time.Now().Round(0) + type args struct { + userID platform.ID + now time.Time + id platform.ID + telegrafConfig *platform.TelegrafConfig + } + + type wants struct { + telegrafConfig *platform.TelegrafConfig + err error + } + tests := []struct { + name string + fields TelegrafConfigFields + args args + wants wants + }{ + { + name: "can't find the id", + fields: TelegrafConfigFields{ + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + userID: MustIDBase16(threeID), + now: now, + id: MustIDBase16(fourID), + telegrafConfig: &platform.TelegrafConfig{ + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + wants: wants{ + err: &platform.Error{ + Code: platform.ENotFound, + Msg: fmt.Sprintf("telegraf config with ID %v not found", MustIDBase16(fourID)), + }, + }, + }, + { + name: "regular update", + fields: TelegrafConfigFields{ + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + userID: MustIDBase16(fourID), + now: now, + id: MustIDBase16(twoID), + telegrafConfig: &platform.TelegrafConfig{ + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment3", + Config: &inputs.CPUStats{}, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + wants: wants{ + telegrafConfig: &platform.TelegrafConfig{ + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(fourID), + LastMod: now, + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment3", + Config: &inputs.CPUStats{}, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s, done := init(tt.fields, t) + defer done() + ctx := context.Background() + + tc, err := s.UpdateTelegrafConfig(ctx, tt.args.id, + tt.args.telegrafConfig, tt.args.userID, tt.args.now) + if err != nil && tt.wants.err == nil { + t.Fatalf("expected errors to be nil got '%v'", err) + } + if err != nil && tt.wants.err != nil { + if platform.ErrorCode(err) != platform.ErrorCode(tt.wants.err) { + t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) + } + } + if diff := cmp.Diff(tc, tt.wants.telegrafConfig, telegrafCmpOptions...); tt.wants.err == nil && diff != "" { + t.Errorf("telegraf configs are different -got/+want\ndiff %s", diff) + } + }) + } +} + +// DeleteTelegrafConfig testing. +func DeleteTelegrafConfig( + init func(TelegrafConfigFields, *testing.T) (platform.TelegrafConfigStore, func()), + t *testing.T, +) { + type args struct { + id platform.ID + userID platform.ID + } + + type wants struct { + telegrafConfigs []*platform.TelegrafConfig + userResourceMappings []*platform.UserResourceMapping + err error + } + tests := []struct { + name string + fields TelegrafConfigFields + args args + wants wants + }{ + { + name: "bad id", + fields: TelegrafConfigFields{ + UserResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + ResourceType: platform.TelegrafResourceType, + }, + { + ResourceID: MustIDBase16(twoID), + UserID: MustIDBase16(threeID), + UserType: platform.Member, + ResourceType: platform.TelegrafResourceType, + }, + }, + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + id: platform.ID(0), + userID: MustIDBase16(threeID), + }, + wants: wants{ + err: &platform.Error{ + Code: platform.EEmptyValue, + Err: platform.ErrInvalidID, + }, + userResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + ResourceType: platform.TelegrafResourceType, + }, + { + ResourceID: MustIDBase16(twoID), + UserID: MustIDBase16(threeID), + UserType: platform.Member, + ResourceType: platform.TelegrafResourceType, + }, + }, + telegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + }, + { + name: "none existing config", + fields: TelegrafConfigFields{ + UserResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + ResourceType: platform.TelegrafResourceType, + }, + { + ResourceID: MustIDBase16(twoID), + UserID: MustIDBase16(threeID), + UserType: platform.Member, + ResourceType: platform.TelegrafResourceType, + }, + }, + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + id: MustIDBase16(fourID), + userID: MustIDBase16(threeID), + }, + wants: wants{ + err: &platform.Error{ + Code: platform.ENotFound, + }, + userResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + ResourceType: platform.TelegrafResourceType, + }, + { + ResourceID: MustIDBase16(twoID), + UserID: MustIDBase16(threeID), + UserType: platform.Member, + ResourceType: platform.TelegrafResourceType, + }, + }, + telegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + }, + { + name: "regular delete", + fields: TelegrafConfigFields{ + UserResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + ResourceType: platform.TelegrafResourceType, + }, + { + ResourceID: MustIDBase16(twoID), + UserID: MustIDBase16(threeID), + UserType: platform.Member, + ResourceType: platform.TelegrafResourceType, + }, + }, + TelegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + { + ID: MustIDBase16(twoID), + Name: "tc2", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Comment: "comment1", + Config: &inputs.File{ + Files: []string{"f1", "f2"}, + }, + }, + { + Comment: "comment2", + Config: &inputs.MemStats{}, + }, + }, + }, + }, + }, + args: args{ + id: MustIDBase16(twoID), + userID: MustIDBase16(threeID), + }, + wants: wants{ + userResourceMappings: []*platform.UserResourceMapping{ + { + ResourceID: MustIDBase16(oneID), + UserID: MustIDBase16(threeID), + UserType: platform.Owner, + ResourceType: platform.TelegrafResourceType, + }, + }, + telegrafConfigs: []*platform.TelegrafConfig{ + { + ID: MustIDBase16(oneID), + Name: "tc1", + LastModBy: MustIDBase16(threeID), + Plugins: []platform.TelegrafPlugin{ + { + Config: &inputs.CPUStats{}, + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s, done := init(tt.fields, t) + defer done() + ctx := context.Background() + err := s.DeleteTelegrafConfig(ctx, tt.args.id) + if err != nil && tt.wants.err == nil { + t.Fatalf("expected errors to be nil got '%v'", err) + } + + if err != nil && tt.wants.err != nil { + if platform.ErrorCode(err) != platform.ErrorCode(tt.wants.err) { + t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) + } + } + tcs, n, err := s.FindTelegrafConfigs(ctx, platform.UserResourceMappingFilter{ + UserID: tt.args.userID, + ResourceType: platform.TelegrafResourceType, + }) + if err != nil && tt.wants.err == nil { + t.Fatalf("expected errors to be nil got '%v'", err) + } + + if err != nil && tt.wants.err != nil { + if platform.ErrorCode(err) != platform.ErrorCode(tt.wants.err) { + t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err) + } + } + if n != len(tt.wants.telegrafConfigs) { + t.Fatalf("telegraf configs length is different got %d, want %d", n, len(tt.wants.telegrafConfigs)) + } + if diff := cmp.Diff(tcs, tt.wants.telegrafConfigs, telegrafCmpOptions...); diff != "" { + t.Errorf("telegraf configs are different -got/+want\ndiff %s", diff) + } + + urms, _, err := s.FindUserResourceMappings(ctx, platform.UserResourceMappingFilter{ + UserID: tt.args.userID, + ResourceType: platform.TelegrafResourceType, + }) + if err != nil { + t.Fatalf("failed to retrieve user resource mappings: %v", err) + } + if diff := cmp.Diff(urms, tt.wants.userResourceMappings, userResourceMappingCmpOptions...); diff != "" { + t.Errorf("user resource mappings are different -got/+want\ndiff %s", diff) + } + }) + } +} diff --git a/user_resource_mapping.go b/user_resource_mapping.go index af799cf71f3..3cbf14974a4 100644 --- a/user_resource_mapping.go +++ b/user_resource_mapping.go @@ -9,6 +9,7 @@ import ( type UserType string type ResourceType string +// available user resource types. const ( Owner UserType = "owner" Member UserType = "member" @@ -17,6 +18,7 @@ const ( TaskResourceType ResourceType = "task" OrgResourceType ResourceType = "org" ViewResourceType ResourceType = "view" + TelegrafResourceType ResourceType = "telegraf" ) // UserResourceMappingService maps the relationships between users and resources @@ -51,7 +53,7 @@ func (m UserResourceMapping) Validate() error { return errors.New("a valid user type is required") } switch m.ResourceType { - case DashboardResourceType, BucketResourceType, TaskResourceType, OrgResourceType, ViewResourceType: + case DashboardResourceType, BucketResourceType, TaskResourceType, OrgResourceType, ViewResourceType, TelegrafResourceType: default: return fmt.Errorf("a valid resource type is required") }