github.com/influxdata/influxdb/v2@v2.7.6/telegraf/service/telegraf.go (about)

     1  package service
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  
     8  	influxdb "github.com/influxdata/influxdb/v2"
     9  	"github.com/influxdata/influxdb/v2/kit/platform"
    10  	"github.com/influxdata/influxdb/v2/kit/platform/errors"
    11  	"github.com/influxdata/influxdb/v2/kv"
    12  	"github.com/influxdata/influxdb/v2/snowflake"
    13  	"github.com/influxdata/influxdb/v2/telegraf"
    14  )
    15  
    16  var (
    17  	// ErrTelegrafNotFound is used when the telegraf configuration is not found.
    18  	ErrTelegrafNotFound = &errors.Error{
    19  		Msg:  "telegraf configuration not found",
    20  		Code: errors.ENotFound,
    21  	}
    22  
    23  	// ErrInvalidTelegrafID is used when the service was provided
    24  	// an invalid ID format.
    25  	ErrInvalidTelegrafID = &errors.Error{
    26  		Code: errors.EInvalid,
    27  		Msg:  "provided telegraf configuration ID has invalid format",
    28  	}
    29  
    30  	// ErrInvalidTelegrafOrgID is the error message for a missing or invalid organization ID.
    31  	ErrInvalidTelegrafOrgID = &errors.Error{
    32  		Code: errors.EEmptyValue,
    33  		Msg:  "provided telegraf configuration organization ID is missing or invalid",
    34  	}
    35  )
    36  
    37  // UnavailableTelegrafServiceError is used if we aren't able to interact with the
    38  // store, it means the store is not available at the moment (e.g. network).
    39  func UnavailableTelegrafServiceError(err error) *errors.Error {
    40  	return &errors.Error{
    41  		Code: errors.EInternal,
    42  		Msg:  fmt.Sprintf("Unable to connect to telegraf service. Please try again; Err: %v", err),
    43  		Op:   "kv/telegraf",
    44  	}
    45  }
    46  
    47  // InternalTelegrafServiceError is used when the error comes from an
    48  // internal system.
    49  func InternalTelegrafServiceError(err error) *errors.Error {
    50  	return &errors.Error{
    51  		Code: errors.EInternal,
    52  		Msg:  fmt.Sprintf("Unknown internal telegraf data error; Err: %v", err),
    53  		Op:   "kv/telegraf",
    54  	}
    55  }
    56  
    57  // CorruptTelegrafError is used when the config cannot be unmarshalled from the
    58  // bytes stored in the kv.
    59  func CorruptTelegrafError(err error) *errors.Error {
    60  	return &errors.Error{
    61  		Code: errors.EInternal,
    62  		Msg:  fmt.Sprintf("Unknown internal telegraf data error; Err: %v", err),
    63  		Op:   "kv/telegraf",
    64  	}
    65  }
    66  
    67  // ErrUnprocessableTelegraf is used when a telegraf is not able to be converted to JSON.
    68  func ErrUnprocessableTelegraf(err error) *errors.Error {
    69  	return &errors.Error{
    70  		Code: errors.EUnprocessableEntity,
    71  		Msg:  fmt.Sprintf("unable to convert telegraf configuration into JSON; Err %v", err),
    72  	}
    73  }
    74  
    75  var (
    76  	telegrafBucket        = []byte("telegrafv1")
    77  	telegrafPluginsBucket = []byte("telegrafPluginsv1")
    78  )
    79  
    80  var _ influxdb.TelegrafConfigStore = (*Service)(nil)
    81  
    82  // Service is a telegraf config service.
    83  type Service struct {
    84  	kv kv.Store
    85  
    86  	byOrganisationIndex *kv.Index
    87  
    88  	IDGenerator platform.IDGenerator
    89  }
    90  
    91  // New constructs and configures a new telegraf config service.
    92  func New(store kv.Store) *Service {
    93  	return &Service{
    94  		kv: store,
    95  		byOrganisationIndex: kv.NewIndex(
    96  			telegraf.ByOrganizationIndexMapping,
    97  			kv.WithIndexReadPathEnabled,
    98  		),
    99  		IDGenerator: snowflake.NewIDGenerator(),
   100  	}
   101  }
   102  
   103  func (s *Service) telegrafBucket(tx kv.Tx) (kv.Bucket, error) {
   104  	b, err := tx.Bucket(telegrafBucket)
   105  	if err != nil {
   106  		return nil, UnavailableTelegrafServiceError(err)
   107  	}
   108  	return b, nil
   109  }
   110  
   111  func (s *Service) telegrafPluginsBucket(tx kv.Tx) (kv.Bucket, error) {
   112  	b, err := tx.Bucket(telegrafPluginsBucket)
   113  	if err != nil {
   114  		return nil, UnavailableTelegrafServiceError(err)
   115  	}
   116  	return b, nil
   117  }
   118  
   119  // FindTelegrafConfigByID returns a single telegraf config by ID.
   120  func (s *Service) FindTelegrafConfigByID(ctx context.Context, id platform.ID) (*influxdb.TelegrafConfig, error) {
   121  	var (
   122  		tc  *influxdb.TelegrafConfig
   123  		err error
   124  	)
   125  
   126  	err = s.kv.View(ctx, func(tx kv.Tx) error {
   127  		tc, err = s.findTelegrafConfigByID(ctx, tx, id)
   128  		return err
   129  	})
   130  
   131  	return tc, err
   132  }
   133  
   134  func (s *Service) findTelegrafConfigByID(ctx context.Context, tx kv.Tx, id platform.ID) (*influxdb.TelegrafConfig, error) {
   135  	encID, err := id.Encode()
   136  	if err != nil {
   137  		return nil, ErrInvalidTelegrafID
   138  	}
   139  
   140  	bucket, err := s.telegrafBucket(tx)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	v, err := bucket.Get(encID)
   146  	if kv.IsNotFound(err) {
   147  		return nil, ErrTelegrafNotFound
   148  	}
   149  	if err != nil {
   150  		return nil, InternalTelegrafServiceError(err)
   151  	}
   152  
   153  	return unmarshalTelegraf(v)
   154  }
   155  
   156  // FindTelegrafConfigs returns a list of telegraf configs that match filter and the total count of matching telegraf configs.
   157  // FindOptions are ignored.
   158  func (s *Service) FindTelegrafConfigs(ctx context.Context, filter influxdb.TelegrafConfigFilter, opt ...influxdb.FindOptions) (tcs []*influxdb.TelegrafConfig, n int, err error) {
   159  	err = s.kv.View(ctx, func(tx kv.Tx) error {
   160  		tcs, n, err = s.findTelegrafConfigs(ctx, tx, filter)
   161  		return err
   162  	})
   163  	return tcs, n, err
   164  }
   165  
   166  func (s *Service) findTelegrafConfigs(ctx context.Context, tx kv.Tx, filter influxdb.TelegrafConfigFilter) ([]*influxdb.TelegrafConfig, int, error) {
   167  	var (
   168  		tcs = make([]*influxdb.TelegrafConfig, 0)
   169  	)
   170  
   171  	visit := func(k, v []byte) (bool, error) {
   172  		var tc influxdb.TelegrafConfig
   173  		if err := json.Unmarshal(v, &tc); err != nil {
   174  			return false, err
   175  		}
   176  
   177  		tcs = append(tcs, &tc)
   178  
   179  		// stop cursing when limit is reached
   180  		return true, nil
   181  	}
   182  
   183  	if filter.OrgID == nil {
   184  		// forward cursor entire bucket
   185  		bucket, err := s.telegrafBucket(tx)
   186  		if err != nil {
   187  			return nil, 0, err
   188  		}
   189  
   190  		// cursors do not support numeric offset
   191  		// but we can at least constrain the response
   192  		// size by the offset + limit since we are
   193  		// not doing any other filtering
   194  		// REMOVE this cursor option if you do any
   195  		// other filtering
   196  
   197  		cursor, err := bucket.ForwardCursor(nil)
   198  		if err != nil {
   199  			return nil, 0, err
   200  		}
   201  
   202  		return tcs, len(tcs), kv.WalkCursor(ctx, cursor, visit)
   203  	}
   204  
   205  	orgID, err := filter.OrgID.Encode()
   206  	if err != nil {
   207  		return nil, 0, err
   208  	}
   209  
   210  	return tcs, len(tcs), s.byOrganisationIndex.Walk(ctx, tx, orgID, visit)
   211  }
   212  
   213  // PutTelegrafConfig put a telegraf config to storage.
   214  func (s *Service) PutTelegrafConfig(ctx context.Context, tc *influxdb.TelegrafConfig) error {
   215  	return s.kv.Update(ctx, func(tx kv.Tx) (err error) {
   216  		return s.putTelegrafConfig(ctx, tx, tc)
   217  	})
   218  }
   219  
   220  func (s *Service) putTelegrafConfig(ctx context.Context, tx kv.Tx, tc *influxdb.TelegrafConfig) error {
   221  	encodedID, err := tc.ID.Encode()
   222  	if err != nil {
   223  		return ErrInvalidTelegrafID
   224  	}
   225  
   226  	if !tc.OrgID.Valid() {
   227  		return ErrInvalidTelegrafOrgID
   228  	}
   229  
   230  	orgID, err := tc.OrgID.Encode()
   231  	if err != nil {
   232  		return err
   233  	}
   234  
   235  	// insert index entry for orgID -> id
   236  	if err := s.byOrganisationIndex.Insert(tx, orgID, encodedID); err != nil {
   237  		return err
   238  	}
   239  
   240  	v, err := marshalTelegraf(tc)
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	bucket, err := s.telegrafBucket(tx)
   246  	if err != nil {
   247  		return err
   248  	}
   249  
   250  	if err := bucket.Put(encodedID, v); err != nil {
   251  		return UnavailableTelegrafServiceError(err)
   252  	}
   253  
   254  	return s.putTelegrafConfigStats(encodedID, tx, tc)
   255  }
   256  
   257  func (s *Service) putTelegrafConfigStats(encodedID []byte, tx kv.Tx, tc *influxdb.TelegrafConfig) error {
   258  	bucket, err := s.telegrafPluginsBucket(tx)
   259  	if err != nil {
   260  		return err
   261  	}
   262  
   263  	v, err := marshalTelegrafPlugins(tc.CountPlugins())
   264  	if err != nil {
   265  		return err
   266  	}
   267  
   268  	if err := bucket.Put(encodedID, v); err != nil {
   269  		return UnavailableTelegrafServiceError(err)
   270  	}
   271  
   272  	return nil
   273  }
   274  
   275  // CreateTelegrafConfig creates a new telegraf config and sets b.ID with the new identifier.
   276  func (s *Service) CreateTelegrafConfig(ctx context.Context, tc *influxdb.TelegrafConfig, userID platform.ID) error {
   277  	return s.kv.Update(ctx, func(tx kv.Tx) error {
   278  		return s.createTelegrafConfig(ctx, tx, tc, userID)
   279  	})
   280  }
   281  
   282  func (s *Service) createTelegrafConfig(ctx context.Context, tx kv.Tx, tc *influxdb.TelegrafConfig, userID platform.ID) error {
   283  	tc.ID = s.IDGenerator.ID()
   284  
   285  	return s.putTelegrafConfig(ctx, tx, tc)
   286  }
   287  
   288  // UpdateTelegrafConfig updates a single telegraf config.
   289  // Returns the new telegraf config after update.
   290  func (s *Service) UpdateTelegrafConfig(ctx context.Context, id platform.ID, tc *influxdb.TelegrafConfig, userID platform.ID) (*influxdb.TelegrafConfig, error) {
   291  	var err error
   292  	err = s.kv.Update(ctx, func(tx kv.Tx) error {
   293  		tc, err = s.updateTelegrafConfig(ctx, tx, id, tc, userID)
   294  		return err
   295  	})
   296  	return tc, err
   297  }
   298  
   299  func (s *Service) updateTelegrafConfig(ctx context.Context, tx kv.Tx, id platform.ID, tc *influxdb.TelegrafConfig, userID platform.ID) (*influxdb.TelegrafConfig, error) {
   300  	current, err := s.findTelegrafConfigByID(ctx, tx, id)
   301  	if err != nil {
   302  		return nil, err
   303  	}
   304  
   305  	// ID and OrganizationID can not be updated
   306  	tc.ID = current.ID
   307  	tc.OrgID = current.OrgID
   308  	err = s.putTelegrafConfig(ctx, tx, tc)
   309  	return tc, err
   310  }
   311  
   312  // DeleteTelegrafConfig removes a telegraf config by ID.
   313  func (s *Service) DeleteTelegrafConfig(ctx context.Context, id platform.ID) error {
   314  	return s.kv.Update(ctx, func(tx kv.Tx) error {
   315  		return s.deleteTelegrafConfig(ctx, tx, id)
   316  	})
   317  }
   318  
   319  func (s *Service) deleteTelegrafConfig(ctx context.Context, tx kv.Tx, id platform.ID) error {
   320  	tc, err := s.findTelegrafConfigByID(ctx, tx, id)
   321  	if err != nil {
   322  		return err
   323  	}
   324  
   325  	encodedID, err := tc.ID.Encode()
   326  	if err != nil {
   327  		return ErrInvalidTelegrafID
   328  	}
   329  
   330  	orgID, err := tc.OrgID.Encode()
   331  	if err != nil {
   332  		return err
   333  	}
   334  
   335  	// removing index entry for orgID -> id
   336  	if err := s.byOrganisationIndex.Delete(tx, orgID, encodedID); err != nil {
   337  		return err
   338  	}
   339  
   340  	bucket, err := s.telegrafBucket(tx)
   341  	if err != nil {
   342  		return err
   343  	}
   344  
   345  	_, err = bucket.Get(encodedID)
   346  	if kv.IsNotFound(err) {
   347  		return ErrTelegrafNotFound
   348  	}
   349  	if err != nil {
   350  		return InternalTelegrafServiceError(err)
   351  	}
   352  
   353  	if err := bucket.Delete(encodedID); err != nil {
   354  		return UnavailableTelegrafServiceError(err)
   355  	}
   356  
   357  	return s.deleteTelegrafConfigStats(encodedID, tx)
   358  }
   359  
   360  func (s *Service) deleteTelegrafConfigStats(encodedID []byte, tx kv.Tx) error {
   361  	bucket, err := s.telegrafPluginsBucket(tx)
   362  	if err != nil {
   363  		return err
   364  	}
   365  
   366  	if err := bucket.Delete(encodedID); err != nil {
   367  		return &errors.Error{
   368  			Code: errors.EInternal,
   369  			Msg:  fmt.Sprintf("Unable to connect to telegraf config stats service. Please try again; Err: %v", err),
   370  			Op:   "kv/telegraf",
   371  		}
   372  	}
   373  
   374  	return nil
   375  }
   376  
   377  // unmarshalTelegraf turns the stored byte slice in the kv into a *influxdb.TelegrafConfig.
   378  func unmarshalTelegraf(v []byte) (*influxdb.TelegrafConfig, error) {
   379  	t := &influxdb.TelegrafConfig{}
   380  	if err := json.Unmarshal(v, t); err != nil {
   381  		return nil, CorruptTelegrafError(err)
   382  	}
   383  	return t, nil
   384  }
   385  
   386  func marshalTelegraf(tc *influxdb.TelegrafConfig) ([]byte, error) {
   387  	v, err := json.Marshal(tc)
   388  	if err != nil {
   389  		return nil, ErrUnprocessableTelegraf(err)
   390  	}
   391  	return v, nil
   392  }
   393  
   394  func marshalTelegrafPlugins(plugins map[string]float64) ([]byte, error) {
   395  	v, err := json.Marshal(plugins)
   396  	if err != nil {
   397  		return nil, ErrUnprocessableTelegraf(err)
   398  	}
   399  	return v, nil
   400  }