2019-02-19 23:47:19 +00:00
|
|
|
package kv
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
2019-11-04 20:28:21 +00:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
2019-06-18 22:05:40 +00:00
|
|
|
|
2019-06-18 17:06:28 +00:00
|
|
|
"github.com/influxdata/influxdb"
|
2019-12-26 07:35:42 +00:00
|
|
|
"github.com/influxdata/influxdb/kit/tracing"
|
2019-02-19 23:47:19 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
variableOrgsIndex = []byte("variableorgsv1")
|
|
|
|
)
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
func newVariableUniqueByNameStore(kv Store) *uniqByNameStore {
|
|
|
|
return &uniqByNameStore{
|
|
|
|
kv: kv,
|
|
|
|
caseInsensitive: true,
|
|
|
|
resource: "variable",
|
|
|
|
bktName: []byte("variablesv1"),
|
|
|
|
indexName: []byte("variablesindexv1"),
|
|
|
|
decodeBucketEntFn: func(key, val []byte) ([]byte, interface{}, error) {
|
|
|
|
var v influxdb.Variable
|
|
|
|
return key, &v, json.Unmarshal(val, &v)
|
|
|
|
},
|
|
|
|
decodeOrgNameFn: func(body []byte) (influxdb.ID, string, error) {
|
|
|
|
var v influxdb.Variable
|
|
|
|
err := json.Unmarshal(body, &v)
|
|
|
|
return v.ID, v.Name, err
|
|
|
|
},
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeVariableOrgsIndexKey(indexKey []byte) (orgID influxdb.ID, variableID influxdb.ID, err error) {
|
|
|
|
if len(indexKey) != 2*influxdb.IDLength {
|
|
|
|
return 0, 0, &influxdb.Error{
|
|
|
|
Code: influxdb.EInvalid,
|
|
|
|
Msg: "malformed variable orgs index key (please report this error)",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := (&orgID).Decode(indexKey[:influxdb.IDLength]); err != nil {
|
|
|
|
return 0, 0, &influxdb.Error{
|
|
|
|
Code: influxdb.EInvalid,
|
|
|
|
Msg: "bad org id",
|
|
|
|
Err: influxdb.ErrInvalidID,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := (&variableID).Decode(indexKey[influxdb.IDLength:]); err != nil {
|
|
|
|
return 0, 0, &influxdb.Error{
|
|
|
|
Code: influxdb.EInvalid,
|
|
|
|
Msg: "bad variable id",
|
|
|
|
Err: influxdb.ErrInvalidID,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return orgID, variableID, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) findOrganizationVariables(ctx context.Context, tx Tx, orgID influxdb.ID) ([]*influxdb.Variable, error) {
|
|
|
|
idx, err := tx.Bucket(variableOrgsIndex)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(leodido): support find options
|
|
|
|
cur, err := idx.Cursor()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
prefix, err := orgID.Encode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
variables := []*influxdb.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 := s.findVariableByID(ctx, tx, id)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
variables = append(variables, m)
|
|
|
|
}
|
|
|
|
|
|
|
|
return variables, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) findVariables(ctx context.Context, tx Tx, filter influxdb.VariableFilter) ([]*influxdb.Variable, error) {
|
|
|
|
if filter.OrganizationID != nil {
|
|
|
|
return s.findOrganizationVariables(ctx, tx, *filter.OrganizationID)
|
|
|
|
}
|
|
|
|
|
|
|
|
if filter.Organization != nil {
|
|
|
|
o, err := s.findOrganizationByName(ctx, tx, *filter.Organization)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return s.findOrganizationVariables(ctx, tx, o.ID)
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
// TODO(jsteenb2): investigate why we don't implement the find options for vars?
|
|
|
|
var o influxdb.FindOptions
|
2019-02-19 23:47:19 +00:00
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
var variables []*influxdb.Variable
|
|
|
|
err := s.variableStore.Find(ctx, tx, o, filterVariablesFn(filter), func(k []byte, v interface{}) {
|
|
|
|
variables = append(variables, v.(*influxdb.Variable))
|
|
|
|
})
|
2019-02-19 23:47:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return variables, nil
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
func filterVariablesFn(filter influxdb.VariableFilter) func([]byte, interface{}) bool {
|
|
|
|
return func(key []byte, val interface{}) bool {
|
|
|
|
variable, ok := val.(*influxdb.Variable)
|
|
|
|
if !ok {
|
|
|
|
return false
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
if filter.ID != nil {
|
|
|
|
return variable.ID == *filter.ID
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
if filter.OrganizationID != nil {
|
|
|
|
return variable.OrganizationID == *filter.OrganizationID
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
return true
|
|
|
|
}
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindVariables returns all variables in the store
|
|
|
|
func (s *Service) FindVariables(ctx context.Context, filter influxdb.VariableFilter, opt ...influxdb.FindOptions) ([]*influxdb.Variable, error) {
|
|
|
|
// todo(leodido) > handle find options
|
|
|
|
res := []*influxdb.Variable{}
|
2019-03-05 00:38:10 +00:00
|
|
|
err := s.kv.View(ctx, func(tx Tx) error {
|
2019-02-19 23:47:19 +00:00
|
|
|
variables, err := s.findVariables(ctx, tx, filter)
|
|
|
|
if err != nil && influxdb.ErrorCode(err) != influxdb.ENotFound {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
res = variables
|
|
|
|
return nil
|
|
|
|
})
|
2019-12-26 07:35:42 +00:00
|
|
|
return res, err
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindVariableByID finds a single variable in the store by its ID
|
|
|
|
func (s *Service) FindVariableByID(ctx context.Context, id influxdb.ID) (*influxdb.Variable, error) {
|
|
|
|
var variable *influxdb.Variable
|
2019-03-05 00:38:10 +00:00
|
|
|
err := s.kv.View(ctx, func(tx Tx) error {
|
2019-12-26 07:35:42 +00:00
|
|
|
m, err := s.findVariableByID(ctx, tx, id)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
variable = m
|
|
|
|
return nil
|
|
|
|
})
|
2019-12-26 07:35:42 +00:00
|
|
|
return variable, err
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) findVariableByID(ctx context.Context, tx Tx, id influxdb.ID) (*influxdb.Variable, error) {
|
2019-12-26 07:35:42 +00:00
|
|
|
body, err := s.variableStore.FindByID(ctx, tx, id)
|
2019-02-19 23:47:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
var v influxdb.Variable
|
|
|
|
if err := json.Unmarshal(body, &v); err != nil {
|
2019-02-19 23:47:19 +00:00
|
|
|
return nil, &influxdb.Error{
|
2019-12-26 07:35:42 +00:00
|
|
|
Code: influxdb.EInternal,
|
|
|
|
Err: err,
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-26 07:35:42 +00:00
|
|
|
return &v, nil
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateVariable creates a new variable and assigns it an ID
|
2019-12-26 07:35:42 +00:00
|
|
|
func (s *Service) CreateVariable(ctx context.Context, v *influxdb.Variable) error {
|
2019-03-05 00:38:10 +00:00
|
|
|
return s.kv.Update(ctx, func(tx Tx) error {
|
2019-12-26 07:35:42 +00:00
|
|
|
if err := v.Valid(); err != nil {
|
2019-11-04 20:28:21 +00:00
|
|
|
return &influxdb.Error{
|
|
|
|
Code: influxdb.EInvalid,
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
v.Name = strings.TrimSpace(v.Name) // TODO: move to service layer
|
2019-11-04 20:28:21 +00:00
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
if _, err := s.variableStore.FindByName(ctx, tx, v.OrganizationID, v.Name); err == nil {
|
|
|
|
return &influxdb.Error{
|
|
|
|
Code: influxdb.EConflict,
|
|
|
|
Msg: fmt.Sprintf("variable with name %s already exists", v.Name),
|
|
|
|
}
|
2019-11-04 20:28:21 +00:00
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
v.ID = s.IDGenerator.ID()
|
2019-02-19 23:47:19 +00:00
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
if err := s.putVariableOrgsIndex(ctx, tx, v); err != nil {
|
2019-02-19 23:47:19 +00:00
|
|
|
return err
|
|
|
|
}
|
2019-06-18 17:06:28 +00:00
|
|
|
now := s.Now()
|
2019-12-26 07:35:42 +00:00
|
|
|
v.CreatedAt = now
|
|
|
|
v.UpdatedAt = now
|
|
|
|
return s.putVariable(ctx, tx, v)
|
2019-02-19 23:47:19 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReplaceVariable puts a variable in the store
|
2019-12-26 07:35:42 +00:00
|
|
|
func (s *Service) ReplaceVariable(ctx context.Context, v *influxdb.Variable) error {
|
2019-03-05 00:38:10 +00:00
|
|
|
return s.kv.Update(ctx, func(tx Tx) error {
|
2019-12-26 07:35:42 +00:00
|
|
|
if err := s.putVariableOrgsIndex(ctx, tx, v); err != nil {
|
2019-02-19 23:47:19 +00:00
|
|
|
return &influxdb.Error{
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
2019-11-04 20:28:21 +00:00
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
if _, err := s.variableStore.FindByName(ctx, tx, v.OrganizationID, v.Name); err == nil {
|
2019-11-04 20:28:21 +00:00
|
|
|
return &influxdb.Error{
|
2019-12-26 07:35:42 +00:00
|
|
|
Code: influxdb.EConflict,
|
|
|
|
Msg: fmt.Sprintf("variable with name %s already exists", v.Name),
|
2019-11-04 20:28:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
return s.putVariable(ctx, tx, v)
|
2019-02-19 23:47:19 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) putVariableOrgsIndex(ctx context.Context, tx Tx, variable *influxdb.Variable) error {
|
2019-12-26 07:35:42 +00:00
|
|
|
span, _ := tracing.StartSpanFromContext(ctx)
|
|
|
|
defer span.Finish()
|
|
|
|
|
2019-02-19 23:47:19 +00:00
|
|
|
key, err := encodeVariableOrgsIndex(variable)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
idx, err := tx.Bucket(variableOrgsIndex)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := idx.Put(key, nil); err != nil {
|
|
|
|
return &influxdb.Error{
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) removeVariableOrgsIndex(ctx context.Context, tx Tx, variable *influxdb.Variable) error {
|
|
|
|
key, err := encodeVariableOrgsIndex(variable)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
idx, err := tx.Bucket(variableOrgsIndex)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := idx.Delete(key); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
func (s *Service) putVariable(ctx context.Context, tx Tx, v *influxdb.Variable) error {
|
|
|
|
m, err := json.Marshal(v)
|
2019-02-19 23:47:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return &influxdb.Error{
|
2019-12-26 07:35:42 +00:00
|
|
|
Code: influxdb.EInternal,
|
2019-02-19 23:47:19 +00:00
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
ent := Entity{
|
|
|
|
ID: v.ID,
|
|
|
|
Name: v.Name,
|
|
|
|
OrgID: v.OrganizationID,
|
|
|
|
Body: m,
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
2019-12-26 07:35:42 +00:00
|
|
|
return s.variableStore.Put(ctx, tx, ent)
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateVariable updates a single variable in the store with a changeset
|
|
|
|
func (s *Service) UpdateVariable(ctx context.Context, id influxdb.ID, update *influxdb.VariableUpdate) (*influxdb.Variable, error) {
|
2019-12-26 07:35:42 +00:00
|
|
|
var v *influxdb.Variable
|
2019-03-05 00:38:10 +00:00
|
|
|
err := s.kv.Update(ctx, func(tx Tx) error {
|
2019-11-04 20:28:21 +00:00
|
|
|
m, err := s.findVariableByID(ctx, tx, id)
|
|
|
|
if err != nil {
|
2019-12-26 07:35:42 +00:00
|
|
|
return err
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
2019-06-18 22:05:40 +00:00
|
|
|
m.UpdatedAt = s.Now()
|
2019-11-04 20:28:21 +00:00
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
v = m
|
2019-11-04 20:28:21 +00:00
|
|
|
|
|
|
|
if update.Name != "" {
|
2019-12-26 07:35:42 +00:00
|
|
|
// TODO: should be moved to service layer
|
|
|
|
update.Name = strings.ToLower(strings.TrimSpace(update.Name))
|
|
|
|
|
|
|
|
vbytes, err := s.variableStore.FindByName(ctx, tx, v.OrganizationID, update.Name)
|
|
|
|
if err == nil {
|
|
|
|
var existingVar influxdb.Variable
|
|
|
|
if err := json.Unmarshal(vbytes, &existingVar); err != nil {
|
|
|
|
return &influxdb.Error{
|
|
|
|
Code: influxdb.EInternal,
|
|
|
|
Err: err,
|
|
|
|
}
|
2019-11-04 20:28:21 +00:00
|
|
|
}
|
2019-12-26 07:35:42 +00:00
|
|
|
if existingVar.ID != v.ID {
|
|
|
|
return &influxdb.Error{
|
|
|
|
Code: influxdb.EConflict,
|
|
|
|
Msg: fmt.Sprintf("variable with name %s already exists", update.Name),
|
|
|
|
}
|
2019-11-04 20:28:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
if err := s.variableStore.deleteInIndex(ctx, tx, v.OrganizationID, v.Name); err != nil {
|
|
|
|
return err
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
2019-12-26 07:35:42 +00:00
|
|
|
v.Name = update.Name
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
update.Apply(m)
|
|
|
|
|
|
|
|
return s.putVariable(ctx, tx, v)
|
2019-02-19 23:47:19 +00:00
|
|
|
})
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
return v, err
|
2019-02-19 23:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteVariable removes a single variable from the store by its ID
|
|
|
|
func (s *Service) DeleteVariable(ctx context.Context, id influxdb.ID) error {
|
2019-03-05 00:38:10 +00:00
|
|
|
return s.kv.Update(ctx, func(tx Tx) error {
|
2019-11-04 20:28:21 +00:00
|
|
|
v, err := s.findVariableByID(ctx, tx, id)
|
2019-02-19 23:47:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
if err := s.removeVariableOrgsIndex(ctx, tx, v); err != nil {
|
2019-02-19 23:47:19 +00:00
|
|
|
return &influxdb.Error{
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
return s.variableStore.Delete(ctx, tx, id)
|
2019-02-19 23:47:19 +00:00
|
|
|
})
|
|
|
|
}
|
2019-11-04 20:28:21 +00:00
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
func encodeVariableOrgsIndex(variable *influxdb.Variable) ([]byte, error) {
|
|
|
|
oID, err := variable.OrganizationID.Encode()
|
2019-11-04 20:28:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Err: err,
|
2019-12-26 07:35:42 +00:00
|
|
|
Msg: "bad organization id",
|
2019-11-04 20:28:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
mID, err := variable.ID.Encode()
|
2019-11-04 20:28:21 +00:00
|
|
|
if err != nil {
|
2019-12-26 07:35:42 +00:00
|
|
|
return nil, &influxdb.Error{
|
2019-11-04 20:28:21 +00:00
|
|
|
Err: err,
|
2019-12-26 07:35:42 +00:00
|
|
|
Msg: "bad variable id",
|
2019-11-04 20:28:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
key := make([]byte, influxdb.IDLength*2)
|
|
|
|
copy(key, oID)
|
|
|
|
copy(key[influxdb.IDLength:], mID)
|
2019-11-04 20:28:21 +00:00
|
|
|
|
2019-12-26 07:35:42 +00:00
|
|
|
return key, nil
|
2019-11-04 20:28:21 +00:00
|
|
|
}
|