influxdb/kv/source.go

318 lines
6.6 KiB
Go

package kv
import (
"context"
"encoding/json"
"fmt"
influxdb "github.com/influxdata/influxdb/v2"
)
var (
sourceBucket = []byte("sourcesv1")
)
// DefaultSource is the default source.
var DefaultSource = influxdb.Source{
Default: true,
Name: "autogen",
Type: influxdb.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))
}
}
// DefaultSource retrieves the default source.
func (s *Service) DefaultSource(ctx context.Context) (*influxdb.Source, error) {
var sr *influxdb.Source
err := s.kv.View(ctx, func(tx Tx) error {
// TODO(desa): make this faster by putting the default source in an index.
srcs, err := s.findSources(ctx, tx, influxdb.FindOptions{})
if err != nil {
return err
}
for _, src := range srcs {
if src.Default {
sr = src
return nil
}
}
return &influxdb.Error{
Code: influxdb.ENotFound,
Msg: "no default source found",
}
})
if err != nil {
return nil, &influxdb.Error{
Err: err,
}
}
return sr, nil
}
// FindSourceByID retrieves a source by id.
func (s *Service) FindSourceByID(ctx context.Context, id influxdb.ID) (*influxdb.Source, error) {
var sr *influxdb.Source
err := s.kv.View(ctx, func(tx Tx) error {
src, pe := s.findSourceByID(ctx, tx, id)
if pe != nil {
return &influxdb.Error{
Err: pe,
}
}
sr = src
return nil
})
return sr, err
}
func (s *Service) findSourceByID(ctx context.Context, tx Tx, id influxdb.ID) (*influxdb.Source, error) {
encodedID, err := id.Encode()
if err != nil {
return nil, &influxdb.Error{
Err: err,
}
}
b, err := tx.Bucket(sourceBucket)
if err != nil {
return nil, err
}
v, err := b.Get(encodedID)
if IsNotFound(err) {
return nil, &influxdb.Error{
Code: influxdb.ENotFound,
Msg: influxdb.ErrSourceNotFound,
}
}
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
var sr influxdb.Source
if err := json.Unmarshal(v, &sr); err != nil {
return nil, &influxdb.Error{
Err: err,
}
}
return &sr, 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 (s *Service) FindSources(ctx context.Context, opt influxdb.FindOptions) ([]*influxdb.Source, int, error) {
ss := []*influxdb.Source{}
err := s.kv.View(ctx, func(tx Tx) error {
srcs, err := s.findSources(ctx, tx, opt)
if err != nil {
return err
}
ss = srcs
return nil
})
if err != nil {
return nil, 0, &influxdb.Error{
Op: influxdb.OpFindSources,
Err: err,
}
}
return ss, len(ss), nil
}
func (s *Service) findSources(ctx context.Context, tx Tx, opt influxdb.FindOptions) ([]*influxdb.Source, error) {
ss := []*influxdb.Source{}
err := s.forEachSource(ctx, tx, func(s *influxdb.Source) bool {
ss = append(ss, s)
return true
})
if err != nil {
return nil, err
}
return ss, nil
}
// CreateSource creates a influxdb source and sets s.ID.
func (s *Service) CreateSource(ctx context.Context, src *influxdb.Source) error {
err := s.kv.Update(ctx, func(tx Tx) error {
src.ID = s.IDGenerator.ID()
// Generating an organization id if it missing or invalid
if !src.OrganizationID.Valid() {
src.OrganizationID = s.IDGenerator.ID()
}
return s.putSource(ctx, tx, src)
})
if err != nil {
return &influxdb.Error{
Err: err,
}
}
return nil
}
// PutSource will put a source without setting an ID.
func (s *Service) PutSource(ctx context.Context, src *influxdb.Source) error {
return s.kv.Update(ctx, func(tx Tx) error {
return s.putSource(ctx, tx, src)
})
}
func (s *Service) putSource(ctx context.Context, tx Tx, src *influxdb.Source) error {
v, err := json.Marshal(src)
if err != nil {
return err
}
encodedID, err := src.ID.Encode()
if err != nil {
return err
}
b, err := tx.Bucket(sourceBucket)
if err != nil {
return err
}
if err := b.Put(encodedID, v); err != nil {
return err
}
return nil
}
// forEachSource will iterate through all sources while fn returns true.
func (s *Service) forEachSource(ctx context.Context, tx Tx, fn func(*influxdb.Source) bool) error {
b, err := tx.Bucket(sourceBucket)
if err != nil {
return err
}
cur, err := b.ForwardCursor(nil)
if err != nil {
return err
}
for k, v := cur.Next(); k != nil; k, v = cur.Next() {
s := &influxdb.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 (s *Service) UpdateSource(ctx context.Context, id influxdb.ID, upd influxdb.SourceUpdate) (*influxdb.Source, error) {
var sr *influxdb.Source
err := s.kv.Update(ctx, func(tx Tx) error {
src, err := s.updateSource(ctx, tx, id, upd)
if err != nil {
return &influxdb.Error{
Err: err,
}
}
sr = src
return nil
})
return sr, err
}
func (s *Service) updateSource(ctx context.Context, tx Tx, id influxdb.ID, upd influxdb.SourceUpdate) (*influxdb.Source, error) {
src, pe := s.findSourceByID(ctx, tx, id)
if pe != nil {
return nil, pe
}
if err := upd.Apply(src); err != nil {
return nil, err
}
if err := s.putSource(ctx, tx, src); err != nil {
return nil, err
}
return src, nil
}
// DeleteSource deletes a source and prunes it from the index.
func (s *Service) DeleteSource(ctx context.Context, id influxdb.ID) error {
return s.kv.Update(ctx, func(tx Tx) error {
pe := s.deleteSource(ctx, tx, id)
if pe != nil {
return &influxdb.Error{
Err: pe,
}
}
return nil
})
}
func (s *Service) deleteSource(ctx context.Context, tx Tx, id influxdb.ID) error {
if id == DefaultSource.ID {
return &influxdb.Error{
Code: influxdb.EForbidden,
Msg: "cannot delete autogen source",
}
}
_, pe := s.findSourceByID(ctx, tx, id)
if pe != nil {
return pe
}
encodedID, err := id.Encode()
if err != nil {
return &influxdb.Error{
Err: err,
}
}
b, err := tx.Bucket(sourceBucket)
if err != nil {
return err
}
if err = b.Delete(encodedID); err != nil {
return &influxdb.Error{
Err: err,
}
}
return nil
}