github.com/Jeffail/benthos/v3@v3.65.0/lib/manager/type.go (about)

     1  package manager
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"path"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/Jeffail/benthos/v3/internal/bloblang"
    12  	"github.com/Jeffail/benthos/v3/internal/bundle"
    13  	imetrics "github.com/Jeffail/benthos/v3/internal/component/metrics"
    14  	"github.com/Jeffail/benthos/v3/internal/docs"
    15  	"github.com/Jeffail/benthos/v3/lib/buffer"
    16  	"github.com/Jeffail/benthos/v3/lib/cache"
    17  	"github.com/Jeffail/benthos/v3/lib/condition"
    18  	"github.com/Jeffail/benthos/v3/lib/input"
    19  	"github.com/Jeffail/benthos/v3/lib/log"
    20  	"github.com/Jeffail/benthos/v3/lib/metrics"
    21  	"github.com/Jeffail/benthos/v3/lib/output"
    22  	"github.com/Jeffail/benthos/v3/lib/processor"
    23  	"github.com/Jeffail/benthos/v3/lib/ratelimit"
    24  	"github.com/Jeffail/benthos/v3/lib/types"
    25  )
    26  
    27  // ErrResourceNotFound represents an error where a named resource could not be
    28  // accessed because it was not found by the manager.
    29  type ErrResourceNotFound string
    30  
    31  // Error implements the standard error interface.
    32  func (e ErrResourceNotFound) Error() string {
    33  	return fmt.Sprintf("unable to locate resource: %v", string(e))
    34  }
    35  
    36  //------------------------------------------------------------------------------
    37  
    38  // APIReg is an interface representing an API builder.
    39  type APIReg interface {
    40  	RegisterEndpoint(path, desc string, h http.HandlerFunc)
    41  }
    42  
    43  //------------------------------------------------------------------------------
    44  
    45  // Type is an implementation of types.Manager, which is expected by Benthos
    46  // components that need to register service wide behaviours such as HTTP
    47  // endpoints and event listeners, and obtain service wide shared resources such
    48  // as caches and other resources.
    49  type Type struct {
    50  	// An optional identifier given to a manager that is used by a unique stream
    51  	// and if specified should be used as a path prefix for API endpoints, and
    52  	// added as a label to logs and metrics.
    53  	stream string
    54  
    55  	// An optional identifier given to a manager that is used by a component and
    56  	// if specified should be added as a label to logs and metrics.
    57  	component string
    58  
    59  	apiReg APIReg
    60  
    61  	inputs       map[string]types.Input
    62  	caches       map[string]types.Cache
    63  	processors   map[string]types.Processor
    64  	outputs      map[string]types.OutputWriter
    65  	rateLimits   map[string]types.RateLimit
    66  	plugins      map[string]interface{}
    67  	resourceLock *sync.RWMutex
    68  
    69  	// Collections of component constructors
    70  	env      *bundle.Environment
    71  	bloblEnv *bloblang.Environment
    72  
    73  	logger log.Modular
    74  	stats  *imetrics.Namespaced
    75  
    76  	pipes    map[string]<-chan types.Transaction
    77  	pipeLock *sync.RWMutex
    78  
    79  	// TODO: V4 Remove this
    80  	conditions map[string]types.Condition
    81  }
    82  
    83  // New returns an instance of manager.Type, which can be shared amongst
    84  // components and logical threads of a Benthos service.
    85  func New(conf Config, apiReg APIReg, log log.Modular, stats metrics.Type) (*Type, error) {
    86  	return NewV2(ResourceConfig{Manager: conf}, apiReg, log, stats)
    87  }
    88  
    89  // OptFunc is an opt setting for a manager type.
    90  type OptFunc func(*Type)
    91  
    92  // OptSetEnvironment determines the environment from which the manager
    93  // initializes components and resources. This option is for internal use only.
    94  func OptSetEnvironment(e *bundle.Environment) OptFunc {
    95  	return func(t *Type) {
    96  		t.env = e
    97  	}
    98  }
    99  
   100  // OptSetBloblangEnvironment determines the environment from which the manager
   101  // parses bloblang functions and methods. This option is for internal use only.
   102  func OptSetBloblangEnvironment(env *bloblang.Environment) OptFunc {
   103  	return func(t *Type) {
   104  		t.bloblEnv = env
   105  	}
   106  }
   107  
   108  // NewV2 returns an instance of manager.Type, which can be shared amongst
   109  // components and logical threads of a Benthos service.
   110  func NewV2(conf ResourceConfig, apiReg APIReg, log log.Modular, stats metrics.Type, opts ...OptFunc) (*Type, error) {
   111  	t := &Type{
   112  		apiReg: apiReg,
   113  
   114  		inputs:       map[string]types.Input{},
   115  		caches:       map[string]types.Cache{},
   116  		processors:   map[string]types.Processor{},
   117  		outputs:      map[string]types.OutputWriter{},
   118  		rateLimits:   map[string]types.RateLimit{},
   119  		plugins:      map[string]interface{}{},
   120  		resourceLock: &sync.RWMutex{},
   121  
   122  		// Environment defaults to global (everything that was imported).
   123  		env:      bundle.GlobalEnvironment,
   124  		bloblEnv: bloblang.GlobalEnvironment(),
   125  
   126  		logger: log,
   127  		stats:  imetrics.NewNamespaced(stats),
   128  
   129  		pipes:    map[string]<-chan types.Transaction{},
   130  		pipeLock: &sync.RWMutex{},
   131  
   132  		conditions: map[string]types.Condition{},
   133  	}
   134  
   135  	for _, opt := range opts {
   136  		opt(t)
   137  	}
   138  
   139  	conf, err := conf.collapsed()
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	// Sometimes resources of a type might refer to other resources of the same
   145  	// type. When they are constructed they will check with the manager to
   146  	// ensure the resource they point to is valid, but not keep the reference.
   147  	// Since we cannot guarantee an order of initialisation we create
   148  	// placeholders during construction.
   149  	for k := range conf.Manager.Inputs {
   150  		t.inputs[k] = nil
   151  	}
   152  	for k := range conf.Manager.Caches {
   153  		t.caches[k] = nil
   154  	}
   155  	for k := range conf.Manager.Conditions {
   156  		t.conditions[k] = nil
   157  	}
   158  	for k := range conf.Manager.Processors {
   159  		t.processors[k] = nil
   160  	}
   161  	for k := range conf.Manager.Outputs {
   162  		t.outputs[k] = nil
   163  	}
   164  	for k := range conf.Manager.RateLimits {
   165  		t.rateLimits[k] = nil
   166  	}
   167  	for k, conf := range conf.Manager.Plugins {
   168  		if _, exists := pluginSpecs[conf.Type]; !exists {
   169  			continue
   170  		}
   171  		t.plugins[k] = nil
   172  	}
   173  
   174  	for k, conf := range conf.Manager.RateLimits {
   175  		if err := t.StoreRateLimit(context.Background(), k, conf); err != nil {
   176  			return nil, err
   177  		}
   178  	}
   179  
   180  	for k, conf := range conf.Manager.Caches {
   181  		if err := t.StoreCache(context.Background(), k, conf); err != nil {
   182  			return nil, err
   183  		}
   184  	}
   185  
   186  	// TODO: Prevent recursive conditions.
   187  	for k, newConf := range conf.Manager.Conditions {
   188  		cMgr := t.forChildComponent("resource.condition." + k)
   189  		newCond, err := condition.New(newConf, cMgr, cMgr.Logger(), cMgr.Metrics())
   190  		if err != nil {
   191  			return nil, fmt.Errorf(
   192  				"failed to create condition resource '%v' of type '%v': %v",
   193  				k, newConf.Type, err,
   194  			)
   195  		}
   196  
   197  		t.conditions[k] = newCond
   198  	}
   199  
   200  	// TODO: Prevent recursive processors.
   201  	for k, conf := range conf.Manager.Processors {
   202  		if err := t.StoreProcessor(context.Background(), k, conf); err != nil {
   203  			return nil, err
   204  		}
   205  	}
   206  
   207  	for k, conf := range conf.Manager.Inputs {
   208  		if err := t.StoreInput(context.Background(), k, conf); err != nil {
   209  			return nil, err
   210  		}
   211  	}
   212  
   213  	for k, conf := range conf.Manager.Outputs {
   214  		if err := t.StoreOutput(context.Background(), k, conf); err != nil {
   215  			return nil, err
   216  		}
   217  	}
   218  
   219  	for k, conf := range conf.Manager.Plugins {
   220  		spec, exists := pluginSpecs[conf.Type]
   221  		if !exists {
   222  			return nil, fmt.Errorf("unrecognised plugin type '%v'", conf.Type)
   223  		}
   224  		pMgr := t.forChildComponent("resource.plugin." + k)
   225  		newP, err := spec.constructor(conf.Plugin, pMgr, pMgr.Logger(), pMgr.Metrics())
   226  		if err != nil {
   227  			return nil, fmt.Errorf(
   228  				"failed to create plugin resource '%v' of type '%v': %v",
   229  				k, conf.Type, err,
   230  			)
   231  		}
   232  		t.plugins[k] = newP
   233  	}
   234  
   235  	return t, nil
   236  }
   237  
   238  //------------------------------------------------------------------------------
   239  
   240  // ForStream returns a variant of this manager to be used by a particular stream
   241  // identifer, where APIs registered will be namespaced by that id.
   242  func (t *Type) ForStream(id string) types.Manager {
   243  	return t.forStream(id)
   244  }
   245  
   246  func (t *Type) forStream(id string) *Type {
   247  	newT := *t
   248  	newT.stream = id
   249  	newT.logger = t.logger.WithFields(map[string]string{
   250  		"stream": id,
   251  	})
   252  	newT.stats = t.stats.WithPrefix(id)
   253  	return &newT
   254  }
   255  
   256  // ForComponent returns a variant of this manager to be used by a particular
   257  // component identifer, where observability components will be automatically
   258  // tagged with the label.
   259  func (t *Type) ForComponent(id string) types.Manager {
   260  	return t.forComponent(id)
   261  }
   262  
   263  func (t *Type) forComponent(id string) *Type {
   264  	newT := *t
   265  	newT.component = id
   266  	newT.logger = t.logger.WithFields(map[string]string{
   267  		"component": id,
   268  	})
   269  
   270  	statsPrefix := id
   271  	if len(newT.stream) > 0 {
   272  		statsPrefix = newT.stream + "." + statsPrefix
   273  	}
   274  	newT.stats = t.stats.WithPrefix(statsPrefix)
   275  	return &newT
   276  }
   277  
   278  // ForChildComponent returns a variant of this manager to be used by a
   279  // particular component identifer, which is a child of the current component,
   280  // where observability components will be automatically tagged with the label.
   281  func (t *Type) ForChildComponent(id string) types.Manager {
   282  	return t.forChildComponent(id)
   283  }
   284  
   285  func (t *Type) forChildComponent(id string) *Type {
   286  	newT := *t
   287  	newT.logger = t.logger.NewModule("." + id)
   288  
   289  	if len(newT.component) > 0 {
   290  		id = newT.component + "." + id
   291  	}
   292  
   293  	statsPrefix := id
   294  	if len(newT.stream) > 0 {
   295  		statsPrefix = newT.stream + "." + statsPrefix
   296  	}
   297  
   298  	newT.stats = t.stats.WithPrefix(statsPrefix)
   299  	newT.component = id
   300  	return &newT
   301  }
   302  
   303  // Label returns the current component label held by a manager.
   304  func (t *Type) Label() string {
   305  	return t.component
   306  }
   307  
   308  //------------------------------------------------------------------------------
   309  
   310  // RegisterEndpoint registers a server wide HTTP endpoint.
   311  func (t *Type) RegisterEndpoint(apiPath, desc string, h http.HandlerFunc) {
   312  	if len(t.stream) > 0 {
   313  		apiPath = path.Join("/", t.stream, apiPath)
   314  	}
   315  	if t.apiReg != nil {
   316  		t.apiReg.RegisterEndpoint(apiPath, desc, h)
   317  	}
   318  }
   319  
   320  // SetPipe registers a new transaction chan to a named pipe.
   321  func (t *Type) SetPipe(name string, tran <-chan types.Transaction) {
   322  	t.pipeLock.Lock()
   323  	t.pipes[name] = tran
   324  	t.pipeLock.Unlock()
   325  }
   326  
   327  // GetPipe attempts to obtain and return a named output Pipe
   328  func (t *Type) GetPipe(name string) (<-chan types.Transaction, error) {
   329  	t.pipeLock.RLock()
   330  	pipe, exists := t.pipes[name]
   331  	t.pipeLock.RUnlock()
   332  	if exists {
   333  		return pipe, nil
   334  	}
   335  	return nil, types.ErrPipeNotFound
   336  }
   337  
   338  // UnsetPipe removes a named pipe transaction chan.
   339  func (t *Type) UnsetPipe(name string, tran <-chan types.Transaction) {
   340  	t.pipeLock.Lock()
   341  	if otran, exists := t.pipes[name]; exists && otran == tran {
   342  		delete(t.pipes, name)
   343  	}
   344  	t.pipeLock.Unlock()
   345  }
   346  
   347  //------------------------------------------------------------------------------
   348  
   349  // WithMetricsMapping returns a manager with the stored metrics exporter wrapped
   350  // with a mapping.
   351  func (t *Type) WithMetricsMapping(m *imetrics.Mapping) *Type {
   352  	newT := *t
   353  	newT.stats = t.stats.WithMapping(m)
   354  	return &newT
   355  }
   356  
   357  // Metrics returns an aggregator preset with the current component context.
   358  func (t *Type) Metrics() metrics.Type {
   359  	return t.stats
   360  }
   361  
   362  // Logger returns a logger preset with the current component context.
   363  func (t *Type) Logger() log.Modular {
   364  	return t.logger
   365  }
   366  
   367  // Environment returns a bundle environment used by the manager. This is for
   368  // internal use only.
   369  func (t *Type) Environment() *bundle.Environment {
   370  	return t.env
   371  }
   372  
   373  // BloblEnvironment returns a Bloblang environment used by the manager. This is
   374  // for internal use only.
   375  func (t *Type) BloblEnvironment() *bloblang.Environment {
   376  	return t.bloblEnv
   377  }
   378  
   379  //------------------------------------------------------------------------------
   380  
   381  // GetDocs returns a documentation spec for an implementation of a component.
   382  func (t *Type) GetDocs(name string, ctype docs.Type) (docs.ComponentSpec, bool) {
   383  	return t.env.GetDocs(name, ctype)
   384  }
   385  
   386  //------------------------------------------------------------------------------
   387  
   388  func closeWithContext(ctx context.Context, c types.Closable) error {
   389  	c.CloseAsync()
   390  	waitFor := time.Second
   391  	deadline, hasDeadline := ctx.Deadline()
   392  	if hasDeadline {
   393  		waitFor = time.Until(deadline)
   394  	}
   395  	err := c.WaitForClose(waitFor)
   396  	for err != nil && !hasDeadline {
   397  		err = c.WaitForClose(time.Second)
   398  	}
   399  	return err
   400  }
   401  
   402  //------------------------------------------------------------------------------
   403  
   404  // NewBuffer attempts to create a new buffer component from a config.
   405  func (t *Type) NewBuffer(conf buffer.Config) (buffer.Type, error) {
   406  	return t.env.BufferInit(conf, t)
   407  }
   408  
   409  //------------------------------------------------------------------------------
   410  
   411  // AccessCache attempts to access a cache resource by a unique identifier and
   412  // executes a closure function with the cache as an argument. Returns an error
   413  // if the cache does not exist (or is otherwise inaccessible).
   414  //
   415  // During the execution of the provided closure it is guaranteed that the
   416  // resource will not be closed or removed. However, it is possible for the
   417  // resource to be accessed by any number of components in parallel.
   418  func (t *Type) AccessCache(ctx context.Context, name string, fn func(types.Cache)) error {
   419  	// TODO: Eventually use ctx to cancel blocking on the mutex lock. Needs
   420  	// profiling for heavy use within a busy loop.
   421  	t.resourceLock.RLock()
   422  	defer t.resourceLock.RUnlock()
   423  	c, ok := t.caches[name]
   424  	if !ok || c == nil {
   425  		return ErrResourceNotFound(name)
   426  	}
   427  	fn(c)
   428  	return nil
   429  }
   430  
   431  // NewCache attempts to create a new cache component from a config.
   432  func (t *Type) NewCache(conf cache.Config) (types.Cache, error) {
   433  	mgr := t
   434  	// A configured label overrides any previously set component label.
   435  	if len(conf.Label) > 0 && t.component != conf.Label {
   436  		mgr = t.forComponent(conf.Label)
   437  	}
   438  	return t.env.CacheInit(conf, mgr)
   439  }
   440  
   441  // StoreCache attempts to store a new cache resource. If an existing resource
   442  // has the same name it is closed and removed _before_ the new one is
   443  // initialized in order to avoid duplicate connections.
   444  func (t *Type) StoreCache(ctx context.Context, name string, conf cache.Config) error {
   445  	t.resourceLock.Lock()
   446  	defer t.resourceLock.Unlock()
   447  
   448  	c, ok := t.caches[name]
   449  	if ok && c != nil {
   450  		// If a previous resource exists with the same name then we do NOT allow
   451  		// it to be replaced unless it can be successfully closed. This ensures
   452  		// that we do not leak connections.
   453  		if err := closeWithContext(ctx, c); err != nil {
   454  			return err
   455  		}
   456  	}
   457  
   458  	newCache, err := t.forComponent("resource.cache." + name).NewCache(conf)
   459  	if err != nil {
   460  		return fmt.Errorf(
   461  			"failed to create cache resource '%v' of type '%v': %w",
   462  			name, conf.Type, err,
   463  		)
   464  	}
   465  
   466  	t.caches[name] = newCache
   467  	return nil
   468  }
   469  
   470  //------------------------------------------------------------------------------
   471  
   472  // AccessInput attempts to access an input resource by a unique identifier and
   473  // executes a closure function with the input as an argument. Returns an error
   474  // if the input does not exist (or is otherwise inaccessible).
   475  //
   476  // During the execution of the provided closure it is guaranteed that the
   477  // resource will not be closed or removed. However, it is possible for the
   478  // resource to be accessed by any number of components in parallel.
   479  func (t *Type) AccessInput(ctx context.Context, name string, fn func(types.Input)) error {
   480  	// TODO: Eventually use ctx to cancel blocking on the mutex lock. Needs
   481  	// profiling for heavy use within a busy loop.
   482  	t.resourceLock.RLock()
   483  	defer t.resourceLock.RUnlock()
   484  	i, ok := t.inputs[name]
   485  	if !ok || i == nil {
   486  		return ErrResourceNotFound(name)
   487  	}
   488  	fn(i)
   489  	return nil
   490  }
   491  
   492  // NewInput attempts to create a new input component from a config.
   493  //
   494  // TODO: V4 Remove the dumb batch field.
   495  func (t *Type) NewInput(conf input.Config, hasBatchProc bool, pipelines ...types.PipelineConstructorFunc) (types.Input, error) {
   496  	mgr := t
   497  	// A configured label overrides any previously set component label.
   498  	if len(conf.Label) > 0 && t.component != conf.Label {
   499  		mgr = t.forComponent(conf.Label)
   500  	}
   501  	return t.env.InputInit(hasBatchProc, conf, mgr, pipelines...)
   502  }
   503  
   504  // StoreInput attempts to store a new input resource. If an existing resource
   505  // has the same name it is closed and removed _before_ the new one is
   506  // initialized in order to avoid duplicate connections.
   507  func (t *Type) StoreInput(ctx context.Context, name string, conf input.Config) error {
   508  	t.resourceLock.Lock()
   509  	defer t.resourceLock.Unlock()
   510  
   511  	i, ok := t.inputs[name]
   512  	if ok && i != nil {
   513  		// If a previous resource exists with the same name then we do NOT allow
   514  		// it to be replaced unless it can be successfully closed. This ensures
   515  		// that we do not leak connections.
   516  		if err := closeWithContext(ctx, i); err != nil {
   517  			return err
   518  		}
   519  	}
   520  
   521  	if conf.Label != "" && conf.Label != name {
   522  		return fmt.Errorf("label '%v' must be empty or match the resource name '%v'", conf.Label, name)
   523  	}
   524  
   525  	newInput, err := t.forComponent("resource.input."+name).NewInput(conf, false)
   526  	if err != nil {
   527  		return fmt.Errorf(
   528  			"failed to create input resource '%v' of type '%v': %w",
   529  			name, conf.Type, err,
   530  		)
   531  	}
   532  
   533  	t.inputs[name] = newInput
   534  	return nil
   535  }
   536  
   537  //------------------------------------------------------------------------------
   538  
   539  // AccessProcessor attempts to access a processor resource by a unique
   540  // identifier and executes a closure function with the processor as an argument.
   541  // Returns an error if the processor does not exist (or is otherwise
   542  // inaccessible).
   543  //
   544  // During the execution of the provided closure it is guaranteed that the
   545  // resource will not be closed or removed. However, it is possible for the
   546  // resource to be accessed by any number of components in parallel.
   547  func (t *Type) AccessProcessor(ctx context.Context, name string, fn func(types.Processor)) error {
   548  	// TODO: Eventually use ctx to cancel blocking on the mutex lock. Needs
   549  	// profiling for heavy use within a busy loop.
   550  	t.resourceLock.RLock()
   551  	defer t.resourceLock.RUnlock()
   552  	p, ok := t.processors[name]
   553  	if !ok || p == nil {
   554  		return ErrResourceNotFound(name)
   555  	}
   556  	fn(p)
   557  	return nil
   558  }
   559  
   560  // NewProcessor attempts to create a new processor component from a config.
   561  func (t *Type) NewProcessor(conf processor.Config) (types.Processor, error) {
   562  	mgr := t
   563  	// A configured label overrides any previously set component label.
   564  	if len(conf.Label) > 0 && t.component != conf.Label {
   565  		mgr = t.forComponent(conf.Label)
   566  	}
   567  	return t.env.ProcessorInit(conf, mgr)
   568  }
   569  
   570  // StoreProcessor attempts to store a new processor resource. If an existing
   571  // resource has the same name it is closed and removed _before_ the new one is
   572  // initialized in order to avoid duplicate connections.
   573  func (t *Type) StoreProcessor(ctx context.Context, name string, conf processor.Config) error {
   574  	t.resourceLock.Lock()
   575  	defer t.resourceLock.Unlock()
   576  
   577  	p, ok := t.processors[name]
   578  	if ok && p != nil {
   579  		// If a previous resource exists with the same name then we do NOT allow
   580  		// it to be replaced unless it can be successfully closed. This ensures
   581  		// that we do not leak connections.
   582  		if err := closeWithContext(ctx, p); err != nil {
   583  			return err
   584  		}
   585  	}
   586  
   587  	if conf.Label != "" && conf.Label != name {
   588  		return fmt.Errorf("label '%v' must be empty or match the resource name '%v'", conf.Label, name)
   589  	}
   590  
   591  	newProcessor, err := t.forComponent("resource.processor." + name).NewProcessor(conf)
   592  	if err != nil {
   593  		return fmt.Errorf(
   594  			"failed to create processor resource '%v' of type '%v': %w",
   595  			name, conf.Type, err,
   596  		)
   597  	}
   598  
   599  	t.processors[name] = newProcessor
   600  	return nil
   601  }
   602  
   603  //------------------------------------------------------------------------------
   604  
   605  // AccessOutput attempts to access an output resource by a unique identifier and
   606  // executes a closure function with the output as an argument. Returns an error
   607  // if the output does not exist (or is otherwise inaccessible).
   608  //
   609  // During the execution of the provided closure it is guaranteed that the
   610  // resource will not be closed or removed. However, it is possible for the
   611  // resource to be accessed by any number of components in parallel.
   612  func (t *Type) AccessOutput(ctx context.Context, name string, fn func(types.OutputWriter)) error {
   613  	// TODO: Eventually use ctx to cancel blocking on the mutex lock. Needs
   614  	// profiling for heavy use within a busy loop.
   615  	t.resourceLock.RLock()
   616  	defer t.resourceLock.RUnlock()
   617  	o, ok := t.outputs[name]
   618  	if !ok || o == nil {
   619  		return ErrResourceNotFound(name)
   620  	}
   621  	fn(o)
   622  	return nil
   623  }
   624  
   625  // NewOutput attempts to create a new output component from a config.
   626  func (t *Type) NewOutput(conf output.Config, pipelines ...types.PipelineConstructorFunc) (types.Output, error) {
   627  	mgr := t
   628  	// A configured label overrides any previously set component label.
   629  	if len(conf.Label) > 0 && t.component != conf.Label {
   630  		mgr = t.forComponent(conf.Label)
   631  	}
   632  	return t.env.OutputInit(conf, mgr, pipelines...)
   633  }
   634  
   635  // StoreOutput attempts to store a new output resource. If an existing resource
   636  // has the same name it is closed and removed _before_ the new one is
   637  // initialized in order to avoid duplicate connections.
   638  func (t *Type) StoreOutput(ctx context.Context, name string, conf output.Config) error {
   639  	t.resourceLock.Lock()
   640  	defer t.resourceLock.Unlock()
   641  
   642  	o, ok := t.outputs[name]
   643  	if ok && o != nil {
   644  		// If a previous resource exists with the same name then we do NOT allow
   645  		// it to be replaced unless it can be successfully closed. This ensures
   646  		// that we do not leak connections.
   647  		if err := closeWithContext(ctx, o); err != nil {
   648  			return err
   649  		}
   650  	}
   651  
   652  	if conf.Label != "" && conf.Label != name {
   653  		return fmt.Errorf("label '%v' must be empty or match the resource name '%v'", conf.Label, name)
   654  	}
   655  
   656  	tmpOutput, err := t.forComponent("resource.output." + name).NewOutput(conf)
   657  	if err == nil {
   658  		if t.outputs[name], err = wrapOutput(tmpOutput); err != nil {
   659  			tmpOutput.CloseAsync()
   660  		}
   661  	}
   662  	if err != nil {
   663  		return fmt.Errorf(
   664  			"failed to create output resource '%v' of type '%v': %w",
   665  			name, conf.Type, err,
   666  		)
   667  	}
   668  	return nil
   669  }
   670  
   671  //------------------------------------------------------------------------------
   672  
   673  // AccessRateLimit attempts to access a rate limit resource by a unique
   674  // identifier and executes a closure function with the rate limit as an
   675  // argument. Returns an error if the rate limit does not exist (or is otherwise
   676  // inaccessible).
   677  //
   678  // During the execution of the provided closure it is guaranteed that the
   679  // resource will not be closed or removed. However, it is possible for the
   680  // resource to be accessed by any number of components in parallel.
   681  func (t *Type) AccessRateLimit(ctx context.Context, name string, fn func(types.RateLimit)) error {
   682  	// TODO: Eventually use ctx to cancel blocking on the mutex lock. Needs
   683  	// profiling for heavy use within a busy loop.
   684  	t.resourceLock.RLock()
   685  	defer t.resourceLock.RUnlock()
   686  	r, ok := t.rateLimits[name]
   687  	if !ok || r == nil {
   688  		return ErrResourceNotFound(name)
   689  	}
   690  	fn(r)
   691  	return nil
   692  }
   693  
   694  // NewRateLimit attempts to create a new rate limit component from a config.
   695  func (t *Type) NewRateLimit(conf ratelimit.Config) (types.RateLimit, error) {
   696  	mgr := t
   697  	// A configured label overrides any previously set component label.
   698  	if len(conf.Label) > 0 && t.component != conf.Label {
   699  		mgr = t.forComponent(conf.Label)
   700  	}
   701  	return t.env.RateLimitInit(conf, mgr)
   702  }
   703  
   704  // StoreRateLimit attempts to store a new rate limit resource. If an existing
   705  // resource has the same name it is closed and removed _before_ the new one is
   706  // initialized in order to avoid duplicate connections.
   707  func (t *Type) StoreRateLimit(ctx context.Context, name string, conf ratelimit.Config) error {
   708  	t.resourceLock.Lock()
   709  	defer t.resourceLock.Unlock()
   710  
   711  	r, ok := t.rateLimits[name]
   712  	if ok && r != nil {
   713  		// If a previous resource exists with the same name then we do NOT allow
   714  		// it to be replaced unless it can be successfully closed. This ensures
   715  		// that we do not leak connections.
   716  		if err := closeWithContext(ctx, r); err != nil {
   717  			return err
   718  		}
   719  	}
   720  
   721  	newRateLimit, err := t.forComponent("resource.rate_limit." + name).NewRateLimit(conf)
   722  	if err != nil {
   723  		return fmt.Errorf(
   724  			"failed to create rate limit resource '%v' of type '%v': %w",
   725  			name, conf.Type, err,
   726  		)
   727  	}
   728  
   729  	t.rateLimits[name] = newRateLimit
   730  	return nil
   731  }
   732  
   733  //------------------------------------------------------------------------------
   734  
   735  // CloseAsync triggers the shut down of all resource types that implement the
   736  // lifetime interface types.Closable.
   737  func (t *Type) CloseAsync() {
   738  	t.resourceLock.Lock()
   739  	defer t.resourceLock.Unlock()
   740  
   741  	for _, c := range t.inputs {
   742  		c.CloseAsync()
   743  	}
   744  	for _, c := range t.caches {
   745  		c.CloseAsync()
   746  	}
   747  	for _, c := range t.conditions {
   748  		if closer, ok := c.(types.Closable); ok {
   749  			closer.CloseAsync()
   750  		}
   751  	}
   752  	for _, p := range t.processors {
   753  		p.CloseAsync()
   754  	}
   755  	for _, c := range t.plugins {
   756  		if closer, ok := c.(types.Closable); ok {
   757  			closer.CloseAsync()
   758  		}
   759  	}
   760  	for _, c := range t.rateLimits {
   761  		c.CloseAsync()
   762  	}
   763  	for _, c := range t.outputs {
   764  		c.CloseAsync()
   765  	}
   766  }
   767  
   768  // WaitForClose blocks until either all closable resource types are shut down or
   769  // a timeout occurs.
   770  func (t *Type) WaitForClose(timeout time.Duration) error {
   771  	t.resourceLock.Lock()
   772  	defer t.resourceLock.Unlock()
   773  
   774  	timesOut := time.Now().Add(timeout)
   775  	for k, c := range t.inputs {
   776  		if err := c.WaitForClose(time.Until(timesOut)); err != nil {
   777  			return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err)
   778  		}
   779  		delete(t.inputs, k)
   780  	}
   781  	for k, c := range t.caches {
   782  		if err := c.WaitForClose(time.Until(timesOut)); err != nil {
   783  			return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err)
   784  		}
   785  		delete(t.caches, k)
   786  	}
   787  	for k, p := range t.processors {
   788  		if err := p.WaitForClose(time.Until(timesOut)); err != nil {
   789  			return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err)
   790  		}
   791  		delete(t.processors, k)
   792  	}
   793  	for k, c := range t.rateLimits {
   794  		if err := c.WaitForClose(time.Until(timesOut)); err != nil {
   795  			return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err)
   796  		}
   797  		delete(t.rateLimits, k)
   798  	}
   799  	for k, c := range t.outputs {
   800  		if err := c.WaitForClose(time.Until(timesOut)); err != nil {
   801  			return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err)
   802  		}
   803  		delete(t.outputs, k)
   804  	}
   805  	for k, c := range t.plugins {
   806  		if closer, ok := c.(types.Closable); ok {
   807  			if err := closer.WaitForClose(time.Until(timesOut)); err != nil {
   808  				return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err)
   809  			}
   810  		}
   811  		delete(t.plugins, k)
   812  	}
   813  	return nil
   814  }
   815  
   816  //------------------------------------------------------------------------------
   817  
   818  // DEPRECATED
   819  // TODO: V4 Remove this
   820  
   821  // SwapMetrics attempts to swap the underlying metrics implementation of a
   822  // manager. This function does nothing if the manager type is not a *Type.
   823  func SwapMetrics(mgr types.Manager, stats metrics.Type) types.Manager {
   824  	if t, ok := mgr.(*Type); ok {
   825  		newMgr := *t
   826  		newMgr.stats = t.stats.WithStats(stats)
   827  		return &newMgr
   828  	}
   829  	return mgr
   830  }
   831  
   832  // GetInput attempts to find a service wide input by its name.
   833  func (t *Type) GetInput(name string) (types.Input, error) {
   834  	if c, exists := t.inputs[name]; exists {
   835  		return c, nil
   836  	}
   837  	return nil, types.ErrInputNotFound
   838  }
   839  
   840  // GetCache attempts to find a service wide cache by its name.
   841  func (t *Type) GetCache(name string) (types.Cache, error) {
   842  	if c, exists := t.caches[name]; exists {
   843  		return c, nil
   844  	}
   845  	return nil, types.ErrCacheNotFound
   846  }
   847  
   848  // GetCondition attempts to find a service wide condition by its name.
   849  func (t *Type) GetCondition(name string) (types.Condition, error) {
   850  	if c, exists := t.conditions[name]; exists {
   851  		return c, nil
   852  	}
   853  	return nil, types.ErrConditionNotFound
   854  }
   855  
   856  // GetProcessor attempts to find a service wide processor by its name.
   857  func (t *Type) GetProcessor(name string) (types.Processor, error) {
   858  	if p, exists := t.processors[name]; exists {
   859  		return p, nil
   860  	}
   861  	return nil, types.ErrProcessorNotFound
   862  }
   863  
   864  // GetRateLimit attempts to find a service wide rate limit by its name.
   865  func (t *Type) GetRateLimit(name string) (types.RateLimit, error) {
   866  	if rl, exists := t.rateLimits[name]; exists {
   867  		return rl, nil
   868  	}
   869  	return nil, types.ErrRateLimitNotFound
   870  }
   871  
   872  // GetOutput attempts to find a service wide output by its name.
   873  func (t *Type) GetOutput(name string) (types.OutputWriter, error) {
   874  	if c, exists := t.outputs[name]; exists {
   875  		return c, nil
   876  	}
   877  	return nil, types.ErrOutputNotFound
   878  }
   879  
   880  // GetPlugin attempts to find a service wide resource plugin by its name.
   881  func (t *Type) GetPlugin(name string) (interface{}, error) {
   882  	if pl, exists := t.plugins[name]; exists {
   883  		return pl, nil
   884  	}
   885  	return nil, types.ErrPluginNotFound
   886  }