diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d9c845202..27bf5cdfa41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ 1. [16345](https://github.com/influxdata/influxdb/pull/16345): Add hide headers flag to influx cli task find cmd 1. [16336](https://github.com/influxdata/influxdb/pull/16336): Manual Overrides for Readiness Endpoint 1. [16347](https://github.com/influxdata/influxdb/pull/16347): Drop legacy inmem service implementation in favor of kv service with inmem dependency +1. [16348](https://github.com/influxdata/influxdb/pull/16348): Drop legacy bolt service implementation in favor of kv service with bolt dependency ### Bug Fixes diff --git a/authorizer/source.go b/authorizer/source.go index cd89f409bb4..37c74fa3973 100644 --- a/authorizer/source.go +++ b/authorizer/source.go @@ -4,7 +4,6 @@ import ( "context" "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/bolt" ) var _ influxdb.SourceService = (*SourceService)(nil) @@ -97,13 +96,6 @@ func (s *SourceService) FindSources(ctx context.Context, opts influxdb.FindOptio return nil, 0, err } - // TODO(desa): this is a totaly hack and needs to be fixed. - // Specifically, we need to remove the concept of a default source. - if src.OrganizationID.String() == bolt.DefaultSourceOrganizationID { - sources = append(sources, src) - continue - } - if influxdb.ErrorCode(err) == influxdb.EUnauthorized { continue } diff --git a/bolt/authorization.go b/bolt/authorization.go deleted file mode 100644 index 0ec6e563f61..00000000000 --- a/bolt/authorization.go +++ /dev/null @@ -1,455 +0,0 @@ -package bolt - -import ( - "context" - "encoding/json" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" -) - -var ( - authorizationBucket = []byte("authorizationsv1") - authorizationIndex = []byte("authorizationindexv1") -) - -var _ platform.AuthorizationService = (*Client)(nil) - -func (c *Client) initializeAuthorizations(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(authorizationBucket)); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists([]byte(authorizationIndex)); err != nil { - return err - } - return nil -} - -// FindAuthorizationByID retrieves a authorization by id. -func (c *Client) FindAuthorizationByID(ctx context.Context, id platform.ID) (*platform.Authorization, error) { - var a *platform.Authorization - var err error - err = c.db.View(func(tx *bolt.Tx) error { - var pe *platform.Error - a, pe = c.findAuthorizationByID(ctx, tx, id) - if pe != nil { - pe.Op = getOp(platform.OpFindAuthorizationByID) - err = pe - } - return err - }) - - return a, err -} - -func (c *Client) findAuthorizationByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.Authorization, *platform.Error) { - encodedID, err := id.Encode() - if err != nil { - return nil, &platform.Error{ - Code: platform.EInvalid, - Err: err, - } - } - - var a platform.Authorization - v := tx.Bucket(authorizationBucket).Get(encodedID) - - if len(v) == 0 { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: "authorization not found", - } - } - - if err := decodeAuthorization(v, &a); err != nil { - return nil, &platform.Error{ - Code: platform.EInvalid, - Err: err, - } - } - - return &a, nil -} - -// FindAuthorizationByToken returns a authorization by token for a particular authorization. -func (c *Client) FindAuthorizationByToken(ctx context.Context, n string) (*platform.Authorization, error) { - var a *platform.Authorization - var err error - err = c.db.View(func(tx *bolt.Tx) error { - var pe *platform.Error - a, pe = c.findAuthorizationByToken(ctx, tx, n) - if pe != nil { - pe.Op = getOp(platform.OpFindAuthorizationByToken) - err = pe - } - return err - }) - - return a, err -} - -func (c *Client) findAuthorizationByToken(ctx context.Context, tx *bolt.Tx, n string) (*platform.Authorization, *platform.Error) { - a := tx.Bucket(authorizationIndex).Get(authorizationIndexKey(n)) - if a == nil { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: "authorization not found", - } - } - var id platform.ID - if err := id.Decode(a); err != nil { - return nil, &platform.Error{ - Code: platform.EInvalid, - Err: err, - } - } - return c.findAuthorizationByID(ctx, tx, id) -} - -func filterAuthorizationsFn(filter platform.AuthorizationFilter) func(a *platform.Authorization) bool { - if filter.ID != nil { - return func(a *platform.Authorization) bool { - return a.ID == *filter.ID - } - } - - if filter.Token != nil { - return func(a *platform.Authorization) bool { - return a.Token == *filter.Token - } - } - // Filter by org and user - if filter.OrgID != nil && filter.UserID != nil { - return func(a *platform.Authorization) bool { - return a.OrgID == *filter.OrgID && a.UserID == *filter.UserID - } - } - - if filter.OrgID != nil { - return func(a *platform.Authorization) bool { - return a.OrgID == *filter.OrgID - } - } - - if filter.UserID != nil { - return func(a *platform.Authorization) bool { - return a.UserID == *filter.UserID - } - } - - return func(a *platform.Authorization) bool { return true } -} - -// FindAuthorizations retrives all authorizations that match an arbitrary authorization filter. -// Filters using ID, or Token should be efficient. -// Other filters will do a linear scan across all authorizations searching for a match. -func (c *Client) FindAuthorizations(ctx context.Context, filter platform.AuthorizationFilter, opt ...platform.FindOptions) ([]*platform.Authorization, int, error) { - if filter.ID != nil { - a, err := c.FindAuthorizationByID(ctx, *filter.ID) - if err != nil { - return nil, 0, &platform.Error{ - Err: err, - Op: getOp(platform.OpFindAuthorizations), - } - } - - return []*platform.Authorization{a}, 1, nil - } - - if filter.Token != nil { - a, err := c.FindAuthorizationByToken(ctx, *filter.Token) - if err != nil { - return nil, 0, &platform.Error{ - Err: err, - Op: getOp(platform.OpFindAuthorizations), - } - } - - return []*platform.Authorization{a}, 1, nil - } - - as := []*platform.Authorization{} - err := c.db.View(func(tx *bolt.Tx) error { - auths, err := c.findAuthorizations(ctx, tx, filter) - if err != nil { - return err - } - as = auths - return nil - }) - - if err != nil { - return nil, 0, &platform.Error{ - Err: err, - Op: getOp(platform.OpFindAuthorizations), - } - } - - return as, len(as), nil -} - -func (c *Client) findAuthorizations(ctx context.Context, tx *bolt.Tx, f platform.AuthorizationFilter) ([]*platform.Authorization, error) { - // If the users name was provided, look up user by ID first - if f.User != nil { - u, err := c.findUserByName(ctx, tx, *f.User) - if err != nil { - return nil, err - } - f.UserID = &u.ID - } - - if f.Org != nil { - o, err := c.findOrganizationByName(ctx, tx, *f.Org) - if err != nil { - return nil, err - } - f.OrgID = &o.ID - } - - as := []*platform.Authorization{} - filterFn := filterAuthorizationsFn(f) - err := c.forEachAuthorization(ctx, tx, func(a *platform.Authorization) bool { - if filterFn(a) { - as = append(as, a) - } - return true - }) - if err != nil { - return nil, err - } - - return as, nil -} - -// CreateAuthorization creates a platform authorization and sets b.ID, and b.UserID if not provided. -func (c *Client) CreateAuthorization(ctx context.Context, a *platform.Authorization) error { - op := getOp(platform.OpCreateAuthorization) - if err := a.Valid(); err != nil { - return &platform.Error{ - Err: err, - Op: op, - } - } - - return c.db.Update(func(tx *bolt.Tx) error { - _, pErr := c.findUserByID(ctx, tx, a.UserID) - if pErr != nil { - return platform.ErrUnableToCreateToken - } - - _, pErr = c.findOrganizationByID(ctx, tx, a.OrgID) - if pErr != nil { - return platform.ErrUnableToCreateToken - } - - if unique := c.uniqueAuthorizationToken(ctx, tx, a); !unique { - return platform.ErrUnableToCreateToken - } - - if a.Token == "" { - token, err := c.TokenGenerator.Token() - if err != nil { - return &platform.Error{ - Err: err, - Op: op, - } - } - a.Token = token - } - - a.ID = c.IDGenerator.ID() - - pe := c.putAuthorization(ctx, tx, a) - if pe != nil { - pe.Op = op - return pe - } - - return nil - }) -} - -// PutAuthorization will put a authorization without setting an ID. -func (c *Client) PutAuthorization(ctx context.Context, a *platform.Authorization) (err error) { - return c.db.Update(func(tx *bolt.Tx) error { - pe := c.putAuthorization(ctx, tx, a) - if pe != nil { - err = pe - } - return err - }) -} - -func encodeAuthorization(a *platform.Authorization) ([]byte, error) { - switch a.Status { - case platform.Active, platform.Inactive: - case "": - a.Status = platform.Active - default: - return nil, &platform.Error{ - Code: platform.EInvalid, - Msg: "unknown authorization status", - } - } - - return json.Marshal(a) -} - -func (c *Client) putAuthorization(ctx context.Context, tx *bolt.Tx, a *platform.Authorization) *platform.Error { - v, err := encodeAuthorization(a) - if err != nil { - return &platform.Error{ - Code: platform.EInvalid, - Err: err, - } - } - - encodedID, err := a.ID.Encode() - if err != nil { - return &platform.Error{ - Code: platform.ENotFound, - Err: err, - } - } - - if err := tx.Bucket(authorizationIndex).Put(authorizationIndexKey(a.Token), encodedID); err != nil { - return &platform.Error{ - Code: platform.EInternal, - Err: err, - } - } - - if err := tx.Bucket(authorizationBucket).Put(encodedID, v); err != nil { - return &platform.Error{ - Err: err, - } - } - - return nil -} - -func authorizationIndexKey(n string) []byte { - return []byte(n) -} - -func decodeAuthorization(b []byte, a *platform.Authorization) error { - if err := json.Unmarshal(b, a); err != nil { - return err - } - if a.Status == "" { - a.Status = platform.Active - } - return nil -} - -// forEachAuthorization will iterate through all authorizations while fn returns true. -func (c *Client) forEachAuthorization(ctx context.Context, tx *bolt.Tx, fn func(*platform.Authorization) bool) error { - cur := tx.Bucket(authorizationBucket).Cursor() - for k, v := cur.First(); k != nil; k, v = cur.Next() { - a := &platform.Authorization{} - - if err := decodeAuthorization(v, a); err != nil { - return err - } - if !fn(a) { - break - } - } - - return nil -} - -func (c *Client) uniqueAuthorizationToken(ctx context.Context, tx *bolt.Tx, a *platform.Authorization) bool { - v := tx.Bucket(authorizationIndex).Get(authorizationIndexKey(a.Token)) - return len(v) == 0 -} - -// DeleteAuthorization deletes a authorization and prunes it from the index. -func (c *Client) DeleteAuthorization(ctx context.Context, id platform.ID) error { - err := c.db.Update(func(tx *bolt.Tx) (err error) { - pe := c.deleteAuthorization(ctx, tx, id) - if pe != nil { - pe.Op = getOp(platform.OpDeleteAuthorization) - err = pe - } - return err - }) - return err -} - -func (c *Client) deleteAuthorization(ctx context.Context, tx *bolt.Tx, id platform.ID) *platform.Error { - a, pe := c.findAuthorizationByID(ctx, tx, id) - if pe != nil { - return pe - } - if err := tx.Bucket(authorizationIndex).Delete(authorizationIndexKey(a.Token)); err != nil { - return &platform.Error{ - Err: err, - } - } - encodedID, err := id.Encode() - if err != nil { - return &platform.Error{ - Err: err, - } - } - - if err := tx.Bucket(authorizationBucket).Delete(encodedID); err != nil { - return &platform.Error{ - Err: err, - } - } - return nil -} - -// UpdateAuthorization updates the status and description if available. -func (c *Client) UpdateAuthorization(ctx context.Context, id platform.ID, upd *platform.AuthorizationUpdate) (*platform.Authorization, error) { - var a *platform.Authorization - err := c.db.Update(func(tx *bolt.Tx) error { - var pe *platform.Error - a, pe = c.updateAuthorization(ctx, tx, id, upd) - if pe != nil { - return &platform.Error{ - Err: pe, - Op: platform.OpUpdateAuthorization, - } - } - return nil - }) - return a, err -} - -func (c *Client) updateAuthorization(ctx context.Context, tx *bolt.Tx, id platform.ID, upd *platform.AuthorizationUpdate) (*platform.Authorization, *platform.Error) { - a, pe := c.findAuthorizationByID(ctx, tx, id) - if pe != nil { - return nil, pe - } - - if upd.Status != nil { - a.Status = *upd.Status - } - if upd.Description != nil { - a.Description = *upd.Description - } - - b, err := encodeAuthorization(a) - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - encodedID, err := id.Encode() - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - if err = tx.Bucket(authorizationBucket).Put(encodedID, b); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - return a, nil -} diff --git a/bolt/authorization_test.go b/bolt/authorization_test.go deleted file mode 100644 index fec9c2cd96f..00000000000 --- a/bolt/authorization_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/bolt" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initAuthorizationService(f platformtesting.AuthorizationFields, t *testing.T) (platform.AuthorizationService, string, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - c.IDGenerator = f.IDGenerator - c.TokenGenerator = f.TokenGenerator - ctx := context.Background() - - for _, u := range f.Users { - if err := c.PutUser(ctx, u); err != nil { - t.Fatalf("failed to populate users") - } - } - - for _, o := range f.Orgs { - if err := c.PutOrganization(ctx, o); err != nil { - t.Fatalf("failed to populate orgs") - } - } - - for _, a := range f.Authorizations { - if err := c.PutAuthorization(ctx, a); err != nil { - t.Fatalf("failed to populate authorizations %s", err) - } - } - - return c, bolt.OpPrefix, func() { - defer closeFn() - for _, u := range f.Users { - if err := c.DeleteUser(ctx, u.ID); err != nil { - t.Logf("failed to remove user: %v", err) - } - } - - for _, o := range f.Orgs { - if err := c.DeleteOrganization(ctx, o.ID); err != nil { - t.Logf("failed to remove org: %v", err) - } - } - - for _, a := range f.Authorizations { - if err := c.DeleteAuthorization(ctx, a.ID); err != nil { - t.Logf("failed to remove authorizations: %v", err) - } - } - } -} - -func TestAuthorizationService(t *testing.T) { - t.Skip("This service is not used, we use the kv bolt implementation") - platformtesting.AuthorizationService(initAuthorizationService, t) -} diff --git a/bolt/bbolt.go b/bolt/bbolt.go index d45462cd0ff..e24eee82285 100644 --- a/bolt/bbolt.go +++ b/bolt/bbolt.go @@ -14,13 +14,6 @@ import ( "go.uber.org/zap" ) -// OpPrefix is the prefix for bolt ops -const OpPrefix = "bolt/" - -func getOp(op string) string { - return OpPrefix + op -} - // Client is a client for the boltDB data store. type Client struct { Path string @@ -77,85 +70,26 @@ func (c *Client) Open(ctx context.Context) error { func (c *Client) initialize(ctx context.Context) error { if err := c.db.Update(func(tx *bolt.Tx) error { // Always create ID bucket. + // TODO: is this still needed? if err := c.initializeID(tx); err != nil { return err } - // Always create Buckets bucket. - if err := c.initializeBuckets(ctx, tx); err != nil { - return err - } - - // Always create Organizations bucket. - if err := c.initializeOrganizations(ctx, tx); err != nil { - return err - } - - // Always create Dashboards bucket. - if err := c.initializeDashboards(ctx, tx); err != nil { - return err - } - - // Always create User bucket. - if err := c.initializeUsers(ctx, tx); err != nil { - return err - } - - // Always create Authorization bucket. - if err := c.initializeAuthorizations(ctx, tx); err != nil { - return err - } - - // Always create Onboarding bucket. - if err := c.initializeOnboarding(ctx, tx); err != nil { - 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 - } - - // Always create Variables bucket. - if err := c.initializeVariables(ctx, tx); err != nil { - return err - } - - // Always create Scraper bucket. - if err := c.initializeScraperTargets(ctx, tx); err != nil { - return err + // TODO: make card to normalize everything under kv? + bkts := [][]byte{ + authorizationBucket, + bucketBucket, + dashboardBucket, + organizationBucket, + scraperBucket, + telegrafBucket, + userBucket, } - - // Always create UserResourceMapping bucket. - if err := c.initializeUserResourceMappings(ctx, tx); err != nil { - return err + for _, bktName := range bkts { + if _, err := tx.CreateBucketIfNotExists(bktName); err != nil { + return err + } } - - // Always create labels bucket. - if err := c.initializeLabels(ctx, tx); err != nil { - return err - } - - // Always create Session bucket. - if err := c.initializeSessions(ctx, tx); err != nil { - return err - } - - // Always create KeyValueLog bucket. - if err := c.initializeKeyValueLog(ctx, tx); err != nil { - return err - } - - // Always create SecretService bucket. - if err := c.initializeSecretService(ctx, tx); err != nil { - return err - } - return nil }); err != nil { return err diff --git a/bolt/bbolt_test.go b/bolt/bbolt_test.go index d31d74bd408..2dcb456584a 100644 --- a/bolt/bbolt_test.go +++ b/bolt/bbolt_test.go @@ -10,13 +10,8 @@ import ( "github.com/influxdata/influxdb/bolt" "go.uber.org/zap/zaptest" - "golang.org/x/crypto/bcrypt" ) -func init() { - bolt.HashCost = bcrypt.MinCost -} - func NewTestClient(t *testing.T) (*bolt.Client, func(), error) { c, closeFn, err := newTestClient(t) if err != nil { diff --git a/bolt/bucket.go b/bolt/bucket.go deleted file mode 100644 index 9e1433be37e..00000000000 --- a/bolt/bucket.go +++ /dev/null @@ -1,684 +0,0 @@ -package bolt - -import ( - "context" - "encoding/json" - "fmt" - "time" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" - platformcontext "github.com/influxdata/influxdb/context" - "github.com/influxdata/influxdb/kit/tracing" -) - -var ( - bucketBucket = []byte("bucketsv1") - bucketIndex = []byte("bucketindexv1") -) - -var _ platform.BucketService = (*Client)(nil) -var _ platform.BucketOperationLogService = (*Client)(nil) - -func (c *Client) initializeBuckets(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(bucketBucket)); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists([]byte(bucketIndex)); err != nil { - return err - } - return nil -} - -// FindBucketByID retrieves a bucket by id. -func (c *Client) FindBucketByID(ctx context.Context, id platform.ID) (*platform.Bucket, error) { - span, ctx := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - var b *platform.Bucket - var err error - - err = c.db.View(func(tx *bolt.Tx) error { - bkt, pe := c.findBucketByID(ctx, tx, id) - if pe != nil { - pe.Op = getOp(platform.OpFindBucketByID) - err = pe - return err - } - b = bkt - return nil - }) - - if err != nil { - return nil, err - } - - return b, nil -} - -func (c *Client) findBucketByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.Bucket, *platform.Error) { - span, _ := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - var b platform.Bucket - - encodedID, err := id.Encode() - if err != nil { - return nil, &platform.Error{ - Code: platform.EInvalid, - Err: err, - } - } - - v := tx.Bucket(bucketBucket).Get(encodedID) - if len(v) == 0 { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: "bucket not found", - } - } - - if err := json.Unmarshal(v, &b); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - return &b, nil -} - -// FindBucketByName returns a bucket by name for a particular organization. -// TODO: have method for finding bucket using organization name and bucket name. -func (c *Client) FindBucketByName(ctx context.Context, orgID platform.ID, n string) (*platform.Bucket, error) { - span, ctx := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - var b *platform.Bucket - var err error - - err = c.db.View(func(tx *bolt.Tx) error { - bkt, pe := c.findBucketByName(ctx, tx, orgID, n) - if pe != nil { - pe.Op = getOp(platform.OpFindBucket) - err = pe - return err - } - b = bkt - return nil - }) - - return b, err -} - -func (c *Client) findBucketByName(ctx context.Context, tx *bolt.Tx, orgID platform.ID, n string) (*platform.Bucket, *platform.Error) { - span, ctx := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - b := &platform.Bucket{ - OrgID: orgID, - Name: n, - } - key, err := bucketIndexKey(b) - if err != nil { - return nil, &platform.Error{ - Code: platform.EInvalid, - Err: err, - } - } - - buf := tx.Bucket(bucketIndex).Get(key) - if buf == nil { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: fmt.Sprintf("bucket %q not found", n), - } - } - - var id platform.ID - if err := id.Decode(buf); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - return c.findBucketByID(ctx, tx, id) -} - -// FindBucket retrives a bucket using an arbitrary bucket filter. -// Filters using ID, or OrganizationID and bucket Name should be efficient. -// Other filters will do a linear scan across buckets until it finds a match. -func (c *Client) FindBucket(ctx context.Context, filter platform.BucketFilter) (*platform.Bucket, error) { - span, ctx := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - var b *platform.Bucket - var err error - - if filter.ID != nil { - b, err = c.FindBucketByID(ctx, *filter.ID) - if err != nil { - return nil, &platform.Error{ - Op: getOp(platform.OpFindBucket), - Err: err, - } - } - return b, nil - } - - if filter.Name != nil && filter.OrganizationID != nil { - return c.FindBucketByName(ctx, *filter.OrganizationID, *filter.Name) - } - - err = c.db.View(func(tx *bolt.Tx) error { - if filter.Org != nil { - o, err := c.findOrganizationByName(ctx, tx, *filter.Org) - if err != nil { - return err - } - filter.OrganizationID = &o.ID - } - - filterFn := filterBucketsFn(filter) - return c.forEachBucket(ctx, tx, false, func(bkt *platform.Bucket) bool { - if filterFn(bkt) { - b = bkt - return false - } - return true - }) - }) - - if err != nil { - return nil, &platform.Error{ - Op: getOp(platform.OpFindBucket), - Err: err, - } - } - - if b == nil { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: "bucket not found", - } - } - - return b, nil -} - -func filterBucketsFn(filter platform.BucketFilter) func(b *platform.Bucket) bool { - if filter.ID != nil { - return func(b *platform.Bucket) bool { - return b.ID == *filter.ID - } - } - - if filter.Name != nil && filter.OrganizationID != nil { - return func(b *platform.Bucket) bool { - return b.Name == *filter.Name && b.OrgID == *filter.OrganizationID - } - } - - if filter.Name != nil { - return func(b *platform.Bucket) bool { - return b.Name == *filter.Name - } - } - - if filter.OrganizationID != nil { - return func(b *platform.Bucket) bool { - return b.OrgID == *filter.OrganizationID - } - } - - return func(b *platform.Bucket) bool { return true } -} - -// FindBuckets retrives all buckets that match an arbitrary bucket filter. -// Filters using ID, or OrganizationID and bucket Name should be efficient. -// Other filters will do a linear scan across all buckets searching for a match. -func (c *Client) FindBuckets(ctx context.Context, filter platform.BucketFilter, opts ...platform.FindOptions) ([]*platform.Bucket, int, error) { - span, ctx := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - if filter.ID != nil { - b, err := c.FindBucketByID(ctx, *filter.ID) - if err != nil { - return nil, 0, err - } - - return []*platform.Bucket{b}, 1, nil - } - - if filter.Name != nil && filter.OrganizationID != nil { - b, err := c.FindBucketByName(ctx, *filter.OrganizationID, *filter.Name) - if err != nil { - return nil, 0, err - } - - return []*platform.Bucket{b}, 1, nil - } - - bs := []*platform.Bucket{} - err := c.db.View(func(tx *bolt.Tx) error { - bkts, err := c.findBuckets(ctx, tx, filter, opts...) - if err != nil { - return err - } - bs = bkts - return nil - }) - - if err != nil { - return nil, 0, err - } - - return bs, len(bs), nil -} - -func (c *Client) findBuckets(ctx context.Context, tx *bolt.Tx, filter platform.BucketFilter, opts ...platform.FindOptions) ([]*platform.Bucket, *platform.Error) { - span, ctx := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - bs := []*platform.Bucket{} - if filter.Org != nil { - o, err := c.findOrganizationByName(ctx, tx, *filter.Org) - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - filter.OrganizationID = &o.ID - } - - var offset, limit, count int - var descending bool - if len(opts) > 0 { - offset = opts[0].Offset - limit = opts[0].Limit - descending = opts[0].Descending - } - - filterFn := filterBucketsFn(filter) - err := c.forEachBucket(ctx, tx, descending, func(b *platform.Bucket) bool { - if filterFn(b) { - if count >= offset { - bs = append(bs, b) - } - count++ - } - - if limit > 0 && len(bs) >= limit { - return false - } - - return true - }) - - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - return bs, nil -} - -// CreateBucket creates a platform bucket and sets b.ID. -func (c *Client) CreateBucket(ctx context.Context, b *platform.Bucket) error { - span, ctx := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - var err error - op := getOp(platform.OpCreateBucket) - return c.db.Update(func(tx *bolt.Tx) error { - if b.OrgID.Valid() { - _, pe := c.findOrganizationByID(ctx, tx, b.OrgID) - if pe != nil { - return &platform.Error{ - Err: pe, - Op: op, - } - } - } - - unique := c.uniqueBucketName(ctx, tx, b) - - if !unique { - // TODO: make standard error - return &platform.Error{ - Code: platform.EConflict, - Op: op, - Msg: fmt.Sprintf("bucket with name %s already exists", b.Name), - } - } - - b.ID = c.IDGenerator.ID() - b.CreatedAt = c.Now() - b.UpdatedAt = c.Now() - - if err = c.appendBucketEventToLog(ctx, tx, b.ID, bucketCreatedEvent); err != nil { - return &platform.Error{ - Op: op, - Err: err, - } - } - - if pe := c.putBucket(ctx, tx, b); pe != nil { - pe.Op = op - err = pe - } - - if pe := c.createBucketUserResourceMappings(ctx, tx, b); pe != nil { - pe.Op = op - err = pe - } - return nil - }) -} - -// PutBucket will put a bucket without setting an ID. -func (c *Client) PutBucket(ctx context.Context, b *platform.Bucket) error { - return c.db.Update(func(tx *bolt.Tx) error { - var err error - pe := c.putBucket(ctx, tx, b) - if pe != nil { - err = pe - } - return err - }) -} - -func (c *Client) createBucketUserResourceMappings(ctx context.Context, tx *bolt.Tx, b *platform.Bucket) *platform.Error { - ms, err := c.findUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{ - ResourceType: platform.OrgsResourceType, - ResourceID: b.OrgID, - }) - if err != nil { - return &platform.Error{ - Err: err, - } - } - - for _, m := range ms { - if err := c.createUserResourceMapping(ctx, tx, &platform.UserResourceMapping{ - ResourceType: platform.BucketsResourceType, - ResourceID: b.ID, - UserID: m.UserID, - UserType: m.UserType, - }); err != nil { - return &platform.Error{ - Err: err, - } - } - } - - return nil -} - -func (c *Client) putBucket(ctx context.Context, tx *bolt.Tx, b *platform.Bucket) *platform.Error { - v, err := json.Marshal(b) - if err != nil { - return &platform.Error{ - Err: err, - } - } - - encodedID, err := b.ID.Encode() - if err != nil { - return &platform.Error{ - Err: err, - } - } - key, pe := bucketIndexKey(b) - if err != nil { - return pe - } - - if err := tx.Bucket(bucketIndex).Put(key, encodedID); err != nil { - return &platform.Error{ - Err: err, - } - } - if err := tx.Bucket(bucketBucket).Put(encodedID, v); err != nil { - return &platform.Error{ - Err: err, - } - } - return nil -} - -func bucketIndexKey(b *platform.Bucket) ([]byte, *platform.Error) { - orgID, err := b.OrgID.Encode() - if err != nil { - return nil, &platform.Error{ - Code: platform.EInvalid, - Err: err, - } - } - k := make([]byte, platform.IDLength+len(b.Name)) - copy(k, orgID) - copy(k[platform.IDLength:], []byte(b.Name)) - return k, nil -} - -// forEachBucket will iterate through all buckets while fn returns true. -func (c *Client) forEachBucket(ctx context.Context, tx *bolt.Tx, descending bool, fn func(*platform.Bucket) bool) error { - cur := tx.Bucket(bucketBucket).Cursor() - - var k, v []byte - if descending { - k, v = cur.Last() - } else { - k, v = cur.First() - } - - for k != nil { - b := &platform.Bucket{} - if err := json.Unmarshal(v, b); err != nil { - return err - } - if !fn(b) { - break - } - - if descending { - k, v = cur.Prev() - } else { - k, v = cur.Next() - } - } - - return nil -} - -func (c *Client) uniqueBucketName(ctx context.Context, tx *bolt.Tx, b *platform.Bucket) bool { - key, err := bucketIndexKey(b) - if err != nil { - return false - } - v := tx.Bucket(bucketIndex).Get(key) - return len(v) == 0 -} - -// UpdateBucket updates a bucket according the parameters set on upd. -func (c *Client) UpdateBucket(ctx context.Context, id platform.ID, upd platform.BucketUpdate) (*platform.Bucket, error) { - var b *platform.Bucket - err := c.db.Update(func(tx *bolt.Tx) error { - bkt, err := c.updateBucket(ctx, tx, id, upd) - if err != nil { - return err - } - b = bkt - return nil - }) - - return b, err -} - -func (c *Client) updateBucket(ctx context.Context, tx *bolt.Tx, id platform.ID, upd platform.BucketUpdate) (*platform.Bucket, error) { - b, err := c.findBucketByID(ctx, tx, id) - if err != nil { - return nil, err - } - - if upd.RetentionPeriod != nil { - b.RetentionPeriod = *upd.RetentionPeriod - } - - if upd.Description != nil { - b.Description = *upd.Description - } - - if upd.Name != nil { - b0, err := c.findBucketByName(ctx, tx, b.OrgID, *upd.Name) - if err == nil && b0.ID != id { - return nil, &platform.Error{ - Code: platform.EConflict, - Msg: "bucket name is not unique", - } - } - - key, err := bucketIndexKey(b) - if err != nil { - return nil, err - } - // Buckets are indexed by name and so the bucket index must be pruned when name is modified. - if err := tx.Bucket(bucketIndex).Delete(key); err != nil { - return nil, err - } - b.Name = *upd.Name - } - - b.UpdatedAt = c.Now() - - if err := c.appendBucketEventToLog(ctx, tx, b.ID, bucketUpdatedEvent); err != nil { - return nil, err - } - - if err := c.putBucket(ctx, tx, b); err != nil { - return nil, err - } - return b, nil -} - -// DeleteBucket deletes a bucket and prunes it from the index. -func (c *Client) DeleteBucket(ctx context.Context, id platform.ID) error { - return c.db.Update(func(tx *bolt.Tx) error { - var err error - if pe := c.deleteBucket(ctx, tx, id); pe != nil { - pe.Op = getOp(platform.OpDeleteBucket) - err = pe - } - return err - }) -} - -func (c *Client) deleteBucket(ctx context.Context, tx *bolt.Tx, id platform.ID) *platform.Error { - b, pe := c.findBucketByID(ctx, tx, id) - if pe != nil { - return pe - } - key, pe := bucketIndexKey(b) - if pe != nil { - return pe - } - // make lowercase deleteBucket with tx - if err := tx.Bucket(bucketIndex).Delete(key); err != nil { - return &platform.Error{ - Err: err, - } - } - encodedID, err := id.Encode() - if err != nil { - return &platform.Error{ - Code: platform.EInvalid, - Err: err, - } - } - if err := tx.Bucket(bucketBucket).Delete(encodedID); err != nil { - return &platform.Error{ - Err: err, - } - } - if err := c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{ - ResourceID: id, - ResourceType: platform.BucketsResourceType, - }); err != nil { - return &platform.Error{ - Err: err, - } - } - - return nil -} - -const bucketOperationLogKeyPrefix = "bucket" - -func encodeBucketOperationLogKey(id platform.ID) ([]byte, error) { - buf, err := id.Encode() - if err != nil { - return nil, err - } - return append([]byte(bucketOperationLogKeyPrefix), buf...), nil -} - -// GetBucketOperationLog retrieves a buckets operation log. -func (c *Client) GetBucketOperationLog(ctx context.Context, id platform.ID, opts platform.FindOptions) ([]*platform.OperationLogEntry, int, error) { - // TODO(desa): might be worthwhile to allocate a slice of size opts.Limit - log := []*platform.OperationLogEntry{} - - err := c.db.View(func(tx *bolt.Tx) error { - key, err := encodeBucketOperationLogKey(id) - if err != nil { - return err - } - - return c.forEachLogEntry(ctx, tx, key, opts, func(v []byte, t time.Time) error { - e := &platform.OperationLogEntry{} - if err := json.Unmarshal(v, e); err != nil { - return err - } - e.Time = t - - log = append(log, e) - - return nil - }) - }) - - if err != nil { - return nil, 0, err - } - - return log, len(log), nil -} - -// TODO(desa): what do we want these to be? -const ( - bucketCreatedEvent = "Bucket Created" - bucketUpdatedEvent = "Bucket Updated" -) - -func (c *Client) appendBucketEventToLog(ctx context.Context, tx *bolt.Tx, id platform.ID, s string) error { - e := &platform.OperationLogEntry{ - Description: s, - } - // TODO(desa): this is fragile and non explicit since it requires an authorizer to be on context. It should be - // replaced with a higher level transaction so that adding to the log can take place in the http handler - // where the userID will exist explicitly. - a, err := platformcontext.GetAuthorizer(ctx) - if err == nil { - // Add the user to the log if you can, but don't error if its not there. - e.UserID = a.GetUserID() - } - - v, err := json.Marshal(e) - if err != nil { - return err - } - - k, err := encodeBucketOperationLogKey(id) - if err != nil { - return err - } - - return c.addLogEntry(ctx, tx, k, v, c.Now()) -} diff --git a/bolt/bucket_test.go b/bolt/bucket_test.go deleted file mode 100644 index c56137cc68f..00000000000 --- a/bolt/bucket_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/bolt" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initBucketService(f platformtesting.BucketFields, t *testing.T) (platform.BucketService, string, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - c.IDGenerator = f.IDGenerator - c.TimeGenerator = f.TimeGenerator - if f.TimeGenerator == nil { - c.TimeGenerator = platform.RealTimeGenerator{} - } - ctx := context.TODO() - for _, o := range f.Organizations { - if err := c.PutOrganization(ctx, o); err != nil { - t.Fatalf("failed to populate organizations") - } - } - for _, b := range f.Buckets { - if err := c.PutBucket(ctx, b); err != nil { - t.Fatalf("failed to populate buckets") - } - } - return c, bolt.OpPrefix, func() { - defer closeFn() - for _, o := range f.Organizations { - if err := c.DeleteOrganization(ctx, o.ID); err != nil { - t.Logf("failed to remove organization: %v", err) - } - } - for _, b := range f.Buckets { - if err := c.DeleteBucket(ctx, b.ID); err != nil { - t.Logf("failed to remove bucket: %v", err) - } - } - } -} - -func TestBucketService(t *testing.T) { - t.Skip("old bolt code") - platformtesting.BucketService(initBucketService, t) -} diff --git a/bolt/dashboard.go b/bolt/dashboard.go deleted file mode 100644 index 22954eab7c4..00000000000 --- a/bolt/dashboard.go +++ /dev/null @@ -1,911 +0,0 @@ -package bolt - -import ( - "bytes" - "context" - "encoding/json" - "time" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" - platformcontext "github.com/influxdata/influxdb/context" -) - -var ( - dashboardBucket = []byte("dashboardsv2") - orgDashboardIndex = []byte("orgsdashboardsv1") - dashboardCellViewBucket = []byte("dashboardcellviewsv1") -) - -// TODO(desa): what do we want these to be? -const ( - dashboardCreatedEvent = "Dashboard Created" - dashboardUpdatedEvent = "Dashboard Updated" - dashboardRemovedEvent = "Dashboard Removed" - - dashboardCellsReplacedEvent = "Dashboard Cells Replaced" - dashboardCellAddedEvent = "Dashboard Cell Added" - dashboardCellRemovedEvent = "Dashboard Cell Removed" - dashboardCellUpdatedEvent = "Dashboard Cell Updated" -) - -var _ platform.DashboardService = (*Client)(nil) -var _ platform.DashboardOperationLogService = (*Client)(nil) - -func (c *Client) initializeDashboards(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists(dashboardBucket); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists(orgDashboardIndex); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists(dashboardCellViewBucket); err != nil { - return err - } - return nil -} - -// FindDashboardByID retrieves a dashboard by id. -func (c *Client) FindDashboardByID(ctx context.Context, id platform.ID) (*platform.Dashboard, error) { - var d *platform.Dashboard - - err := c.db.View(func(tx *bolt.Tx) error { - dash, err := c.findDashboardByID(ctx, tx, id) - if err != nil { - return err - } - d = dash - return nil - }) - - if err != nil { - return nil, &platform.Error{ - Op: getOp(platform.OpFindDashboardByID), - Err: err, - } - } - - return d, nil -} - -func (c *Client) findDashboardByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.Dashboard, error) { - encodedID, err := id.Encode() - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - v := tx.Bucket(dashboardBucket).Get(encodedID) - if len(v) == 0 { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: platform.ErrDashboardNotFound, - } - - } - - var d platform.Dashboard - if err := json.Unmarshal(v, &d); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - return &d, nil -} - -// FindDashboard retrieves a dashboard using an arbitrary dashboard filter. -func (c *Client) FindDashboard(ctx context.Context, filter platform.DashboardFilter, opts ...platform.FindOptions) (*platform.Dashboard, error) { - if len(filter.IDs) == 1 { - return c.FindDashboardByID(ctx, *filter.IDs[0]) - } - - var d *platform.Dashboard - err := c.db.View(func(tx *bolt.Tx) error { - filterFn := filterDashboardsFn(filter) - return c.forEachDashboard(ctx, tx, opts[0].Descending, func(dash *platform.Dashboard) bool { - if filterFn(dash) { - d = dash - return false - } - return true - }) - }) - - if err != nil { - return nil, err - } - - if d == nil { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: platform.ErrDashboardNotFound, - } - } - - return d, nil -} - -func filterDashboardsFn(filter platform.DashboardFilter) func(d *platform.Dashboard) bool { - if len(filter.IDs) > 0 { - m := map[string]struct{}{} - for _, id := range filter.IDs { - m[id.String()] = struct{}{} - } - return func(d *platform.Dashboard) bool { - _, ok := m[d.ID.String()] - return ok - } - } - - return func(d *platform.Dashboard) bool { return true } -} - -// FindDashboards retrives all dashboards that match an arbitrary dashboard filter. -func (c *Client) FindDashboards(ctx context.Context, filter platform.DashboardFilter, opts platform.FindOptions) ([]*platform.Dashboard, int, error) { - ds := []*platform.Dashboard{} - if len(filter.IDs) == 1 { - d, err := c.FindDashboardByID(ctx, *filter.IDs[0]) - if err != nil && platform.ErrorCode(err) != platform.ENotFound { - return ds, 0, &platform.Error{ - Err: err, - Op: getOp(platform.OpFindDashboardByID), - } - } - if d == nil { - return ds, 0, nil - } - return []*platform.Dashboard{d}, 1, nil - } - err := c.db.View(func(tx *bolt.Tx) error { - dashs, err := c.findDashboards(ctx, tx, filter, opts) - if err != nil && platform.ErrorCode(err) != platform.ENotFound { - return err - } - ds = dashs - return nil - }) - - if err != nil { - return nil, 0, &platform.Error{ - Err: err, - Op: getOp(platform.OpFindDashboards), - } - } - - platform.SortDashboards(opts, ds) - - return ds, len(ds), nil -} - -func (c *Client) findOrganizationDashboards(ctx context.Context, tx *bolt.Tx, orgID platform.ID) ([]*platform.Dashboard, error) { - // TODO(desa): support find options. - cur := tx.Bucket(orgDashboardIndex).Cursor() - prefix, err := orgID.Encode() - if err != nil { - return nil, err - } - - ds := []*platform.Dashboard{} - for k, _ := cur.Seek(prefix); bytes.HasPrefix(k, prefix); k, _ = cur.Next() { - _, id, err := decodeOrgDashboardIndexKey(k) - if err != nil { - return nil, err - } - - d, err := c.findDashboardByID(ctx, tx, id) - if err != nil { - return nil, err - } - - ds = append(ds, d) - } - - return ds, nil -} - -func decodeOrgDashboardIndexKey(indexKey []byte) (orgID platform.ID, dashID platform.ID, err error) { - if len(indexKey) != 2*platform.IDLength { - return 0, 0, &platform.Error{Code: platform.EInvalid, Msg: "malformed org dashboard index key (please report this error)"} - } - - if err := (&orgID).Decode(indexKey[:platform.IDLength]); err != nil { - return 0, 0, &platform.Error{Code: platform.EInvalid, Msg: "bad org id", Err: platform.ErrInvalidID} - } - - if err := (&dashID).Decode(indexKey[platform.IDLength:]); err != nil { - return 0, 0, &platform.Error{Code: platform.EInvalid, Msg: "bad dashboard id", Err: platform.ErrInvalidID} - } - - return orgID, dashID, nil -} - -func (c *Client) findDashboards(ctx context.Context, tx *bolt.Tx, filter platform.DashboardFilter, opts ...platform.FindOptions) ([]*platform.Dashboard, error) { - if filter.OrganizationID != nil { - return c.findOrganizationDashboards(ctx, tx, *filter.OrganizationID) - } - - if filter.Organization != nil { - o, err := c.findOrganizationByName(ctx, tx, *filter.Organization) - if err != nil { - return nil, err - } - return c.findOrganizationDashboards(ctx, tx, o.ID) - } - - var offset, limit, count int - var descending bool - if len(opts) > 0 { - offset = opts[0].Offset - limit = opts[0].Limit - descending = opts[0].Descending - } - - ds := []*platform.Dashboard{} - filterFn := filterDashboardsFn(filter) - err := c.forEachDashboard(ctx, tx, descending, func(d *platform.Dashboard) bool { - if filterFn(d) { - if count >= offset { - ds = append(ds, d) - } - count++ - } - if limit > 0 && len(ds) >= limit { - return false - } - return true - }) - - if err != nil { - return nil, err - } - - return ds, nil -} - -// CreateDashboard creates a platform dashboard and sets d.ID. -func (c *Client) CreateDashboard(ctx context.Context, d *platform.Dashboard) error { - err := c.db.Update(func(tx *bolt.Tx) error { - d.ID = c.IDGenerator.ID() - - for _, cell := range d.Cells { - cell.ID = c.IDGenerator.ID() - - if err := c.createCellView(ctx, tx, d.ID, cell.ID, nil); err != nil { - return err - } - } - - if err := c.appendDashboardEventToLog(ctx, tx, d.ID, dashboardCreatedEvent); err != nil { - return err - } - - if err := c.putOrganizationDashboardIndex(ctx, tx, d); err != nil { - return err - } - - d.Meta.CreatedAt = c.Now() - d.Meta.UpdatedAt = c.Now() - - return c.putDashboardWithMeta(ctx, tx, d) - }) - if err != nil { - return &platform.Error{ - Err: err, - Op: getOp(platform.OpCreateDashboard), - } - } - return nil -} - -func (c *Client) createCellView(ctx context.Context, tx *bolt.Tx, dashID, cellID platform.ID, view *platform.View) error { - if view == nil { - // If not view exists create the view - view = &platform.View{} - } - // TODO: this is temporary until we can fully remove the view service. - view.ID = cellID - return c.putDashboardCellView(ctx, tx, dashID, cellID, view) -} - -// ReplaceDashboardCells updates the positions of each cell in a dashboard concurrently. -func (c *Client) ReplaceDashboardCells(ctx context.Context, id platform.ID, cs []*platform.Cell) error { - err := c.db.Update(func(tx *bolt.Tx) error { - d, err := c.findDashboardByID(ctx, tx, id) - if err != nil { - return err - } - - ids := map[string]*platform.Cell{} - for _, cell := range d.Cells { - ids[cell.ID.String()] = cell - } - - for _, cell := range cs { - if !cell.ID.Valid() { - return &platform.Error{ - Code: platform.EInvalid, - Msg: "cannot provide empty cell id", - } - } - - if _, ok := ids[cell.ID.String()]; !ok { - return &platform.Error{ - Code: platform.EConflict, - Msg: "cannot replace cells that were not already present", - } - } - } - - d.Cells = cs - if err := c.appendDashboardEventToLog(ctx, tx, d.ID, dashboardCellsReplacedEvent); err != nil { - return err - } - - return c.putDashboardWithMeta(ctx, tx, d) - }) - if err != nil { - return &platform.Error{ - Op: getOp(platform.OpReplaceDashboardCells), - Err: err, - } - } - return nil -} - -func (c *Client) addDashboardCell(ctx context.Context, tx *bolt.Tx, id platform.ID, cell *platform.Cell, opts platform.AddDashboardCellOptions) error { - d, err := c.findDashboardByID(ctx, tx, id) - if err != nil { - return err - } - cell.ID = c.IDGenerator.ID() - if err := c.createCellView(ctx, tx, id, cell.ID, opts.View); err != nil { - return err - } - - d.Cells = append(d.Cells, cell) - - if err := c.appendDashboardEventToLog(ctx, tx, d.ID, dashboardCellAddedEvent); err != nil { - return err - } - - return c.putDashboardWithMeta(ctx, tx, d) -} - -// AddDashboardCell adds a cell to a dashboard and sets the cells ID. -func (c *Client) AddDashboardCell(ctx context.Context, id platform.ID, cell *platform.Cell, opts platform.AddDashboardCellOptions) error { - err := c.db.Update(func(tx *bolt.Tx) error { - return c.addDashboardCell(ctx, tx, id, cell, opts) - }) - if err != nil { - return &platform.Error{ - Err: err, - Op: getOp(platform.OpAddDashboardCell), - } - } - return nil -} - -// RemoveDashboardCell removes a cell from a dashboard. -func (c *Client) RemoveDashboardCell(ctx context.Context, dashboardID, cellID platform.ID) error { - op := getOp(platform.OpRemoveDashboardCell) - return c.db.Update(func(tx *bolt.Tx) error { - d, err := c.findDashboardByID(ctx, tx, dashboardID) - if err != nil { - return &platform.Error{ - Err: err, - Op: op, - } - } - - idx := -1 - for i, cell := range d.Cells { - if cell.ID == cellID { - idx = i - break - } - } - if idx == -1 { - return &platform.Error{ - Code: platform.ENotFound, - Op: op, - Msg: platform.ErrCellNotFound, - } - } - - if err := c.deleteDashboardCellView(ctx, tx, d.ID, d.Cells[idx].ID); err != nil { - return &platform.Error{ - Err: err, - Op: op, - } - } - - d.Cells = append(d.Cells[:idx], d.Cells[idx+1:]...) - - if err := c.appendDashboardEventToLog(ctx, tx, d.ID, dashboardCellRemovedEvent); err != nil { - return &platform.Error{ - Err: err, - Op: op, - } - } - - if err := c.putDashboardWithMeta(ctx, tx, d); err != nil { - return &platform.Error{ - Err: err, - Op: op, - } - } - return nil - }) -} - -// GetDashboardCellView retrieves the view for a dashboard cell. -func (c *Client) GetDashboardCellView(ctx context.Context, dashboardID, cellID platform.ID) (*platform.View, error) { - var v *platform.View - err := c.db.View(func(tx *bolt.Tx) error { - view, err := c.findDashboardCellView(ctx, tx, dashboardID, cellID) - if err != nil { - return err - } - - v = view - return nil - }) - - if err != nil { - return nil, &platform.Error{ - Err: err, - Op: getOp(platform.OpGetDashboardCellView), - } - } - - return v, nil -} - -func (c *Client) findDashboardCellView(ctx context.Context, tx *bolt.Tx, dashboardID, cellID platform.ID) (*platform.View, error) { - k, err := encodeDashboardCellViewID(dashboardID, cellID) - if err != nil { - return nil, platform.NewError(platform.WithErrorErr(err)) - } - v := tx.Bucket(dashboardCellViewBucket).Get(k) - if len(v) == 0 { - return nil, platform.NewError(platform.WithErrorCode(platform.ENotFound), platform.WithErrorMsg(platform.ErrViewNotFound)) - } - - view := &platform.View{} - if err := json.Unmarshal(v, view); err != nil { - return nil, platform.NewError(platform.WithErrorErr(err)) - } - - return view, nil -} - -func (c *Client) deleteDashboardCellView(ctx context.Context, tx *bolt.Tx, dashboardID, cellID platform.ID) error { - k, err := encodeDashboardCellViewID(dashboardID, cellID) - if err != nil { - return platform.NewError(platform.WithErrorErr(err)) - } - - if err := tx.Bucket(dashboardCellViewBucket).Delete(k); err != nil { - return platform.NewError(platform.WithErrorErr(err)) - } - - return nil -} - -func (c *Client) putDashboardCellView(ctx context.Context, tx *bolt.Tx, dashboardID, cellID platform.ID, view *platform.View) error { - k, err := encodeDashboardCellViewID(dashboardID, cellID) - if err != nil { - return platform.NewError(platform.WithErrorErr(err)) - } - - v, err := json.Marshal(view) - if err != nil { - return platform.NewError(platform.WithErrorErr(err)) - } - - if err := tx.Bucket(dashboardCellViewBucket).Put(k, v); err != nil { - return platform.NewError(platform.WithErrorErr(err)) - } - - return nil -} - -func encodeDashboardCellViewID(dashID, cellID platform.ID) ([]byte, error) { - did, err := dashID.Encode() - if err != nil { - return nil, err - } - - cid, err := cellID.Encode() - if err != nil { - return nil, err - } - - buf := bytes.NewBuffer(nil) - if _, err := buf.Write(did); err != nil { - return nil, err - } - - if _, err := buf.Write(cid); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -// UpdateDashboardCellView updates the view for a dashboard cell. -func (c *Client) UpdateDashboardCellView(ctx context.Context, dashboardID, cellID platform.ID, upd platform.ViewUpdate) (*platform.View, error) { - var v *platform.View - - err := c.db.Update(func(tx *bolt.Tx) error { - view, err := c.findDashboardCellView(ctx, tx, dashboardID, cellID) - if err != nil { - return err - } - - if err := upd.Apply(view); err != nil { - return err - } - - if err := c.putDashboardCellView(ctx, tx, dashboardID, cellID, view); err != nil { - return err - } - - v = view - return nil - }) - - if err != nil { - return nil, &platform.Error{ - Err: err, - Op: getOp(platform.OpUpdateDashboardCellView), - } - } - - return v, nil -} - -// UpdateDashboardCell udpates a cell on a dashboard. -func (c *Client) UpdateDashboardCell(ctx context.Context, dashboardID, cellID platform.ID, upd platform.CellUpdate) (*platform.Cell, error) { - op := getOp(platform.OpUpdateDashboardCell) - if err := upd.Valid(); err != nil { - return nil, &platform.Error{ - Err: err, - Op: op, - } - } - - var cell *platform.Cell - err := c.db.Update(func(tx *bolt.Tx) error { - d, err := c.findDashboardByID(ctx, tx, dashboardID) - if err != nil { - return err - } - - idx := -1 - for i, cell := range d.Cells { - if cell.ID == cellID { - idx = i - break - } - } - if idx == -1 { - return &platform.Error{ - Code: platform.ENotFound, - Op: op, - Msg: platform.ErrCellNotFound, - } - } - - if err := upd.Apply(d.Cells[idx]); err != nil { - return err - } - - cell = d.Cells[idx] - - if err := c.appendDashboardEventToLog(ctx, tx, d.ID, dashboardCellUpdatedEvent); err != nil { - return err - } - - return c.putDashboardWithMeta(ctx, tx, d) - }) - - if err != nil { - return nil, &platform.Error{ - Err: err, - Op: op, - } - } - - return cell, nil -} - -// PutDashboard will put a dashboard without setting an ID. -func (c *Client) PutDashboard(ctx context.Context, d *platform.Dashboard) error { - return c.db.Update(func(tx *bolt.Tx) error { - for _, cell := range d.Cells { - if err := c.createCellView(ctx, tx, d.ID, cell.ID, nil); err != nil { - return err - } - } - - if err := c.putOrganizationDashboardIndex(ctx, tx, d); err != nil { - return err - } - - return c.putDashboard(ctx, tx, d) - }) -} - -func encodeOrgDashboardIndex(orgID platform.ID, dashID platform.ID) ([]byte, error) { - oid, err := orgID.Encode() - if err != nil { - return nil, err - } - - did, err := dashID.Encode() - if err != nil { - return nil, err - } - - key := make([]byte, 0, len(oid)+len(did)) - key = append(key, oid...) - key = append(key, did...) - - return key, nil -} - -func (c *Client) putOrganizationDashboardIndex(ctx context.Context, tx *bolt.Tx, d *platform.Dashboard) error { - k, err := encodeOrgDashboardIndex(d.OrganizationID, d.ID) - if err != nil { - return err - } - if err := tx.Bucket(orgDashboardIndex).Put(k, nil); err != nil { - return err - } - - return nil -} - -func (c *Client) removeOrganizationDashboardIndex(ctx context.Context, tx *bolt.Tx, d *platform.Dashboard) error { - k, err := encodeOrgDashboardIndex(d.OrganizationID, d.ID) - if err != nil { - return err - } - if err := tx.Bucket(orgDashboardIndex).Delete(k); err != nil { - return err - } - - return nil -} - -func (c *Client) putDashboard(ctx context.Context, tx *bolt.Tx, d *platform.Dashboard) error { - v, err := json.Marshal(d) - if err != nil { - return err - } - encodedID, err := d.ID.Encode() - if err != nil { - return err - } - if err := tx.Bucket(dashboardBucket).Put(encodedID, v); err != nil { - return err - } - return nil -} - -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.Now() - return c.putDashboard(ctx, tx, d) -} - -// forEachDashboard will iterate through all dashboards while fn returns true. -func (c *Client) forEachDashboard(ctx context.Context, tx *bolt.Tx, descending bool, fn func(*platform.Dashboard) bool) error { - cur := tx.Bucket(dashboardBucket).Cursor() - - var k, v []byte - if descending { - k, v = cur.Last() - } else { - k, v = cur.First() - } - - for k != nil { - d := &platform.Dashboard{} - if err := json.Unmarshal(v, d); err != nil { - return err - } - - if !fn(d) { - break - } - - if descending { - k, v = cur.Prev() - } else { - k, v = cur.Next() - } - } - - return nil -} - -// UpdateDashboard updates a dashboard according the parameters set on upd. -func (c *Client) UpdateDashboard(ctx context.Context, id platform.ID, upd platform.DashboardUpdate) (*platform.Dashboard, error) { - if err := upd.Valid(); err != nil { - return nil, err - } - - var d *platform.Dashboard - err := c.db.Update(func(tx *bolt.Tx) error { - dash, err := c.updateDashboard(ctx, tx, id, upd) - if err != nil { - return err - } - d = dash - - return nil - }) - if err != nil { - return nil, &platform.Error{ - Err: err, - Op: getOp(platform.OpUpdateDashboard), - } - } - - return d, err -} - -func (c *Client) updateDashboard(ctx context.Context, tx *bolt.Tx, id platform.ID, upd platform.DashboardUpdate) (*platform.Dashboard, error) { - d, err := c.findDashboardByID(ctx, tx, id) - if err != nil { - return nil, err - } - - if err := upd.Apply(d); err != nil { - return nil, err - } - - if err := c.appendDashboardEventToLog(ctx, tx, d.ID, dashboardUpdatedEvent); err != nil { - return nil, err - } - - if err := c.putDashboardWithMeta(ctx, tx, d); err != nil { - return nil, err - } - - return d, nil -} - -// DeleteDashboard deletes a dashboard and prunes it from the index. -func (c *Client) DeleteDashboard(ctx context.Context, id platform.ID) error { - return c.db.Update(func(tx *bolt.Tx) error { - if pe := c.deleteDashboard(ctx, tx, id); pe != nil { - return &platform.Error{ - Err: pe, - Op: getOp(platform.OpDeleteDashboard), - } - } - return nil - }) -} - -func (c *Client) deleteDashboard(ctx context.Context, tx *bolt.Tx, id platform.ID) error { - d, pe := c.findDashboardByID(ctx, tx, id) - if pe != nil { - return pe - } - - for _, cell := range d.Cells { - if err := c.deleteDashboardCellView(ctx, tx, d.ID, cell.ID); err != nil { - return &platform.Error{ - Err: err, - } - } - } - - encodedID, err := id.Encode() - if err != nil { - return &platform.Error{ - Err: err, - } - } - - if err := c.removeOrganizationDashboardIndex(ctx, tx, d); err != nil { - return platform.NewError(platform.WithErrorErr(err)) - } - - if err := tx.Bucket(dashboardBucket).Delete(encodedID); err != nil { - return &platform.Error{ - Err: err, - } - } - - err = c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{ - ResourceID: id, - ResourceType: platform.DashboardsResourceType, - }) - if err != nil { - return &platform.Error{ - Err: err, - } - } - - if err := c.appendDashboardEventToLog(ctx, tx, d.ID, dashboardRemovedEvent); err != nil { - return &platform.Error{ - Err: err, - } - } - - return nil -} - -const dashboardOperationLogKeyPrefix = "dashboard" - -func encodeDashboardOperationLogKey(id platform.ID) ([]byte, error) { - buf, err := id.Encode() - if err != nil { - return nil, err - } - return append([]byte(dashboardOperationLogKeyPrefix), buf...), nil -} - -// GetDashboardOperationLog retrieves a dashboards operation log. -func (c *Client) GetDashboardOperationLog(ctx context.Context, id platform.ID, opts platform.FindOptions) ([]*platform.OperationLogEntry, int, error) { - // TODO(desa): might be worthwhile to allocate a slice of size opts.Limit - log := []*platform.OperationLogEntry{} - - err := c.db.View(func(tx *bolt.Tx) error { - key, err := encodeDashboardOperationLogKey(id) - if err != nil { - return err - } - - return c.forEachLogEntry(ctx, tx, key, opts, func(v []byte, t time.Time) error { - e := &platform.OperationLogEntry{} - if err := json.Unmarshal(v, e); err != nil { - return err - } - e.Time = t - - log = append(log, e) - - return nil - }) - }) - - if err != nil { - return nil, 0, err - } - - return log, len(log), nil -} - -func (c *Client) appendDashboardEventToLog(ctx context.Context, tx *bolt.Tx, id platform.ID, s string) error { - e := &platform.OperationLogEntry{ - Description: s, - } - // TODO(desa): this is fragile and non explicit since it requires an authorizer to be on context. It should be - // replaced with a higher level transaction so that adding to the log can take place in the http handler - // where the userID will exist explicitly. - a, err := platformcontext.GetAuthorizer(ctx) - if err == nil { - // Add the user to the log if you can, but don't error if its not there. - e.UserID = a.GetUserID() - } - - v, err := json.Marshal(e) - if err != nil { - return err - } - - k, err := encodeDashboardOperationLogKey(id) - if err != nil { - return err - } - - return c.addLogEntry(ctx, tx, k, v, c.Now()) -} diff --git a/bolt/dashboard_test.go b/bolt/dashboard_test.go deleted file mode 100644 index a9b43c61d78..00000000000 --- a/bolt/dashboard_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/bolt" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (platform.DashboardService, string, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - - if f.TimeGenerator == nil { - f.TimeGenerator = platform.RealTimeGenerator{} - } - - c.IDGenerator = f.IDGenerator - c.TimeGenerator = f.TimeGenerator - - ctx := context.TODO() - for _, b := range f.Dashboards { - if err := c.PutDashboard(ctx, b); err != nil { - t.Fatalf("failed to populate dashboards") - } - } - return c, bolt.OpPrefix, func() { - defer closeFn() - for _, b := range f.Dashboards { - if err := c.DeleteDashboard(ctx, b.ID); err != nil { - t.Logf("failed to remove dashboard: %v", err) - } - } - } -} - -func TestDashboardService(t *testing.T) { - platformtesting.DashboardService(initDashboardService, t) -} diff --git a/bolt/id.go b/bolt/id.go index 530d6172c01..00b4964ea38 100644 --- a/bolt/id.go +++ b/bolt/id.go @@ -19,7 +19,7 @@ var ( var _ platform.IDGenerator = (*Client)(nil) func (c *Client) initializeID(tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(idsBucket)); err != nil { + if _, err := tx.CreateBucketIfNotExists(idsBucket); err != nil { return err } diff --git a/bolt/id_test.go b/bolt/id_test.go index 1a8aed94586..5946c942459 100644 --- a/bolt/id_test.go +++ b/bolt/id_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + platform "github.com/influxdata/influxdb" "github.com/influxdata/influxdb/mock" ) @@ -14,6 +15,7 @@ func TestID(t *testing.T) { } defer closeFn() + testID := platform.ID(70000) c.IDGenerator = mock.NewIDGenerator(testID.String(), t) if err := c.Open(context.Background()); err != nil { diff --git a/bolt/keyvalue_log.go b/bolt/keyvalue_log.go deleted file mode 100644 index 7b06589658c..00000000000 --- a/bolt/keyvalue_log.go +++ /dev/null @@ -1,371 +0,0 @@ -package bolt - -import ( - "bytes" - "context" - "crypto/sha1" - "encoding/binary" - "encoding/json" - "fmt" - "time" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/kit/tracing" -) - -var ( - keyValueLogBucket = []byte("keyvaluelogv1") - keyValueLogIndex = []byte("keyvaluelogindexv1") -) - -var _ platform.KeyValueLog = (*Client)(nil) - -type keyValueLogBounds struct { - Start int64 `json:"start"` - Stop int64 `json:"stop"` -} - -func newKeyValueLogBounds(now time.Time) *keyValueLogBounds { - return &keyValueLogBounds{ - Start: now.UTC().UnixNano(), - Stop: now.UTC().UnixNano(), - } -} - -func (b *keyValueLogBounds) update(t time.Time) { - now := t.UTC().UnixNano() - if now < b.Start { - b.Start = now - } else if b.Stop < now { - b.Stop = now - } -} - -// StartTime retrieves the start value of a bounds as a time.Time -func (b *keyValueLogBounds) StartTime() time.Time { - return time.Unix(0, b.Start) -} - -// StopTime retrieves the stop value of a bounds as a time.Time -func (b *keyValueLogBounds) StopTime() time.Time { - return time.Unix(0, b.Stop) -} - -// Bounds returns the key boundaries for the keyvaluelog for a resourceType/resourceID pair. -func (b *keyValueLogBounds) Bounds(k []byte) ([]byte, []byte, error) { - start, err := encodeLogEntryKey(k, b.Start) - if err != nil { - return nil, nil, err - } - stop, err := encodeLogEntryKey(k, b.Stop) - if err != nil { - return nil, nil, err - } - return start, stop, nil -} - -func encodeLogEntryKey(key []byte, v int64) ([]byte, error) { - prefix := encodeKeyValueIndexKey(key) - k := make([]byte, len(prefix)+8) - - buf := bytes.NewBuffer(k) - _, err := buf.Write(prefix) - if err != nil { - return nil, err - } - - // This needs to be big-endian so that the iteration order is preserved when scanning keys - if err := binary.Write(buf, binary.BigEndian, v); err != nil { - return nil, err - } - return buf.Bytes(), err -} - -func decodeLogEntryKey(key []byte) ([]byte, time.Time, error) { - buf := bytes.NewReader(key[len(key)-8:]) - var ts int64 - // This needs to be big-endian so that the iteration order is preserved when scanning keys - err := binary.Read(buf, binary.BigEndian, &ts) - if err != nil { - return nil, time.Unix(0, 0), err - } - return key[:len(key)-8], time.Unix(0, ts), nil -} - -func encodeKeyValueIndexKey(k []byte) []byte { - // keys produced must be fixed length to ensure that we can iterate through the keyspace without any error. - h := sha1.New() - h.Write([]byte(k)) - return h.Sum(nil) -} - -func (c *Client) initializeKeyValueLog(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(keyValueLogBucket)); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists([]byte(keyValueLogIndex)); err != nil { - return err - } - return nil -} - -var errKeyValueLogBoundsNotFound = fmt.Errorf("oplog not found") - -func (c *Client) getKeyValueLogBounds(ctx context.Context, tx *bolt.Tx, key []byte) (*keyValueLogBounds, error) { - span, _ := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - k := encodeKeyValueIndexKey(key) - - v := tx.Bucket(keyValueLogIndex).Get(k) - - if len(v) == 0 { - return nil, errKeyValueLogBoundsNotFound - } - - bounds := &keyValueLogBounds{} - if err := json.Unmarshal(v, bounds); err != nil { - return nil, err - } - - return bounds, nil -} - -func (c *Client) putKeyValueLogBounds(ctx context.Context, tx *bolt.Tx, key []byte, bounds *keyValueLogBounds) error { - k := encodeKeyValueIndexKey(key) - - v, err := json.Marshal(bounds) - if err != nil { - return err - } - - if err := tx.Bucket(keyValueLogIndex).Put(k, v); err != nil { - return err - } - - return nil -} - -func (c *Client) updateKeyValueLogBounds(ctx context.Context, tx *bolt.Tx, k []byte, t time.Time) error { - span, ctx := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - // retrieve the keyValue log boundaries - bounds, err := c.getKeyValueLogBounds(ctx, tx, k) - if err != nil && err != errKeyValueLogBoundsNotFound { - return err - } - - if err == errKeyValueLogBoundsNotFound { - // if the bounds don't exist yet, create them - bounds = newKeyValueLogBounds(t) - } - - // update the bounds to if needed - bounds.update(t) - if err := c.putKeyValueLogBounds(ctx, tx, k, bounds); err != nil { - return err - } - - return nil -} - -// ForEachLogEntry retrieves the keyValue log for a resource type ID combination. KeyValues may be returned in ascending and descending order. -func (c *Client) ForEachLogEntry(ctx context.Context, k []byte, opts platform.FindOptions, fn func([]byte, time.Time) error) error { - return c.db.View(func(tx *bolt.Tx) error { - return c.forEachLogEntry(ctx, tx, k, opts, fn) - }) -} - -func (c *Client) forEachLogEntry(ctx context.Context, tx *bolt.Tx, k []byte, opts platform.FindOptions, fn func([]byte, time.Time) error) error { - b, err := c.getKeyValueLogBounds(ctx, tx, k) - if err != nil { - return err - } - - cur := tx.Bucket(keyValueLogBucket).Cursor() - - next := cur.Next - startKey, stopKey, err := b.Bounds(k) - if err != nil { - return err - } - - if opts.Descending { - next = cur.Prev - startKey, stopKey = stopKey, startKey - } - - k, v := cur.Seek(startKey) - if !bytes.Equal(k, startKey) { - return fmt.Errorf("the first key not the key found in the log bounds. This should be impossible. Please report this error") - } - - count := 0 - - if opts.Offset == 0 { - // Seek returns the kv at the position that was seeked to which should be the first element - // in the sequence of keyValues. If this condition is reached we need to start of iteration - // at 1 instead of 0. - _, ts, err := decodeLogEntryKey(k) - if err != nil { - return err - } - if err := fn(v, ts); err != nil { - return err - } - count++ - if bytes.Equal(startKey, stopKey) { - // If the start and stop are the same, then there is only a single entry in the log - return nil - } - } else { - // Skip offset many items - for i := 0; i < opts.Offset-1; i++ { - k, _ := next() - if bytes.Equal(k, stopKey) { - return nil - } - } - } - - for { - if count >= opts.Limit && opts.Limit != 0 { - break - } - - k, v := next() - - _, ts, err := decodeLogEntryKey(k) - if err != nil { - return err - } - - if err := fn(v, ts); err != nil { - return err - } - - if bytes.Equal(k, stopKey) { - // if we've reached the stop key, there are no keys log entries left - // in the keyspace. - break - } - - count++ - } - - return nil - -} - -// LogKeyValue logs an keyValue for a particular resource type ID pairing. -func (c *Client) AddLogEntry(ctx context.Context, k, v []byte, t time.Time) error { - return c.db.Update(func(tx *bolt.Tx) error { - return c.addLogEntry(ctx, tx, k, v, t) - }) -} - -func (c *Client) addLogEntry(ctx context.Context, tx *bolt.Tx, k, v []byte, t time.Time) error { - if err := c.updateKeyValueLogBounds(ctx, tx, k, t); err != nil { - return err - } - - if err := c.putLogEntry(ctx, tx, k, v, t); err != nil { - return err - } - - return nil -} - -func (c *Client) putLogEntry(ctx context.Context, tx *bolt.Tx, k, v []byte, t time.Time) error { - key, err := encodeLogEntryKey(k, t.UTC().UnixNano()) - if err != nil { - return err - } - - if err := tx.Bucket(keyValueLogBucket).Put(key, v); err != nil { - return err - } - - return nil -} - -func (c *Client) getLogEntry(ctx context.Context, tx *bolt.Tx, k []byte, t time.Time) ([]byte, time.Time, error) { - key, err := encodeLogEntryKey(k, t.UTC().UnixNano()) - if err != nil { - return nil, t, err - } - - v := tx.Bucket(keyValueLogBucket).Get(key) - - if len(v) == 0 { - return nil, t, fmt.Errorf("log entry not found") - } - - return v, t, nil -} - -// FirstLogEntry retrieves the first log entry for a key value log. -func (c *Client) FirstLogEntry(ctx context.Context, k []byte) ([]byte, time.Time, error) { - var v []byte - var t time.Time - - err := c.db.View(func(tx *bolt.Tx) error { - val, ts, err := c.firstLogEntry(ctx, tx, k) - if err != nil { - return err - } - - v, t = val, ts - - return nil - }) - - if err != nil { - return nil, t, err - } - - return v, t, nil -} - -// LastLogEntry retrieves the first log entry for a key value log. -func (c *Client) LastLogEntry(ctx context.Context, k []byte) ([]byte, time.Time, error) { - var v []byte - var t time.Time - - err := c.db.View(func(tx *bolt.Tx) error { - val, ts, err := c.lastLogEntry(ctx, tx, k) - if err != nil { - return err - } - - v, t = val, ts - - return nil - }) - - if err != nil { - return nil, t, err - } - - return v, t, nil -} - -func (c *Client) firstLogEntry(ctx context.Context, tx *bolt.Tx, k []byte) ([]byte, time.Time, error) { - bounds, err := c.getKeyValueLogBounds(ctx, tx, k) - if err != nil { - return nil, bounds.StartTime(), err - } - - return c.getLogEntry(ctx, tx, k, bounds.StartTime()) -} - -func (c *Client) lastLogEntry(ctx context.Context, tx *bolt.Tx, k []byte) ([]byte, time.Time, error) { - bounds, err := c.getKeyValueLogBounds(ctx, tx, k) - if err != nil { - return nil, bounds.StopTime(), err - } - - return c.getLogEntry(ctx, tx, k, bounds.StopTime()) -} diff --git a/bolt/keyvalue_log_test.go b/bolt/keyvalue_log_test.go deleted file mode 100644 index 763aa4363e9..00000000000 --- a/bolt/keyvalue_log_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initKeyValueLog(f platformtesting.KeyValueLogFields, t *testing.T) (platform.KeyValueLog, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - ctx := context.Background() - for _, e := range f.LogEntries { - if err := c.AddLogEntry(ctx, e.Key, e.Value, e.Time); err != nil { - t.Fatalf("failed to populate log entries") - } - } - return c, func() { - closeFn() - } -} - -// TestKeyValueLog runs the conformance test for a keyvalue log -func TestKeyValueLog(t *testing.T) { - platformtesting.KeyValueLog(initKeyValueLog, t) -} diff --git a/bolt/kv.go b/bolt/kv.go index 35b1218ab24..fbc4b519ade 100644 --- a/bolt/kv.go +++ b/bolt/kv.go @@ -14,7 +14,7 @@ import ( ) // check that *KVStore implement kv.Store interface. -var _ (kv.Store) = (*KVStore)(nil) +var _ kv.Store = (*KVStore)(nil) // KVStore is a kv.Store backed by boltdb. type KVStore struct { diff --git a/bolt/label.go b/bolt/label.go deleted file mode 100644 index 39c92a1746f..00000000000 --- a/bolt/label.go +++ /dev/null @@ -1,472 +0,0 @@ -package bolt - -import ( - "bytes" - "context" - "encoding/json" - - bolt "github.com/coreos/bbolt" - influxdb "github.com/influxdata/influxdb" -) - -var ( - labelBucket = []byte("labelsv1") - labelMappingBucket = []byte("labelmappingsv1") -) - -func (c *Client) initializeLabels(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(labelBucket)); err != nil { - return err - } - - if _, err := tx.CreateBucketIfNotExists([]byte(labelMappingBucket)); err != nil { - return err - } - - return nil -} - -// FindLabelByID finds a label by its ID -func (c *Client) FindLabelByID(ctx context.Context, id influxdb.ID) (*influxdb.Label, error) { - var l *influxdb.Label - - err := c.db.View(func(tx *bolt.Tx) error { - label, pe := c.findLabelByID(ctx, tx, id) - if pe != nil { - return pe - } - l = label - return nil - }) - - if err != nil { - return nil, &influxdb.Error{ - Op: getOp(influxdb.OpFindLabelByID), - Err: err, - } - } - - return l, nil -} - -func (c *Client) findLabelByID(ctx context.Context, tx *bolt.Tx, id influxdb.ID) (*influxdb.Label, *influxdb.Error) { - encodedID, err := id.Encode() - if err != nil { - return nil, &influxdb.Error{ - Err: err, - } - } - - var l influxdb.Label - v := tx.Bucket(labelBucket).Get(encodedID) - - if len(v) == 0 { - return nil, &influxdb.Error{ - Code: influxdb.ENotFound, - Msg: influxdb.ErrLabelNotFound, - } - } - - if err := json.Unmarshal(v, &l); err != nil { - return nil, &influxdb.Error{ - Err: err, - } - } - - return &l, nil -} - -func filterLabelsFn(filter influxdb.LabelFilter) func(l *influxdb.Label) bool { - return func(label *influxdb.Label) bool { - return (filter.Name == "" || (filter.Name == label.Name)) - } -} - -// FindLabels returns a list of labels that match a filter. -func (c *Client) FindLabels(ctx context.Context, filter influxdb.LabelFilter, opt ...influxdb.FindOptions) ([]*influxdb.Label, error) { - ls := []*influxdb.Label{} - err := c.db.View(func(tx *bolt.Tx) error { - labels, err := c.findLabels(ctx, tx, filter) - if err != nil { - return err - } - ls = labels - return nil - }) - - if err != nil { - return nil, err - } - - return ls, nil -} - -func (c *Client) findLabels(ctx context.Context, tx *bolt.Tx, filter influxdb.LabelFilter) ([]*influxdb.Label, error) { - ls := []*influxdb.Label{} - filterFn := filterLabelsFn(filter) - err := c.forEachLabel(ctx, tx, func(l *influxdb.Label) bool { - if filterFn(l) { - ls = append(ls, l) - } - return true - }) - - if err != nil { - return nil, err - } - - return ls, nil -} - -// func filterMappingsFn(filter influxdb.LabelMappingFilter) func(m *influxdb.LabelMapping) bool { -// return func(mapping *influxdb.LabelMapping) bool { -// return (filter.ResourceID.String() == mapping.ResourceID.String()) && -// (filter.LabelID == nil || filter.LabelID == mapping.LabelID) -// } -// } - -func decodeLabelMappingKey(key []byte) (resourceID influxdb.ID, labelID influxdb.ID, err error) { - if len(key) != 2*influxdb.IDLength { - return 0, 0, &influxdb.Error{Code: influxdb.EInvalid, Msg: "malformed label mapping key (please report this error)"} - } - - if err := (&resourceID).Decode(key[:influxdb.IDLength]); err != nil { - return 0, 0, &influxdb.Error{Code: influxdb.EInvalid, Msg: "bad resource id", Err: influxdb.ErrInvalidID} - } - - if err := (&labelID).Decode(key[influxdb.IDLength:]); err != nil { - return 0, 0, &influxdb.Error{Code: influxdb.EInvalid, Msg: "bad label id", Err: influxdb.ErrInvalidID} - } - - return resourceID, labelID, nil -} - -func (c *Client) FindResourceLabels(ctx context.Context, filter influxdb.LabelMappingFilter) ([]*influxdb.Label, error) { - if !filter.ResourceID.Valid() { - return nil, &influxdb.Error{Code: influxdb.EInvalid, Msg: "filter requires a valid resource id", Err: influxdb.ErrInvalidID} - } - - ls := []*influxdb.Label{} - err := c.db.View(func(tx *bolt.Tx) error { - cur := tx.Bucket(labelMappingBucket).Cursor() - prefix, err := filter.ResourceID.Encode() - if err != nil { - return err - } - - for k, _ := cur.Seek(prefix); bytes.HasPrefix(k, prefix); k, _ = cur.Next() { - _, id, err := decodeLabelMappingKey(k) - if err != nil { - return err - } - - l, err := c.findLabelByID(ctx, tx, id) - if l == nil && err != nil { - // TODO(jm): return error instead of continuing once orphaned mappings are fixed - // (see https://github.com/influxdata/influxdb/issues/11278) - continue - } - - ls = append(ls, l) - } - return nil - }) - - if err != nil { - return nil, &influxdb.Error{ - Err: err, - Op: getOp(influxdb.OpFindLabelMapping), - } - } - - return ls, nil -} - -// CreateLabelMapping creates a new mapping between a resource and a label. -func (c *Client) CreateLabelMapping(ctx context.Context, m *influxdb.LabelMapping) error { - _, err := c.FindLabelByID(ctx, m.LabelID) - if err != nil { - return &influxdb.Error{ - Err: err, - Op: getOp(influxdb.OpCreateLabel), - } - } - - err = c.db.Update(func(tx *bolt.Tx) error { - return c.putLabelMapping(ctx, tx, m) - }) - - if err != nil { - return &influxdb.Error{ - Err: err, - Op: getOp(influxdb.OpCreateLabel), - } - } - - return nil -} - -// DeleteLabelMapping deletes a label mapping. -func (c *Client) DeleteLabelMapping(ctx context.Context, m *influxdb.LabelMapping) error { - err := c.db.Update(func(tx *bolt.Tx) error { - return c.deleteLabelMapping(ctx, tx, m) - }) - if err != nil { - return &influxdb.Error{ - Op: getOp(influxdb.OpDeleteLabelMapping), - Err: err, - } - } - return nil -} - -func (c *Client) deleteLabelMapping(ctx context.Context, tx *bolt.Tx, m *influxdb.LabelMapping) error { - key, err := labelMappingKey(m) - if err != nil { - return &influxdb.Error{ - Err: err, - } - } - - if err := tx.Bucket(labelMappingBucket).Delete(key); err != nil { - return &influxdb.Error{ - Err: err, - } - } - - return nil -} - -// CreateLabel creates a new label. -func (c *Client) CreateLabel(ctx context.Context, l *influxdb.Label) error { - err := c.db.Update(func(tx *bolt.Tx) error { - l.ID = c.IDGenerator.ID() - - return c.putLabel(ctx, tx, l) - }) - - if err != nil { - return &influxdb.Error{ - Err: err, - Op: getOp(influxdb.OpCreateLabel), - } - } - return nil -} - -// PutLabel creates a label from the provided struct, without generating a new ID. -func (c *Client) PutLabel(ctx context.Context, l *influxdb.Label) error { - return c.db.Update(func(tx *bolt.Tx) error { - var err error - pe := c.putLabel(ctx, tx, l) - if pe != nil { - err = pe - } - return err - }) -} - -func labelMappingKey(m *influxdb.LabelMapping) ([]byte, error) { - lid, err := m.LabelID.Encode() - if err != nil { - return nil, &influxdb.Error{ - Code: influxdb.EInvalid, - Err: err, - } - } - - rid, err := m.ResourceID.Encode() - if err != nil { - return nil, &influxdb.Error{ - Code: influxdb.EInvalid, - Err: err, - } - } - - key := make([]byte, len(rid)+len(lid)) - copy(key, rid) - copy(key[len(rid):], lid) - - return key, nil -} - -func (c *Client) forEachLabel(ctx context.Context, tx *bolt.Tx, fn func(*influxdb.Label) bool) error { - cur := tx.Bucket(labelBucket).Cursor() - for k, v := cur.First(); k != nil; k, v = cur.Next() { - l := &influxdb.Label{} - if err := json.Unmarshal(v, l); err != nil { - return err - } - if !fn(l) { - break - } - } - - return nil -} - -// UpdateLabel updates a label. -func (c *Client) UpdateLabel(ctx context.Context, id influxdb.ID, upd influxdb.LabelUpdate) (*influxdb.Label, error) { - var label *influxdb.Label - err := c.db.Update(func(tx *bolt.Tx) error { - labelResponse, pe := c.updateLabel(ctx, tx, id, upd) - if pe != nil { - return &influxdb.Error{ - Err: pe, - Op: getOp(influxdb.OpUpdateLabel), - } - } - label = labelResponse - return nil - }) - - return label, err -} - -func (c *Client) updateLabel(ctx context.Context, tx *bolt.Tx, id influxdb.ID, upd influxdb.LabelUpdate) (*influxdb.Label, error) { - label, err := c.findLabelByID(ctx, tx, id) - if err != nil { - return nil, err - } - - if label.Properties == nil { - label.Properties = make(map[string]string) - } - - for k, v := range upd.Properties { - if v == "" { - delete(label.Properties, k) - } else { - label.Properties[k] = v - } - } - - if upd.Name != "" { - label.Name = upd.Name - } - - if err := label.Validate(); err != nil { - return nil, &influxdb.Error{ - Code: influxdb.EInvalid, - Err: err, - } - } - - if err := c.putLabel(ctx, tx, label); err != nil { - return nil, &influxdb.Error{ - Err: err, - } - } - - return label, nil -} - -// set a label and overwrite any existing label -func (c *Client) putLabel(ctx context.Context, tx *bolt.Tx, l *influxdb.Label) error { - v, err := json.Marshal(l) - if err != nil { - return &influxdb.Error{ - Err: err, - } - } - - encodedID, err := l.ID.Encode() - if err != nil { - return &influxdb.Error{ - Err: err, - } - } - - if err := tx.Bucket(labelBucket).Put(encodedID, v); err != nil { - return &influxdb.Error{ - Err: err, - } - } - - return nil -} - -// PutLabelMapping writes a label mapping to boltdb -func (c *Client) PutLabelMapping(ctx context.Context, m *influxdb.LabelMapping) error { - return c.db.Update(func(tx *bolt.Tx) error { - var err error - pe := c.putLabelMapping(ctx, tx, m) - if pe != nil { - err = pe - } - return err - }) -} - -func (c *Client) putLabelMapping(ctx context.Context, tx *bolt.Tx, m *influxdb.LabelMapping) error { - v, err := json.Marshal(m) - if err != nil { - return &influxdb.Error{ - Err: err, - } - } - - key, err := labelMappingKey(m) - if err != nil { - return &influxdb.Error{ - Err: err, - } - } - - if err := tx.Bucket(labelMappingBucket).Put(key, v); err != nil { - return &influxdb.Error{ - Err: err, - } - } - - return nil -} - -// DeleteLabel deletes a label. -func (c *Client) DeleteLabel(ctx context.Context, id influxdb.ID) error { - err := c.db.Update(func(tx *bolt.Tx) error { - return c.deleteLabel(ctx, tx, id) - }) - if err != nil { - return &influxdb.Error{ - Op: getOp(influxdb.OpDeleteLabel), - Err: err, - } - } - return nil -} - -func (c *Client) deleteLabel(ctx context.Context, tx *bolt.Tx, id influxdb.ID) error { - _, err := c.findLabelByID(ctx, tx, id) - if err != nil { - return err - } - encodedID, idErr := id.Encode() - if idErr != nil { - return &influxdb.Error{ - Err: idErr, - } - } - - return tx.Bucket(labelBucket).Delete(encodedID) -} - -// func (c *Client) deleteLabels(ctx context.Context, tx *bolt.Tx, filter influxdb.LabelFilter) error { -// ls, err := c.findLabels(ctx, tx, filter) -// if err != nil { -// return err -// } -// for _, l := range ls { -// encodedID, idErr := l.ID.Encode() -// if idErr != nil { -// return &influxdb.Error{ -// Err: idErr, -// } -// } -// -// if err = tx.Bucket(labelBucket).Delete(encodedID); err != nil { -// return err -// } -// } -// return nil -// } diff --git a/bolt/lookup_service.go b/bolt/lookup_service.go deleted file mode 100644 index d7b452c19c4..00000000000 --- a/bolt/lookup_service.go +++ /dev/null @@ -1,63 +0,0 @@ -package bolt - -import ( - "context" - - platform "github.com/influxdata/influxdb" -) - -var _ platform.LookupService = (*Client)(nil) - -// Name returns the name for the resource and ID. -func (c *Client) Name(ctx context.Context, resource platform.ResourceType, id platform.ID) (string, error) { - if err := resource.Valid(); err != nil { - return "", err - } - - if ok := id.Valid(); !ok { - return "", platform.ErrInvalidID - } - - switch resource { - case platform.TasksResourceType: // 5 // TODO(goller): unify task bolt storage here so we can lookup names - case platform.AuthorizationsResourceType: // 0 TODO(goller): authorizations should also have optional names - case platform.BucketsResourceType: // 1 - r, err := c.FindBucketByID(ctx, id) - if err != nil { - return "", err - } - return r.Name, nil - case platform.DashboardsResourceType: // 2 - r, err := c.FindDashboardByID(ctx, id) - if err != nil { - return "", err - } - return r.Name, nil - case platform.OrgsResourceType: // 3 - r, err := c.FindOrganizationByID(ctx, id) - if err != nil { - return "", err - } - return r.Name, nil - case platform.SourcesResourceType: // 4 - r, err := c.FindSourceByID(ctx, id) - if err != nil { - return "", err - } - return r.Name, nil - case platform.TelegrafsResourceType: // 6 - r, err := c.FindTelegrafConfigByID(ctx, id) - if err != nil { - return "", err - } - return r.Name, nil - case platform.UsersResourceType: // 7 - r, err := c.FindUserByID(ctx, id) - if err != nil { - return "", err - } - return r.Name, nil - } - - return "", nil -} diff --git a/bolt/lookup_service_test.go b/bolt/lookup_service_test.go deleted file mode 100644 index 63e2762d622..00000000000 --- a/bolt/lookup_service_test.go +++ /dev/null @@ -1,264 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/bolt" - "github.com/influxdata/influxdb/mock" - platformtesting "github.com/influxdata/influxdb/testing" -) - -var ( - testID = platform.ID(70000) - existingBucketID = platform.ID(mock.FirstMockID + 1) - firstMockID = platform.ID(mock.FirstMockID) -) - -func TestClient_Name(t *testing.T) { - type initFn func(ctx context.Context, c *bolt.Client) error - type args struct { - resource platform.Resource - init initFn - } - tests := []struct { - name string - args args - want string - wantErr bool - }{ - { - name: "error if id is invalid", - args: args{ - resource: platform.Resource{ - Type: platform.DashboardsResourceType, - ID: platformtesting.IDPtr(platform.InvalidID()), - }, - }, - wantErr: true, - }, - { - name: "error if resource is invalid", - args: args{ - resource: platform.Resource{ - Type: platform.ResourceType("invalid"), - }, - }, - wantErr: true, - }, - { - name: "authorization resource without a name returns empty string", - args: args{ - resource: platform.Resource{ - Type: platform.AuthorizationsResourceType, - ID: platformtesting.IDPtr(testID), - }, - }, - want: "", - }, - { - name: "task resource without a name returns empty string", - args: args{ - resource: platform.Resource{ - Type: platform.TasksResourceType, - ID: platformtesting.IDPtr(testID), - }, - }, - want: "", - }, - { - name: "bucket with existing id returns name", - args: args{ - resource: platform.Resource{ - Type: platform.BucketsResourceType, - ID: &existingBucketID, - }, - init: func(ctx context.Context, s *bolt.Client) error { - o := platform.Organization{ - Name: "o1", - } - - err := s.CreateOrganization(ctx, &o) - if err != nil { - return err - } - - return s.CreateBucket(ctx, &platform.Bucket{ - Name: "b1", - OrgID: platform.ID(mock.FirstMockID), - }) - }, - }, - want: "b1", - }, - { - name: "bucket with non-existent id returns error", - args: args{ - resource: platform.Resource{ - Type: platform.BucketsResourceType, - ID: platformtesting.IDPtr(testID), - }, - }, - wantErr: true, - }, - { - name: "dashboard with existing id returns name", - args: args{ - resource: platform.Resource{ - Type: platform.DashboardsResourceType, - ID: &firstMockID, - }, - init: func(ctx context.Context, s *bolt.Client) error { - return s.CreateDashboard(ctx, &platform.Dashboard{ - Name: "dashboard1", - OrganizationID: 1, - }) - }, - }, - want: "dashboard1", - }, - { - name: "dashboard with non-existent id returns error", - args: args{ - resource: platform.Resource{ - Type: platform.DashboardsResourceType, - ID: platformtesting.IDPtr(testID), - }, - }, - wantErr: true, - }, - { - name: "org with existing id returns name", - args: args{ - resource: platform.Resource{ - Type: platform.OrgsResourceType, - ID: &firstMockID, - }, - init: func(ctx context.Context, s *bolt.Client) error { - return s.CreateOrganization(ctx, &platform.Organization{ - Name: "org1", - }) - }, - }, - want: "org1", - }, - { - name: "org with non-existent id returns error", - args: args{ - resource: platform.Resource{ - Type: platform.OrgsResourceType, - ID: platformtesting.IDPtr(testID), - }, - }, - wantErr: true, - }, - { - name: "source with existing id returns name", - args: args{ - resource: platform.Resource{ - Type: platform.SourcesResourceType, - ID: &firstMockID, - }, - init: func(ctx context.Context, s *bolt.Client) error { - return s.CreateSource(ctx, &platform.Source{ - Name: "source1", - }) - }, - }, - want: "source1", - }, - { - name: "source with non-existent id returns error", - args: args{ - resource: platform.Resource{ - Type: platform.SourcesResourceType, - ID: platformtesting.IDPtr(testID), - }, - }, - wantErr: true, - }, - { - name: "telegraf with existing id returns name", - args: args{ - resource: platform.Resource{ - Type: platform.TelegrafsResourceType, - ID: &firstMockID, - }, - init: func(ctx context.Context, s *bolt.Client) error { - return s.CreateTelegrafConfig(ctx, &platform.TelegrafConfig{ - OrgID: platformtesting.MustIDBase16("0000000000000009"), - Name: "telegraf1", - Config: "[agent]", - }, testID) - }, - }, - want: "telegraf1", - }, - { - name: "telegraf with non-existent id returns error", - args: args{ - resource: platform.Resource{ - Type: platform.TelegrafsResourceType, - ID: platformtesting.IDPtr(testID), - }, - }, - wantErr: true, - }, - { - name: "user with existing id returns name", - args: args{ - resource: platform.Resource{ - Type: platform.UsersResourceType, - ID: &firstMockID, - }, - init: func(ctx context.Context, s *bolt.Client) error { - return s.CreateUser(ctx, &platform.User{ - Name: "user1", - }) - }, - }, - want: "user1", - }, - { - name: "user with non-existent id returns error", - args: args{ - resource: platform.Resource{ - Type: platform.UsersResourceType, - ID: platformtesting.IDPtr(testID), - }, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c, done, err := NewTestClient(t) - if err != nil { - t.Fatalf("unable to create bolt test client: %v", err) - } - defer done() - mockIDGen := mock.NewMockIDGenerator() - c.IDGenerator = mockIDGen - ctx := context.Background() - if tt.args.init != nil { - if err := tt.args.init(ctx, c); err != nil { - t.Errorf("Service.Name() unable to initialize service: %v", err) - } - } - id := platform.InvalidID() - if tt.args.resource.ID != nil { - id = *tt.args.resource.ID - } - - got, err := c.Name(ctx, tt.args.resource.Type, id) - if (err != nil) != tt.wantErr { - t.Errorf("Service.Name() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("Service.Name() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/bolt/metrics.go b/bolt/metrics.go index ffbffd8cc4b..968c96ea49e 100644 --- a/bolt/metrics.go +++ b/bolt/metrics.go @@ -7,6 +7,18 @@ import ( var _ prometheus.Collector = (*Client)(nil) +// available buckets +// TODO: nuke this whole thing? +var ( + authorizationBucket = []byte("authorizationsv1") + bucketBucket = []byte("bucketsv1") + dashboardBucket = []byte("dashboardsv2") + organizationBucket = []byte("organizationsv1") + scraperBucket = []byte("scraperv2") + telegrafBucket = []byte("telegrafv1") + userBucket = []byte("usersv1") +) + var ( orgsDesc = prometheus.NewDesc( "influxdb_organizations_total", @@ -88,13 +100,13 @@ func (c *Client) Collect(ch chan<- prometheus.Metric) { orgs, buckets, users, tokens := 0, 0, 0, 0 dashboards, scrapers, telegrafs := 0, 0, 0 _ = c.db.View(func(tx *bolt.Tx) error { - orgs = tx.Bucket(organizationBucket).Stats().KeyN buckets = tx.Bucket(bucketBucket).Stats().KeyN - users = tx.Bucket(userBucket).Stats().KeyN - tokens = tx.Bucket(authorizationBucket).Stats().KeyN dashboards = tx.Bucket(dashboardBucket).Stats().KeyN + orgs = tx.Bucket(organizationBucket).Stats().KeyN scrapers = tx.Bucket(scraperBucket).Stats().KeyN telegrafs = tx.Bucket(telegrafBucket).Stats().KeyN + tokens = tx.Bucket(authorizationBucket).Stats().KeyN + users = tx.Bucket(userBucket).Stats().KeyN return nil }) diff --git a/bolt/metrics_test.go b/bolt/metrics_test.go index 4a7885297d2..e0fe6fe2be4 100644 --- a/bolt/metrics_test.go +++ b/bolt/metrics_test.go @@ -1,10 +1,8 @@ package bolt_test import ( - "context" "testing" - platform "github.com/influxdata/influxdb" "github.com/influxdata/influxdb/kit/prom" "github.com/influxdata/influxdb/kit/prom/promtest" "go.uber.org/zap/zaptest" @@ -40,50 +38,3 @@ func TestInitialMetrics(t *testing.T) { } } } - -func TestMetrics_Onboarding(t *testing.T) { - client, teardown, err := NewTestClient(t) - if err != nil { - t.Fatalf("unable to setup bolt client: %v", err) - } - defer teardown() - - reg := prom.NewRegistry(zaptest.NewLogger(t)) - reg.MustRegister(client) - - ctx := context.Background() - if _, _ = client.Generate(ctx, - &platform.OnboardingRequest{ - User: "u1", - Password: "password1", - Org: "o1", - Bucket: "b1", - }); err != nil { - t.Fatalf("unable to setup onboarding: %v", err) - } - - _ = client.CreateDashboard(ctx, &platform.Dashboard{ - OrganizationID: platform.ID(1), - }) - - mfs, err := reg.Gather() - if err != nil { - t.Fatal(err) - } - - metrics := map[string]int{ - "influxdb_organizations_total": 1, - "influxdb_buckets_total": 1, - "influxdb_users_total": 1, - "influxdb_tokens_total": 1, - "influxdb_dashboards_total": 1, - "boltdb_reads_total": 1, - } - - for name, count := range metrics { - c := promtest.MustFindMetric(t, mfs, name, nil) - if got := c.GetCounter().GetValue(); int(got) != count { - t.Errorf("expected %s counter to be %d, got %v", name, count, got) - } - } -} diff --git a/bolt/onboarding.go b/bolt/onboarding.go deleted file mode 100644 index 2c6db4cdb62..00000000000 --- a/bolt/onboarding.go +++ /dev/null @@ -1,142 +0,0 @@ -package bolt - -import ( - "context" - "fmt" - "time" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" -) - -var onboardingBucket = []byte("onboardingv1") -var onboardingKey = []byte("onboarding_key") - -var _ platform.OnboardingService = (*Client)(nil) - -func (c *Client) initializeOnboarding(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(onboardingBucket)); err != nil { - return err - } - return nil -} - -// IsOnboarding checks onboardingBucket -// to see if the onboarding key is true. -func (c *Client) IsOnboarding(ctx context.Context) (isOnboarding bool, err error) { - err = c.db.View(func(tx *bolt.Tx) error { - result := tx.Bucket(onboardingBucket).Get(onboardingKey) - isOnboarding = len(result) == 0 - return nil - }) - return isOnboarding, err -} - -// PutOnboardingStatus will update the flag, -// so future onboarding request will be denied. -func (c *Client) PutOnboardingStatus(ctx context.Context, v bool) error { - if v { - return c.db.Update(func(tx *bolt.Tx) error { - return tx.Bucket(onboardingBucket).Put(onboardingKey, []byte{0x1}) - }) - } - return nil -} - -// Generate OnboardingResults from onboarding request, -// update db so this request will be disabled for the second run. -func (c *Client) Generate(ctx context.Context, req *platform.OnboardingRequest) (*platform.OnboardingResults, error) { - isOnboarding, err := c.IsOnboarding(ctx) - if err != nil { - return nil, err - } - if !isOnboarding { - return nil, &platform.Error{ - Code: platform.EConflict, - Msg: "onboarding has already been completed", - } - } - - if req.Password == "" { - return nil, &platform.Error{ - Code: platform.EEmptyValue, - Msg: "password is empty", - } - } - - if req.User == "" { - return nil, &platform.Error{ - Code: platform.EEmptyValue, - Msg: "username is empty", - } - } - - if req.Org == "" { - return nil, &platform.Error{ - Code: platform.EEmptyValue, - Msg: "org name is empty", - } - } - - if req.Bucket == "" { - return nil, &platform.Error{ - Code: platform.EEmptyValue, - Msg: "bucket name is empty", - } - } - - u := &platform.User{Name: req.User} - if err := c.CreateUser(ctx, u); err != nil { - return nil, err - } - - if err = c.SetPassword(ctx, u.ID, req.Password); err != nil { - return nil, err - } - - o := &platform.Organization{ - Name: req.Org, - } - if err = c.CreateOrganization(ctx, o); err != nil { - return nil, err - } - bucket := &platform.Bucket{ - Name: req.Bucket, - OrgID: o.ID, - RetentionPeriod: time.Duration(req.RetentionPeriod) * time.Hour, - } - if err = c.CreateBucket(ctx, bucket); err != nil { - return nil, err - } - - if err := c.CreateUserResourceMapping(ctx, &platform.UserResourceMapping{ - ResourceType: platform.OrgsResourceType, - ResourceID: o.ID, - UserID: u.ID, - UserType: platform.Owner, - }); err != nil { - return nil, err - } - - auth := &platform.Authorization{ - UserID: u.ID, - Description: fmt.Sprintf("%s's Token", u.Name), - OrgID: o.ID, - Permissions: platform.OperPermissions(), - Token: req.Token, - } - if err = c.CreateAuthorization(ctx, auth); err != nil { - return nil, err - } - - if err = c.PutOnboardingStatus(ctx, true); err != nil { - return nil, err - } - - return &platform.OnboardingResults{ - User: u, - Org: o, - Bucket: bucket, - Auth: auth, - }, nil -} diff --git a/bolt/onboarding_test.go b/bolt/onboarding_test.go deleted file mode 100644 index b0eaf116b81..00000000000 --- a/bolt/onboarding_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initOnboardingService(f platformtesting.OnboardingFields, t *testing.T) (platform.OnboardingService, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - c.IDGenerator = f.IDGenerator - c.TokenGenerator = f.TokenGenerator - c.TimeGenerator = f.TimeGenerator - if c.TimeGenerator == nil { - c.TimeGenerator = platform.RealTimeGenerator{} - } - ctx := context.TODO() - if err = c.PutOnboardingStatus(ctx, !f.IsOnboarding); err != nil { - t.Fatalf("failed to set new onboarding finished: %v", err) - } - - return c, func() { - defer closeFn() - if err := c.PutOnboardingStatus(ctx, false); err != nil { - t.Logf("failed to remove onboarding finished: %v", err) - } - } -} - -func TestOnboardingService_Generate(t *testing.T) { - t.Skip("This service is not used, we use the kv bolt implementation") - platformtesting.Generate(initOnboardingService, t) -} diff --git a/bolt/organization.go b/bolt/organization.go deleted file mode 100644 index a66898b301c..00000000000 --- a/bolt/organization.go +++ /dev/null @@ -1,570 +0,0 @@ -package bolt - -import ( - "context" - "encoding/json" - "fmt" - "strings" - "time" - - bolt "github.com/coreos/bbolt" - "github.com/influxdata/influxdb" - influxdbcontext "github.com/influxdata/influxdb/context" - "github.com/influxdata/influxdb/kit/tracing" -) - -var ( - organizationBucket = []byte("organizationsv1") - organizationIndex = []byte("organizationindexv1") -) - -var _ influxdb.OrganizationService = (*Client)(nil) -var _ influxdb.OrganizationOperationLogService = (*Client)(nil) - -func (c *Client) initializeOrganizations(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(organizationBucket)); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists([]byte(organizationIndex)); err != nil { - return err - } - return nil -} - -// FindOrganizationByID retrieves a organization by id. -func (c *Client) FindOrganizationByID(ctx context.Context, id influxdb.ID) (*influxdb.Organization, error) { - var o *influxdb.Organization - err := c.db.View(func(tx *bolt.Tx) error { - org, pe := c.findOrganizationByID(ctx, tx, id) - if pe != nil { - return &influxdb.Error{ - Op: getOp(influxdb.OpFindOrganizationByID), - Err: pe, - } - } - o = org - return nil - }) - - if err != nil { - return nil, err - } - - return o, nil -} - -func (c *Client) findOrganizationByID(ctx context.Context, tx *bolt.Tx, id influxdb.ID) (*influxdb.Organization, *influxdb.Error) { - span, _ := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - encodedID, err := id.Encode() - if err != nil { - return nil, &influxdb.Error{ - Code: influxdb.EInvalid, - Err: err, - } - } - - v := tx.Bucket(organizationBucket).Get(encodedID) - if len(v) == 0 { - return nil, &influxdb.Error{ - Code: influxdb.ENotFound, - Msg: "organization not found", - } - } - - var o influxdb.Organization - if err := json.Unmarshal(v, &o); err != nil { - return nil, &influxdb.Error{ - Err: err, - } - } - - return &o, nil -} - -// FindOrganizationByName returns a organization by name for a particular organization. -func (c *Client) FindOrganizationByName(ctx context.Context, n string) (*influxdb.Organization, error) { - var o *influxdb.Organization - - err := c.db.View(func(tx *bolt.Tx) error { - org, pe := c.findOrganizationByName(ctx, tx, n) - if pe != nil { - return pe - } - o = org - return nil - }) - - return o, err -} - -func (c *Client) findOrganizationByName(ctx context.Context, tx *bolt.Tx, n string) (*influxdb.Organization, *influxdb.Error) { - span, ctx := tracing.StartSpanFromContext(ctx) - defer span.Finish() - - o := tx.Bucket(organizationIndex).Get(organizationIndexKey(n)) - if o == nil { - return nil, &influxdb.Error{ - Code: influxdb.ENotFound, - Msg: fmt.Sprintf("organization name \"%s\" not found", n), - } - } - - var id influxdb.ID - if err := id.Decode(o); err != nil { - return nil, &influxdb.Error{ - Code: influxdb.EInvalid, - Err: err, - } - } - return c.findOrganizationByID(ctx, tx, id) -} - -// FindOrganization retrives a organization using an arbitrary organization filter. -// Filters using ID, or Name should be efficient. -// Other filters will do a linear scan across organizations until it finds a match. -func (c *Client) FindOrganization(ctx context.Context, filter influxdb.OrganizationFilter) (*influxdb.Organization, error) { - op := getOp(influxdb.OpFindOrganization) - if filter.ID != nil { - o, err := c.FindOrganizationByID(ctx, *filter.ID) - if err != nil { - return nil, &influxdb.Error{ - Err: err, - Op: op, - } - } - return o, nil - } - - if filter.Name != nil { - o, err := c.FindOrganizationByName(ctx, *filter.Name) - if err != nil { - return nil, &influxdb.Error{ - Err: err, - Op: op, - } - } - return o, nil - } - - // If name and ID are not set, then, this is an invalid usage of the API. - return nil, influxdb.ErrInvalidOrgFilter -} - -func filterOrganizationsFn(filter influxdb.OrganizationFilter) func(o *influxdb.Organization) bool { - if filter.ID != nil { - return func(o *influxdb.Organization) bool { - return o.ID == *filter.ID - } - } - - if filter.Name != nil { - return func(o *influxdb.Organization) bool { - return o.Name == *filter.Name - } - } - - return func(o *influxdb.Organization) bool { return true } -} - -// FindOrganizations retrives all organizations that match an arbitrary organization filter. -// Filters using ID, or Name should be efficient. -// Other filters will do a linear scan across all organizations searching for a match. -func (c *Client) FindOrganizations(ctx context.Context, filter influxdb.OrganizationFilter, opt ...influxdb.FindOptions) ([]*influxdb.Organization, int, error) { - op := getOp(influxdb.OpFindOrganizations) - if filter.ID != nil { - o, err := c.FindOrganizationByID(ctx, *filter.ID) - if err != nil { - return nil, 0, &influxdb.Error{ - Err: err, - Op: op, - } - } - - return []*influxdb.Organization{o}, 1, nil - } - - if filter.Name != nil { - o, err := c.FindOrganizationByName(ctx, *filter.Name) - if err != nil { - return nil, 0, &influxdb.Error{ - Err: err, - Op: op, - } - } - - return []*influxdb.Organization{o}, 1, nil - } - - os := []*influxdb.Organization{} - filterFn := filterOrganizationsFn(filter) - err := c.db.View(func(tx *bolt.Tx) error { - return forEachOrganization(ctx, tx, func(o *influxdb.Organization) bool { - if filterFn(o) { - os = append(os, o) - } - return true - }) - }) - - if err != nil { - return nil, 0, &influxdb.Error{ - Err: err, - Op: op, - } - } - - return os, len(os), nil -} - -// CreateOrganization creates a influxdb organization and sets b.ID. -func (c *Client) CreateOrganization(ctx context.Context, o *influxdb.Organization) error { - op := getOp(influxdb.OpCreateOrganization) - return c.db.Update(func(tx *bolt.Tx) error { - if err := c.validOrganizationName(ctx, tx, o); err != nil { - return &influxdb.Error{ - Op: op, - Err: err, - } - } - - o.ID = c.IDGenerator.ID() - o.CreatedAt = c.Now() - o.UpdatedAt = c.Now() - if err := c.appendOrganizationEventToLog(ctx, tx, o.ID, organizationCreatedEvent); err != nil { - return &influxdb.Error{ - Err: err, - Op: op, - } - } - - if err := c.putOrganization(ctx, tx, o); err != nil { - return &influxdb.Error{ - Err: err, - Op: op, - } - } - return nil - }) -} - -// PutOrganization will put a organization without setting an ID. -func (c *Client) PutOrganization(ctx context.Context, o *influxdb.Organization) error { - var err error - return c.db.Update(func(tx *bolt.Tx) error { - if pe := c.putOrganization(ctx, tx, o); pe != nil { - err = pe - } - return err - }) -} - -func (c *Client) putOrganization(ctx context.Context, tx *bolt.Tx, o *influxdb.Organization) *influxdb.Error { - v, err := json.Marshal(o) - if err != nil { - return &influxdb.Error{ - Err: err, - } - } - encodedID, err := o.ID.Encode() - if err != nil { - return &influxdb.Error{ - Code: influxdb.EInvalid, - Err: err, - } - } - if err := tx.Bucket(organizationIndex).Put(organizationIndexKey(o.Name), encodedID); err != nil { - return &influxdb.Error{ - Err: err, - } - } - if err = tx.Bucket(organizationBucket).Put(encodedID, v); err != nil { - return &influxdb.Error{ - Err: err, - } - } - return nil -} - -func organizationIndexKey(n string) []byte { - return []byte(n) -} - -// forEachOrganization will iterate through all organizations while fn returns true. -func forEachOrganization(ctx context.Context, tx *bolt.Tx, fn func(*influxdb.Organization) bool) error { - cur := tx.Bucket(organizationBucket).Cursor() - for k, v := cur.First(); k != nil; k, v = cur.Next() { - o := &influxdb.Organization{} - if err := json.Unmarshal(v, o); err != nil { - return err - } - if !fn(o) { - break - } - } - - return nil -} - -func (c *Client) validOrganizationName(ctx context.Context, tx *bolt.Tx, o *influxdb.Organization) error { - if o.Name = strings.TrimSpace(o.Name); o.Name == "" { - return influxdb.ErrOrgNameisEmpty - } - v := tx.Bucket(organizationIndex).Get(organizationIndexKey(o.Name)) - if len(v) != 0 { - return &influxdb.Error{ - Code: influxdb.EConflict, - Msg: fmt.Sprintf("organization with name %s already exists", o.Name), - } - } - return nil -} - -// UpdateOrganization updates a organization according the parameters set on upd. -func (c *Client) UpdateOrganization(ctx context.Context, id influxdb.ID, upd influxdb.OrganizationUpdate) (*influxdb.Organization, error) { - var o *influxdb.Organization - err := c.db.Update(func(tx *bolt.Tx) error { - org, pe := c.updateOrganization(ctx, tx, id, upd) - if pe != nil { - return &influxdb.Error{ - Err: pe, - Op: getOp(influxdb.OpUpdateOrganization), - } - } - o = org - return nil - }) - - return o, err -} - -func (c *Client) updateOrganization(ctx context.Context, tx *bolt.Tx, id influxdb.ID, upd influxdb.OrganizationUpdate) (*influxdb.Organization, *influxdb.Error) { - o, pe := c.findOrganizationByID(ctx, tx, id) - if pe != nil { - return nil, pe - } - - if upd.Name != nil { - // Organizations are indexed by name and so the organization index must be pruned - // when name is modified. - if err := tx.Bucket(organizationIndex).Delete(organizationIndexKey(o.Name)); err != nil { - return nil, &influxdb.Error{ - Err: err, - } - } - o.Name = *upd.Name - if err := c.validOrganizationName(ctx, tx, o); err != nil { - return nil, &influxdb.Error{ - Err: err, - } - } - } - - if upd.Description != nil { - o.Description = *upd.Description - } - - o.UpdatedAt = c.Now() - - if err := c.appendOrganizationEventToLog(ctx, tx, o.ID, organizationUpdatedEvent); err != nil { - return nil, &influxdb.Error{ - Err: err, - } - } - - if pe := c.putOrganization(ctx, tx, o); pe != nil { - return nil, pe - } - - return o, nil -} - -// DeleteOrganization deletes a organization and prunes it from the index. -func (c *Client) DeleteOrganization(ctx context.Context, id influxdb.ID) error { - err := c.db.Update(func(tx *bolt.Tx) error { - if pe := c.deleteOrganizationsBuckets(ctx, tx, id); pe != nil { - return pe - } - if pe := c.deleteOrganization(ctx, tx, id); pe != nil { - return pe - } - return nil - }) - if err != nil { - return &influxdb.Error{ - Op: getOp(influxdb.OpDeleteOrganization), - Err: err, - } - } - return nil -} - -func (c *Client) deleteOrganization(ctx context.Context, tx *bolt.Tx, id influxdb.ID) *influxdb.Error { - o, pe := c.findOrganizationByID(ctx, tx, id) - if pe != nil { - return pe - } - if err := tx.Bucket(organizationIndex).Delete(organizationIndexKey(o.Name)); err != nil { - return &influxdb.Error{ - Err: err, - } - } - encodedID, err := id.Encode() - if err != nil { - return &influxdb.Error{ - Code: influxdb.EInvalid, - Err: err, - } - } - if err = tx.Bucket(organizationBucket).Delete(encodedID); err != nil { - return &influxdb.Error{ - Err: err, - } - } - return nil -} - -func (c *Client) deleteOrganizationsBuckets(ctx context.Context, tx *bolt.Tx, id influxdb.ID) *influxdb.Error { - filter := influxdb.BucketFilter{ - OrganizationID: &id, - } - bs, pe := c.findBuckets(ctx, tx, filter) - if pe != nil { - return pe - } - for _, b := range bs { - if pe := c.deleteBucket(ctx, tx, b.ID); pe != nil { - return pe - } - } - return nil -} - -// GeOrganizationOperationLog retrieves a organization operation log. -func (c *Client) GetOrganizationOperationLog(ctx context.Context, id influxdb.ID, opts influxdb.FindOptions) ([]*influxdb.OperationLogEntry, int, error) { - // TODO(desa): might be worthwhile to allocate a slice of size opts.Limit - log := []*influxdb.OperationLogEntry{} - - err := c.db.View(func(tx *bolt.Tx) error { - key, err := encodeBucketOperationLogKey(id) - if err != nil { - return err - } - - return c.forEachLogEntry(ctx, tx, key, opts, func(v []byte, t time.Time) error { - e := &influxdb.OperationLogEntry{} - if err := json.Unmarshal(v, e); err != nil { - return err - } - e.Time = t - - log = append(log, e) - - return nil - }) - }) - - if err != nil { - return nil, 0, err - } - - return log, len(log), nil -} - -// TODO(desa): what do we want these to be? -const ( - organizationCreatedEvent = "Organization Created" - organizationUpdatedEvent = "Organization Updated" -) - -func encodeOrganizationOperationLogKey(id influxdb.ID) ([]byte, error) { - buf, err := id.Encode() - if err != nil { - return nil, err - } - return append([]byte(bucketOperationLogKeyPrefix), buf...), nil -} - -func (c *Client) appendOrganizationEventToLog(ctx context.Context, tx *bolt.Tx, id influxdb.ID, s string) error { - e := &influxdb.OperationLogEntry{ - Description: s, - } - // TODO(desa): this is fragile and non explicit since it requires an authorizer to be on context. It should be - // replaced with a higher level transaction so that adding to the log can take place in the http handler - // where the organizationID will exist explicitly. - a, err := influxdbcontext.GetAuthorizer(ctx) - if err == nil { - // Add the organization to the log if you can, but don't error if its not there. - e.UserID = a.GetUserID() - } - - v, err := json.Marshal(e) - if err != nil { - return err - } - - k, err := encodeOrganizationOperationLogKey(id) - if err != nil { - return err - } - - 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) { - switch rt { - case influxdb.AuthorizationsResourceType: - r, err := c.FindAuthorizationByID(ctx, id) - if err != nil { - return influxdb.InvalidID(), err - } - return r.OrgID, nil - case influxdb.BucketsResourceType: - r, err := c.FindBucketByID(ctx, id) - if err != nil { - return influxdb.InvalidID(), err - } - return r.OrgID, nil - case influxdb.DashboardsResourceType: - r, err := c.FindDashboardByID(ctx, id) - if err != nil { - return influxdb.InvalidID(), err - } - return r.OrganizationID, nil - case influxdb.OrgsResourceType: - r, err := c.FindOrganizationByID(ctx, id) - if err != nil { - return influxdb.InvalidID(), err - } - return r.ID, nil - case influxdb.SourcesResourceType: - r, err := c.FindSourceByID(ctx, id) - if err != nil { - return influxdb.InvalidID(), err - } - return r.OrganizationID, nil - case influxdb.TelegrafsResourceType: - r, err := c.FindTelegrafConfigByID(ctx, id) - if err != nil { - return influxdb.InvalidID(), err - } - return r.OrgID, nil - case influxdb.VariablesResourceType: - r, err := c.FindVariableByID(ctx, id) - if err != nil { - return influxdb.InvalidID(), err - } - return r.OrganizationID, nil - case influxdb.ScraperResourceType: - r, err := c.GetTargetByID(ctx, id) - if err != nil { - return influxdb.InvalidID(), err - } - return r.OrgID, nil - } - - return influxdb.InvalidID(), &influxdb.Error{ - Msg: fmt.Sprintf("unsupported resource type %s", rt), - } -} diff --git a/bolt/organization_test.go b/bolt/organization_test.go deleted file mode 100644 index 6c3de38bcde..00000000000 --- a/bolt/organization_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/bolt" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initOrganizationService(f platformtesting.OrganizationFields, t *testing.T) (platform.OrganizationService, string, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - c.IDGenerator = f.IDGenerator - c.TimeGenerator = f.TimeGenerator - if f.TimeGenerator == nil { - c.TimeGenerator = platform.RealTimeGenerator{} - } - ctx := context.TODO() - for _, u := range f.Organizations { - if err := c.PutOrganization(ctx, u); err != nil { - t.Fatalf("failed to populate organizations") - } - } - return c, bolt.OpPrefix, func() { - defer closeFn() - for _, o := range f.Organizations { - if err := c.DeleteOrganization(ctx, o.ID); err != nil { - t.Logf("failed to remove organizations: %v", err) - } - } - } -} - -func TestOrganizationService(t *testing.T) { - t.Skip("organization service no longer used. Remove all of this bolt stuff") - platformtesting.OrganizationService(initOrganizationService, t) -} diff --git a/bolt/passwords.go b/bolt/passwords.go deleted file mode 100644 index 8d58d7e08a3..00000000000 --- a/bolt/passwords.go +++ /dev/null @@ -1,116 +0,0 @@ -package bolt - -import ( - "context" - "fmt" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" - "golang.org/x/crypto/bcrypt" -) - -// MinPasswordLength is the shortest password we allow into the system. -const MinPasswordLength = 8 - -var ( - // EIncorrectPassword is returned when any password operation fails in which - // we do not want to leak information. - EIncorrectPassword = &platform.Error{ - Code: platform.EForbidden, - Msg: "your username or password is incorrect", - } - - // EIncorrectUser is returned when any user is failed to be found which indicates - // the userID provided is for a user that does not exist. - EIncorrectUser = &platform.Error{ - Code: platform.EForbidden, - Msg: "your userID is incorrect", - } - - // EShortPassword is used when a password is less than the minimum - // acceptable password length. - EShortPassword = &platform.Error{ - Code: platform.EInvalid, - Msg: "passwords must be at least 8 characters long", - } -) - -// CorruptUserIDError is used when the ID was encoded incorrectly previously. -// This is some sort of internal server error. -func CorruptUserIDError(name string, err error) error { - return &platform.Error{ - Code: platform.EInternal, - Msg: fmt.Sprintf("User ID for %s has been corrupted; Err: %v", name, err), - Op: "bolt/setPassword", - } -} - -var _ platform.PasswordsService = (*Client)(nil) - -// SetPassword stores the password hash associated with a user. -func (c *Client) SetPassword(ctx context.Context, userID platform.ID, password string) error { - return c.db.Update(func(tx *bolt.Tx) error { - return c.setPassword(ctx, tx, userID, password) - }) -} - -// HashCost currently using the default cost of bcrypt -var HashCost = bcrypt.DefaultCost - -func (c *Client) setPassword(ctx context.Context, tx *bolt.Tx, userID platform.ID, password string) error { - if len(password) < MinPasswordLength { - return EShortPassword - } - - hash, err := bcrypt.GenerateFromPassword([]byte(password), HashCost) - if err != nil { - return err - } - - encodedID, err := userID.Encode() - if err != nil { - return CorruptUserIDError(userID.String(), err) - } - - if _, err := c.findUserByID(ctx, tx, userID); err != nil { - return EIncorrectUser - } - - return tx.Bucket(userpasswordBucket).Put(encodedID, hash) -} - -// ComparePassword compares a provided password with the stored password hash. -func (c *Client) ComparePassword(ctx context.Context, userID platform.ID, password string) error { - return c.db.View(func(tx *bolt.Tx) error { - return c.comparePassword(ctx, tx, userID, password) - }) -} - -func (c *Client) comparePassword(ctx context.Context, tx *bolt.Tx, userID platform.ID, password string) error { - encodedID, err := userID.Encode() - if err != nil { - return err - } - - if _, err := c.findUserByID(ctx, tx, userID); err != nil { - return EIncorrectUser - } - - hash := tx.Bucket(userpasswordBucket).Get(encodedID) - - if err := bcrypt.CompareHashAndPassword(hash, []byte(password)); err != nil { - // User exists but the password was incorrect - return EIncorrectPassword - } - return nil -} - -// CompareAndSetPassword replaces the old password with the new password if thee old password is correct. -func (c *Client) CompareAndSetPassword(ctx context.Context, userID platform.ID, old string, new string) error { - return c.db.Update(func(tx *bolt.Tx) error { - if err := c.comparePassword(ctx, tx, userID, old); err != nil { - return err - } - return c.setPassword(ctx, tx, userID, new) - }) -} diff --git a/bolt/passwords_test.go b/bolt/passwords_test.go deleted file mode 100644 index 08a1f070ff3..00000000000 --- a/bolt/passwords_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initPasswordsService(f platformtesting.PasswordFields, t *testing.T) (platform.PasswordsService, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - c.IDGenerator = f.IDGenerator - ctx := context.Background() - for _, u := range f.Users { - if err := c.PutUser(ctx, u); err != nil { - t.Fatalf("failed to populate users") - } - } - - for i := range f.Passwords { - if err := c.SetPassword(ctx, f.Users[i].ID, f.Passwords[i]); err != nil { - t.Fatalf("error setting passsword user, %s %s: %v", f.Users[i].Name, f.Passwords[i], err) - } - } - - return c, func() { - defer closeFn() - for _, u := range f.Users { - if err := c.DeleteUser(ctx, u.ID); err != nil { - t.Logf("failed to remove users: %v", err) - } - } - } -} - -func TestPasswords(t *testing.T) { - t.Parallel() - platformtesting.SetPassword(initPasswordsService, t) -} - -func TestPasswords_CompareAndSet(t *testing.T) { - t.Parallel() - platformtesting.CompareAndSetPassword(initPasswordsService, t) -} diff --git a/bolt/scraper.go b/bolt/scraper.go deleted file mode 100644 index f0c4d419969..00000000000 --- a/bolt/scraper.go +++ /dev/null @@ -1,241 +0,0 @@ -package bolt - -import ( - "context" - "encoding/json" - - bolt "github.com/coreos/bbolt" - influxdb "github.com/influxdata/influxdb" -) - -var ( - scraperBucket = []byte("scraperv2") -) - -var _ influxdb.ScraperTargetStoreService = (*Client)(nil) - -func (c *Client) initializeScraperTargets(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(scraperBucket)); err != nil { - return err - } - return nil -} - -// ListTargets will list all scrape targets. -func (c *Client) ListTargets(ctx context.Context, filter influxdb.ScraperTargetFilter) (list []influxdb.ScraperTarget, err error) { - list = make([]influxdb.ScraperTarget, 0) - err = c.db.View(func(tx *bolt.Tx) (err error) { - cur := tx.Bucket(scraperBucket).Cursor() - for k, v := cur.First(); k != nil; k, v = cur.Next() { - target := new(influxdb.ScraperTarget) - if err = json.Unmarshal(v, target); err != nil { - return err - } - if err != nil { - return err - } - if filter.IDs != nil { - if _, ok := filter.IDs[target.ID]; !ok { - continue - } - } - if filter.Name != nil && target.Name != *filter.Name { - continue - } - if filter.Org != nil { - o, err := c.findOrganizationByName(ctx, tx, *filter.Org) - if err != nil { - return err - } - if target.OrgID != o.ID { - continue - } - } - if filter.OrgID != nil { - o, err := c.findOrganizationByID(ctx, tx, *filter.OrgID) - if err != nil { - return err - } - if target.OrgID != o.ID { - continue - } - } - list = append(list, *target) - } - return err - }) - if err != nil { - return nil, &influxdb.Error{ - Op: getOp(influxdb.OpListTargets), - Err: err, - } - } - return list, err -} - -// AddTarget add a new scraper target into storage. -func (c *Client) AddTarget(ctx context.Context, target *influxdb.ScraperTarget, userID influxdb.ID) (err error) { - if !target.OrgID.Valid() { - return &influxdb.Error{ - Code: influxdb.EInvalid, - Msg: "provided organization ID has invalid format", - Op: OpPrefix + influxdb.OpAddTarget, - } - } - if !target.BucketID.Valid() { - return &influxdb.Error{ - Code: influxdb.EInvalid, - Msg: "provided bucket ID has invalid format", - Op: OpPrefix + influxdb.OpAddTarget, - } - } - err = c.db.Update(func(tx *bolt.Tx) error { - target.ID = c.IDGenerator.ID() - if err := c.putTarget(ctx, tx, target); err != nil { - return err - } - urm := &influxdb.UserResourceMapping{ - ResourceID: target.ID, - UserID: userID, - UserType: influxdb.Owner, - ResourceType: influxdb.ScraperResourceType, - } - return c.createUserResourceMapping(ctx, tx, urm) - }) - if err != nil { - return &influxdb.Error{ - Err: err, - Op: OpPrefix + influxdb.OpAddTarget, - } - } - return nil -} - -// RemoveTarget removes a scraper target from the bucket. -func (c *Client) RemoveTarget(ctx context.Context, id influxdb.ID) error { - err := c.db.Update(func(tx *bolt.Tx) error { - _, pe := c.findTargetByID(ctx, tx, id) - if pe != nil { - return pe - } - encID, err := id.Encode() - if err != nil { - return &influxdb.Error{ - Code: influxdb.EInvalid, - Err: err, - } - } - if err = tx.Bucket(scraperBucket).Delete(encID); err != nil { - return nil - } - return c.deleteUserResourceMappings(ctx, tx, influxdb.UserResourceMappingFilter{ - ResourceID: id, - ResourceType: influxdb.ScraperResourceType, - }) - }) - if err != nil { - return &influxdb.Error{ - Err: err, - Op: OpPrefix + influxdb.OpRemoveTarget, - } - } - return nil -} - -// UpdateTarget updates a scraper target. -func (c *Client) UpdateTarget(ctx context.Context, update *influxdb.ScraperTarget, userID influxdb.ID) (target *influxdb.ScraperTarget, err error) { - op := getOp(influxdb.OpUpdateTarget) - var pe *influxdb.Error - if !update.ID.Valid() { - return nil, &influxdb.Error{ - Code: influxdb.EInvalid, - Op: op, - Msg: "provided scraper target ID has invalid format", - } - } - err = c.db.Update(func(tx *bolt.Tx) error { - target, pe = c.findTargetByID(ctx, tx, update.ID) - if pe != nil { - return pe - } - if !update.BucketID.Valid() { - update.BucketID = target.BucketID - } - if !update.OrgID.Valid() { - update.OrgID = target.OrgID - } - target = update - return c.putTarget(ctx, tx, target) - }) - - if err != nil { - return nil, &influxdb.Error{ - Op: op, - Err: err, - } - } - - return target, nil -} - -// GetTargetByID retrieves a scraper target by id. -func (c *Client) GetTargetByID(ctx context.Context, id influxdb.ID) (target *influxdb.ScraperTarget, err error) { - var pe *influxdb.Error - err = c.db.View(func(tx *bolt.Tx) error { - target, pe = c.findTargetByID(ctx, tx, id) - if pe != nil { - return &influxdb.Error{ - Op: getOp(influxdb.OpGetTargetByID), - Err: pe, - } - } - return nil - }) - if err != nil { - return nil, err - } - return target, nil -} - -func (c *Client) findTargetByID(ctx context.Context, tx *bolt.Tx, id influxdb.ID) (target *influxdb.ScraperTarget, pe *influxdb.Error) { - target = new(influxdb.ScraperTarget) - encID, err := id.Encode() - if err != nil { - return nil, &influxdb.Error{ - Err: err, - } - } - v := tx.Bucket(scraperBucket).Get(encID) - if len(v) == 0 { - return nil, &influxdb.Error{ - Code: influxdb.ENotFound, - Msg: "scraper target is not found", - } - } - - if err := json.Unmarshal(v, target); err != nil { - return nil, &influxdb.Error{ - Err: err, - } - } - return target, nil -} - -func (c *Client) putTarget(ctx context.Context, tx *bolt.Tx, target *influxdb.ScraperTarget) (err error) { - v, err := json.Marshal(target) - if err != nil { - return err - } - encID, err := target.ID.Encode() - if err != nil { - return err - } - return tx.Bucket(scraperBucket).Put(encID, v) -} - -// PutTarget will put a scraper target without setting an ID. -func (c *Client) PutTarget(ctx context.Context, target *influxdb.ScraperTarget) error { - return c.db.Update(func(tx *bolt.Tx) error { - return c.putTarget(ctx, tx, target) - }) -} diff --git a/bolt/scraper_test.go b/bolt/scraper_test.go deleted file mode 100644 index 4b305d94a07..00000000000 --- a/bolt/scraper_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/bolt" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initScraperTargetStoreService(f platformtesting.TargetFields, t *testing.T) (platform.ScraperTargetStoreService, string, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - c.IDGenerator = f.IDGenerator - ctx := context.Background() - for _, target := range f.Targets { - if err := c.PutTarget(ctx, target); err != nil { - t.Fatalf("failed to populate targets: %v", err) - } - } - for _, m := range f.UserResourceMappings { - if err := c.CreateUserResourceMapping(ctx, m); err != nil { - t.Fatalf("failed to populate user resource mapping") - } - } - for _, o := range f.Organizations { - if err := c.PutOrganization(ctx, o); err != nil { - t.Fatalf("failed to populate orgs: %v", err) - } - } - return c, bolt.OpPrefix, func() { - defer closeFn() - for _, target := range f.Targets { - if err := c.RemoveTarget(ctx, target.ID); err != nil { - t.Logf("failed to remove targets: %v", err) - } - } - for _, o := range f.Organizations { - if err := c.DeleteOrganization(ctx, o.ID); err != nil { - t.Logf("failed to remove org: %v", err) - } - } - } -} - -func TestScraperTargetStoreService(t *testing.T) { - platformtesting.ScraperService(initScraperTargetStoreService, t) -} diff --git a/bolt/secret.go b/bolt/secret.go deleted file mode 100644 index b55f6c844ec..00000000000 --- a/bolt/secret.go +++ /dev/null @@ -1,256 +0,0 @@ -package bolt - -import ( - "context" - "encoding/base64" - "errors" - - bolt "github.com/coreos/bbolt" - influxdb "github.com/influxdata/influxdb" -) - -var ( - secretBucket = []byte("secretsv1") -) - -var _ influxdb.SecretService = (*Client)(nil) - -func (c *Client) initializeSecretService(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(secretBucket)); err != nil { - return err - } - return nil -} - -// LoadSecret retrieves the secret value v found at key k for organization orgID. -func (c *Client) LoadSecret(ctx context.Context, orgID influxdb.ID, k string) (string, error) { - var v string - err := c.db.View(func(tx *bolt.Tx) error { - val, err := c.loadSecret(ctx, tx, orgID, k) - if err != nil { - return err - } - - v = val - return nil - }) - - if err != nil { - return "", err - } - - return v, nil -} - -func (c *Client) loadSecret(ctx context.Context, tx *bolt.Tx, orgID influxdb.ID, k string) (string, error) { - key, err := encodeSecretKey(orgID, k) - if err != nil { - return "", err - } - - val := tx.Bucket(secretBucket).Get(key) - if len(val) == 0 { - return "", &influxdb.Error{ - Code: influxdb.ENotFound, - Msg: influxdb.ErrSecretNotFound, - } - } - - v, err := decodeSecretValue(val) - if err != nil { - return "", err - } - - return v, nil -} - -// GetSecretKeys retrieves all secret keys that are stored for the organization orgID. -func (c *Client) GetSecretKeys(ctx context.Context, orgID influxdb.ID) ([]string, error) { - var vs []string - err := c.db.View(func(tx *bolt.Tx) error { - vals, err := c.getSecretKeys(ctx, tx, orgID) - if err != nil { - return err - } - - vs = vals - return nil - }) - - if err != nil { - return nil, err - } - - return vs, nil -} - -func (c *Client) getSecretKeys(ctx context.Context, tx *bolt.Tx, orgID influxdb.ID) ([]string, error) { - cur := tx.Bucket(secretBucket).Cursor() - prefix, err := orgID.Encode() - if err != nil { - return nil, err - } - k, _ := cur.Seek(prefix) - - if len(k) == 0 { - return []string{}, nil - } - - id, key, err := decodeSecretKey(k) - if err != nil { - return nil, err - } - - if id != orgID { - return []string{}, &influxdb.Error{ - Code: influxdb.ENotFound, - Msg: "organization has no secret keys", - } - } - - keys := []string{key} - - for { - k, _ = cur.Next() - - if len(k) == 0 { - // We've reached the end of the keys so we're done - break - } - - id, key, err = decodeSecretKey(k) - if err != nil { - return nil, err - } - - if id != orgID { - // We've reached the end of the keyspace for the provided orgID - break - } - - keys = append(keys, key) - } - - return keys, nil -} - -// PutSecret stores the secret pair (k,v) for the organization orgID. -func (c *Client) PutSecret(ctx context.Context, orgID influxdb.ID, k, v string) error { - return c.db.Update(func(tx *bolt.Tx) error { - return c.putSecret(ctx, tx, orgID, k, v) - }) -} - -func (c *Client) putSecret(ctx context.Context, tx *bolt.Tx, orgID influxdb.ID, k, v string) error { - key, err := encodeSecretKey(orgID, k) - if err != nil { - return err - } - - val := encodeSecretValue(v) - - if err := tx.Bucket(secretBucket).Put(key, val); err != nil { - return err - } - return nil -} - -func encodeSecretKey(orgID influxdb.ID, k string) ([]byte, error) { - buf, err := orgID.Encode() - if err != nil { - return nil, err - } - - key := make([]byte, 0, influxdb.IDLength+len(k)) - key = append(key, buf...) - key = append(key, k...) - - return key, nil -} - -func decodeSecretKey(key []byte) (influxdb.ID, string, error) { - if len(key) < influxdb.IDLength { - // This should not happen. - return influxdb.InvalidID(), "", errors.New("provided key is too short to contain an ID (please report this error)") - } - - var id influxdb.ID - if err := id.Decode(key[:influxdb.IDLength]); err != nil { - return influxdb.InvalidID(), "", err - } - - k := string(key[influxdb.IDLength:]) - - return id, k, nil -} - -func decodeSecretValue(val []byte) (string, error) { - // store the secret value base64 encoded so that it's marginally better than plaintext - v := make([]byte, base64.StdEncoding.DecodedLen(len(val))) - if _, err := base64.StdEncoding.Decode(v, val); err != nil { - return "", err - } - - return string(v), nil -} - -func encodeSecretValue(v string) []byte { - val := make([]byte, base64.StdEncoding.EncodedLen(len(v))) - base64.StdEncoding.Encode(val, []byte(v)) - return val -} - -// PutSecrets puts all provided secrets and overwrites any previous values. -func (c *Client) PutSecrets(ctx context.Context, orgID influxdb.ID, m map[string]string) error { - return c.db.Update(func(tx *bolt.Tx) error { - keys, err := c.getSecretKeys(ctx, tx, orgID) - if err != nil { - return err - } - for k, v := range m { - if err := c.putSecret(ctx, tx, orgID, k, v); err != nil { - return err - } - } - for _, k := range keys { - if _, ok := m[k]; !ok { - if err := c.deleteSecret(ctx, tx, orgID, k); err != nil { - return err - } - } - } - return nil - }) -} - -// PatchSecrets patches all provided secrets and updates any previous values. -func (c *Client) PatchSecrets(ctx context.Context, orgID influxdb.ID, m map[string]string) error { - return c.db.Update(func(tx *bolt.Tx) error { - for k, v := range m { - if err := c.putSecret(ctx, tx, orgID, k, v); err != nil { - return err - } - } - return nil - }) -} - -// DeleteSecret removes secrets from the secret store. -func (c *Client) DeleteSecret(ctx context.Context, orgID influxdb.ID, ks ...string) error { - return c.db.Update(func(tx *bolt.Tx) error { - for _, k := range ks { - if err := c.deleteSecret(ctx, tx, orgID, k); err != nil { - return err - } - } - return nil - }) -} - -func (c *Client) deleteSecret(ctx context.Context, tx *bolt.Tx, orgID influxdb.ID, k string) error { - key, err := encodeSecretKey(orgID, k) - if err != nil { - return err - } - return tx.Bucket(secretBucket).Delete(key) -} diff --git a/bolt/secret_test.go b/bolt/secret_test.go deleted file mode 100644 index 1aa750d914a..00000000000 --- a/bolt/secret_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initSecretService(f platformtesting.SecretServiceFields, t *testing.T) (platform.SecretService, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - ctx := context.TODO() - for _, s := range f.Secrets { - for k, v := range s.Env { - if err := c.PutSecret(ctx, s.OrganizationID, k, v); err != nil { - t.Fatalf("failed to populate secrets") - } - } - } - return c, func() { - defer closeFn() - } -} - -func TestSecretService(t *testing.T) { - platformtesting.SecretService(initSecretService, t) -} diff --git a/bolt/session.go b/bolt/session.go deleted file mode 100644 index 94e4436384d..00000000000 --- a/bolt/session.go +++ /dev/null @@ -1,208 +0,0 @@ -package bolt - -import ( - "context" - "encoding/json" - "time" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" -) - -var ( - sessionBucket = []byte("sessionsv1") -) - -var _ platform.SessionService = (*Client)(nil) - -func (c *Client) initializeSessions(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(sessionBucket)); err != nil { - return err - } - return nil -} - -// RenewSession extends the expire time to newExpiration. -func (c *Client) RenewSession(ctx context.Context, session *platform.Session, newExpiration time.Time) error { - op := getOp(platform.OpRenewSession) - if session == nil { - return &platform.Error{ - Op: op, - Msg: "session is nil", - } - } - return c.db.Update(func(tx *bolt.Tx) error { - session.ExpiresAt = newExpiration - if err := c.putSession(ctx, tx, session); err != nil { - return &platform.Error{ - Op: op, - Err: err, - } - } - return nil - }) -} - -// FindSession retrieves the session found at the provided key. -func (c *Client) FindSession(ctx context.Context, key string) (*platform.Session, error) { - op := getOp(platform.OpFindSession) - var sess *platform.Session - err := c.db.View(func(tx *bolt.Tx) error { - s, err := c.findSession(ctx, tx, key) - if err != nil { - return err - } - - sess = s - return nil - }) - - if err != nil { - return nil, &platform.Error{ - Err: err, - Op: op, - } - } - - if err := sess.Expired(); err != nil { - // todo(leodido) > do we want to return session also if expired? - return sess, &platform.Error{ - Err: err, - Op: op, - } - } - return sess, nil -} - -func (c *Client) findSession(ctx context.Context, tx *bolt.Tx, key string) (*platform.Session, *platform.Error) { - v := tx.Bucket(sessionBucket).Get([]byte(key)) - if len(v) == 0 { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: platform.ErrSessionNotFound, - } - } - - s := &platform.Session{} - if err := json.Unmarshal(v, s); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - // TODO(desa): these values should be cached so it's not so expensive to lookup each time. - f := platform.UserResourceMappingFilter{UserID: s.UserID} - mappings, err := c.findUserResourceMappings(ctx, tx, f) - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - ps := make([]platform.Permission, 0, len(mappings)) - for _, m := range mappings { - p, err := m.ToPermissions() - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - ps = append(ps, p...) - } - ps = append(ps, platform.MePermissions(s.UserID)...) - s.Permissions = ps - return s, nil -} - -// PutSession puts the session at key. -func (c *Client) PutSession(ctx context.Context, s *platform.Session) error { - return c.db.Update(func(tx *bolt.Tx) error { - if err := c.putSession(ctx, tx, s); err != nil { - return err - } - return nil - }) -} - -func (c *Client) putSession(ctx context.Context, tx *bolt.Tx, s *platform.Session) *platform.Error { - v, err := json.Marshal(s) - if err != nil { - return &platform.Error{ - Err: err, - } - } - if err := tx.Bucket(sessionBucket).Put([]byte(s.Key), v); err != nil { - return &platform.Error{ - Err: err, - } - } - return nil -} - -// ExpireSession expires the session at the provided key. -func (c *Client) ExpireSession(ctx context.Context, key string) error { - return c.db.Update(func(tx *bolt.Tx) error { - s, err := c.findSession(ctx, tx, key) - if err != nil { - return err - } - - s.ExpiresAt = time.Now() - - if err := c.putSession(ctx, tx, s); err != nil { - return err - } - return nil - }) -} - -// CreateSession creates a session for a user with the users maximal privileges. -func (c *Client) CreateSession(ctx context.Context, user string) (*platform.Session, error) { - var sess *platform.Session - err := c.db.Update(func(tx *bolt.Tx) error { - s, err := c.createSession(ctx, tx, user) - if err != nil { - return err - } - - sess = s - - return nil - }) - - if err != nil { - return nil, err - } - - return sess, nil -} - -func (c *Client) createSession(ctx context.Context, tx *bolt.Tx, user string) (*platform.Session, *platform.Error) { - u, pe := c.findUserByName(ctx, tx, user) - if pe != nil { - return nil, pe - } - - s := &platform.Session{} - s.ID = c.IDGenerator.ID() - k, err := c.TokenGenerator.Token() - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - s.Key = k - s.UserID = u.ID - s.CreatedAt = time.Now() - // TODO(desa): make this configurable - s.ExpiresAt = s.CreatedAt.Add(time.Hour) - // TODO(desa): not totally sure what to do here. Possibly we should have a maximal privilege permission. - s.Permissions = []platform.Permission{} - - if err := c.putSession(ctx, tx, s); err != nil { - return nil, err - } - - return s, nil -} diff --git a/bolt/session_test.go b/bolt/session_test.go deleted file mode 100644 index 02d036abcbf..00000000000 --- a/bolt/session_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/bolt" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initSessionService(f platformtesting.SessionFields, t *testing.T) (platform.SessionService, string, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - c.IDGenerator = f.IDGenerator - c.TokenGenerator = f.TokenGenerator - ctx := context.Background() - for _, u := range f.Users { - if err := c.PutUser(ctx, u); err != nil { - t.Fatalf("failed to populate users") - } - } - for _, s := range f.Sessions { - if err := c.PutSession(ctx, s); err != nil { - t.Fatalf("failed to populate sessions") - } - } - return c, bolt.OpPrefix, func() { - defer closeFn() - for _, u := range f.Users { - if err := c.DeleteUser(ctx, u.ID); err != nil { - t.Logf("failed to remove users: %v", err) - } - } - } -} - -func TestSessionService(t *testing.T) { - platformtesting.SessionService(initSessionService, t) -} diff --git a/bolt/source.go b/bolt/source.go deleted file mode 100644 index ae1aaf27014..00000000000 --- a/bolt/source.go +++ /dev/null @@ -1,310 +0,0 @@ -package bolt - -import ( - "context" - "encoding/json" - "fmt" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" -) - -var ( - sourceBucket = []byte("sourcesv1") -) - -// DefaultSource is the default source. -var DefaultSource = platform.Source{ - Default: true, - Name: "autogen", - Type: platform.SelfSourceType, -} - -const ( - // DefaultSourceID it the default source identifier - DefaultSourceID = "020f755c3c082000" - // DefaultSourceOrganizationID is the default source's organization identifier - DefaultSourceOrganizationID = "50616e67652c206c" -) - -func init() { - if err := DefaultSource.ID.DecodeFromString(DefaultSourceID); err != nil { - panic(fmt.Sprintf("failed to decode default source id: %v", err)) - } - - if err := DefaultSource.OrganizationID.DecodeFromString(DefaultSourceOrganizationID); err != nil { - panic(fmt.Sprintf("failed to decode default source organization id: %v", err)) - } -} - -func (c *Client) initializeSources(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists(sourceBucket); err != nil { - return err - } - - _, pe := c.findSourceByID(ctx, tx, DefaultSource.ID) - if pe != nil && platform.ErrorCode(pe) != platform.ENotFound { - return pe - } - - if platform.ErrorCode(pe) == platform.ENotFound { - if err := c.putSource(ctx, tx, &DefaultSource); err != nil { - return err - } - } - - return nil -} - -// DefaultSource retrieves the default source. -func (c *Client) DefaultSource(ctx context.Context) (*platform.Source, error) { - var s *platform.Source - - err := c.db.View(func(tx *bolt.Tx) error { - // TODO(desa): make this faster by putting the default source in an index. - srcs, err := c.findSources(ctx, tx, platform.FindOptions{}) - if err != nil { - return err - } - for _, src := range srcs { - if src.Default { - s = src - return nil - } - } - return &platform.Error{ - Code: platform.ENotFound, - Msg: "no default source found", - } - }) - - if err != nil { - return nil, &platform.Error{ - Op: getOp(platform.OpDefaultSource), - Err: err, - } - } - - return s, nil -} - -// FindSourceByID retrieves a source by id. -func (c *Client) FindSourceByID(ctx context.Context, id platform.ID) (*platform.Source, error) { - var s *platform.Source - - err := c.db.View(func(tx *bolt.Tx) error { - src, pe := c.findSourceByID(ctx, tx, id) - if pe != nil { - return &platform.Error{ - Err: pe, - Op: getOp(platform.OpFindSourceByID), - } - } - s = src - return nil - }) - return s, err -} - -func (c *Client) findSourceByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.Source, *platform.Error) { - encodedID, err := id.Encode() - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - v := tx.Bucket(sourceBucket).Get(encodedID) - if len(v) == 0 { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: platform.ErrSourceNotFound, - } - } - - var s platform.Source - if err := json.Unmarshal(v, &s); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - return &s, nil -} - -// FindSources retrives all sources that match an arbitrary source filter. -// Filters using ID, or OrganizationID and source Name should be efficient. -// Other filters will do a linear scan across all sources searching for a match. -func (c *Client) FindSources(ctx context.Context, opt platform.FindOptions) ([]*platform.Source, int, error) { - ss := []*platform.Source{} - err := c.db.View(func(tx *bolt.Tx) error { - srcs, err := c.findSources(ctx, tx, opt) - if err != nil { - return err - } - ss = srcs - return nil - }) - - if err != nil { - return nil, 0, &platform.Error{ - Op: platform.OpFindSources, - Err: err, - } - } - - return ss, len(ss), nil -} - -func (c *Client) findSources(ctx context.Context, tx *bolt.Tx, opt platform.FindOptions) ([]*platform.Source, error) { - ss := []*platform.Source{} - - err := c.forEachSource(ctx, tx, func(s *platform.Source) bool { - ss = append(ss, s) - return true - }) - - if err != nil { - return nil, err - } - - return ss, nil -} - -// CreateSource creates a platform source and sets s.ID. -func (c *Client) CreateSource(ctx context.Context, s *platform.Source) error { - err := c.db.Update(func(tx *bolt.Tx) error { - s.ID = c.IDGenerator.ID() - - // Generating an organization id if it missing or invalid - if !s.OrganizationID.Valid() { - s.OrganizationID = c.IDGenerator.ID() - } - - return c.putSource(ctx, tx, s) - }) - if err != nil { - return &platform.Error{ - Err: err, - Op: getOp(platform.OpCreateSource), - } - } - return nil -} - -// PutSource will put a source without setting an ID. -func (c *Client) PutSource(ctx context.Context, s *platform.Source) error { - return c.db.Update(func(tx *bolt.Tx) error { - return c.putSource(ctx, tx, s) - }) -} - -func (c *Client) putSource(ctx context.Context, tx *bolt.Tx, s *platform.Source) error { - v, err := json.Marshal(s) - if err != nil { - return err - } - - encodedID, err := s.ID.Encode() - if err != nil { - return err - } - - if err := tx.Bucket(sourceBucket).Put(encodedID, v); err != nil { - return err - } - - return nil -} - -// forEachSource will iterate through all sources while fn returns true. -func (c *Client) forEachSource(ctx context.Context, tx *bolt.Tx, fn func(*platform.Source) bool) error { - cur := tx.Bucket(sourceBucket).Cursor() - for k, v := cur.First(); k != nil; k, v = cur.Next() { - s := &platform.Source{} - if err := json.Unmarshal(v, s); err != nil { - return err - } - if !fn(s) { - break - } - } - - return nil -} - -// UpdateSource updates a source according the parameters set on upd. -func (c *Client) UpdateSource(ctx context.Context, id platform.ID, upd platform.SourceUpdate) (*platform.Source, error) { - var s *platform.Source - err := c.db.Update(func(tx *bolt.Tx) error { - src, err := c.updateSource(ctx, tx, id, upd) - if err != nil { - return &platform.Error{ - Err: err, - Op: getOp(platform.OpUpdateSource), - } - } - s = src - return nil - }) - - return s, err -} - -func (c *Client) updateSource(ctx context.Context, tx *bolt.Tx, id platform.ID, upd platform.SourceUpdate) (*platform.Source, error) { - s, pe := c.findSourceByID(ctx, tx, id) - if pe != nil { - return nil, pe - } - - if err := upd.Apply(s); err != nil { - return nil, err - } - - if err := c.putSource(ctx, tx, s); err != nil { - return nil, err - } - - return s, nil -} - -// DeleteSource deletes a source and prunes it from the index. -func (c *Client) DeleteSource(ctx context.Context, id platform.ID) error { - return c.db.Update(func(tx *bolt.Tx) error { - pe := c.deleteSource(ctx, tx, id) - if pe != nil { - return &platform.Error{ - Err: pe, - Op: getOp(platform.OpDeleteSource), - } - } - return nil - }) -} - -func (c *Client) deleteSource(ctx context.Context, tx *bolt.Tx, id platform.ID) *platform.Error { - if id == DefaultSource.ID { - return &platform.Error{ - Code: platform.EForbidden, - Msg: "cannot delete autogen source", - } - } - _, pe := c.findSourceByID(ctx, tx, id) - if pe != nil { - return pe - } - - encodedID, err := id.Encode() - if err != nil { - return &platform.Error{ - Err: err, - } - } - - if err = tx.Bucket(sourceBucket).Delete(encodedID); err != nil { - return &platform.Error{ - Err: err, - } - } - return nil -} diff --git a/bolt/source_test.go b/bolt/source_test.go deleted file mode 100644 index 5c491261bb4..00000000000 --- a/bolt/source_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/bolt" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initSourceService(f platformtesting.SourceFields, t *testing.T) (platform.SourceService, string, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - c.IDGenerator = f.IDGenerator - ctx := context.TODO() - for _, b := range f.Sources { - if err := c.PutSource(ctx, b); err != nil { - t.Fatalf("failed to populate buckets") - } - } - return c, bolt.OpPrefix, func() { - defer closeFn() - for _, b := range f.Sources { - if err := c.DeleteSource(ctx, b.ID); err != nil { - t.Logf("failed to remove bucket: %v", err) - } - } - } -} - -func TestSourceService_CreateSource(t *testing.T) { - platformtesting.CreateSource(initSourceService, t) -} - -func TestSourceService_FindSourceByID(t *testing.T) { - platformtesting.FindSourceByID(initSourceService, t) -} - -func TestSourceService_FindSources(t *testing.T) { - platformtesting.FindSources(initSourceService, t) -} - -func TestSourceService_DeleteSource(t *testing.T) { - platformtesting.DeleteSource(initSourceService, t) -} diff --git a/bolt/telegraf.go b/bolt/telegraf.go deleted file mode 100644 index 92464ca9b88..00000000000 --- a/bolt/telegraf.go +++ /dev/null @@ -1,225 +0,0 @@ -package bolt - -import ( - "context" - "encoding/json" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" -) - -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) { - err = c.db.View(func(tx *bolt.Tx) error { - var pe *platform.Error - tc, pe = c.findTelegrafConfigByID(ctx, tx, id) - if pe != nil { - return pe - } - return nil - }) - 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.EInvalid, - Msg: "provided telegraf configuration ID has invalid format", - } - } - d := tx.Bucket(telegrafBucket).Get(encID) - if d == nil { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: platform.ErrTelegrafConfigNotFound, - } - } - tc := new(platform.TelegrafConfig) - err = json.Unmarshal(d, tc) - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - return tc, nil -} - -func (c *Client) findTelegrafConfigs(ctx context.Context, tx *bolt.Tx, filter platform.TelegrafConfigFilter, opt ...platform.FindOptions) ([]*platform.TelegrafConfig, int, *platform.Error) { - tcs := make([]*platform.TelegrafConfig, 0) - m, err := c.findUserResourceMappings(ctx, tx, filter.UserResourceMappingFilter) - if err != nil { - return nil, 0, &platform.Error{ - Err: err, - } - } - if len(m) == 0 { - return tcs, 0, nil - } - for _, item := range m { - tc, err := c.findTelegrafConfigByID(ctx, tx, item.ResourceID) - if err != nil && platform.ErrorCode(err) != platform.ENotFound { - return nil, 0, &platform.Error{ - // return internal error, for any mapping issue - Err: err, - } - } - if tc != nil { - // Restrict results by organization ID, if it has been provided - if filter.OrgID != nil && filter.OrgID.Valid() && tc.OrgID != *filter.OrgID { - continue - } - tcs = append(tcs, tc) - } - } - 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.TelegrafConfigFilter, opt ...platform.FindOptions) (tcs []*platform.TelegrafConfig, n int, err error) { - op := OpPrefix + platform.OpFindTelegrafConfigs - 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, - } - } - if !tc.OrgID.Valid() { - return &platform.Error{ - Code: platform.EEmptyValue, - Msg: platform.ErrTelegrafConfigInvalidOrgID, - } - } - 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) error { - op := OpPrefix + platform.OpCreateTelegrafConfig - return c.db.Update(func(tx *bolt.Tx) error { - tc.ID = c.IDGenerator.ID() - - pErr := c.putTelegrafConfig(ctx, tx, tc) - if pErr != nil { - pErr.Op = op - return pErr - } - - urm := &platform.UserResourceMapping{ - ResourceID: tc.ID, - UserID: userID, - UserType: platform.Owner, - ResourceType: platform.TelegrafsResourceType, - } - if err := c.createUserResourceMapping(ctx, tx, urm); err != nil { - return err - } - - return nil - }) -} - -// 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) (*platform.TelegrafConfig, error) { - op := OpPrefix + platform.OpUpdateTelegrafConfig - err := c.db.Update(func(tx *bolt.Tx) (err error) { - current, pErr := c.findTelegrafConfigByID(ctx, tx, id) - if pErr != nil { - pErr.Op = op - err = pErr - return err - } - tc.ID = id - // OrgID can not be updated - tc.OrgID = current.OrgID - pErr = c.putTelegrafConfig(ctx, tx, tc) - if pErr != nil { - return &platform.Error{ - Err: pErr, - } - } - return nil - }) - return tc, err -} - -// DeleteTelegrafConfig removes a telegraf config by ID. -func (c *Client) DeleteTelegrafConfig(ctx context.Context, id platform.ID) error { - err := c.db.Update(func(tx *bolt.Tx) error { - encodedID, err := id.Encode() - if err != nil { - return &platform.Error{ - Code: platform.EInvalid, - Msg: "provided telegraf configuration ID has invalid format", - } - } - err = tx.Bucket(telegrafBucket).Delete(encodedID) - if err != nil { - return err - } - return c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{ - ResourceID: id, - ResourceType: platform.TelegrafsResourceType, - }) - }) - if err != nil { - err = &platform.Error{ - Code: platform.ErrorCode(err), - 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 deleted file mode 100644 index fe5621eb4b8..00000000000 --- a/bolt/telegraf_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initTelegrafConfigStore(f platformtesting.TelegrafConfigFields, t *testing.T) (platform.TelegrafConfigStore, func()) { - c, closeFn, err := NewTestClient(t) - 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.go b/bolt/user.go deleted file mode 100644 index e74f0a47e87..00000000000 --- a/bolt/user.go +++ /dev/null @@ -1,486 +0,0 @@ -package bolt - -import ( - "context" - "encoding/json" - "fmt" - "time" - - bolt "github.com/coreos/bbolt" - influxdb "github.com/influxdata/influxdb" - platform "github.com/influxdata/influxdb" - platformcontext "github.com/influxdata/influxdb/context" -) - -var ( - userBucket = []byte("usersv1") - userIndex = []byte("userindexv1") - userpasswordBucket = []byte("userspasswordv1") -) - -var _ platform.UserService = (*Client)(nil) -var _ platform.UserOperationLogService = (*Client)(nil) - -func (c *Client) initializeUsers(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(userBucket)); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists([]byte(userIndex)); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists([]byte(userpasswordBucket)); err != nil { - return err - } - return nil -} - -// FindUserByID retrieves a user by id. -func (c *Client) FindUserByID(ctx context.Context, id platform.ID) (*platform.User, error) { - var u *platform.User - - err := c.db.View(func(tx *bolt.Tx) error { - usr, pe := c.findUserByID(ctx, tx, id) - if pe != nil { - return pe - } - u = usr - return nil - }) - - if err != nil { - return nil, &platform.Error{ - Op: getOp(platform.OpFindUserByID), - Err: err, - } - } - - return u, nil -} - -func (c *Client) findUserByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.User, *platform.Error) { - encodedID, err := id.Encode() - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - var u platform.User - v := tx.Bucket(userBucket).Get(encodedID) - - if len(v) == 0 { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: "user not found", - } - } - - if err := json.Unmarshal(v, &u); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - return &u, nil -} - -// FindUserByName returns a user by name for a particular user. -func (c *Client) FindUserByName(ctx context.Context, n string) (*platform.User, error) { - var u *platform.User - - err := c.db.View(func(tx *bolt.Tx) error { - usr, pe := c.findUserByName(ctx, tx, n) - if pe != nil { - return pe - } - u = usr - return nil - }) - - return u, err -} - -func (c *Client) findUserByName(ctx context.Context, tx *bolt.Tx, n string) (*platform.User, *platform.Error) { - u := tx.Bucket(userIndex).Get(userIndexKey(n)) - if u == nil { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: "user not found", - } - } - - var id platform.ID - if err := id.Decode(u); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - return c.findUserByID(ctx, tx, id) -} - -// FindUser retrives a user using an arbitrary user filter. -func (c *Client) FindUser(ctx context.Context, filter platform.UserFilter) (*platform.User, error) { - var u *platform.User - var err error - op := getOp(platform.OpFindUser) - if filter.ID != nil { - u, err = c.FindUserByID(ctx, *filter.ID) - if err != nil { - return nil, &platform.Error{ - Op: op, - Err: err, - } - } - return u, nil - } - - if filter.Name != nil { - u, err = c.FindUserByName(ctx, *filter.Name) - if err != nil { - return nil, &platform.Error{ - Op: op, - Err: err, - } - } - return u, nil - } - - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: "user not found", - } -} - -func filterUsersFn(filter platform.UserFilter) func(u *platform.User) bool { - if filter.ID != nil { - return func(u *platform.User) bool { - return u.ID.Valid() && u.ID == *filter.ID - } - } - - if filter.Name != nil { - return func(u *platform.User) bool { - return u.Name == *filter.Name - } - } - - return func(u *platform.User) bool { return true } -} - -// FindUsers retrives all users that match an arbitrary user filter. -// Filters using ID, or Name should be efficient. -// Other filters will do a linear scan across all users searching for a match. -func (c *Client) FindUsers(ctx context.Context, filter platform.UserFilter, opt ...platform.FindOptions) ([]*platform.User, int, error) { - op := getOp(platform.OpFindUsers) - if filter.ID != nil { - u, err := c.FindUserByID(ctx, *filter.ID) - if err != nil { - return nil, 0, &platform.Error{ - Err: err, - Op: op, - } - } - - return []*platform.User{u}, 1, nil - } - - if filter.Name != nil { - u, err := c.FindUserByName(ctx, *filter.Name) - if err != nil { - return nil, 0, &platform.Error{ - Err: err, - Op: op, - } - } - - return []*platform.User{u}, 1, nil - } - - us := []*platform.User{} - filterFn := filterUsersFn(filter) - err := c.db.View(func(tx *bolt.Tx) error { - return forEachUser(ctx, tx, func(u *platform.User) bool { - if filterFn(u) { - us = append(us, u) - } - return true - }) - }) - - if err != nil { - return nil, 0, err - } - - return us, len(us), nil -} - -// CreateUser creates a platform user and sets b.ID. -func (c *Client) CreateUser(ctx context.Context, u *platform.User) error { - err := c.db.Update(func(tx *bolt.Tx) error { - unique := c.uniqueUserName(ctx, tx, u) - - if !unique { - return &platform.Error{ - Code: platform.EConflict, - Msg: fmt.Sprintf("user with name %s already exists", u.Name), - } - } - - u.ID = c.IDGenerator.ID() - u.Status = influxdb.Active - - if err := c.appendUserEventToLog(ctx, tx, u.ID, userCreatedEvent); err != nil { - return err - } - - return c.putUser(ctx, tx, u) - }) - if err != nil { - return &platform.Error{ - Err: err, - Op: getOp(platform.OpCreateUser), - } - } - return nil -} - -// PutUser will put a user without setting an ID. -func (c *Client) PutUser(ctx context.Context, u *platform.User) error { - return c.db.Update(func(tx *bolt.Tx) error { - return c.putUser(ctx, tx, u) - }) -} - -func (c *Client) putUser(ctx context.Context, tx *bolt.Tx, u *platform.User) error { - v, err := json.Marshal(u) - if err != nil { - return err - } - encodedID, err := u.ID.Encode() - if err != nil { - return err - } - if err := tx.Bucket(userIndex).Put(userIndexKey(u.Name), encodedID); err != nil { - return err - } - return tx.Bucket(userBucket).Put(encodedID, v) -} - -func userIndexKey(n string) []byte { - return []byte(n) -} - -// forEachUser will iterate through all users while fn returns true. -func forEachUser(ctx context.Context, tx *bolt.Tx, fn func(*platform.User) bool) error { - cur := tx.Bucket(userBucket).Cursor() - for k, v := cur.First(); k != nil; k, v = cur.Next() { - u := &platform.User{} - if err := json.Unmarshal(v, u); err != nil { - return err - } - if !fn(u) { - break - } - } - - return nil -} - -func (c *Client) uniqueUserName(ctx context.Context, tx *bolt.Tx, u *platform.User) bool { - v := tx.Bucket(userIndex).Get(userIndexKey(u.Name)) - return len(v) == 0 -} - -// UpdateUser updates a user according the parameters set on upd. -func (c *Client) UpdateUser(ctx context.Context, id platform.ID, upd platform.UserUpdate) (*platform.User, error) { - var u *platform.User - err := c.db.Update(func(tx *bolt.Tx) error { - usr, pe := c.updateUser(ctx, tx, id, upd) - if pe != nil { - return &platform.Error{ - Err: pe, - Op: getOp(platform.OpUpdateUser), - } - } - u = usr - return nil - }) - - return u, err -} - -func (c *Client) updateUser(ctx context.Context, tx *bolt.Tx, id platform.ID, upd platform.UserUpdate) (*platform.User, *platform.Error) { - u, err := c.findUserByID(ctx, tx, id) - if err != nil { - return nil, err - } - - if upd.Name != nil { - // Users are indexed by name and so the user index must be pruned - // when name is modified. - if err := tx.Bucket(userIndex).Delete(userIndexKey(u.Name)); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - u.Name = *upd.Name - } - - if upd.Status != nil { - u.Status = *upd.Status - } - - if err := c.appendUserEventToLog(ctx, tx, u.ID, userUpdatedEvent); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - if err := c.putUser(ctx, tx, u); err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - return u, nil -} - -// DeleteUser deletes a user and prunes it from the index. -func (c *Client) DeleteUser(ctx context.Context, id platform.ID) error { - err := c.db.Update(func(tx *bolt.Tx) error { - if pe := c.deleteUsersAuthorizations(ctx, tx, id); pe != nil { - return pe - } - if pe := c.deleteUser(ctx, tx, id); pe != nil { - return pe - } - return nil - }) - if err != nil { - return &platform.Error{ - Op: getOp(platform.OpDeleteUser), - Err: err, - } - } - return nil -} - -func (c *Client) deleteUser(ctx context.Context, tx *bolt.Tx, id platform.ID) *platform.Error { - u, pe := c.findUserByID(ctx, tx, id) - if pe != nil { - return pe - } - encodedID, err := id.Encode() - if err != nil { - return &platform.Error{ - Err: err, - } - } - if err := tx.Bucket(userIndex).Delete(userIndexKey(u.Name)); err != nil { - return &platform.Error{ - Err: err, - } - } - if err := tx.Bucket(userBucket).Delete(encodedID); err != nil { - return &platform.Error{ - Err: err, - } - } - if err := c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{ - UserID: id, - }); err != nil { - return &platform.Error{ - Err: err, - } - } - return nil -} - -func (c *Client) deleteUsersAuthorizations(ctx context.Context, tx *bolt.Tx, id platform.ID) *platform.Error { - authFilter := platform.AuthorizationFilter{ - UserID: &id, - } - as, err := c.findAuthorizations(ctx, tx, authFilter) - if err != nil { - return &platform.Error{ - Err: err, - } - } - for _, a := range as { - if err := c.deleteAuthorization(ctx, tx, a.ID); err != nil { - return err - } - } - return nil -} - -// GetUserOperationLog retrieves a user operation log. -func (c *Client) GetUserOperationLog(ctx context.Context, id platform.ID, opts platform.FindOptions) ([]*platform.OperationLogEntry, int, error) { - // TODO(desa): might be worthwhile to allocate a slice of size opts.Limit - log := []*platform.OperationLogEntry{} - - err := c.db.View(func(tx *bolt.Tx) error { - key, err := encodeBucketOperationLogKey(id) - if err != nil { - return err - } - - return c.forEachLogEntry(ctx, tx, key, opts, func(v []byte, t time.Time) error { - e := &platform.OperationLogEntry{} - if err := json.Unmarshal(v, e); err != nil { - return err - } - e.Time = t - - log = append(log, e) - - return nil - }) - }) - - if err != nil { - return nil, 0, err - } - - return log, len(log), nil -} - -// TODO(desa): what do we want these to be? -const ( - userCreatedEvent = "User Created" - userUpdatedEvent = "User Updated" -) - -func encodeUserOperationLogKey(id platform.ID) ([]byte, error) { - buf, err := id.Encode() - if err != nil { - return nil, err - } - return append([]byte(bucketOperationLogKeyPrefix), buf...), nil -} - -func (c *Client) appendUserEventToLog(ctx context.Context, tx *bolt.Tx, id platform.ID, s string) error { - e := &platform.OperationLogEntry{ - Description: s, - } - // TODO(desa): this is fragile and non explicit since it requires an authorizer to be on context. It should be - // replaced with a higher level transaction so that adding to the log can take place in the http handler - // where the userID will exist explicitly. - a, err := platformcontext.GetAuthorizer(ctx) - if err == nil { - // Add the user to the log if you can, but don't error if its not there. - e.UserID = a.GetUserID() - } - - v, err := json.Marshal(e) - if err != nil { - return err - } - - k, err := encodeUserOperationLogKey(id) - if err != nil { - return err - } - - return c.addLogEntry(ctx, tx, k, v, c.Now()) -} diff --git a/bolt/user_resource_mapping.go b/bolt/user_resource_mapping.go deleted file mode 100644 index b8ae8ab47e4..00000000000 --- a/bolt/user_resource_mapping.go +++ /dev/null @@ -1,274 +0,0 @@ -package bolt - -import ( - "context" - "encoding/json" - "fmt" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" -) - -var ( - userResourceMappingBucket = []byte("userresourcemappingsv1") -) - -func (c *Client) initializeUserResourceMappings(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(userResourceMappingBucket)); err != nil { - return err - } - return nil -} - -func filterMappingsFn(filter platform.UserResourceMappingFilter) func(m *platform.UserResourceMapping) bool { - return func(mapping *platform.UserResourceMapping) bool { - return (!filter.UserID.Valid() || (filter.UserID == mapping.UserID)) && - (!filter.ResourceID.Valid() || (filter.ResourceID == mapping.ResourceID)) && - (filter.UserType == "" || (filter.UserType == mapping.UserType)) && - (filter.ResourceType == "" || (filter.ResourceType == mapping.ResourceType)) - } -} - -// FindUserResourceMappings returns a list of UserResourceMappings that match filter and the total count of matching mappings. -func (c *Client) FindUserResourceMappings(ctx context.Context, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) ([]*platform.UserResourceMapping, int, error) { - ms := []*platform.UserResourceMapping{} - err := c.db.View(func(tx *bolt.Tx) error { - mappings, err := c.findUserResourceMappings(ctx, tx, filter) - if err != nil { - return err - } - ms = mappings - return nil - }) - - if err != nil { - return nil, 0, err - } - - return ms, len(ms), nil -} - -func (c *Client) findUserResourceMappings(ctx context.Context, tx *bolt.Tx, filter platform.UserResourceMappingFilter) ([]*platform.UserResourceMapping, error) { - ms := []*platform.UserResourceMapping{} - filterFn := filterMappingsFn(filter) - err := c.forEachUserResourceMapping(ctx, tx, func(m *platform.UserResourceMapping) bool { - if filterFn(m) { - ms = append(ms, m) - } - return true - }) - - if err != nil { - return nil, err - } - - return ms, nil -} - -func (c *Client) findUserResourceMapping(ctx context.Context, tx *bolt.Tx, filter platform.UserResourceMappingFilter) (*platform.UserResourceMapping, error) { - ms, err := c.findUserResourceMappings(ctx, tx, filter) - if err != nil { - return nil, err - } - - if len(ms) == 0 { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: "user to resource mapping not found", - } - } - - return ms[0], nil -} - -func (c *Client) CreateUserResourceMapping(ctx context.Context, m *platform.UserResourceMapping) error { - return c.db.Update(func(tx *bolt.Tx) error { - if err := c.createUserResourceMapping(ctx, tx, m); err != nil { - return err - } - - if m.ResourceType == platform.OrgsResourceType { - return c.createOrgDependentMappings(ctx, tx, m) - } - - return nil - }) -} - -func (c *Client) createUserResourceMapping(ctx context.Context, tx *bolt.Tx, m *platform.UserResourceMapping) error { - unique := c.uniqueUserResourceMapping(ctx, tx, m) - - if !unique { - return &platform.Error{ - Code: platform.EInternal, - Msg: fmt.Sprintf("Unexpected error when assigning user to a resource: mapping for user %s already exists", m.UserID.String()), - } - } - - v, err := json.Marshal(m) - if err != nil { - return err - } - - key, err := userResourceKey(m) - if err != nil { - return err - } - - if err := tx.Bucket(userResourceMappingBucket).Put(key, v); err != nil { - return err - } - - return nil -} - -// This method creates the user/resource mappings for resources that belong to an organization. -func (c *Client) createOrgDependentMappings(ctx context.Context, tx *bolt.Tx, m *platform.UserResourceMapping) error { - bf := platform.BucketFilter{OrganizationID: &m.ResourceID} - bs, err := c.findBuckets(ctx, tx, bf) - if err != nil { - return err - } - for _, b := range bs { - m := &platform.UserResourceMapping{ - ResourceType: platform.BucketsResourceType, - ResourceID: b.ID, - UserType: m.UserType, - UserID: m.UserID, - } - if err := c.createUserResourceMapping(ctx, tx, m); err != nil { - return err - } - // TODO(desa): add support for all other resource types. - } - - return nil -} - -func userResourceKey(m *platform.UserResourceMapping) ([]byte, error) { - encodedResourceID, err := m.ResourceID.Encode() - if err != nil { - return nil, err - } - - encodedUserID, err := m.UserID.Encode() - if err != nil { - return nil, err - } - - key := make([]byte, len(encodedResourceID)+len(encodedUserID)) - copy(key, encodedResourceID) - copy(key[len(encodedResourceID):], encodedUserID) - - return key, nil -} - -func (c *Client) forEachUserResourceMapping(ctx context.Context, tx *bolt.Tx, fn func(*platform.UserResourceMapping) bool) error { - cur := tx.Bucket(userResourceMappingBucket).Cursor() - for k, v := cur.First(); k != nil; k, v = cur.Next() { - m := &platform.UserResourceMapping{} - if err := json.Unmarshal(v, m); err != nil { - return err - } - if !fn(m) { - break - } - } - - return nil -} - -func (c *Client) uniqueUserResourceMapping(ctx context.Context, tx *bolt.Tx, m *platform.UserResourceMapping) bool { - key, err := userResourceKey(m) - if err != nil { - return false - } - - v := tx.Bucket(userResourceMappingBucket).Get(key) - return len(v) == 0 -} - -// DeleteUserResourceMapping deletes a user resource mapping. -func (c *Client) DeleteUserResourceMapping(ctx context.Context, resourceID platform.ID, userID platform.ID) error { - return c.db.Update(func(tx *bolt.Tx) error { - m, err := c.findUserResourceMapping(ctx, tx, platform.UserResourceMappingFilter{ - ResourceID: resourceID, - UserID: userID, - }) - if err != nil { - return err - } - - if err := c.deleteUserResourceMapping(ctx, tx, platform.UserResourceMappingFilter{ - ResourceID: resourceID, - UserID: userID, - }); err != nil { - return err - } - - if m.ResourceType == platform.OrgsResourceType { - return c.deleteOrgDependentMappings(ctx, tx, m) - } - - return nil - }) -} - -func (c *Client) deleteUserResourceMapping(ctx context.Context, tx *bolt.Tx, filter platform.UserResourceMappingFilter) error { - ms, err := c.findUserResourceMappings(ctx, tx, filter) - if err != nil { - return err - } - if len(ms) == 0 { - return &platform.Error{ - Code: platform.ENotFound, - Msg: "user to resource mapping not found", - } - } - - key, err := userResourceKey(ms[0]) - if err != nil { - return err - } - - 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 -} - -// This method deletes the user/resource mappings for resources that belong to an organization. -func (c *Client) deleteOrgDependentMappings(ctx context.Context, tx *bolt.Tx, m *platform.UserResourceMapping) error { - bf := platform.BucketFilter{OrganizationID: &m.ResourceID} - bs, err := c.findBuckets(ctx, tx, bf) - if err != nil { - return err - } - for _, b := range bs { - if err := c.deleteUserResourceMapping(ctx, tx, platform.UserResourceMappingFilter{ - ResourceType: platform.BucketsResourceType, - ResourceID: b.ID, - UserID: m.UserID, - }); err != nil { - return err - } - // TODO(desa): add support for all other resource types. - } - - return nil -} diff --git a/bolt/user_resource_mapping_test.go b/bolt/user_resource_mapping_test.go deleted file mode 100644 index 34c5ab9887d..00000000000 --- a/bolt/user_resource_mapping_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - platform "github.com/influxdata/influxdb" - platformtesting "github.com/influxdata/influxdb/testing" -) - -func initUserResourceMappingService(f platformtesting.UserResourceFields, t *testing.T) (platform.UserResourceMappingService, func()) { - c, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new bolt client: %v", err) - } - ctx := context.Background() - for _, m := range f.UserResourceMappings { - if err := c.CreateUserResourceMapping(ctx, m); err != nil { - t.Fatalf("failed to populate mappings") - } - } - - return c, func() { - defer closeFn() - for _, m := range f.UserResourceMappings { - if err := c.DeleteUserResourceMapping(ctx, m.ResourceID, m.UserID); err != nil { - t.Logf("failed to remove user resource mapping: %v", err) - } - } - } -} - -func TestUserResourceMappingService_FindUserResourceMappings(t *testing.T) { - platformtesting.FindUserResourceMappings(initUserResourceMappingService, t) -} - -func TestUserResourceMappingService_CreateUserResourceMapping(t *testing.T) { - platformtesting.CreateUserResourceMapping(initUserResourceMappingService, t) -} - -func TestUserResourceMappingService_DeleteUserResourceMapping(t *testing.T) { - platformtesting.DeleteUserResourceMapping(initUserResourceMappingService, t) -} diff --git a/bolt/user_test.go b/bolt/user_test.go deleted file mode 100644 index 469f0f6db81..00000000000 --- a/bolt/user_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package bolt_test - -import ( - "context" - "testing" - - "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/kv" - influxdbtesting "github.com/influxdata/influxdb/testing" -) - -func initUserService(f influxdbtesting.UserFields, t *testing.T) (influxdb.UserService, string, func()) { - svc, closeFn, err := NewTestClient(t) - if err != nil { - t.Fatalf("failed to create new kv store: %v", err) - } - svc.IDGenerator = f.IDGenerator - - ctx := context.Background() - /* - if err := svc.Initialize(ctx); err != nil { - t.Fatalf("error initializing user service: %v", err) - } - */ - - for _, u := range f.Users { - if err := svc.PutUser(ctx, u); err != nil { - t.Fatalf("failed to populate users") - } - } - return svc, kv.OpPrefix, func() { - defer closeFn() - for _, u := range f.Users { - if err := svc.DeleteUser(ctx, u.ID); err != nil { - t.Logf("failed to remove users: %v", err) - } - } - } -} - -func TestUserService(t *testing.T) { - influxdbtesting.UserService(initUserService, t) -} diff --git a/bolt/variable.go b/bolt/variable.go deleted file mode 100644 index f1b5b290f6b..00000000000 --- a/bolt/variable.go +++ /dev/null @@ -1,396 +0,0 @@ -package bolt - -import ( - "bytes" - "context" - "encoding/json" - - bolt "github.com/coreos/bbolt" - platform "github.com/influxdata/influxdb" -) - -var ( - variableBucket = []byte("variablesv1") - variableOrgsIndex = []byte("variableorgsv1") -) - -func (c *Client) initializeVariables(ctx context.Context, tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(variableBucket)); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists(variableOrgsIndex); err != nil { - return err - } - return nil -} - -func decodeVariableOrgsIndexKey(indexKey []byte) (orgID platform.ID, variableID platform.ID, err error) { - if len(indexKey) != 2*platform.IDLength { - return 0, 0, &platform.Error{ - Code: platform.EInvalid, - Msg: "malformed variable orgs index key (please report this error)", - } - } - - if err := (&orgID).Decode(indexKey[:platform.IDLength]); err != nil { - return 0, 0, &platform.Error{ - Code: platform.EInvalid, - Msg: "bad org id", - Err: platform.ErrInvalidID, - } - } - - if err := (&variableID).Decode(indexKey[platform.IDLength:]); err != nil { - return 0, 0, &platform.Error{ - Code: platform.EInvalid, - Msg: "bad variable id", - Err: platform.ErrInvalidID, - } - } - - return orgID, variableID, nil -} - -func (c *Client) findOrganizationVariables(ctx context.Context, tx *bolt.Tx, orgID platform.ID) ([]*platform.Variable, error) { - // TODO(leodido): support find options - cur := tx.Bucket(variableOrgsIndex).Cursor() - prefix, err := orgID.Encode() - if err != nil { - return nil, err - } - - variables := []*platform.Variable{} - for k, _ := cur.Seek(prefix); bytes.HasPrefix(k, prefix); k, _ = cur.Next() { - _, id, err := decodeVariableOrgsIndexKey(k) - if err != nil { - return nil, err - } - - m, err := c.findVariableByID(ctx, tx, id) - if err != nil { - return nil, err - } - - variables = append(variables, m) - } - - return variables, nil -} - -func (c *Client) findVariables(ctx context.Context, tx *bolt.Tx, filter platform.VariableFilter) ([]*platform.Variable, error) { - if filter.OrganizationID != nil { - return c.findOrganizationVariables(ctx, tx, *filter.OrganizationID) - } - - if filter.Organization != nil { - o, err := c.findOrganizationByName(ctx, tx, *filter.Organization) - if err != nil { - return nil, err - } - return c.findOrganizationVariables(ctx, tx, o.ID) - } - - variables := []*platform.Variable{} - filterFn := filterVariablesFn(filter) - err := c.forEachVariable(ctx, tx, func(m *platform.Variable) bool { - if filterFn(m) { - variables = append(variables, m) - } - return true - }) - - if err != nil { - return nil, err - } - - return variables, nil -} - -func filterVariablesFn(filter platform.VariableFilter) func(m *platform.Variable) bool { - if filter.ID != nil { - return func(m *platform.Variable) bool { - return m.ID == *filter.ID - } - } - - if filter.OrganizationID != nil { - return func(m *platform.Variable) bool { - return m.OrganizationID == *filter.OrganizationID - } - } - - return func(m *platform.Variable) bool { return true } -} - -// forEachVariable will iterate through all variables while fn returns true. -func (c *Client) forEachVariable(ctx context.Context, tx *bolt.Tx, fn func(*platform.Variable) bool) error { - cur := tx.Bucket(variableBucket).Cursor() - for k, v := cur.First(); k != nil; k, v = cur.Next() { - m := &platform.Variable{} - if err := json.Unmarshal(v, m); err != nil { - return err - } - if !fn(m) { - break - } - } - - return nil -} - -// FindVariables returns all variables in the store -func (c *Client) FindVariables(ctx context.Context, filter platform.VariableFilter, opt ...platform.FindOptions) ([]*platform.Variable, error) { - // todo(leodido) > handle find options - op := getOp(platform.OpFindVariables) - res := []*platform.Variable{} - err := c.db.View(func(tx *bolt.Tx) error { - variables, err := c.findVariables(ctx, tx, filter) - if err != nil && platform.ErrorCode(err) != platform.ENotFound { - return err - } - res = variables - return nil - }) - - if err != nil { - return nil, &platform.Error{ - Op: op, - Err: err, - } - } - - return res, nil -} - -// FindVariableByID finds a single variable in the store by its ID -func (c *Client) FindVariableByID(ctx context.Context, id platform.ID) (*platform.Variable, error) { - op := getOp(platform.OpFindVariableByID) - var variable *platform.Variable - err := c.db.View(func(tx *bolt.Tx) error { - m, pe := c.findVariableByID(ctx, tx, id) - if pe != nil { - return &platform.Error{ - Op: op, - Err: pe, - } - } - variable = m - return nil - }) - if err != nil { - return nil, err - } - - return variable, nil -} - -func (c *Client) findVariableByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.Variable, error) { - encID, err := id.Encode() - if err != nil { - return nil, &platform.Error{ - Code: platform.EInvalid, - Err: err, - } - } - - d := tx.Bucket(variableBucket).Get(encID) - if d == nil { - return nil, &platform.Error{ - Code: platform.ENotFound, - Msg: platform.ErrVariableNotFound, - } - } - - variable := &platform.Variable{} - err = json.Unmarshal(d, &variable) - if err != nil { - return nil, &platform.Error{ - Err: err, - } - } - - return variable, nil -} - -// CreateVariable creates a new variable and assigns it an ID -func (c *Client) CreateVariable(ctx context.Context, variable *platform.Variable) error { - op := getOp(platform.OpCreateVariable) - return c.db.Update(func(tx *bolt.Tx) error { - variable.ID = c.IDGenerator.ID() - - if err := c.putVariableOrgsIndex(ctx, tx, variable); err != nil { - return err - } - - if pe := c.putVariable(ctx, tx, variable); pe != nil { - return &platform.Error{ - Op: op, - Err: pe, - } - - } - return nil - }) -} - -// ReplaceVariable puts a variable in the store -func (c *Client) ReplaceVariable(ctx context.Context, variable *platform.Variable) error { - op := getOp(platform.OpReplaceVariable) - return c.db.Update(func(tx *bolt.Tx) error { - if err := c.putVariableOrgsIndex(ctx, tx, variable); err != nil { - return &platform.Error{ - Op: op, - Err: err, - } - } - return c.putVariable(ctx, tx, variable) - }) -} - -func encodeVariableOrgsIndex(variable *platform.Variable) ([]byte, error) { - oID, err := variable.OrganizationID.Encode() - if err != nil { - return nil, &platform.Error{ - Err: err, - Msg: "bad organization id", - } - } - - mID, err := variable.ID.Encode() - if err != nil { - return nil, &platform.Error{ - Err: err, - Msg: "bad variable id", - } - } - - key := make([]byte, 0, platform.IDLength*2) - key = append(key, oID...) - key = append(key, mID...) - - return key, nil -} - -func (c *Client) putVariableOrgsIndex(ctx context.Context, tx *bolt.Tx, variable *platform.Variable) error { - key, err := encodeVariableOrgsIndex(variable) - if err != nil { - return err - } - - if err := tx.Bucket(variableOrgsIndex).Put(key, nil); err != nil { - return &platform.Error{ - Err: err, - } - } - - return nil -} - -func (c *Client) removeVariableOrgsIndex(ctx context.Context, tx *bolt.Tx, variable *platform.Variable) error { - key, err := encodeVariableOrgsIndex(variable) - if err != nil { - return err - } - - if err := tx.Bucket(variableOrgsIndex).Delete(key); err != nil { - return err - } - - return nil -} - -func (c *Client) putVariable(ctx context.Context, tx *bolt.Tx, variable *platform.Variable) error { - m, err := json.Marshal(variable) - if err != nil { - return &platform.Error{ - Err: err, - } - } - - encID, err := variable.ID.Encode() - if err != nil { - return &platform.Error{ - Code: platform.EInvalid, - Err: err, - } - } - - if err := tx.Bucket(variableBucket).Put(encID, m); err != nil { - return &platform.Error{ - Err: err, - } - } - - return nil -} - -// UpdateVariable updates a single variable in the store with a changeset -func (c *Client) UpdateVariable(ctx context.Context, id platform.ID, update *platform.VariableUpdate) (*platform.Variable, error) { - op := getOp(platform.OpUpdateVariable) - var variable *platform.Variable - err := c.db.Update(func(tx *bolt.Tx) error { - m, pe := c.findVariableByID(ctx, tx, id) - if pe != nil { - return &platform.Error{ - Op: op, - Err: pe, - } - } - - if err := update.Apply(m); err != nil { - return &platform.Error{ - Op: op, - Err: err, - } - } - - variable = m - if pe = c.putVariable(ctx, tx, variable); pe != nil { - return &platform.Error{ - Op: op, - Err: pe, - } - } - return nil - }) - - return variable, err -} - -// DeleteVariable removes a single variable from the store by its ID -func (c *Client) DeleteVariable(ctx context.Context, id platform.ID) error { - op := getOp(platform.OpDeleteVariable) - return c.db.Update(func(tx *bolt.Tx) error { - m, pe := c.findVariableByID(ctx, tx, id) - if pe != nil { - return &platform.Error{ - Op: op, - Err: pe, - } - } - - encID, err := id.Encode() - if err != nil { - return &platform.Error{ - Op: op, - Err: err, - } - } - - if err := c.removeVariableOrgsIndex(ctx, tx, m); err != nil { - return &platform.Error{ - Op: op, - Err: err, - } - } - - if err := tx.Bucket(variableBucket).Delete(encID); err != nil { - return &platform.Error{ - Op: op, - Err: err, - } - } - - return nil - }) -} diff --git a/example_test.go b/example_test.go deleted file mode 100644 index b9811725078..00000000000 --- a/example_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package influxdb_test - -import ( - "context" - "fmt" - "time" - - platform "github.com/influxdata/influxdb" - "github.com/influxdata/influxdb/bolt" - "go.uber.org/zap" -) - -func ExampleKeyValueLog() { - c := bolt.NewClient(zap.NewNop()) - c.Path = "example.bolt" - ctx := context.Background() - if err := c.Open(ctx); err != nil { - panic(err) - } - - for i := 0; i < 10; i++ { - if err := c.AddLogEntry(ctx, []byte("bucket_0_auditlog"), []byte(fmt.Sprintf("abc-%v", i)), time.Now()); err != nil { - panic(err) - } - } - - opts := platform.FindOptions{Limit: 2, Offset: 1, Descending: false} - if err := c.ForEachLogEntry(ctx, []byte("bucket_0_auditlog"), opts, func(v []byte, t time.Time) error { - fmt.Println(t.UTC()) - fmt.Println(string(v)) - fmt.Println() - return nil - }); err != nil { - panic(err) - } - - v, t, err := c.LastLogEntry(ctx, []byte("bucket_0_auditlog")) - if err != nil { - panic(err) - } - fmt.Println(t.UTC()) - fmt.Println(string(v)) - fmt.Println() - - v, t, err = c.FirstLogEntry(ctx, []byte("bucket_0_auditlog")) - if err != nil { - panic(err) - } - fmt.Println(t.UTC()) - fmt.Println(string(v)) - fmt.Println() -}