From f75f27c0bd8f4b34ea52dc45490fb928809bdcf3 Mon Sep 17 00:00:00 2001 From: Kelvin Wang Date: Fri, 19 Apr 2019 15:46:58 -0400 Subject: [PATCH] feat(http): add audit log --- bolt/bbolt.go | 10 ++------- bolt/bucket.go | 2 +- bolt/dashboard.go | 6 ++--- bolt/dashboard_test.go | 8 +++---- bolt/organization.go | 2 +- bolt/user.go | 2 +- cmd/influxd/launcher/launcher.go | 2 +- crud_log.go | 25 +++++++++++++++++++++ document.go | 1 + http/dashboard_test.go | 2 +- http/document_service.go | 6 +++++ http/document_test.go | 23 ++++++++++++++++--- http/swagger.yml | 8 +++++++ inmem/dashboard.go | 4 ++-- inmem/dashboard_test.go | 2 +- inmem/service.go | 11 ++------- kv/bucket.go | 2 +- kv/dashboard.go | 6 ++--- kv/dashboard_test.go | 7 +++--- kv/document.go | 8 +++---- kv/kvlog.go | 9 +++++--- kv/org.go | 2 +- kv/service.go | 14 +++--------- kv/user.go | 2 +- mock/document_service.go | 1 + mock/generators.go | 11 +++++++++ testing/dashboards.go | 38 ++++++++++++++++---------------- testing/document.go | 18 +++++++++++++-- 28 files changed, 148 insertions(+), 84 deletions(-) create mode 100644 crud_log.go diff --git a/bolt/bbolt.go b/bolt/bbolt.go index a172aae87c..c3e8815b01 100644 --- a/bolt/bbolt.go +++ b/bolt/bbolt.go @@ -29,7 +29,7 @@ type Client struct { IDGenerator platform.IDGenerator TokenGenerator platform.TokenGenerator - time func() time.Time + platform.TimeGenerator } // NewClient returns an instance of a Client. @@ -38,7 +38,7 @@ func NewClient() *Client { Logger: zap.NewNop(), IDGenerator: snowflake.NewIDGenerator(), TokenGenerator: rand.NewTokenGenerator(64), - time: time.Now, + TimeGenerator: platform.RealTimeGenerator{}, } } @@ -53,12 +53,6 @@ func (c *Client) WithLogger(l *zap.Logger) { c.Logger = l } -// WithTime sets the function for computing the current time. Used for updating meta data -// about objects stored. Should only be used in tests for mocking. -func (c *Client) WithTime(fn func() time.Time) { - c.time = fn -} - // Open / create boltDB file. func (c *Client) Open(ctx context.Context) error { // Ensure the required directory structure exists. diff --git a/bolt/bucket.go b/bolt/bucket.go index 767cf17d30..ee30a0fc3b 100644 --- a/bolt/bucket.go +++ b/bolt/bucket.go @@ -676,5 +676,5 @@ func (c *Client) appendBucketEventToLog(ctx context.Context, tx *bolt.Tx, id pla return err } - return c.addLogEntry(ctx, tx, k, v, c.time()) + return c.addLogEntry(ctx, tx, k, v, c.Now()) } diff --git a/bolt/dashboard.go b/bolt/dashboard.go index 3af3d2bc24..44331899c0 100644 --- a/bolt/dashboard.go +++ b/bolt/dashboard.go @@ -286,7 +286,7 @@ func (c *Client) CreateDashboard(ctx context.Context, d *platform.Dashboard) err } // TODO(desa): don't populate this here. use the first/last methods of the oplog to get meta fields. - d.Meta.CreatedAt = c.time() + d.Meta.CreatedAt = c.Now() return c.putDashboardWithMeta(ctx, tx, d) }) @@ -698,7 +698,7 @@ func (c *Client) putDashboard(ctx context.Context, tx *bolt.Tx, d *platform.Dash func (c *Client) putDashboardWithMeta(ctx context.Context, tx *bolt.Tx, d *platform.Dashboard) error { // TODO(desa): don't populate this here. use the first/last methods of the oplog to get meta fields. - d.Meta.UpdatedAt = c.time() + d.Meta.UpdatedAt = c.Now() return c.putDashboard(ctx, tx, d) } @@ -907,5 +907,5 @@ func (c *Client) appendDashboardEventToLog(ctx context.Context, tx *bolt.Tx, id return err } - return c.addLogEntry(ctx, tx, k, v, c.time()) + return c.addLogEntry(ctx, tx, k, v, c.Now()) } diff --git a/bolt/dashboard_test.go b/bolt/dashboard_test.go index fcf0eb6e2c..a9843f1b99 100644 --- a/bolt/dashboard_test.go +++ b/bolt/dashboard_test.go @@ -3,7 +3,6 @@ package bolt_test import ( "context" "testing" - "time" platform "github.com/influxdata/influxdb" "github.com/influxdata/influxdb/bolt" @@ -16,12 +15,13 @@ func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (plat t.Fatalf("failed to create new bolt client: %v", err) } - if f.NowFn == nil { - f.NowFn = time.Now + if f.TimeGenerator == nil { + f.TimeGenerator = platform.RealTimeGenerator{} } c.IDGenerator = f.IDGenerator - c.WithTime(f.NowFn) + c.TimeGenerator = f.TimeGenerator + ctx := context.TODO() for _, b := range f.Dashboards { if err := c.PutDashboard(ctx, b); err != nil { diff --git a/bolt/organization.go b/bolt/organization.go index 8cd49e7cd2..28035dd2f3 100644 --- a/bolt/organization.go +++ b/bolt/organization.go @@ -501,7 +501,7 @@ func (c *Client) appendOrganizationEventToLog(ctx context.Context, tx *bolt.Tx, return err } - return c.addLogEntry(ctx, tx, k, v, c.time()) + return c.addLogEntry(ctx, tx, k, v, c.Now()) } func (c *Client) FindResourceOrganizationID(ctx context.Context, rt influxdb.ResourceType, id influxdb.ID) (influxdb.ID, error) { diff --git a/bolt/user.go b/bolt/user.go index 502193e317..8ea8bec152 100644 --- a/bolt/user.go +++ b/bolt/user.go @@ -476,5 +476,5 @@ func (c *Client) appendUserEventToLog(ctx context.Context, tx *bolt.Tx, id platf return err } - return c.addLogEntry(ctx, tx, k, v, c.time()) + return c.addLogEntry(ctx, tx, k, v, c.Now()) } diff --git a/cmd/influxd/launcher/launcher.go b/cmd/influxd/launcher/launcher.go index 013b06b740..87dbccd352 100644 --- a/cmd/influxd/launcher/launcher.go +++ b/cmd/influxd/launcher/launcher.go @@ -45,7 +45,7 @@ import ( _ "github.com/influxdata/influxdb/tsdb/tsm1" // needed for tsm1 "github.com/influxdata/influxdb/vault" pzap "github.com/influxdata/influxdb/zap" - "github.com/opentracing/opentracing-go" + opentracing "github.com/opentracing/opentracing-go" "github.com/prometheus/client_golang/prometheus" "github.com/spf13/cobra" jaegerconfig "github.com/uber/jaeger-client-go/config" diff --git a/crud_log.go b/crud_log.go new file mode 100644 index 0000000000..9fd605fb4b --- /dev/null +++ b/crud_log.go @@ -0,0 +1,25 @@ +package influxdb + +import ( + "time" +) + +// CRUDLog is the struct to store crud related ops. +type CRUDLog struct { + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +// TimeGenerator represents a generator for now. +type TimeGenerator interface { + // Now creates the generated time. + Now() time.Time +} + +// RealTimeGenerator will generate the real time. +type RealTimeGenerator struct{} + +// Now returns the current time. +func (g RealTimeGenerator) Now() time.Time { + return time.Now() +} diff --git a/document.go b/document.go index aa0a0ca710..732a458765 100644 --- a/document.go +++ b/document.go @@ -28,6 +28,7 @@ type DocumentMeta struct { Type string `json:"type,omitempty"` Description string `json:"description,omitempty"` Version string `json:"version,omitempty"` + CRUDLog } // DocumentStore is used to perform CRUD operations on documents. It follows an options diff --git a/http/dashboard_test.go b/http/dashboard_test.go index 67fcbf89eb..589a6d886e 100644 --- a/http/dashboard_test.go +++ b/http/dashboard_test.go @@ -1363,7 +1363,7 @@ func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (plat t.Helper() svc := inmem.NewService() svc.IDGenerator = f.IDGenerator - svc.WithTime(f.NowFn) + svc.TimeGenerator = f.TimeGenerator ctx := context.Background() for _, d := range f.Dashboards { if err := svc.PutDashboard(ctx, d); err != nil { diff --git a/http/document_service.go b/http/document_service.go index 510744d314..f0d0a7ad86 100644 --- a/http/document_service.go +++ b/http/document_service.go @@ -140,6 +140,8 @@ func (h *DocumentHandler) handlePostDocument(w http.ResponseWriter, r *http.Requ return } + h.Logger.Info("document created") + if err := encodeResponse(ctx, w, http.StatusCreated, newDocumentResponse(req.Namespace, req.Document)); err != nil { logEncodingError(h.Logger, r, err) return @@ -459,6 +461,8 @@ func (h *DocumentHandler) handleDeleteDocument(w http.ResponseWriter, r *http.Re return } + h.Logger.Info("document deleted") + w.WriteHeader(http.StatusNoContent) } @@ -526,6 +530,8 @@ func (h *DocumentHandler) handlePutDocument(w http.ResponseWriter, r *http.Reque return } + h.Logger.Info("document updated") + ds, err := s.FindDocuments(ctx, influxdb.WhereID(req.Document.ID), influxdb.IncludeContent) if err != nil { EncodeError(ctx, err, w) diff --git a/http/document_test.go b/http/document_test.go index 5e841a2c44..6083811545 100644 --- a/http/document_test.go +++ b/http/document_test.go @@ -8,6 +8,7 @@ import ( "net/http" "net/http/httptest" "testing" + "time" "github.com/influxdata/influxdb" pcontext "github.com/influxdata/influxdb/context" @@ -43,6 +44,9 @@ var ( label3MappingJSON, _ = json.Marshal(influxdb.LabelMapping{ LabelID: label3ID, }) + mockGen = mock.TimeGenerator{ + FakeValue: time.Date(2006, 5, 24, 1, 2, 3, 4, time.UTC), + } doc1 = influxdb.Document{ ID: doc1ID, Meta: influxdb.DocumentMeta{ @@ -127,6 +131,8 @@ var ( "meta": { "name": "doc1", "type": "typ1", + "createdAt": "0001-01-01T00:00:00Z", + "updatedAt": "0001-01-01T00:00:00Z", "description": "desc1" } }, @@ -137,7 +143,9 @@ var ( }, "content": "content2", "meta": { - "name": "doc2" + "name": "doc2", + "createdAt": "0001-01-01T00:00:00Z", + "updatedAt": "0001-01-01T00:00:00Z" } } ] @@ -427,6 +435,7 @@ func TestService_handlePostDocumentLabel(t *testing.T) { DocumentService: &mock.DocumentService{ FindDocumentStoreFn: func(context.Context, string) (influxdb.DocumentStore, error) { return &mock.DocumentStore{ + TimeGenerator: mockGen, FindDocumentsFn: func(ctx context.Context, opts ...influxdb.DocumentFindOptions) ([]*influxdb.Document, error) { return []*influxdb.Document{&doc4}, nil }, @@ -801,7 +810,9 @@ func TestService_handlePostDocuments(t *testing.T) { DocumentService: &mock.DocumentService{ FindDocumentStoreFn: func(context.Context, string) (influxdb.DocumentStore, error) { return &mock.DocumentStore{ + TimeGenerator: mockGen, CreateDocumentFn: func(ctx context.Context, d *influxdb.Document, opts ...influxdb.DocumentOptions) error { + d.Meta.CreatedAt = mockGen.Now() return nil }, }, nil @@ -826,7 +837,9 @@ func TestService_handlePostDocuments(t *testing.T) { "self": "/api/v2/documents/template/020f755c3c082014" }, "meta": { - "name": "doc5" + "name": "doc5", + "createdAt": "2006-05-24T01:02:03.000000004Z", + "updatedAt": "0001-01-01T00:00:00Z" }}`, }, }, @@ -836,8 +849,10 @@ func TestService_handlePostDocuments(t *testing.T) { DocumentService: &mock.DocumentService{ FindDocumentStoreFn: func(context.Context, string) (influxdb.DocumentStore, error) { return &mock.DocumentStore{ + TimeGenerator: mockGen, CreateDocumentFn: func(ctx context.Context, d *influxdb.Document, opts ...influxdb.DocumentOptions) error { d.Labels = []*influxdb.Label{&label1, &label2} + d.Meta.CreatedAt = mockGen.Now() return nil }, }, nil @@ -877,7 +892,9 @@ func TestService_handlePostDocuments(t *testing.T) { "name": "l2" }], "meta": { - "name": "doc6" + "name": "doc6", + "createdAt": "2006-05-24T01:02:03.000000004Z", + "updatedAt": "0001-01-01T00:00:00Z" }}`, }, }, diff --git a/http/swagger.yml b/http/swagger.yml index a7bfef0066..dc0d967d38 100644 --- a/http/swagger.yml +++ b/http/swagger.yml @@ -6983,6 +6983,14 @@ components: type: string version: type: string + createdAt: + type: string + format: date-time + readOnly: true + updatedAt: + type: string + format: date-time + readOnly: true required: - name - version diff --git a/inmem/dashboard.go b/inmem/dashboard.go index 3e5a0991df..dd0fdfb2c9 100644 --- a/inmem/dashboard.go +++ b/inmem/dashboard.go @@ -129,7 +129,7 @@ func (s *Service) FindDashboards(ctx context.Context, filter platform.DashboardF // CreateDashboard implements platform.DashboardService interface. func (s *Service) CreateDashboard(ctx context.Context, d *platform.Dashboard) error { d.ID = s.IDGenerator.ID() - d.Meta.CreatedAt = s.time() + d.Meta.CreatedAt = s.Now() err := s.PutDashboardWithMeta(ctx, d) if err != nil { return &platform.Error{ @@ -160,7 +160,7 @@ func (s *Service) PutCellView(ctx context.Context, cell *platform.Cell) error { // PutDashboardWithMeta sets a dashboard while updating the meta field of a dashboard. func (s *Service) PutDashboardWithMeta(ctx context.Context, d *platform.Dashboard) error { - d.Meta.UpdatedAt = s.time() + d.Meta.UpdatedAt = s.Now() return s.PutDashboard(ctx, d) } diff --git a/inmem/dashboard_test.go b/inmem/dashboard_test.go index a00c210796..f72f9a61a2 100644 --- a/inmem/dashboard_test.go +++ b/inmem/dashboard_test.go @@ -11,8 +11,8 @@ import ( func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (platform.DashboardService, string, func()) { s := NewService() s.IDGenerator = f.IDGenerator + s.TimeGenerator = f.TimeGenerator ctx := context.Background() - s.WithTime(f.NowFn) for _, b := range f.Dashboards { if err := s.PutDashboard(ctx, b); err != nil { t.Fatalf("failed to populate Dashboards") diff --git a/inmem/service.go b/inmem/service.go index 3d0099c86a..5b2b6e8860 100644 --- a/inmem/service.go +++ b/inmem/service.go @@ -3,7 +3,6 @@ package inmem import ( "context" "sync" - "time" platform "github.com/influxdata/influxdb" "github.com/influxdata/influxdb/rand" @@ -35,7 +34,7 @@ type Service struct { TokenGenerator platform.TokenGenerator IDGenerator platform.IDGenerator - time func() time.Time + platform.TimeGenerator } // NewService creates an instance of a Service. @@ -43,18 +42,12 @@ func NewService() *Service { s := &Service{ TokenGenerator: rand.NewTokenGenerator(64), IDGenerator: snowflake.NewIDGenerator(), - time: time.Now, + TimeGenerator: platform.RealTimeGenerator{}, } s.initializeSources(context.TODO()) return s } -// WithTime sets the function for computing the current time. Used for updating meta data -// about objects stored. Should only be used in tests for mocking. -func (s *Service) WithTime(fn func() time.Time) { - s.time = fn -} - // Flush removes all data from the in-memory store func (s *Service) Flush() { s.flush(&s.authorizationKV) diff --git a/kv/bucket.go b/kv/bucket.go index 92bca682b7..5111a03e1d 100644 --- a/kv/bucket.go +++ b/kv/bucket.go @@ -759,7 +759,7 @@ func (s *Service) appendBucketEventToLog(ctx context.Context, tx Tx, id influxdb return err } - return s.addLogEntry(ctx, tx, k, v, s.time()) + return s.addLogEntry(ctx, tx, k, v, s.Now()) } // UnexpectedBucketError is used when the error comes from an internal system. diff --git a/kv/dashboard.go b/kv/dashboard.go index 8c04636a69..9da7604fe7 100644 --- a/kv/dashboard.go +++ b/kv/dashboard.go @@ -301,7 +301,7 @@ func (s *Service) CreateDashboard(ctx context.Context, d *influxdb.Dashboard) er } // TODO(desa): don't populate this here. use the first/last methods of the oplog to get meta fields. - d.Meta.CreatedAt = s.time() + d.Meta.CreatedAt = s.Now() if err := s.putDashboardWithMeta(ctx, tx, d); err != nil { return err @@ -752,7 +752,7 @@ func (s *Service) putDashboard(ctx context.Context, tx Tx, d *influxdb.Dashboard func (s *Service) putDashboardWithMeta(ctx context.Context, tx Tx, d *influxdb.Dashboard) error { // TODO(desa): don't populate this here. use the first/last methods of the oplog to get meta fields. - d.Meta.UpdatedAt = s.time() + d.Meta.UpdatedAt = s.Now() return s.putDashboard(ctx, tx, d) } @@ -972,5 +972,5 @@ func (s *Service) appendDashboardEventToLog(ctx context.Context, tx Tx, id influ return err } - return s.addLogEntry(ctx, tx, k, v, s.time()) + return s.addLogEntry(ctx, tx, k, v, s.Now()) } diff --git a/kv/dashboard_test.go b/kv/dashboard_test.go index b2ea4e5376..560771d25c 100644 --- a/kv/dashboard_test.go +++ b/kv/dashboard_test.go @@ -3,7 +3,6 @@ package kv_test import ( "context" "testing" - "time" "github.com/influxdata/influxdb" "github.com/influxdata/influxdb/kv" @@ -46,12 +45,12 @@ func initInmemDashboardService(f influxdbtesting.DashboardFields, t *testing.T) func initDashboardService(s kv.Store, f influxdbtesting.DashboardFields, t *testing.T) (influxdb.DashboardService, string, func()) { - if f.NowFn == nil { - f.NowFn = time.Now + if f.TimeGenerator == nil { + f.TimeGenerator = influxdb.RealTimeGenerator{} } svc := kv.NewService(s) svc.IDGenerator = f.IDGenerator - svc.WithTime(f.NowFn) + svc.TimeGenerator = f.TimeGenerator ctx := context.Background() if err := svc.Initialize(ctx); err != nil { diff --git a/kv/document.go b/kv/document.go index 2f2f2e6a48..0a7fab8700 100644 --- a/kv/document.go +++ b/kv/document.go @@ -348,7 +348,7 @@ func (i *DocumentIndex) GetAccessorsDocuments(ownerType string, ownerID influxdb func (s *Service) createDocument(ctx context.Context, tx Tx, ns string, d *influxdb.Document) error { d.ID = s.IDGenerator.ID() - + d.Meta.CreatedAt = s.Now() if err := s.putDocument(ctx, tx, ns, d); err != nil { return err } @@ -357,7 +357,7 @@ func (s *Service) createDocument(ctx context.Context, tx Tx, ns string, d *influ } func (s *Service) putDocument(ctx context.Context, tx Tx, ns string, d *influxdb.Document) error { - if err := s.putDocumentMeta(ctx, tx, ns, d.ID, &d.Meta); err != nil { + if err := s.putDocumentMeta(ctx, tx, ns, d.ID, d.Meta); err != nil { return err } @@ -397,7 +397,7 @@ func (s *Service) putDocumentContent(ctx context.Context, tx Tx, ns string, id i return s.putAtID(ctx, tx, path.Join(ns, documentContentBucket), id, data) } -func (s *Service) putDocumentMeta(ctx context.Context, tx Tx, ns string, id influxdb.ID, m *influxdb.DocumentMeta) error { +func (s *Service) putDocumentMeta(ctx context.Context, tx Tx, ns string, id influxdb.ID, m influxdb.DocumentMeta) error { return s.putAtID(ctx, tx, path.Join(ns, documentMetaBucket), id, m) } @@ -735,7 +735,7 @@ func (s *DocumentStore) UpdateDocument(ctx context.Context, d *influxdb.Document func (s *Service) updateDocument(ctx context.Context, tx Tx, ns string, d *influxdb.Document) error { // TODO(desa): deindex meta - + d.Meta.UpdatedAt = s.Now() if err := s.putDocument(ctx, tx, ns, d); err != nil { return err } diff --git a/kv/kvlog.go b/kv/kvlog.go index 8e21d844ff..658038f51a 100644 --- a/kv/kvlog.go +++ b/kv/kvlog.go @@ -108,7 +108,10 @@ func (s *Service) initializeKVLog(ctx context.Context, tx Tx) error { return nil } -var errKeyValueLogBoundsNotFound = fmt.Errorf("oplog not found") +var errKeyValueLogBoundsNotFound = &platform.Error{ + Code: platform.ENotFound, + Msg: "oplog not found", +} func (s *Service) getKeyValueLogBounds(ctx context.Context, tx Tx, key []byte) (*keyValueLogBounds, error) { k := encodeKeyValueIndexKey(key) @@ -381,7 +384,7 @@ func (s *Service) LastLogEntry(ctx context.Context, k []byte) ([]byte, time.Time func (s *Service) firstLogEntry(ctx context.Context, tx Tx, k []byte) ([]byte, time.Time, error) { bounds, err := s.getKeyValueLogBounds(ctx, tx, k) if err != nil { - return nil, bounds.StartTime(), err + return nil, time.Time{}, err } return s.getLogEntry(ctx, tx, k, bounds.StartTime()) @@ -390,7 +393,7 @@ func (s *Service) firstLogEntry(ctx context.Context, tx Tx, k []byte) ([]byte, t func (s *Service) lastLogEntry(ctx context.Context, tx Tx, k []byte) ([]byte, time.Time, error) { bounds, err := s.getKeyValueLogBounds(ctx, tx, k) if err != nil { - return nil, bounds.StopTime(), err + return nil, time.Time{}, err } return s.getLogEntry(ctx, tx, k, bounds.StopTime()) diff --git a/kv/org.go b/kv/org.go index 1e6b4af96b..cbb1aef147 100644 --- a/kv/org.go +++ b/kv/org.go @@ -570,7 +570,7 @@ func (s *Service) appendOrganizationEventToLog(ctx context.Context, tx Tx, id in return err } - return s.addLogEntry(ctx, tx, k, v, s.time()) + return s.addLogEntry(ctx, tx, k, v, s.Now()) } // FindResourceOrganizationID is used to find the organization that a resource belongs to five the id of a resource and a resource type. diff --git a/kv/service.go b/kv/service.go index 6f3200d81e..600bc9fcf0 100644 --- a/kv/service.go +++ b/kv/service.go @@ -2,7 +2,6 @@ package kv import ( "context" - "time" "go.uber.org/zap" @@ -25,9 +24,8 @@ type Service struct { IDGenerator influxdb.IDGenerator TokenGenerator influxdb.TokenGenerator - Hash Crypt - - time func() time.Time + influxdb.TimeGenerator + Hash Crypt } // NewService returns an instance of a Service. @@ -38,7 +36,7 @@ func NewService(kv Store) *Service { TokenGenerator: rand.NewTokenGenerator(64), Hash: &Bcrypt{}, kv: kv, - time: time.Now, + TimeGenerator: influxdb.RealTimeGenerator{}, } } @@ -117,12 +115,6 @@ func (s *Service) Initialize(ctx context.Context) error { }) } -// WithTime sets the function for computing the current time. Used for updating meta data -// about objects stored. Should only be used in tests for mocking. -func (s *Service) WithTime(fn func() time.Time) { - s.time = fn -} - // WithStore sets kv store for the service. // Should only be used in tests for mocking. func (s *Service) WithStore(store Store) { diff --git a/kv/user.go b/kv/user.go index 12eb21ed79..9ed6f812b9 100644 --- a/kv/user.go +++ b/kv/user.go @@ -515,7 +515,7 @@ func (s *Service) appendUserEventToLog(ctx context.Context, tx Tx, id influxdb.I return err } - return s.addLogEntry(ctx, tx, k, v, s.time()) + return s.addLogEntry(ctx, tx, k, v, s.Now()) } var ( diff --git a/mock/document_service.go b/mock/document_service.go index f61d3016b3..69c6db12ac 100644 --- a/mock/document_service.go +++ b/mock/document_service.go @@ -38,6 +38,7 @@ func NewDocumentService() *DocumentService { // DocumentStore is the mocked document store. type DocumentStore struct { + TimeGenerator TimeGenerator CreateDocumentFn func(ctx context.Context, d *influxdb.Document, opts ...influxdb.DocumentOptions) error UpdateDocumentFn func(ctx context.Context, d *influxdb.Document, opts ...influxdb.DocumentOptions) error FindDocumentsFn func(ctx context.Context, opts ...influxdb.DocumentFindOptions) ([]*influxdb.Document, error) diff --git a/mock/generators.go b/mock/generators.go index 782ad19dab..0a043ed856 100644 --- a/mock/generators.go +++ b/mock/generators.go @@ -2,6 +2,7 @@ package mock import ( "testing" + "time" platform "github.com/influxdata/influxdb" ) @@ -47,3 +48,13 @@ type TokenGenerator struct { func (g TokenGenerator) Token() (string, error) { return g.TokenFn() } + +// TimeGenerator stores a fake value of time. +type TimeGenerator struct { + FakeValue time.Time +} + +// Now will return the FakeValue stored in the struct. +func (g TimeGenerator) Now() time.Time { + return g.FakeValue +} diff --git a/testing/dashboards.go b/testing/dashboards.go index 0557906dfc..5fe3b4a2db 100644 --- a/testing/dashboards.go +++ b/testing/dashboards.go @@ -31,10 +31,10 @@ var dashboardCmpOptions = cmp.Options{ // DashboardFields will include the IDGenerator, and dashboards type DashboardFields struct { - IDGenerator platform.IDGenerator - NowFn func() time.Time - Dashboards []*platform.Dashboard - Views []*platform.View + IDGenerator platform.IDGenerator + TimeGenerator platform.TimeGenerator + Dashboards []*platform.Dashboard + Views []*platform.View } // DashboardService tests all the service functions. @@ -125,7 +125,7 @@ func CreateDashboard( return MustIDBase16(dashTwoID) }, }, - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, Dashboards: []*platform.Dashboard{ { ID: MustIDBase16(dashOneID), @@ -168,7 +168,7 @@ func CreateDashboard( return MustIDBase16(dashTwoID) }, }, - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, Dashboards: []*platform.Dashboard{ { ID: MustIDBase16(dashOneID), @@ -253,7 +253,7 @@ func AddDashboardCell( return MustIDBase16(dashTwoID) }, }, - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, Dashboards: []*platform.Dashboard{ { ID: MustIDBase16(dashOneID), @@ -296,7 +296,7 @@ func AddDashboardCell( { name: "add cell with no id", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, IDGenerator: &mock.IDGenerator{ IDFn: func() platform.ID { return MustIDBase16(dashTwoID) @@ -342,7 +342,7 @@ func AddDashboardCell( { name: "add cell with id not exist", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, IDGenerator: &mock.IDGenerator{ IDFn: func() platform.ID { return MustIDBase16(dashTwoID) @@ -1059,7 +1059,7 @@ func UpdateDashboard( { name: "update name", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, Dashboards: []*platform.Dashboard{ { ID: MustIDBase16(dashOneID), @@ -1091,7 +1091,7 @@ func UpdateDashboard( { name: "update description", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, Dashboards: []*platform.Dashboard{ { ID: MustIDBase16(dashOneID), @@ -1124,7 +1124,7 @@ func UpdateDashboard( { name: "update description and name", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, Dashboards: []*platform.Dashboard{ { ID: MustIDBase16(dashOneID), @@ -1158,7 +1158,7 @@ func UpdateDashboard( { name: "update with id not exist", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, Dashboards: []*platform.Dashboard{ { ID: MustIDBase16(dashOneID), @@ -1234,7 +1234,7 @@ func RemoveDashboardCell( { name: "basic remove cell", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, IDGenerator: &mock.IDGenerator{ IDFn: func() platform.ID { return MustIDBase16(dashTwoID) @@ -1332,7 +1332,7 @@ func UpdateDashboardCell( { name: "basic update cell", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, IDGenerator: &mock.IDGenerator{ IDFn: func() platform.ID { return MustIDBase16(dashTwoID) @@ -1388,7 +1388,7 @@ func UpdateDashboardCell( { name: "invalid cell update without attribute", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, IDGenerator: &mock.IDGenerator{ IDFn: func() platform.ID { return MustIDBase16(dashTwoID) @@ -1441,7 +1441,7 @@ func UpdateDashboardCell( { name: "invalid cell update cell id not exist", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, IDGenerator: &mock.IDGenerator{ IDFn: func() platform.ID { return MustIDBase16(dashTwoID) @@ -1539,7 +1539,7 @@ func ReplaceDashboardCells( { name: "basic replace cells", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, IDGenerator: &mock.IDGenerator{ IDFn: func() platform.ID { return MustIDBase16(dashTwoID) @@ -1620,7 +1620,7 @@ func ReplaceDashboardCells( { name: "try to add a cell that didn't previously exist", fields: DashboardFields{ - NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) }, + TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC)}, IDGenerator: &mock.IDGenerator{ IDFn: func() platform.ID { return MustIDBase16(dashTwoID) diff --git a/testing/document.go b/testing/document.go index 74e0501ffa..fc707c412c 100644 --- a/testing/document.go +++ b/testing/document.go @@ -5,11 +5,13 @@ import ( "context" "sort" "testing" + "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/influxdata/influxdb" "github.com/influxdata/influxdb/kv" + "github.com/influxdata/influxdb/mock" ) // NewDocumentIntegrationTest will test the documents related funcs. @@ -17,10 +19,13 @@ func NewDocumentIntegrationTest(store kv.Store) func(t *testing.T) { return func(t *testing.T) { ctx := context.Background() svc := kv.NewService(store) + mockTimeGen := new(mock.TimeGenerator) if err := svc.Initialize(ctx); err != nil { t.Fatalf("failed to initialize service: %v", err) } + svc.TimeGenerator = mockTimeGen + s, err := svc.CreateDocumentStore(ctx, "testing") if err != nil { t.Fatalf("failed to create document store: %v", err) @@ -68,6 +73,7 @@ func NewDocumentIntegrationTest(store kv.Store) func(t *testing.T) { "v1": "v1", }, } + mockTimeGen.FakeValue = time.Date(2009, 1, 2, 3, 0, 0, 0, time.UTC) if err := s.CreateDocument(ctx, d1, influxdb.AuthorizedWithOrg(s1, o1.Name), influxdb.WithLabel(l1.ID)); err != nil { t.Errorf("failed to create document: %v", err) } @@ -82,10 +88,12 @@ func NewDocumentIntegrationTest(store kv.Store) func(t *testing.T) { "i2": "i2", }, } + mockTimeGen.FakeValue = time.Date(2009, 1, 2, 3, 0, 1, 0, time.UTC) if err := s.CreateDocument(ctx, d2, influxdb.AuthorizedWithOrg(s2, o1.Name), influxdb.WithLabel(l2.ID)); err == nil { t.Fatalf("should not have be authorized to create document") } + mockTimeGen.FakeValue = time.Date(2009, 1, 2, 3, 0, 1, 0, time.UTC) if err := s.CreateDocument(ctx, d2, influxdb.AuthorizedWithOrg(s2, o2.Name)); err != nil { t.Errorf("should have been authorized to create document: %v", err) } @@ -100,6 +108,7 @@ func NewDocumentIntegrationTest(store kv.Store) func(t *testing.T) { "k2": "v2", }, } + mockTimeGen.FakeValue = time.Date(2009, 1, 2, 3, 0, 2, 0, time.UTC) if err := s.CreateDocument(ctx, d3, influxdb.AuthorizedWithOrg(s1, o2.Name)); err == nil { t.Errorf("should not have be authorized to create document") } @@ -107,6 +116,7 @@ func NewDocumentIntegrationTest(store kv.Store) func(t *testing.T) { t.Run("can create unowned document", func(t *testing.T) { // TODO(desa): should this be allowed? + mockTimeGen.FakeValue = time.Date(2009, 1, 2, 3, 0, 2, 0, time.UTC) if err := s.CreateDocument(ctx, d3); err != nil { t.Fatalf("should have been able to create document: %v", err) } @@ -128,14 +138,18 @@ func NewDocumentIntegrationTest(store kv.Store) func(t *testing.T) { }) }) + d1.Meta.CreatedAt = time.Date(2009, 1, 2, 3, 0, 0, 0, time.UTC) dl1 := new(influxdb.Document) *dl1 = *d1 dl1.Labels = append([]*influxdb.Label{}, l1) + d2.Meta.CreatedAt = time.Date(2009, 1, 2, 3, 0, 1, 0, time.UTC) dl2 := new(influxdb.Document) *dl2 = *d2 dl2.Labels = append([]*influxdb.Label{}, d2.Labels...) + d3.Meta.CreatedAt = time.Date(2009, 1, 2, 3, 0, 2, 0, time.UTC) + t.Run("bare call to find returns all documents", func(t *testing.T) { ds, err := ss.FindDocuments(ctx) if err != nil { @@ -153,8 +167,8 @@ func NewDocumentIntegrationTest(store kv.Store) func(t *testing.T) { t.Fatalf("failed to retrieve documents: %v", err) } - if exp, got := []*influxdb.Document{dl1}, ds; !docsEqual(exp, got) { - t.Errorf("documents are different -got/+want\ndiff %s", docsDiff(exp, got)) + if exp, got := []*influxdb.Document{dl1}, ds; !docsEqual(got, exp) { + t.Errorf("documents are different -got/+want\ndiff %s", docsDiff(got, exp)) } })