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

     1  package manager
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/Jeffail/benthos/v3/lib/cache"
     8  	"github.com/Jeffail/benthos/v3/lib/condition"
     9  	"github.com/Jeffail/benthos/v3/lib/input"
    10  	"github.com/Jeffail/benthos/v3/lib/output"
    11  	"github.com/Jeffail/benthos/v3/lib/processor"
    12  	"github.com/Jeffail/benthos/v3/lib/ratelimit"
    13  	"github.com/Jeffail/benthos/v3/lib/util/config"
    14  )
    15  
    16  // ResourceConfig contains fields for specifying resource components at the root
    17  // of a Benthos config.
    18  type ResourceConfig struct {
    19  	// Called manager for backwards compatibility.
    20  	Manager            Config             `json:"resources,omitempty" yaml:"resources,omitempty"`
    21  	ResourceInputs     []input.Config     `json:"input_resources,omitempty" yaml:"input_resources,omitempty"`
    22  	ResourceProcessors []processor.Config `json:"processor_resources,omitempty" yaml:"processor_resources,omitempty"`
    23  	ResourceOutputs    []output.Config    `json:"output_resources,omitempty" yaml:"output_resources,omitempty"`
    24  	ResourceCaches     []cache.Config     `json:"cache_resources,omitempty" yaml:"cache_resources,omitempty"`
    25  	ResourceRateLimits []ratelimit.Config `json:"rate_limit_resources,omitempty" yaml:"rate_limit_resources,omitempty"`
    26  }
    27  
    28  // NewResourceConfig creates a ResourceConfig with default values.
    29  func NewResourceConfig() ResourceConfig {
    30  	return ResourceConfig{
    31  		Manager:            NewConfig(),
    32  		ResourceInputs:     []input.Config{},
    33  		ResourceProcessors: []processor.Config{},
    34  		ResourceOutputs:    []output.Config{},
    35  		ResourceCaches:     []cache.Config{},
    36  		ResourceRateLimits: []ratelimit.Config{},
    37  	}
    38  }
    39  
    40  // Collapses all the slice based resources into maps, returning an error if any
    41  // labels are duplicated or empty.
    42  func (r *ResourceConfig) collapsed() (ResourceConfig, error) {
    43  	newMaps := NewConfig()
    44  
    45  	for k, v := range r.Manager.Caches {
    46  		newMaps.Caches[k] = v
    47  	}
    48  	for _, c := range r.ResourceCaches {
    49  		if c.Label == "" {
    50  			return *r, errors.New("cache resource has an empty label")
    51  		}
    52  		if _, exists := newMaps.Caches[c.Label]; exists {
    53  			return *r, fmt.Errorf("cache resource label '%v' collides with a previously defined resource", c.Label)
    54  		}
    55  		newMaps.Caches[c.Label] = c
    56  	}
    57  
    58  	for k, v := range r.Manager.Conditions {
    59  		newMaps.Conditions[k] = v
    60  	}
    61  
    62  	for k, v := range r.Manager.Inputs {
    63  		newMaps.Inputs[k] = v
    64  	}
    65  	for _, c := range r.ResourceInputs {
    66  		if c.Label == "" {
    67  			return *r, errors.New("input resource has an empty label")
    68  		}
    69  		if _, exists := newMaps.Inputs[c.Label]; exists {
    70  			return *r, fmt.Errorf("input resource label '%v' collides with a previously defined resource", c.Label)
    71  		}
    72  		newMaps.Inputs[c.Label] = c
    73  	}
    74  
    75  	for k, v := range r.Manager.Outputs {
    76  		newMaps.Outputs[k] = v
    77  	}
    78  	for _, c := range r.ResourceOutputs {
    79  		if c.Label == "" {
    80  			return *r, errors.New("output resource has an empty label")
    81  		}
    82  		if _, exists := newMaps.Outputs[c.Label]; exists {
    83  			return *r, fmt.Errorf("output resource label '%v' collides with a previously defined resource", c.Label)
    84  		}
    85  		newMaps.Outputs[c.Label] = c
    86  	}
    87  
    88  	for k, v := range r.Manager.Plugins {
    89  		newMaps.Plugins[k] = v
    90  	}
    91  
    92  	for k, v := range r.Manager.Processors {
    93  		newMaps.Processors[k] = v
    94  	}
    95  	for _, c := range r.ResourceProcessors {
    96  		if c.Label == "" {
    97  			return *r, errors.New("processor resource has an empty label")
    98  		}
    99  		if _, exists := newMaps.Processors[c.Label]; exists {
   100  			return *r, fmt.Errorf("processor resource label '%v' collides with a previously defined resource", c.Label)
   101  		}
   102  		newMaps.Processors[c.Label] = c
   103  	}
   104  
   105  	for k, v := range r.Manager.RateLimits {
   106  		newMaps.RateLimits[k] = v
   107  	}
   108  	for _, c := range r.ResourceRateLimits {
   109  		if c.Label == "" {
   110  			return *r, errors.New("rate limit resource has an empty label")
   111  		}
   112  		if _, exists := newMaps.RateLimits[c.Label]; exists {
   113  			return *r, fmt.Errorf("rate limit resource label '%v' collides with a previously defined resource", c.Label)
   114  		}
   115  		newMaps.RateLimits[c.Label] = c
   116  	}
   117  
   118  	return ResourceConfig{
   119  		Manager: newMaps,
   120  	}, nil
   121  }
   122  
   123  // AddFrom takes another Config and adds all of its resources to itself. If
   124  // there are any resource name collisions an error is returned.
   125  func (r *ResourceConfig) AddFrom(extra *ResourceConfig) error {
   126  	if err := r.Manager.AddFrom(&extra.Manager); err != nil {
   127  		return err
   128  	}
   129  	// TODO: Detect duplicates.
   130  	r.ResourceInputs = append(r.ResourceInputs, extra.ResourceInputs...)
   131  	r.ResourceProcessors = append(r.ResourceProcessors, extra.ResourceProcessors...)
   132  	r.ResourceOutputs = append(r.ResourceOutputs, extra.ResourceOutputs...)
   133  	r.ResourceCaches = append(r.ResourceCaches, extra.ResourceCaches...)
   134  	r.ResourceRateLimits = append(r.ResourceRateLimits, extra.ResourceRateLimits...)
   135  	return nil
   136  }
   137  
   138  // Config contains all configuration fields for a Benthos service manager.
   139  type Config struct {
   140  	Inputs     map[string]input.Config     `json:"inputs,omitempty" yaml:"inputs,omitempty"`
   141  	Conditions map[string]condition.Config `json:"conditions,omitempty" yaml:"conditions,omitempty"`
   142  	Processors map[string]processor.Config `json:"processors,omitempty" yaml:"processors,omitempty"`
   143  	Outputs    map[string]output.Config    `json:"outputs,omitempty" yaml:"outputs,omitempty"`
   144  	Caches     map[string]cache.Config     `json:"caches,omitempty" yaml:"caches,omitempty"`
   145  	RateLimits map[string]ratelimit.Config `json:"rate_limits,omitempty" yaml:"rate_limits,omitempty"`
   146  	Plugins    map[string]PluginConfig     `json:"plugins,omitempty" yaml:"plugins,omitempty"`
   147  }
   148  
   149  // NewConfig returns a Config with default values.
   150  func NewConfig() Config {
   151  	return Config{
   152  		Inputs:     map[string]input.Config{},
   153  		Conditions: map[string]condition.Config{},
   154  		Processors: map[string]processor.Config{},
   155  		Outputs:    map[string]output.Config{},
   156  		Caches:     map[string]cache.Config{},
   157  		RateLimits: map[string]ratelimit.Config{},
   158  		Plugins:    map[string]PluginConfig{},
   159  	}
   160  }
   161  
   162  // AddFrom takes another Config and adds all of its resources to itself. If
   163  // there are any resource name collisions an error is returned.
   164  func (c *Config) AddFrom(extra *Config) error {
   165  	for k, v := range extra.Inputs {
   166  		if _, exists := c.Inputs[k]; exists {
   167  			return fmt.Errorf("resource input name collision: %v", k)
   168  		}
   169  		c.Inputs[k] = v
   170  	}
   171  	for k, v := range extra.Conditions {
   172  		if _, exists := c.Conditions[k]; exists {
   173  			return fmt.Errorf("resource condition name collision: %v", k)
   174  		}
   175  		c.Conditions[k] = v
   176  	}
   177  	for k, v := range extra.Processors {
   178  		if _, exists := c.Processors[k]; exists {
   179  			return fmt.Errorf("resource processor name collision: %v", k)
   180  		}
   181  		c.Processors[k] = v
   182  	}
   183  	for k, v := range extra.Outputs {
   184  		if _, exists := c.Outputs[k]; exists {
   185  			return fmt.Errorf("resource output name collision: %v", k)
   186  		}
   187  		c.Outputs[k] = v
   188  	}
   189  	for k, v := range extra.Caches {
   190  		if _, exists := c.Caches[k]; exists {
   191  			return fmt.Errorf("resource cache name collision: %v", k)
   192  		}
   193  		c.Caches[k] = v
   194  	}
   195  	for k, v := range extra.RateLimits {
   196  		if _, exists := c.RateLimits[k]; exists {
   197  			return fmt.Errorf("resource ratelimit name collision: %v", k)
   198  		}
   199  		c.RateLimits[k] = v
   200  	}
   201  	for k, v := range extra.Plugins {
   202  		if _, exists := c.Plugins[k]; exists {
   203  			return fmt.Errorf("resource plugin name collision: %v", k)
   204  		}
   205  		c.Plugins[k] = v
   206  	}
   207  	return nil
   208  }
   209  
   210  // AddExamples inserts example caches and conditions if none exist in the
   211  // config.
   212  func AddExamples(c *Config) {
   213  	if len(c.Inputs) == 0 {
   214  		c.Inputs["example"] = input.NewConfig()
   215  	}
   216  	if len(c.Conditions) == 0 {
   217  		c.Conditions["example"] = condition.NewConfig()
   218  	}
   219  	if len(c.Processors) == 0 {
   220  		c.Processors["example"] = processor.NewConfig()
   221  	}
   222  	if len(c.Outputs) == 0 {
   223  		c.Outputs["example"] = output.NewConfig()
   224  	}
   225  	if len(c.Caches) == 0 {
   226  		c.Caches["example"] = cache.NewConfig()
   227  	}
   228  	if len(c.RateLimits) == 0 {
   229  		c.RateLimits["example"] = ratelimit.NewConfig()
   230  	}
   231  }
   232  
   233  //------------------------------------------------------------------------------
   234  
   235  // SanitiseConfig creates a sanitised version of a manager config.
   236  func SanitiseConfig(conf Config) (interface{}, error) {
   237  	return conf.Sanitised(false)
   238  }
   239  
   240  // Sanitised returns a sanitised version of the config, meaning sections that
   241  // aren't relevant to behaviour are removed. Also optionally removes deprecated
   242  // fields.
   243  func (c Config) Sanitised(removeDeprecated bool) (interface{}, error) {
   244  	var err error
   245  
   246  	inputs := map[string]interface{}{}
   247  	for k, v := range c.Inputs {
   248  		if inputs[k], err = v.Sanitised(removeDeprecated); err != nil {
   249  			return nil, err
   250  		}
   251  	}
   252  
   253  	caches := map[string]interface{}{}
   254  	for k, v := range c.Caches {
   255  		if caches[k], err = v.Sanitised(removeDeprecated); err != nil {
   256  			return nil, err
   257  		}
   258  	}
   259  
   260  	conditions := map[string]interface{}{}
   261  	for k, v := range c.Conditions {
   262  		if conditions[k], err = v.Sanitised(removeDeprecated); err != nil {
   263  			return nil, err
   264  		}
   265  	}
   266  
   267  	processors := map[string]interface{}{}
   268  	for k, v := range c.Processors {
   269  		if processors[k], err = v.Sanitised(removeDeprecated); err != nil {
   270  			return nil, err
   271  		}
   272  	}
   273  
   274  	outputs := map[string]interface{}{}
   275  	for k, v := range c.Outputs {
   276  		if outputs[k], err = v.Sanitised(removeDeprecated); err != nil {
   277  			return nil, err
   278  		}
   279  	}
   280  
   281  	rateLimits := map[string]interface{}{}
   282  	for k, v := range c.RateLimits {
   283  		if rateLimits[k], err = v.Sanitised(removeDeprecated); err != nil {
   284  			return nil, err
   285  		}
   286  	}
   287  
   288  	plugins := map[string]interface{}{}
   289  	for k, v := range c.Plugins {
   290  		if spec, exists := pluginSpecs[v.Type]; exists {
   291  			if spec.confSanitiser != nil {
   292  				outputMap := config.Sanitised{}
   293  				outputMap["type"] = v.Type
   294  				outputMap["plugin"] = spec.confSanitiser(v.Plugin)
   295  				plugins[k] = outputMap
   296  			} else {
   297  				plugins[k] = v
   298  			}
   299  		}
   300  	}
   301  
   302  	m := map[string]interface{}{
   303  		"inputs":      inputs,
   304  		"conditions":  conditions,
   305  		"processors":  processors,
   306  		"outputs":     outputs,
   307  		"caches":      caches,
   308  		"rate_limits": rateLimits,
   309  	}
   310  	if len(plugins) > 0 {
   311  		m["plugins"] = plugins
   312  	}
   313  	return m, nil
   314  }