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  }