influxdb/tenant/service_bucket.go

237 lines
5.9 KiB
Go

package tenant
import (
"context"
"fmt"
"strings"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/kit/platform/errors"
"github.com/influxdata/influxdb/v2/kv"
)
type BucketSvc struct {
store *Store
svc *Service
}
func NewBucketSvc(st *Store, svc *Service) *BucketSvc {
return &BucketSvc{
store: st,
svc: svc,
}
}
// FindBucketByID returns a single bucket by ID.
func (s *BucketSvc) FindBucketByID(ctx context.Context, id platform.ID) (*influxdb.Bucket, error) {
var bucket *influxdb.Bucket
err := s.store.View(ctx, func(tx kv.Tx) error {
b, err := s.store.GetBucket(ctx, tx, id)
if err != nil {
return err
}
bucket = b
return nil
})
if err != nil {
return nil, err
}
return bucket, nil
}
func (s *BucketSvc) FindBucketByName(ctx context.Context, orgID platform.ID, name string) (*influxdb.Bucket, error) {
var bucket *influxdb.Bucket
err := s.store.View(ctx, func(tx kv.Tx) error {
b, err := s.store.GetBucketByName(ctx, tx, orgID, name)
if err != nil {
return err
}
bucket = b
return nil
})
if err != nil {
return nil, err
}
return bucket, nil
}
// FindBucket returns the first bucket that matches filter.
func (s *BucketSvc) FindBucket(ctx context.Context, filter influxdb.BucketFilter) (*influxdb.Bucket, error) {
if filter.ID != nil {
return s.FindBucketByID(ctx, *filter.ID)
}
if filter.Name != nil && filter.OrganizationID != nil {
return s.FindBucketByName(ctx, *filter.OrganizationID, *filter.Name)
}
buckets, _, err := s.FindBuckets(ctx, filter, influxdb.FindOptions{
Limit: 1,
})
if err != nil {
return nil, err
}
if len(buckets) < 1 {
return nil, ErrBucketNotFound
}
return buckets[0], nil
}
// FindBuckets returns a list of buckets that match filter and the total count of matching buckets.
// Additional options provide pagination & sorting.
func (s *BucketSvc) FindBuckets(ctx context.Context, filter influxdb.BucketFilter, opt ...influxdb.FindOptions) ([]*influxdb.Bucket, int, error) {
if filter.ID != nil {
b, err := s.FindBucketByID(ctx, *filter.ID)
if err != nil {
return nil, 0, err
}
return []*influxdb.Bucket{b}, 1, nil
}
if filter.OrganizationID == nil && filter.Org != nil {
org, err := s.svc.FindOrganization(ctx, influxdb.OrganizationFilter{Name: filter.Org})
if err != nil {
return nil, 0, err
}
filter.OrganizationID = &org.ID
}
var buckets []*influxdb.Bucket
err := s.store.View(ctx, func(tx kv.Tx) error {
if filter.Name != nil && filter.OrganizationID != nil {
b, err := s.store.GetBucketByName(ctx, tx, *filter.OrganizationID, *filter.Name)
if err != nil {
return err
}
buckets = []*influxdb.Bucket{b}
return nil
}
bs, err := s.store.ListBuckets(ctx, tx, BucketFilter{
Name: filter.Name,
OrganizationID: filter.OrganizationID,
}, opt...)
if err != nil {
return err
}
buckets = bs
return nil
})
if err != nil {
return nil, 0, err
}
return buckets, len(buckets), nil
}
// CreateBucket creates a new bucket and sets b.ID with the new identifier.
func (s *BucketSvc) CreateBucket(ctx context.Context, b *influxdb.Bucket) error {
if !b.OrgID.Valid() {
// we need a valid org id
return ErrOrgNotFound
}
if err := validBucketName(b.Name, b.Type); err != nil {
return err
}
// make sure the org exists
if _, err := s.svc.FindOrganizationByID(ctx, b.OrgID); err != nil {
return err
}
return s.store.Update(ctx, func(tx kv.Tx) error {
return s.store.CreateBucket(ctx, tx, b)
})
}
// UpdateBucket updates a single bucket with changeset.
// Returns the new bucket state after update.
func (s *BucketSvc) UpdateBucket(ctx context.Context, id platform.ID, upd influxdb.BucketUpdate) (*influxdb.Bucket, error) {
var bucket *influxdb.Bucket
err := s.store.Update(ctx, func(tx kv.Tx) error {
b, err := s.store.UpdateBucket(ctx, tx, id, upd)
if err != nil {
return err
}
bucket = b
return nil
})
if err != nil {
return nil, err
}
return bucket, nil
}
// DeleteBucket removes a bucket by ID.
func (s *BucketSvc) DeleteBucket(ctx context.Context, id platform.ID) error {
err := s.store.Update(ctx, func(tx kv.Tx) error {
bucket, err := s.store.GetBucket(ctx, tx, id)
if err != nil {
return err
}
if bucket.Type == influxdb.BucketTypeSystem && !isInternal(ctx) {
// TODO: I think we should allow bucket deletes but maybe im wrong.
return errDeleteSystemBucket
}
if err := s.store.DeleteBucket(ctx, tx, id); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return s.removeResourceRelations(ctx, id)
}
// removeResourceRelations allows us to clean up any resource relationship that would have normally been left over after a delete action of a resource.
func (s *BucketSvc) removeResourceRelations(ctx context.Context, resourceID platform.ID) error {
urms, _, err := s.svc.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
ResourceID: resourceID,
})
if err != nil {
return err
}
for _, urm := range urms {
err := s.svc.DeleteUserResourceMapping(ctx, urm.ResourceID, urm.UserID)
if err != nil && err != ErrURMNotFound {
return err
}
}
return nil
}
// validBucketName reports any errors with bucket names
func validBucketName(name string, typ influxdb.BucketType) error {
// names starting with an underscore are reserved for system buckets
if strings.HasPrefix(name, "_") && typ != influxdb.BucketTypeSystem {
return &errors.Error{
Code: errors.EInvalid,
Msg: fmt.Sprintf("bucket name %s is invalid. Buckets may not start with underscore", name),
Op: influxdb.OpCreateBucket,
}
}
// quotation marks will cause queries to fail
if strings.Contains(name, "\"") {
return &errors.Error{
Code: errors.EInvalid,
Msg: fmt.Sprintf("bucket name %s is invalid. Bucket names may not include quotation marks", name),
Op: influxdb.OpCreateBucket,
}
}
return nil
}