feat(kv): random ids without comma, space, backslash for org and bucket

At times snowflake id generation would create org and bucket IDs with
characters that had special meaning for the storage engine.

The storage engine concats the org and bucket bytes together into a
single 128 bit value.  That value is used in the old measurement
section.  Measurement was transformed into the tag, _measurement.

However, certain properties of the older measurement data location
are still required for the org/bucket bytes.  We cannot have
commas, spaces, nor backslashes.

This PR puts a specific ID generator in place during the creation of
orgs and buckets.  The IDs are just random numbers but with each
of the restricted chars incremented by one.  While this changes the
entropy distribution somewhat, it does not matter too much for our
purposes.

... because now org and bucket ids are checked for previous existence
transactionally in the key-value stores.  If the ID does already exist
then we try to generate a new key up to 100 times.
pull/15049/head
Chris Goller 2019-09-07 18:00:26 -05:00
parent 2c871428c2
commit 623224614e
17 changed files with 717 additions and 327 deletions

View File

@ -36,5 +36,6 @@ func initOrganizationService(f platformtesting.OrganizationFields, t *testing.T)
}
func TestOrganizationService(t *testing.T) {
t.Skip("organization service no longer used. Remove all of this bolt stuff")
platformtesting.OrganizationService(initOrganizationService, t)
}

View File

