2018-12-03 16:07:08 +00:00
|
|
|
package bolt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
|
2018-12-07 18:25:11 +00:00
|
|
|
bolt "github.com/coreos/bbolt"
|
2018-12-03 16:07:08 +00:00
|
|
|
"github.com/influxdata/platform"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
labelBucket = []byte("labelsv1")
|
|
|
|
)
|
|
|
|
|
|
|
|
func (c *Client) initializeLabels(ctx context.Context, tx *bolt.Tx) error {
|
|
|
|
if _, err := tx.CreateBucketIfNotExists([]byte(labelBucket)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func filterLabelsFn(filter platform.LabelFilter) func(l *platform.Label) bool {
|
|
|
|
return func(label *platform.Label) bool {
|
|
|
|
return (filter.Name == "" || (filter.Name == label.Name)) &&
|
|
|
|
(!filter.ResourceID.Valid() || (filter.ResourceID == label.ResourceID))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-18 09:06:28 +00:00
|
|
|
// func (c *Client) findLabel(ctx context.Context, tx *bolt.Tx, resourceID platform.ID, name string) (*platform.Label, error) {
|
|
|
|
// key, err := labelKey(&platform.Label{ResourceID: resourceID, Name: name})
|
|
|
|
// if err != nil {
|
|
|
|
// return nil, err
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// l := &platform.Label{}
|
|
|
|
// v := tx.Bucket(labelBucket).Get(key)
|
|
|
|
// if err := json.Unmarshal(v, l); err != nil {
|
|
|
|
// return nil, err
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// return l, nil
|
|
|
|
// }
|
|
|
|
|
2018-12-03 16:07:08 +00:00
|
|
|
// FindLabels returns a list of labels that match a filter.
|
|
|
|
func (c *Client) FindLabels(ctx context.Context, filter platform.LabelFilter, opt ...platform.FindOptions) ([]*platform.Label, error) {
|
|
|
|
ls := []*platform.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 platform.LabelFilter) ([]*platform.Label, error) {
|
|
|
|
ls := []*platform.Label{}
|
|
|
|
filterFn := filterLabelsFn(filter)
|
|
|
|
err := c.forEachLabel(ctx, tx, func(l *platform.Label) bool {
|
|
|
|
if filterFn(l) {
|
|
|
|
ls = append(ls, l)
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ls, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) CreateLabel(ctx context.Context, l *platform.Label) error {
|
|
|
|
return c.db.Update(func(tx *bolt.Tx) error {
|
|
|
|
return c.createLabel(ctx, tx, l)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) createLabel(ctx context.Context, tx *bolt.Tx, l *platform.Label) error {
|
|
|
|
unique := c.uniqueLabel(ctx, tx, l)
|
|
|
|
|
|
|
|
if !unique {
|
2018-12-18 07:59:04 +00:00
|
|
|
return &platform.Error{
|
|
|
|
Code: platform.EConflict,
|
|
|
|
Op: getOp(platform.OpCreateLabel),
|
|
|
|
Msg: fmt.Sprintf("label %s already exists", l.Name),
|
|
|
|
}
|
2018-12-03 16:07:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
v, err := json.Marshal(l)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
key, err := labelKey(l)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := tx.Bucket(labelBucket).Put(key, v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func labelKey(l *platform.Label) ([]byte, error) {
|
|
|
|
encodedResourceID, err := l.ResourceID.Encode()
|
|
|
|
if err != nil {
|
2018-12-18 09:06:28 +00:00
|
|
|
return nil, &platform.Error{
|
|
|
|
Code: platform.EInvalid,
|
|
|
|
Err: err,
|
|
|
|
}
|
2018-12-03 16:07:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
key := make([]byte, len(encodedResourceID)+len(l.Name))
|
|
|
|
copy(key, encodedResourceID)
|
|
|
|
copy(key[len(encodedResourceID):], l.Name)
|
|
|
|
|
|
|
|
return key, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) forEachLabel(ctx context.Context, tx *bolt.Tx, fn func(*platform.Label) bool) error {
|
|
|
|
cur := tx.Bucket(labelBucket).Cursor()
|
|
|
|
for k, v := cur.First(); k != nil; k, v = cur.Next() {
|
|
|
|
l := &platform.Label{}
|
|
|
|
if err := json.Unmarshal(v, l); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !fn(l) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) uniqueLabel(ctx context.Context, tx *bolt.Tx, l *platform.Label) bool {
|
|
|
|
key, err := labelKey(l)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
v := tx.Bucket(labelBucket).Get(key)
|
|
|
|
return len(v) == 0
|
|
|
|
}
|
|
|
|
|
2018-12-18 06:30:41 +00:00
|
|
|
// UpdateLabel updates a label.
|
|
|
|
func (c *Client) UpdateLabel(ctx context.Context, l *platform.Label, upd platform.LabelUpdate) (*platform.Label, error) {
|
|
|
|
var label *platform.Label
|
|
|
|
err := c.db.Update(func(tx *bolt.Tx) error {
|
2018-12-18 17:22:48 +00:00
|
|
|
labelResponse, pe := c.updateLabel(ctx, tx, l, upd)
|
|
|
|
if pe != nil {
|
|
|
|
return &platform.Error{
|
|
|
|
Err: pe,
|
|
|
|
Op: getOp(platform.OpUpdateLabel),
|
|
|
|
}
|
2018-12-18 06:30:41 +00:00
|
|
|
}
|
|
|
|
label = labelResponse
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return label, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) updateLabel(ctx context.Context, tx *bolt.Tx, l *platform.Label, upd platform.LabelUpdate) (*platform.Label, error) {
|
2018-12-18 09:06:28 +00:00
|
|
|
ls, err := c.findLabels(ctx, tx, platform.LabelFilter{Name: l.Name, ResourceID: l.ResourceID})
|
2018-12-18 06:30:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-12-18 09:06:28 +00:00
|
|
|
if len(ls) == 0 {
|
2018-12-18 06:30:41 +00:00
|
|
|
return nil, &platform.Error{
|
2018-12-18 09:06:28 +00:00
|
|
|
Code: platform.ENotFound,
|
|
|
|
Err: platform.ErrLabelNotFound,
|
2018-12-18 06:30:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-18 09:06:28 +00:00
|
|
|
label := ls[0]
|
|
|
|
|
2018-12-20 20:52:48 +00:00
|
|
|
properties := make(map[string]string)
|
|
|
|
|
|
|
|
for k, v := range label.Properties {
|
|
|
|
properties[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range upd.Properties {
|
|
|
|
if v == "" {
|
|
|
|
delete(properties, k)
|
|
|
|
} else {
|
|
|
|
properties[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
label.Properties = properties
|
|
|
|
|
2018-12-18 09:06:28 +00:00
|
|
|
if err := label.Validate(); err != nil {
|
2018-12-18 07:59:04 +00:00
|
|
|
return nil, &platform.Error{
|
|
|
|
Code: platform.EInvalid,
|
2018-12-18 17:22:48 +00:00
|
|
|
Err: err,
|
2018-12-18 07:59:04 +00:00
|
|
|
}
|
2018-12-18 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
2018-12-18 09:06:28 +00:00
|
|
|
if err := c.putLabel(ctx, tx, label); err != nil {
|
2018-12-18 17:22:48 +00:00
|
|
|
return nil, &platform.Error{
|
|
|
|
Err: err,
|
|
|
|
}
|
2018-12-18 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
2018-12-18 09:06:28 +00:00
|
|
|
return label, nil
|
2018-12-18 06:30:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// set a label and overwrite any existing label
|
2018-12-18 09:06:28 +00:00
|
|
|
func (c *Client) putLabel(ctx context.Context, tx *bolt.Tx, l *platform.Label) error {
|
|
|
|
v, err := json.Marshal(l)
|
|
|
|
if err != nil {
|
|
|
|
return &platform.Error{
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
key, pe := labelKey(l)
|
|
|
|
if pe != nil {
|
|
|
|
return pe
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := tx.Bucket(labelBucket).Put(key, v); err != nil {
|
|
|
|
return &platform.Error{
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-18 06:30:41 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-03 16:07:08 +00:00
|
|
|
// DeleteLabel deletes a label.
|
|
|
|
func (c *Client) DeleteLabel(ctx context.Context, l platform.Label) error {
|
|
|
|
return c.db.Update(func(tx *bolt.Tx) error {
|
2018-12-18 06:30:41 +00:00
|
|
|
return c.deleteLabel(ctx, tx, platform.LabelFilter{Name: l.Name, ResourceID: l.ResourceID})
|
2018-12-03 16:07:08 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) deleteLabel(ctx context.Context, tx *bolt.Tx, filter platform.LabelFilter) error {
|
|
|
|
ls, err := c.findLabels(ctx, tx, filter)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(ls) == 0 {
|
2018-12-18 07:59:04 +00:00
|
|
|
return &platform.Error{
|
|
|
|
Code: platform.ENotFound,
|
|
|
|
Op: getOp(platform.OpDeleteLabel),
|
|
|
|
Err: platform.ErrLabelNotFound,
|
|
|
|
}
|
2018-12-03 16:07:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
key, err := labelKey(ls[0])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return tx.Bucket(labelBucket).Delete(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) deleteLabels(ctx context.Context, tx *bolt.Tx, filter platform.LabelFilter) error {
|
|
|
|
ls, err := c.findLabels(ctx, tx, filter)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, l := range ls {
|
|
|
|
key, err := labelKey(l)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = tx.Bucket(labelBucket).Delete(key); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|