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 }