github.com/Jeffail/benthos/v3@v3.65.0/public/service/environment.go (about) 1 package service 2 3 import ( 4 "fmt" 5 6 ibloblang "github.com/Jeffail/benthos/v3/internal/bloblang" 7 "github.com/Jeffail/benthos/v3/internal/bundle" 8 ibuffer "github.com/Jeffail/benthos/v3/internal/component/buffer" 9 "github.com/Jeffail/benthos/v3/internal/docs" 10 "github.com/Jeffail/benthos/v3/lib/buffer" 11 "github.com/Jeffail/benthos/v3/lib/cache" 12 "github.com/Jeffail/benthos/v3/lib/input" 13 "github.com/Jeffail/benthos/v3/lib/output" 14 "github.com/Jeffail/benthos/v3/lib/processor" 15 "github.com/Jeffail/benthos/v3/lib/ratelimit" 16 "github.com/Jeffail/benthos/v3/lib/types" 17 "github.com/Jeffail/benthos/v3/public/bloblang" 18 ) 19 20 // Environment is a collection of Benthos component plugins that can be used in 21 // order to build and run streaming pipelines with access to different sets of 22 // plugins. This can be useful for sandboxing, testing, etc, but most plugin 23 // authors do not need to create an Environment and can simply use the global 24 // environment. 25 type Environment struct { 26 internal *bundle.Environment 27 bloblangEnv *bloblang.Environment 28 } 29 30 var globalEnvironment = &Environment{ 31 internal: bundle.GlobalEnvironment, 32 bloblangEnv: bloblang.GlobalEnvironment(), 33 } 34 35 // NewEnvironment creates a new environment that inherits all globally defined 36 // plugins, but can have plugins defined on it that are isolated. 37 func NewEnvironment() *Environment { 38 return globalEnvironment.Clone() 39 } 40 41 // Clone an environment, creating a new environment containing the same plugins 42 // that can be modified independently of the source. 43 func (e *Environment) Clone() *Environment { 44 return &Environment{ 45 internal: e.internal.Clone(), 46 bloblangEnv: e.bloblangEnv.WithoutFunctions().WithoutMethods(), 47 } 48 } 49 50 // UseBloblangEnvironment configures the service environment to restrict 51 // components constructed with it to a specific Bloblang environment. 52 func (e *Environment) UseBloblangEnvironment(bEnv *bloblang.Environment) { 53 e.bloblangEnv = bEnv 54 } 55 56 // NewStreamBuilder creates a new StreamBuilder upon the defined environment, 57 // only components known to this environment will be available to the stream 58 // builder. 59 func (e *Environment) NewStreamBuilder() *StreamBuilder { 60 sb := NewStreamBuilder() 61 sb.env = e 62 return sb 63 } 64 65 //------------------------------------------------------------------------------ 66 67 func (e *Environment) getBloblangParserEnv() *ibloblang.Environment { 68 if unwrapper, ok := e.bloblangEnv.XUnwrapper().(interface { 69 Unwrap() *ibloblang.Environment 70 }); ok { 71 return unwrapper.Unwrap() 72 } 73 return ibloblang.GlobalEnvironment() 74 } 75 76 //------------------------------------------------------------------------------ 77 78 // RegisterBatchBuffer attempts to register a new buffer plugin by providing a 79 // description of the configuration for the buffer and a constructor for the 80 // buffer processor. The constructor will be called for each instantiation of 81 // the component within a config. 82 // 83 // Consumed message batches must be created by upstream components (inputs, etc) 84 // otherwise this buffer will simply receive batches containing single 85 // messages. 86 func (e *Environment) RegisterBatchBuffer(name string, spec *ConfigSpec, ctor BatchBufferConstructor) error { 87 componentSpec := spec.component 88 componentSpec.Name = name 89 componentSpec.Type = docs.TypeBuffer 90 return e.internal.BufferAdd(func(conf buffer.Config, nm bundle.NewManagement) (buffer.Type, error) { 91 pluginConf, err := extractConfig(nm, spec, name, conf.Plugin, conf) 92 if err != nil { 93 return nil, err 94 } 95 b, err := ctor(pluginConf, newResourcesFromManager(nm)) 96 if err != nil { 97 return nil, err 98 } 99 return ibuffer.NewStream(conf.Type, newAirGapBatchBuffer(b), nm.Logger(), nm.Metrics()), nil 100 }, componentSpec) 101 } 102 103 // WalkBuffers executes a provided function argument for every buffer component 104 // that has been registered to the environment. 105 func (e *Environment) WalkBuffers(fn func(name string, config *ConfigView)) { 106 for _, v := range e.internal.BufferDocs() { 107 fn(v.Name, &ConfigView{ 108 component: v, 109 }) 110 } 111 } 112 113 // RegisterCache attempts to register a new cache plugin by providing a 114 // description of the configuration for the plugin as well as a constructor for 115 // the cache itself. The constructor will be called for each instantiation of 116 // the component within a config. 117 func (e *Environment) RegisterCache(name string, spec *ConfigSpec, ctor CacheConstructor) error { 118 componentSpec := spec.component 119 componentSpec.Name = name 120 componentSpec.Type = docs.TypeCache 121 return e.internal.CacheAdd(func(conf cache.Config, nm bundle.NewManagement) (types.Cache, error) { 122 pluginConf, err := extractConfig(nm, spec, name, conf.Plugin, conf) 123 if err != nil { 124 return nil, err 125 } 126 c, err := ctor(pluginConf, newResourcesFromManager(nm)) 127 if err != nil { 128 return nil, err 129 } 130 return newAirGapCache(c, nm.Metrics()), nil 131 }, componentSpec) 132 } 133 134 // WalkCaches executes a provided function argument for every cache component 135 // that has been registered to the environment. 136 func (e *Environment) WalkCaches(fn func(name string, config *ConfigView)) { 137 for _, v := range e.internal.CacheDocs() { 138 fn(v.Name, &ConfigView{ 139 component: v, 140 }) 141 } 142 } 143 144 // RegisterInput attempts to register a new input plugin by providing a 145 // description of the configuration for the plugin as well as a constructor for 146 // the input itself. The constructor will be called for each instantiation of 147 // the component within a config. 148 // 149 // If your input implementation doesn't have a specific mechanism for dealing 150 // with a nack (when the AckFunc provides a non-nil error) then you can instead 151 // wrap your input implementation with AutoRetryNacks to get automatic retries. 152 func (e *Environment) RegisterInput(name string, spec *ConfigSpec, ctor InputConstructor) error { 153 componentSpec := spec.component 154 componentSpec.Name = name 155 componentSpec.Type = docs.TypeInput 156 return e.internal.InputAdd(bundle.InputConstructorFromSimple(func(conf input.Config, nm bundle.NewManagement) (input.Type, error) { 157 pluginConf, err := extractConfig(nm, spec, name, conf.Plugin, conf) 158 if err != nil { 159 return nil, err 160 } 161 i, err := ctor(pluginConf, newResourcesFromManager(nm)) 162 if err != nil { 163 return nil, err 164 } 165 rdr := newAirGapReader(i) 166 return input.NewAsyncReader(conf.Type, false, rdr, nm.Logger(), nm.Metrics()) 167 }), componentSpec) 168 } 169 170 // RegisterBatchInput attempts to register a new batched input plugin by 171 // providing a description of the configuration for the plugin as well as a 172 // constructor for the input itself. The constructor will be called for each 173 // instantiation of the component within a config. 174 // 175 // If your input implementation doesn't have a specific mechanism for dealing 176 // with a nack (when the AckFunc provides a non-nil error) then you can instead 177 // wrap your input implementation with AutoRetryNacksBatched to get automatic 178 // retries. 179 func (e *Environment) RegisterBatchInput(name string, spec *ConfigSpec, ctor BatchInputConstructor) error { 180 componentSpec := spec.component 181 componentSpec.Name = name 182 componentSpec.Type = docs.TypeInput 183 return e.internal.InputAdd(bundle.InputConstructorFromSimple(func(conf input.Config, nm bundle.NewManagement) (input.Type, error) { 184 pluginConf, err := extractConfig(nm, spec, name, conf.Plugin, conf) 185 if err != nil { 186 return nil, err 187 } 188 i, err := ctor(pluginConf, newResourcesFromManager(nm)) 189 if err != nil { 190 return nil, err 191 } 192 rdr := newAirGapBatchReader(i) 193 return input.NewAsyncReader(conf.Type, false, rdr, nm.Logger(), nm.Metrics()) 194 }), componentSpec) 195 } 196 197 // WalkInputs executes a provided function argument for every input component 198 // that has been registered to the environment. 199 func (e *Environment) WalkInputs(fn func(name string, config *ConfigView)) { 200 for _, v := range e.internal.InputDocs() { 201 fn(v.Name, &ConfigView{ 202 component: v, 203 }) 204 } 205 } 206 207 // RegisterOutput attempts to register a new output plugin by providing a 208 // description of the configuration for the plugin as well as a constructor for 209 // the output itself. The constructor will be called for each instantiation of 210 // the component within a config. 211 func (e *Environment) RegisterOutput(name string, spec *ConfigSpec, ctor OutputConstructor) error { 212 componentSpec := spec.component 213 componentSpec.Name = name 214 componentSpec.Type = docs.TypeOutput 215 return e.internal.OutputAdd(bundle.OutputConstructorFromSimple( 216 func(conf output.Config, nm bundle.NewManagement) (output.Type, error) { 217 pluginConf, err := extractConfig(nm, spec, name, conf.Plugin, conf) 218 if err != nil { 219 return nil, err 220 } 221 op, maxInFlight, err := ctor(pluginConf, newResourcesFromManager(nm)) 222 if err != nil { 223 return nil, err 224 } 225 if maxInFlight < 1 { 226 return nil, fmt.Errorf("invalid maxInFlight parameter: %v", maxInFlight) 227 } 228 w := newAirGapWriter(op) 229 o, err := output.NewAsyncWriter(conf.Type, maxInFlight, w, nm.Logger(), nm.Metrics()) 230 if err != nil { 231 return nil, err 232 } 233 return output.OnlySinglePayloads(o), nil 234 }, 235 ), componentSpec) 236 } 237 238 // RegisterBatchOutput attempts to register a new output plugin by providing a 239 // description of the configuration for the plugin as well as a constructor for 240 // the output itself. The constructor will be called for each instantiation of 241 // the component within a config. 242 // 243 // The constructor of a batch output is able to return a batch policy to be 244 // applied before calls to write are made, creating batches from the stream of 245 // messages. However, batches can also be created by upstream components 246 // (inputs, buffers, etc). 247 // 248 // If a batch has been formed upstream it is possible that its size may exceed 249 // the policy specified in your constructor. 250 func (e *Environment) RegisterBatchOutput(name string, spec *ConfigSpec, ctor BatchOutputConstructor) error { 251 componentSpec := spec.component 252 componentSpec.Name = name 253 componentSpec.Type = docs.TypeOutput 254 return e.internal.OutputAdd(bundle.OutputConstructorFromSimple( 255 func(conf output.Config, nm bundle.NewManagement) (output.Type, error) { 256 pluginConf, err := extractConfig(nm, spec, name, conf.Plugin, conf) 257 if err != nil { 258 return nil, err 259 } 260 op, batchPolicy, maxInFlight, err := ctor(pluginConf, newResourcesFromManager(nm)) 261 if err != nil { 262 return nil, err 263 } 264 265 if maxInFlight < 1 { 266 return nil, fmt.Errorf("invalid maxInFlight parameter: %v", maxInFlight) 267 } 268 269 w := newAirGapBatchWriter(op) 270 o, err := output.NewAsyncWriter(conf.Type, maxInFlight, w, nm.Logger(), nm.Metrics()) 271 if err != nil { 272 return nil, err 273 } 274 return output.NewBatcherFromConfig(batchPolicy.toInternal(), o, nm, nm.Logger(), nm.Metrics()) 275 }, 276 ), componentSpec) 277 } 278 279 // WalkOutputs executes a provided function argument for every output component 280 // that has been registered to the environment. 281 func (e *Environment) WalkOutputs(fn func(name string, config *ConfigView)) { 282 for _, v := range e.internal.OutputDocs() { 283 fn(v.Name, &ConfigView{ 284 component: v, 285 }) 286 } 287 } 288 289 // RegisterProcessor attempts to register a new processor plugin by providing 290 // a description of the configuration for the processor and a constructor for 291 // the processor itself. The constructor will be called for each instantiation 292 // of the component within a config. 293 // 294 // For simple transformations consider implementing a Bloblang plugin method 295 // instead. 296 func (e *Environment) RegisterProcessor(name string, spec *ConfigSpec, ctor ProcessorConstructor) error { 297 componentSpec := spec.component 298 componentSpec.Name = name 299 componentSpec.Type = docs.TypeProcessor 300 return e.internal.ProcessorAdd(func(conf processor.Config, nm bundle.NewManagement) (processor.Type, error) { 301 pluginConf, err := extractConfig(nm, spec, name, conf.Plugin, conf) 302 if err != nil { 303 return nil, err 304 } 305 r, err := ctor(pluginConf, newResourcesFromManager(nm)) 306 if err != nil { 307 return nil, err 308 } 309 return newAirGapProcessor(conf.Type, r, nm.Metrics()), nil 310 }, componentSpec) 311 } 312 313 // RegisterBatchProcessor attempts to register a new processor plugin by 314 // providing a description of the configuration for the processor and a 315 // constructor for the processor itself. The constructor will be called for each 316 // instantiation of the component within a config. 317 // 318 // Message batches must be created by upstream components (inputs, buffers, etc) 319 // otherwise this processor will simply receive batches containing single 320 // messages. 321 func (e *Environment) RegisterBatchProcessor(name string, spec *ConfigSpec, ctor BatchProcessorConstructor) error { 322 componentSpec := spec.component 323 componentSpec.Name = name 324 componentSpec.Type = docs.TypeProcessor 325 return e.internal.ProcessorAdd(func(conf processor.Config, nm bundle.NewManagement) (processor.Type, error) { 326 pluginConf, err := extractConfig(nm, spec, name, conf.Plugin, conf) 327 if err != nil { 328 return nil, err 329 } 330 r, err := ctor(pluginConf, newResourcesFromManager(nm)) 331 if err != nil { 332 return nil, err 333 } 334 return newAirGapBatchProcessor(conf.Type, r, nm.Metrics()), nil 335 }, componentSpec) 336 } 337 338 // WalkProcessors executes a provided function argument for every processor 339 // component that has been registered to the environment. 340 func (e *Environment) WalkProcessors(fn func(name string, config *ConfigView)) { 341 for _, v := range e.internal.ProcessorDocs() { 342 fn(v.Name, &ConfigView{ 343 component: v, 344 }) 345 } 346 } 347 348 // RegisterRateLimit attempts to register a new rate limit plugin by providing 349 // a description of the configuration for the plugin as well as a constructor 350 // for the rate limit itself. The constructor will be called for each 351 // instantiation of the component within a config. 352 func (e *Environment) RegisterRateLimit(name string, spec *ConfigSpec, ctor RateLimitConstructor) error { 353 componentSpec := spec.component 354 componentSpec.Name = name 355 componentSpec.Type = docs.TypeRateLimit 356 return e.internal.RateLimitAdd(func(conf ratelimit.Config, nm bundle.NewManagement) (types.RateLimit, error) { 357 pluginConf, err := extractConfig(nm, spec, name, conf.Plugin, conf) 358 if err != nil { 359 return nil, err 360 } 361 r, err := ctor(pluginConf, newResourcesFromManager(nm)) 362 if err != nil { 363 return nil, err 364 } 365 return newAirGapRateLimit(r, nm.Metrics()), nil 366 }, componentSpec) 367 } 368 369 // WalkRateLimits executes a provided function argument for every rate limit 370 // component that has been registered to the environment. 371 func (e *Environment) WalkRateLimits(fn func(name string, config *ConfigView)) { 372 for _, v := range e.internal.RateLimitDocs() { 373 fn(v.Name, &ConfigView{ 374 component: v, 375 }) 376 } 377 } 378 379 // WalkMetrics executes a provided function argument for every metrics component 380 // that has been registered to the environment. Note that metrics components 381 // available to an environment cannot be modified 382 func (e *Environment) WalkMetrics(fn func(name string, config *ConfigView)) { 383 for _, v := range bundle.AllMetrics.Docs() { 384 fn(v.Name, &ConfigView{ 385 component: v, 386 }) 387 } 388 } 389 390 // WalkTracers executes a provided function argument for every tracer component 391 // that has been registered to the environment. Note that tracer components 392 // available to an environment cannot be modified 393 func (e *Environment) WalkTracers(fn func(name string, config *ConfigView)) { 394 for _, v := range bundle.AllTracers.Docs() { 395 fn(v.Name, &ConfigView{ 396 component: v, 397 }) 398 } 399 }