@ -13,6 +13,7 @@ import (
platform "github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/inmem"
"github.com/influxdata/influxdb/kv"
"github.com/influxdata/influxdb/mock"
platformtesting "github.com/influxdata/influxdb/testing"
"github.com/julienschmidt/httprouter"
@ -1075,14 +1076,19 @@ func TestService_handlePostBucketOwner(t *testing.T) {
}
func initBucketService(f platformtesting.BucketFields, t *testing.T) (platform.BucketService, string, func()) {
svc := inmem.NewService()
svc := kv.NewService(inmem.NewKVStore())
svc.IDGenerator = f.IDGenerator
svc.OrgBucketIDs = f.OrgBucketIDs
svc.TimeGenerator = f.TimeGenerator
if f.TimeGenerator == nil {
svc.TimeGenerator = platform.RealTimeGenerator{}
}
ctx := context.Background()
if err := svc.Initialize(ctx); err != nil {
t.Fatal(err)
}
for _, o := range f.Organizations {
if err := svc.PutOrganization(ctx, o); err != nil {
t.Fatalf("failed to populate organizations")

View File

@ -14,6 +14,7 @@ import (
platform "github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/inmem"
"github.com/influxdata/influxdb/kv"
"github.com/influxdata/influxdb/mock"
platformtesting "github.com/influxdata/influxdb/testing"
)
@ -34,14 +35,19 @@ func NewMockOrgBackend() *OrgBackend {
func initOrganizationService(f platformtesting.OrganizationFields, t *testing.T) (platform.OrganizationService, string, func()) {
t.Helper()
svc := inmem.NewService()
svc := kv.NewService(inmem.NewKVStore())
svc.IDGenerator = f.IDGenerator
svc.OrgBucketIDs = f.OrgBucketIDs
svc.TimeGenerator = f.TimeGenerator
if f.TimeGenerator == nil {
svc.TimeGenerator = platform.RealTimeGenerator{}
}
ctx := context.Background()
if err := svc.Initialize(ctx); err != nil {
t.Fatal(err)
}
for _, o := range f.Organizations {
if err := svc.PutOrganization(ctx, o); err != nil {
t.Fatalf("failed to populate organizations")
@ -62,6 +68,7 @@ func initOrganizationService(f platformtesting.OrganizationFields, t *testing.T)
return &client, inmem.OpPrefix, done
}
func TestOrganizationService(t *testing.T) {
t.Parallel()
platformtesting.OrganizationService(initOrganizationService, t)
}

View File

@ -30,5 +30,6 @@ func initBucketService(f platformtesting.BucketFields, t *testing.T) (platform.B
}
func TestBucketService(t *testing.T) {
t.Skip("bucket service no longer used. Remove all of this inmem stuff")
platformtesting.BucketService(initBucketService, t)
}

View File

@ -25,5 +25,6 @@ func initOrganizationService(f platformtesting.OrganizationFields, t *testing.T)
}
func TestOrganizationService(t *testing.T) {
t.Skip("organization service no longer used. Remove all of this inmem stuff")
platformtesting.OrganizationService(initOrganizationService, t)
}

View File

@ -3,7 +3,6 @@ package kv
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
@ -425,7 +424,7 @@ func (s *Service) createBucket(ctx context.Context, tx Tx, b *influxdb.Bucket) (
return err
}
if b.ID, err = s.generateBucketID(); err != nil {
if b.ID, err = s.generateBucketID(ctx, tx); err != nil {
return err
}
@ -448,14 +447,8 @@ func (s *Service) createBucket(ctx context.Context, tx Tx, b *influxdb.Bucket) (
return nil
}
func (s *Service) generateBucketID() (influxdb.ID, error) {
for i := 0; i < MaxIDGenerationN; i++ {
id := s.IDGenerator.ID()
if s.IsValidOrgBucketID == nil || s.IsValidOrgBucketID(id) {
return id, nil
}
}
return 0, errors.New("unable to generate valid bucket id")
func (s *Service) generateBucketID(ctx context.Context, tx Tx) (influxdb.ID, error) {
return s.generateSafeID(ctx, tx, bucketBucket)
}
// PutBucket will put a bucket without setting an ID.

View File

@ -5,7 +5,6 @@ import (
"testing"
"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/inmem"
"github.com/influxdata/influxdb/kv"
influxdbtesting "github.com/influxdata/influxdb/testing"
)
@ -46,6 +45,7 @@ func initInmemBucketService(f influxdbtesting.BucketFields, t *testing.T) (influ
func initBucketService(s kv.Store, f influxdbtesting.BucketFields, t *testing.T) (influxdb.BucketService, string, func()) {
svc := kv.NewService(s)
svc.OrgBucketIDs = f.OrgBucketIDs
svc.IDGenerator = f.IDGenerator
svc.TimeGenerator = f.TimeGenerator
if f.TimeGenerator == nil {
@ -79,16 +79,3 @@ func initBucketService(s kv.Store, f influxdbtesting.BucketFields, t *testing.T)
}
}
}
func TestService_CreateBucket(t *testing.T) {
t.Run("InvalidBucketID", func(t *testing.T) {
svc := kv.NewService(inmem.NewKVStore())
if err := svc.PutOrganization(context.Background(), &influxdb.Organization{ID: 123, Name: "ORG"}); err != nil {
t.Fatal(err)
}
svc.IsValidOrgBucketID = func(id influxdb.ID) bool { return false }
if err := svc.CreateBucket(context.Background(), &influxdb.Bucket{OrgID: 123, Name: "BUCKET"}); err == nil || err.Error() != `unable to generate valid bucket id` {
t.Fatalf("unexpected error: %s", err)
}
})
}

View File

@ -11,7 +11,7 @@ import (
)
var (
testID = influxdb.ID(1)
testID = influxdb.ID(10000)
testIDStr = testID.String()
)
@ -84,9 +84,11 @@ func testLookupName(newStore StoreFn, t *testing.T) {
ID: influxdbtesting.IDPtr(testID),
},
init: func(ctx context.Context, s *kv.Service) error {
_ = s.CreateOrganization(ctx, &influxdb.Organization{
o1 := &influxdb.Organization{
Name: "o1",
})
}
_ = s.CreateOrganization(ctx, o1)
t.Log(o1)
return s.CreateBucket(ctx, &influxdb.Bucket{
Name: "b1",
OrgID: testID,
@ -243,6 +245,7 @@ func testLookupName(newStore StoreFn, t *testing.T) {
defer done()
svc.IDGenerator = mock.NewIDGenerator(testIDStr, t)
svc.WithSpecialOrgBucketIDs(svc.IDGenerator)
ctx := context.Background()
if tt.args.init != nil {
if err := tt.args.init(ctx, svc); err != nil {

View File

@ -46,6 +46,7 @@ func initInmemOnboardingService(f influxdbtesting.OnboardingFields, t *testing.T
func initOnboardingService(s kv.Store, f influxdbtesting.OnboardingFields, t *testing.T) (influxdb.OnboardingService, func()) {
svc := kv.NewService(s)
svc.IDGenerator = f.IDGenerator
svc.OrgBucketIDs = f.IDGenerator
svc.TokenGenerator = f.TokenGenerator
svc.TimeGenerator = f.TimeGenerator
if f.TimeGenerator == nil {

View File

@ -3,7 +3,6 @@ package kv
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
@ -15,14 +14,26 @@ import (
"github.com/influxdata/influxdb/kit/tracing"
)
// MaxIDGenerationN is the maximum number of times an ID generation is done before failing.
const MaxIDGenerationN = 100
const (
// MaxIDGenerationN is the maximum number of times an ID generation is done before failing.
MaxIDGenerationN = 100
// ReservedIDs are the number of IDs reserved from 1 - ReservedIDs we use
// for our system org/buckets
ReservedIDs = 1000
)
var (
organizationBucket = []byte("organizationsv1")
organizationIndex = []byte("organizationindexv1")
)
// ErrFailureGeneratingID occurs ony when the random number generator
// cannot generate an ID in MaxIDGenerationN times.
var ErrFailureGeneratingID = &influxdb.Error{
Code: influxdb.EInternal,
Msg: "unable to generate valid id",
}
var _ influxdb.OrganizationService = (*Service)(nil)
var _ influxdb.OrganizationOperationLogService = (*Service)(nil)
@ -271,7 +282,7 @@ func (s *Service) createOrganization(ctx context.Context, tx Tx, o *influxdb.Org
return err
}
if o.ID, err = s.generateOrgID(); err != nil {
if o.ID, err = s.generateOrgID(ctx, tx); err != nil {
return err
}
o.CreatedAt = s.Now()
@ -290,14 +301,8 @@ func (s *Service) createOrganization(ctx context.Context, tx Tx, o *influxdb.Org
return nil
}
func (s *Service) generateOrgID() (influxdb.ID, error) {
for i := 0; i < MaxIDGenerationN; i++ {
id := s.IDGenerator.ID()
if s.IsValidOrgBucketID == nil || s.IsValidOrgBucketID(id) {
return id, nil
}
}
return 0, errors.New("unable to generate valid org id")
func (s *Service) generateOrgID(ctx context.Context, tx Tx) (influxdb.ID, error) {
return s.generateSafeID(ctx, tx, organizationBucket)
}
// PutOrganization will put a organization without setting an ID.

View File

@ -5,7 +5,6 @@ import (
"testing"
"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/inmem"
"github.com/influxdata/influxdb/kv"
influxdbtesting "github.com/influxdata/influxdb/testing"
)
@ -46,6 +45,7 @@ func initInmemOrganizationService(f influxdbtesting.OrganizationFields, t *testi
func initOrganizationService(s kv.Store, f influxdbtesting.OrganizationFields, t *testing.T) (influxdb.OrganizationService, string, func()) {
svc := kv.NewService(s)
svc.OrgBucketIDs = f.OrgBucketIDs
svc.IDGenerator = f.IDGenerator
svc.TimeGenerator = f.TimeGenerator
if f.TimeGenerator == nil {
@ -71,13 +71,3 @@ func initOrganizationService(s kv.Store, f influxdbtesting.OrganizationFields, t
}
}
}
func TestService_CreateOrganization(t *testing.T) {
t.Run("InvalidOrgID", func(t *testing.T) {
svc := kv.NewService(inmem.NewKVStore())
svc.IsValidOrgBucketID = func(id influxdb.ID) bool { return false }
if err := svc.CreateOrganization(context.Background(), &influxdb.Organization{Name: "ORG"}); err == nil || err.Error() != `unable to generate valid org id` {
t.Fatalf("unexpected error: %#v", err)
}
})
}

View File

@ -24,20 +24,25 @@ type Service struct {
Logger *zap.Logger
Config ServiceConfig
IDGenerator influxdb.IDGenerator
IDGenerator influxdb.IDGenerator
// special ID generator that never returns bytes with backslash,
// comma, or space. Used to support very specific encoding of org &
// bucket into the old measurement in storage.
OrgBucketIDs influxdb.IDGenerator
TokenGenerator influxdb.TokenGenerator
influxdb.TimeGenerator
Hash Crypt
// Organization & bucket specific validation.
IsValidOrgBucketID func(influxdb.ID) bool
}
// NewService returns an instance of a Service.
func NewService(kv Store, configs ...ServiceConfig) *Service {
s := &Service{
Logger: zap.NewNop(),
IDGenerator: snowflake.NewIDGenerator(),
Logger: zap.NewNop(),
IDGenerator: snowflake.NewIDGenerator(),
// Seed the random number generator with the current time
OrgBucketIDs: rand.NewOrgBucketID(time.Now().UnixNano()),
TokenGenerator: rand.NewTokenGenerator(64),
Hash: &Bcrypt{},
kv: kv,
@ -150,3 +155,11 @@ func (s *Service) Initialize(ctx context.Context) error {
func (s *Service) WithStore(store Store) {
s.kv = store
}
// WithSpecialOrgBucketIDs sets the generator for the org
// and bucket ids.
//
// Should only be used in tests for mocking.
func (s *Service) WithSpecialOrgBucketIDs(gen influxdb.IDGenerator) {
s.OrgBucketIDs = gen
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
influxdb "github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/kit/tracing"
)
// UnexpectedIndexError is used when the error comes from an internal system.
@ -23,6 +24,13 @@ var NotUniqueError = &influxdb.Error{
Msg: fmt.Sprintf("name already exists"),
}
// NotUniqueIDError is used when attempting to create an org or bucket that already
// exists.
var NotUniqueIDError = &influxdb.Error{
Code: influxdb.EConflict,
Msg: fmt.Sprintf("ID already exists"),
}
func (s *Service) unique(ctx context.Context, tx Tx, indexBucket, indexKey []byte) error {
bucket, err := tx.Bucket(indexBucket)
if err != nil {
@ -43,3 +51,52 @@ func (s *Service) unique(ctx context.Context, tx Tx, indexBucket, indexKey []byt
// any other error is some sort of internal server error
return UnexpectedIndexError(err)
}
func (s *Service) uniqueID(ctx context.Context, tx Tx, bucket []byte, id influxdb.ID) error {
span, _ := tracing.StartSpanFromContext(ctx)
defer span.Finish()
encodedID, err := id.Encode()
if err != nil {
return &influxdb.Error{
Code: influxdb.EInvalid,
Err: err,
}
}
b, err := tx.Bucket(bucket)
if err != nil {
return err
}
_, err = b.Get(encodedID)
if IsNotFound(err) {
return nil
}
return NotUniqueIDError
}
// generateSafeID attempts to create ids for buckets
// and orgs that are without backslash, commas, and spaces, BUT ALSO do not already exist.
func (s *Service) generateSafeID(ctx context.Context, tx Tx, bucket []byte) (influxdb.ID, error) {
for i := 0; i < MaxIDGenerationN; i++ {
id := s.OrgBucketIDs.ID()
// we have reserved a certain number of IDs
// for orgs and buckets.
if id < ReservedIDs {
continue
}
err := s.uniqueID(ctx, tx, bucket, id)
if err == nil {
return id, nil
}
if err == NotUniqueIDError {
continue
}
return influxdb.InvalidID(), err
}
return influxdb.InvalidID(), ErrFailureGeneratingID
}

68
rand/id.go Normal file
View File

@ -0,0 +1,68 @@
package rand
import (
"encoding/binary"
"math/rand"
"sync"
"github.com/influxdata/influxdb"
)
var _ influxdb.IDGenerator = (*OrgBucketID)(nil)
// OrgBucketID creates an id that does not have ascii
// backslash, commas, or spaces. Used to create IDs for organizations
// and buckets.
//
// It is implemented without those characters because orgbucket
// pairs are placed in the old measurement field. Measurement
// was interpreted as a string delimited with commas. Therefore,
// to continue to use the underlying storage engine we need to
// sanitize ids.
//
// Safe for concurrent use by multiple goroutines.
type OrgBucketID struct {
m sync.Mutex
src *rand.Rand
}
// NewOrgBucketID creates an influxdb.IDGenerator that creates
// random numbers seeded with seed. Ascii backslash, comma,
// and space are manipulated by incrementing.
//
// Typically, seed with `time.Now().UnixNano()`
func NewOrgBucketID(seed int64) *OrgBucketID {
return &OrgBucketID{
src: rand.New(rand.NewSource(seed)),
}
}
// Seed allows one to override the current seed.
// Typically, this override is done for tests.
func (r *OrgBucketID) Seed(seed int64) {
r.m.Lock()
r.src = rand.New(rand.NewSource(seed))
r.m.Unlock()
}
// ID generates an ID that does not have backslashes, commas, or spaces.
func (r *OrgBucketID) ID() influxdb.ID {
r.m.Lock()
n := r.src.Uint64()
r.m.Unlock()
n = sanitize(n)
return influxdb.ID(n)
}
func sanitize(n uint64) uint64 {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, n)
for i := range b {
switch b[i] {
case 0x5C, 0x2C, 0x20:
b[i] = b[i] + 1
}
}
return binary.BigEndian.Uint64(b)
}

51
rand/id_test.go Normal file
View File

@ -0,0 +1,51 @@
package rand
import (
"encoding/binary"
"reflect"
"testing"
"github.com/influxdata/influxdb"
)
func TestOrgBucketID_ID(t *testing.T) {
tests := []struct {
name string
seed int64
want influxdb.ID
}{
{
name: "when seeded with 6 the first random number contains characters",
seed: 6,
want: influxdb.ID(0xaddff35d7fe88f15),
},
{
name: "when seeded with 1234567890 we get a random number without any bad chars",
seed: 1234567890,
want: influxdb.ID(0x8a95c1bf40518fee),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := NewOrgBucketID(tt.seed)
if got := r.ID(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("OrgBucketID.ID() = %v, want %v", got, tt.want)
}
})
}
}
func TestOrgBucketID_ID_sanitized(t *testing.T) {
r := NewOrgBucketID(42)
b := make([]byte, 8)
for i := 0; i < 1000; i++ {
id := r.ID()
binary.LittleEndian.PutUint64(b, uint64(id))
for j := range b {
switch b[j] {
case 0x5C, 0x2C, 0x20:
t.Fatalf("unexpected bytes found in IDs")
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,13 +3,15 @@ package testing
import (
"bytes"
"context"
"fmt"
"sort"
"testing"
"time"
"github.com/google/go-cmp/cmp"
platform "github.com/influxdata/influxdb"
"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/mock"
"github.com/influxdata/influxdb/rand"
)
const (
@ -21,8 +23,8 @@ var organizationCmpOptions = cmp.Options{
cmp.Comparer(func(x, y []byte) bool {
return bytes.Equal(x, y)
}),
cmp.Transformer("Sort", func(in []*platform.Organization) []*platform.Organization {
out := append([]*platform.Organization(nil), in...) // Copy input to avoid mutating it
cmp.Transformer("Sort", func(in []*influxdb.Organization) []*influxdb.Organization {
out := append([]*influxdb.Organization(nil), in...) // Copy input to avoid mutating it
sort.Slice(out, func(i, j int) bool {
return out[i].ID.String() > out[j].ID.String()
})
@ -32,18 +34,19 @@ var organizationCmpOptions = cmp.Options{
// OrganizationFields will include the IDGenerator, and organizations
type OrganizationFields struct {
IDGenerator platform.IDGenerator
Organizations []*platform.Organization
TimeGenerator platform.TimeGenerator
IDGenerator influxdb.IDGenerator
Organizations []*influxdb.Organization
TimeGenerator influxdb.TimeGenerator
OrgBucketIDs influxdb.IDGenerator
}
// OrganizationService tests all the service functions.
func OrganizationService(
init func(OrganizationFields, *testing.T) (platform.OrganizationService, string, func()), t *testing.T,
init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()), t *testing.T,
) {
tests := []struct {
name string
fn func(init func(OrganizationFields, *testing.T) (platform.OrganizationService, string, func()),
fn func(init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()),
t *testing.T)
}{
{
@ -80,15 +83,15 @@ func OrganizationService(
// CreateOrganization testing
func CreateOrganization(
init func(OrganizationFields, *testing.T) (platform.OrganizationService, string, func()),
init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()),
t *testing.T,
) {
type args struct {
organization *platform.Organization
organization *influxdb.Organization
}
type wants struct {
err error
organizations []*platform.Organization
organizations []*influxdb.Organization
}
tests := []struct {
@ -101,23 +104,24 @@ func CreateOrganization(
name: "create organizations with empty set",
fields: OrganizationFields{
IDGenerator: mock.NewIDGenerator(orgOneID, t),
OrgBucketIDs: mock.NewIDGenerator(orgOneID, t),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{},
Organizations: []*influxdb.Organization{},
},
args: args{
organization: &platform.Organization{
organization: &influxdb.Organization{
Name: "name1",
ID: MustIDBase16(orgOneID),
Description: "desc1",
},
},
wants: wants{
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
Name: "name1",
ID: MustIDBase16(orgOneID),
Description: "desc1",
CRUDLog: platform.CRUDLog{
CRUDLog: influxdb.CRUDLog{
CreatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
},
@ -129,8 +133,9 @@ func CreateOrganization(
name: "basic create organization",
fields: OrganizationFields{
IDGenerator: mock.NewIDGenerator(orgTwoID, t),
OrgBucketIDs: mock.NewIDGenerator(orgTwoID, t),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -138,13 +143,13 @@ func CreateOrganization(
},
},
args: args{
organization: &platform.Organization{
organization: &influxdb.Organization{
ID: MustIDBase16(orgTwoID),
Name: "organization2",
},
},
wants: wants{
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -152,7 +157,7 @@ func CreateOrganization(
{
ID: MustIDBase16(orgTwoID),
Name: "organization2",
CRUDLog: platform.CRUDLog{
CRUDLog: influxdb.CRUDLog{
CreatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
},
@ -164,8 +169,9 @@ func CreateOrganization(
name: "empty name",
fields: OrganizationFields{
IDGenerator: mock.NewIDGenerator(orgTwoID, t),
OrgBucketIDs: mock.NewIDGenerator(orgTwoID, t),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -173,26 +179,27 @@ func CreateOrganization(
},
},
args: args{
organization: &platform.Organization{
organization: &influxdb.Organization{
ID: MustIDBase16(orgOneID),
},
},
wants: wants{
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
err: platform.ErrOrgNameisEmpty,
err: influxdb.ErrOrgNameisEmpty,
},
},
{
name: "name only have spaces",
fields: OrganizationFields{
IDGenerator: mock.NewIDGenerator(orgTwoID, t),
OrgBucketIDs: mock.NewIDGenerator(orgTwoID, t),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -200,27 +207,28 @@ func CreateOrganization(
},
},
args: args{
organization: &platform.Organization{
organization: &influxdb.Organization{
ID: MustIDBase16(orgOneID),
Name: " ",
},
},
wants: wants{
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
err: platform.ErrOrgNameisEmpty,
err: influxdb.ErrOrgNameisEmpty,
},
},
{
name: "names should be unique",
fields: OrganizationFields{
IDGenerator: mock.NewIDGenerator(orgTwoID, t),
OrgBucketIDs: mock.NewIDGenerator(orgTwoID, t),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -228,21 +236,21 @@ func CreateOrganization(
},
},
args: args{
organization: &platform.Organization{
organization: &influxdb.Organization{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
wants: wants{
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
err: &platform.Error{
Code: platform.EConflict,
Op: platform.OpCreateOrganization,
err: &influxdb.Error{
Code: influxdb.EConflict,
Op: influxdb.OpCreateOrganization,
Msg: "organization with name organization1 already exists",
},
},
@ -251,8 +259,9 @@ func CreateOrganization(
name: "create organization with no id",
fields: OrganizationFields{
IDGenerator: mock.NewIDGenerator(orgTwoID, t),
OrgBucketIDs: mock.NewIDGenerator(orgTwoID, t),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -260,12 +269,12 @@ func CreateOrganization(
},
},
args: args{
organization: &platform.Organization{
organization: &influxdb.Organization{
Name: "organization2",
},
},
wants: wants{
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -273,7 +282,130 @@ func CreateOrganization(
{
ID: MustIDBase16(orgTwoID),
Name: "organization2",
CRUDLog: platform.CRUDLog{
CRUDLog: influxdb.CRUDLog{
CreatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
},
},
},
},
},
{
name: "names should be unique",
fields: OrganizationFields{
IDGenerator: mock.NewIDGenerator(orgTwoID, t),
OrgBucketIDs: mock.NewIDGenerator(orgTwoID, t),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
},
args: args{
organization: &influxdb.Organization{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
wants: wants{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
err: &influxdb.Error{
Code: influxdb.EConflict,
Op: influxdb.OpCreateOrganization,
Msg: "organization with name organization1 already exists",
},
},
},
{
name: "ids should be unique",
fields: OrganizationFields{
IDGenerator: mock.NewIDGenerator(orgOneID, t),
OrgBucketIDs: mock.NewIDGenerator(orgOneID, t),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
},
args: args{
organization: &influxdb.Organization{
ID: MustIDBase16(orgOneID),
Name: "organization2",
},
},
wants: wants{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
err: &influxdb.Error{
Code: influxdb.EInternal,
Msg: fmt.Sprintf("unable to generate valid id"),
},
},
},
{
name: "reserved ids should not be created",
fields: OrganizationFields{
IDGenerator: mock.NewIDGenerator("000000000000000a", t),
OrgBucketIDs: mock.NewIDGenerator("000000000000000a", t),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
},
args: args{
organization: &influxdb.Organization{
ID: MustIDBase16(orgOneID),
Name: "organization2",
},
},
wants: wants{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
},
},
err: &influxdb.Error{
Code: influxdb.EInternal,
Msg: fmt.Sprintf("unable to generate valid id"),
},
},
},
{
name: "randomly generted org ids should not have commas, spaces, or backslashes",
fields: OrganizationFields{
OrgBucketIDs: rand.NewOrgBucketID(42),
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*influxdb.Organization{},
},
args: args{
organization: &influxdb.Organization{
Name: "o1",
},
},
wants: wants{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16("afbf64b1967f8c53"),
Name: "o1",
CRUDLog: influxdb.CRUDLog{
CreatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
},
@ -296,7 +428,7 @@ func CreateOrganization(
defer s.DeleteOrganization(ctx, tt.args.organization.ID)
// }
organizations, _, err := s.FindOrganizations(ctx, platform.OrganizationFilter{})
organizations, _, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
diffPlatformErrors(tt.name, err, nil, opPrefix, t)
if diff := cmp.Diff(organizations, tt.wants.organizations, organizationCmpOptions...); diff != "" {
t.Errorf("organizations are different -got/+want\ndiff %s", diff)
@ -307,15 +439,15 @@ func CreateOrganization(
// FindOrganizationByID testing
func FindOrganizationByID(
init func(OrganizationFields, *testing.T) (platform.OrganizationService, string, func()),
init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()),
t *testing.T,
) {
type args struct {
id platform.ID
id influxdb.ID
}
type wants struct {
err error
organization *platform.Organization
organization *influxdb.Organization
}
tests := []struct {
@ -327,7 +459,7 @@ func FindOrganizationByID(
{
name: "basic find organization by id",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -342,7 +474,7 @@ func FindOrganizationByID(
id: MustIDBase16(orgTwoID),
},
wants: wants{
organization: &platform.Organization{
organization: &influxdb.Organization{
ID: MustIDBase16(orgTwoID),
Name: "organization2",
},
@ -351,7 +483,7 @@ func FindOrganizationByID(
{
name: "didn't find organization by id",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -367,9 +499,9 @@ func FindOrganizationByID(
},
wants: wants{
organization: nil,
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindOrganizationByID,
err: &influxdb.Error{
Code: influxdb.ENotFound,
Op: influxdb.OpFindOrganizationByID,
Msg: "organization not found",
},
},
@ -394,16 +526,16 @@ func FindOrganizationByID(
// FindOrganizations testing
func FindOrganizations(
init func(OrganizationFields, *testing.T) (platform.OrganizationService, string, func()),
init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()),
t *testing.T,
) {
type args struct {
ID platform.ID
ID influxdb.ID
name string
}
type wants struct {
organizations []*platform.Organization
organizations []*influxdb.Organization
err error
}
tests := []struct {
@ -415,7 +547,7 @@ func FindOrganizations(
{
name: "find all organizations",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "abc",
@ -429,7 +561,7 @@ func FindOrganizations(
},
args: args{},
wants: wants{
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "abc",
@ -445,7 +577,7 @@ func FindOrganizations(
{
name: "find organization by id",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "abc",
@ -460,7 +592,7 @@ func FindOrganizations(
ID: MustIDBase16(orgTwoID),
},
wants: wants{
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgTwoID),
Name: "xyz",
@ -471,7 +603,7 @@ func FindOrganizations(
{
name: "find organization by name",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "abc",
@ -486,7 +618,7 @@ func FindOrganizations(
name: "xyz",
},
wants: wants{
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgTwoID),
Name: "xyz",
@ -497,7 +629,7 @@ func FindOrganizations(
{
name: "find organization by id not exists",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "abc",
@ -512,10 +644,10 @@ func FindOrganizations(
ID: MustIDBase16(threeID),
},
wants: wants{
organizations: []*platform.Organization{},
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindOrganizations,
organizations: []*influxdb.Organization{},
err: &influxdb.Error{
Code: influxdb.ENotFound,
Op: influxdb.OpFindOrganizations,
Msg: "organization not found",
},
},
@ -523,7 +655,7 @@ func FindOrganizations(
{
name: "find organization by name not exists",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "abc",
@ -538,10 +670,10 @@ func FindOrganizations(
name: "na",
},
wants: wants{
organizations: []*platform.Organization{},
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindOrganizations,
organizations: []*influxdb.Organization{},
err: &influxdb.Error{
Code: influxdb.ENotFound,
Op: influxdb.OpFindOrganizations,
Msg: "organization name \"na\" not found",
},
},
@ -554,7 +686,7 @@ func FindOrganizations(
defer done()
ctx := context.Background()
filter := platform.OrganizationFilter{}
filter := influxdb.OrganizationFilter{}
if tt.args.ID.Valid() {
filter.ID = &tt.args.ID
}
@ -574,7 +706,7 @@ func FindOrganizations(
// DeleteOrganization testing
func DeleteOrganization(
init func(OrganizationFields, *testing.T) (platform.OrganizationService, string, func()),
init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()),
t *testing.T,
) {
type args struct {
@ -582,7 +714,7 @@ func DeleteOrganization(
}
type wants struct {
err error
organizations []*platform.Organization
organizations []*influxdb.Organization
}
tests := []struct {
@ -594,7 +726,7 @@ func DeleteOrganization(
{
name: "delete organizations using exist id",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
Name: "orgA",
ID: MustIDBase16(orgOneID),
@ -609,7 +741,7 @@ func DeleteOrganization(
ID: orgOneID,
},
wants: wants{
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
Name: "orgB",
ID: MustIDBase16(orgTwoID),
@ -620,7 +752,7 @@ func DeleteOrganization(
{
name: "delete organizations using id that does not exist",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
Name: "orgA",
ID: MustIDBase16(orgOneID),
@ -635,12 +767,12 @@ func DeleteOrganization(
ID: "1234567890654321",
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpDeleteOrganization,
err: &influxdb.Error{
Code: influxdb.ENotFound,
Op: influxdb.OpDeleteOrganization,
Msg: "organization not found",
},
organizations: []*platform.Organization{
organizations: []*influxdb.Organization{
{
Name: "orgA",
ID: MustIDBase16(orgOneID),
@ -662,7 +794,7 @@ func DeleteOrganization(
err := s.DeleteOrganization(ctx, MustIDBase16(tt.args.ID))
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
filter := platform.OrganizationFilter{}
filter := influxdb.OrganizationFilter{}
organizations, _, err := s.FindOrganizations(ctx, filter)
diffPlatformErrors(tt.name, err, nil, opPrefix, t)
@ -675,16 +807,16 @@ func DeleteOrganization(
// FindOrganization testing
func FindOrganization(
init func(OrganizationFields, *testing.T) (platform.OrganizationService, string, func()),
init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()),
t *testing.T,
) {
type args struct {
name string
id platform.ID
id influxdb.ID
}
type wants struct {
organization *platform.Organization
organization *influxdb.Organization
err error
}
@ -697,7 +829,7 @@ func FindOrganization(
{
name: "find organization by name",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "abc",
@ -712,7 +844,7 @@ func FindOrganization(
name: "abc",
},
wants: wants{
organization: &platform.Organization{
organization: &influxdb.Organization{
ID: MustIDBase16(orgOneID),
Name: "abc",
},
@ -724,9 +856,9 @@ func FindOrganization(
name: "unknown",
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindOrganization,
err: &influxdb.Error{
Code: influxdb.ENotFound,
Op: influxdb.OpFindOrganization,
Msg: "organization name \"unknown\" not found",
},
},
@ -734,11 +866,11 @@ func FindOrganization(
{
name: "find organization in which no id filter matches should return no org",
args: args{
id: platform.ID(3),
id: influxdb.ID(3),
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
err: &influxdb.Error{
Code: influxdb.ENotFound,
Msg: "organization not found",
},
},
@ -746,7 +878,7 @@ func FindOrganization(
{
name: "find organization no filter is set returns an error about filters not provided",
fields: OrganizationFields{
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "o1",
@ -754,21 +886,21 @@ func FindOrganization(
},
},
wants: wants{
err: platform.ErrInvalidOrgFilter,
err: influxdb.ErrInvalidOrgFilter,
},
},
{
name: "missing organization returns error",
fields: OrganizationFields{
Organizations: []*platform.Organization{},
Organizations: []*influxdb.Organization{},
},
args: args{
name: "abc",
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindOrganization,
err: &influxdb.Error{
Code: influxdb.ENotFound,
Op: influxdb.OpFindOrganization,
Msg: "organization name \"abc\" not found",
},
},
@ -780,11 +912,11 @@ func FindOrganization(
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.Background()
filter := platform.OrganizationFilter{}
filter := influxdb.OrganizationFilter{}
if tt.args.name != "" {
filter.Name = &tt.args.name
}
if tt.args.id != platform.InvalidID() {
if tt.args.id != influxdb.InvalidID() {
filter.ID = &tt.args.id
}
@ -800,17 +932,17 @@ func FindOrganization(
// UpdateOrganization testing
func UpdateOrganization(
init func(OrganizationFields, *testing.T) (platform.OrganizationService, string, func()),
init func(OrganizationFields, *testing.T) (influxdb.OrganizationService, string, func()),
t *testing.T,
) {
type args struct {
id platform.ID
id influxdb.ID
name *string
description *string
}
type wants struct {
err error
organization *platform.Organization
organization *influxdb.Organization
}
tests := []struct {
@ -823,7 +955,7 @@ func UpdateOrganization(
name: "update id not exists",
fields: OrganizationFields{
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -839,9 +971,9 @@ func UpdateOrganization(
name: strPtr("changed"),
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpUpdateOrganization,
err: &influxdb.Error{
Code: influxdb.ENotFound,
Op: influxdb.OpUpdateOrganization,
Msg: "organization not found",
},
},
@ -850,7 +982,7 @@ func UpdateOrganization(
name: "update name",
fields: OrganizationFields{
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -866,10 +998,10 @@ func UpdateOrganization(
name: strPtr("changed"),
},
wants: wants{
organization: &platform.Organization{
organization: &influxdb.Organization{
ID: MustIDBase16(orgOneID),
Name: "changed",
CRUDLog: platform.CRUDLog{
CRUDLog: influxdb.CRUDLog{
UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
},
},
@ -879,7 +1011,7 @@ func UpdateOrganization(
name: "update name not unique",
fields: OrganizationFields{
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -895,9 +1027,9 @@ func UpdateOrganization(
name: strPtr("organization2"),
},
wants: wants{
err: &platform.Error{
Code: platform.EConflict,
Op: platform.OpUpdateOrganization,
err: &influxdb.Error{
Code: influxdb.EConflict,
Op: influxdb.OpUpdateOrganization,
Msg: "organization with name organization2 already exists",
},
},
@ -906,7 +1038,7 @@ func UpdateOrganization(
name: "update name is empty",
fields: OrganizationFields{
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -922,14 +1054,14 @@ func UpdateOrganization(
name: strPtr(""),
},
wants: wants{
err: platform.ErrOrgNameisEmpty,
err: influxdb.ErrOrgNameisEmpty,
},
},
{
name: "update name only has space",
fields: OrganizationFields{
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -945,14 +1077,14 @@ func UpdateOrganization(
name: strPtr(" "),
},
wants: wants{
err: platform.ErrOrgNameisEmpty,
err: influxdb.ErrOrgNameisEmpty,
},
},
{
name: "update description",
fields: OrganizationFields{
TimeGenerator: mock.TimeGenerator{FakeValue: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC)},
Organizations: []*platform.Organization{
Organizations: []*influxdb.Organization{
{
ID: MustIDBase16(orgOneID),
Name: "organization1",
@ -970,11 +1102,11 @@ func UpdateOrganization(
description: strPtr("changed"),
},
wants: wants{
organization: &platform.Organization{
organization: &influxdb.Organization{
ID: MustIDBase16(orgOneID),
Name: "organization1",
Description: "changed",
CRUDLog: platform.CRUDLog{
CRUDLog: influxdb.CRUDLog{
UpdatedAt: time.Date(2006, 5, 4, 1, 2, 3, 0, time.UTC),
},
},
@ -988,7 +1120,7 @@ func UpdateOrganization(
defer done()
ctx := context.Background()
upd := platform.OrganizationUpdate{}
upd := influxdb.OrganizationUpdate{}
upd.Name = tt.args.name
upd.Description = tt.args.description