github.com/Jeffail/benthos/v3@v3.65.0/lib/input/constructor.go (about)

     1  package input
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/Jeffail/benthos/v3/internal/docs"
     7  	"github.com/Jeffail/benthos/v3/internal/interop"
     8  	"github.com/Jeffail/benthos/v3/lib/input/reader"
     9  	"github.com/Jeffail/benthos/v3/lib/log"
    10  	"github.com/Jeffail/benthos/v3/lib/metrics"
    11  	"github.com/Jeffail/benthos/v3/lib/pipeline"
    12  	"github.com/Jeffail/benthos/v3/lib/processor"
    13  	"github.com/Jeffail/benthos/v3/lib/types"
    14  	"github.com/Jeffail/benthos/v3/lib/util/config"
    15  	yaml "gopkg.in/yaml.v3"
    16  )
    17  
    18  //------------------------------------------------------------------------------
    19  
    20  // Category describes the general category of an input.
    21  type Category string
    22  
    23  // Input categories
    24  var (
    25  	CategoryLocal    Category = "Local"
    26  	CategoryAWS      Category = "AWS"
    27  	CategoryGCP      Category = "GCP"
    28  	CategoryAzure    Category = "Azure"
    29  	CategoryServices Category = "Services"
    30  	CategoryNetwork  Category = "Network"
    31  	CategoryUtility  Category = "Utility"
    32  )
    33  
    34  // TypeSpec is a struct containing constructors, markdown descriptions and an
    35  // optional sanitisation function for each input type.
    36  type TypeSpec struct {
    37  	constructor ConstructorFunc
    38  
    39  	Status      docs.Status
    40  	Version     string
    41  	Summary     string
    42  	Description string
    43  	Categories  []Category
    44  	Footnotes   string
    45  	config      docs.FieldSpec
    46  	FieldSpecs  docs.FieldSpecs
    47  	Examples    []docs.AnnotatedExample
    48  }
    49  
    50  // ConstructorFunc is a func signature able to construct an input.
    51  type ConstructorFunc func(bool, Config, types.Manager, log.Modular, metrics.Type, ...types.PipelineConstructorFunc) (Type, error)
    52  
    53  // WalkConstructors iterates each component constructor.
    54  func WalkConstructors(fn func(ConstructorFunc, docs.ComponentSpec)) {
    55  	inferred := docs.ComponentFieldsFromConf(NewConfig())
    56  	for k, v := range Constructors {
    57  		conf := v.config
    58  		if len(v.FieldSpecs) > 0 {
    59  			conf = docs.FieldComponent().WithChildren(v.FieldSpecs.DefaultAndTypeFrom(inferred[k])...)
    60  		} else {
    61  			conf.Children = conf.Children.DefaultAndTypeFrom(inferred[k])
    62  		}
    63  		spec := docs.ComponentSpec{
    64  			Type:        docs.TypeInput,
    65  			Name:        k,
    66  			Summary:     v.Summary,
    67  			Description: v.Description,
    68  			Footnotes:   v.Footnotes,
    69  			Config:      conf,
    70  			Examples:    v.Examples,
    71  			Status:      v.Status,
    72  			Version:     v.Version,
    73  		}
    74  		if len(v.Categories) > 0 {
    75  			spec.Categories = make([]string, 0, len(v.Categories))
    76  			for _, cat := range v.Categories {
    77  				spec.Categories = append(spec.Categories, string(cat))
    78  			}
    79  		}
    80  		fn(v.constructor, spec)
    81  	}
    82  	for k, v := range pluginSpecs {
    83  		spec := docs.ComponentSpec{
    84  			Type:   docs.TypeInput,
    85  			Name:   k,
    86  			Status: docs.StatusExperimental,
    87  			Plugin: true,
    88  			Config: docs.FieldComponent().Unlinted(),
    89  		}
    90  		fn(v.constructor, spec)
    91  	}
    92  }
    93  
    94  // AppendProcessorsFromConfig takes a variant arg of pipeline constructor
    95  // functions and returns a new slice of them where the processors of the
    96  // provided input configuration will also be initialized.
    97  func AppendProcessorsFromConfig(
    98  	conf Config,
    99  	mgr types.Manager,
   100  	log log.Modular,
   101  	stats metrics.Type,
   102  	pipelines ...types.PipelineConstructorFunc,
   103  ) []types.PipelineConstructorFunc {
   104  	if len(conf.Processors) > 0 {
   105  		pipelines = append([]types.PipelineConstructorFunc{func(i *int) (types.Pipeline, error) {
   106  			if i == nil {
   107  				procs := 0
   108  				i = &procs
   109  			}
   110  			processors := make([]types.Processor, len(conf.Processors))
   111  			for j, procConf := range conf.Processors {
   112  				newMgr, newLog, newStats := interop.LabelChild(fmt.Sprintf("processor.%v", *i), mgr, log, stats)
   113  				var err error
   114  				processors[j], err = processor.New(procConf, newMgr, newLog, newStats)
   115  				if err != nil {
   116  					return nil, fmt.Errorf("failed to create processor '%v': %v", procConf.Type, err)
   117  				}
   118  				*i++
   119  			}
   120  			return pipeline.NewProcessor(log, stats, processors...), nil
   121  		}}, pipelines...)
   122  	}
   123  	return pipelines
   124  }
   125  
   126  // TODO: V4 Remove this.
   127  func appendProcessorsFromConfigBatchAware(
   128  	hasBatchProc bool,
   129  	conf Config,
   130  	mgr types.Manager,
   131  	log log.Modular,
   132  	stats metrics.Type,
   133  	pipelines ...types.PipelineConstructorFunc,
   134  ) (bool, []types.PipelineConstructorFunc) {
   135  	if len(conf.Processors) > 0 {
   136  		for _, procConf := range conf.Processors {
   137  			if procConf.Type == processor.TypeBatch {
   138  				hasBatchProc = true
   139  			}
   140  		}
   141  		pipelines = append([]types.PipelineConstructorFunc{func(i *int) (types.Pipeline, error) {
   142  			if i == nil {
   143  				procs := 0
   144  				i = &procs
   145  			}
   146  			processors := make([]types.Processor, len(conf.Processors))
   147  			for j, procConf := range conf.Processors {
   148  				newMgr, newLog, newStats := interop.LabelChild(fmt.Sprintf("processor.%v", *i), mgr, log, stats)
   149  				var err error
   150  				processors[j], err = processor.New(procConf, newMgr, newLog, newStats)
   151  				if err != nil {
   152  					return nil, fmt.Errorf("failed to create processor '%v': %v", procConf.Type, err)
   153  				}
   154  				*i++
   155  			}
   156  			return pipeline.NewProcessor(log, stats, processors...), nil
   157  		}}, pipelines...)
   158  	}
   159  	return hasBatchProc, pipelines
   160  }
   161  
   162  func fromSimpleConstructor(fn func(Config, types.Manager, log.Modular, metrics.Type) (Type, error)) ConstructorFunc {
   163  	return func(
   164  		hasBatchProc bool,
   165  		conf Config,
   166  		mgr types.Manager,
   167  		log log.Modular,
   168  		stats metrics.Type,
   169  		pipelines ...types.PipelineConstructorFunc,
   170  	) (Type, error) {
   171  		input, err := fn(conf, mgr, log, stats)
   172  		if err != nil {
   173  			return nil, fmt.Errorf("failed to create input '%v': %w", conf.Type, err)
   174  		}
   175  		pipelines = AppendProcessorsFromConfig(conf, mgr, log, stats, pipelines...)
   176  		return WrapWithPipelines(input, pipelines...)
   177  	}
   178  }
   179  
   180  func fromBatchAwareConstructor(fn func(bool, Config, types.Manager, log.Modular, metrics.Type) (Type, error)) ConstructorFunc {
   181  	return func(
   182  		hasBatchProc bool,
   183  		conf Config,
   184  		mgr types.Manager,
   185  		log log.Modular,
   186  		stats metrics.Type,
   187  		pipelines ...types.PipelineConstructorFunc,
   188  	) (Type, error) {
   189  		input, err := fn(hasBatchProc, conf, mgr, log, stats)
   190  		if err != nil {
   191  			return nil, fmt.Errorf("failed to create input '%v': %w", conf.Type, err)
   192  		}
   193  		pipelines = AppendProcessorsFromConfig(conf, mgr, log, stats, pipelines...)
   194  		return WrapWithPipelines(input, pipelines...)
   195  	}
   196  }
   197  
   198  // Constructors is a map of all input types with their specs.
   199  var Constructors = map[string]TypeSpec{}
   200  
   201  //------------------------------------------------------------------------------
   202  
   203  // String constants representing each input type.
   204  // Deprecated: Do not add new components here. Instead, use the public plugin
   205  // APIs. Examples can be found in: ./internal/impl
   206  const (
   207  	TypeAMQP              = "amqp"
   208  	TypeAMQP09            = "amqp_0_9"
   209  	TypeAMQP1             = "amqp_1"
   210  	TypeAWSKinesis        = "aws_kinesis"
   211  	TypeAWSS3             = "aws_s3"
   212  	TypeAWSSQS            = "aws_sqs"
   213  	TypeAzureBlobStorage  = "azure_blob_storage"
   214  	TypeAzureQueueStorage = "azure_queue_storage"
   215  	TypeBloblang          = "bloblang"
   216  	TypeBroker            = "broker"
   217  	TypeCSVFile           = "csv"
   218  	TypeDynamic           = "dynamic"
   219  	TypeFile              = "file"
   220  	TypeFiles             = "files"
   221  	TypeGCPCloudStorage   = "gcp_cloud_storage"
   222  	TypeGCPPubSub         = "gcp_pubsub"
   223  	TypeGenerate          = "generate"
   224  	TypeHDFS              = "hdfs"
   225  	TypeHTTPClient        = "http_client"
   226  	TypeHTTPServer        = "http_server"
   227  	TypeInproc            = "inproc"
   228  	TypeKafka             = "kafka"
   229  	TypeKafkaBalanced     = "kafka_balanced"
   230  	TypeKinesis           = "kinesis"
   231  	TypeKinesisBalanced   = "kinesis_balanced"
   232  	TypeMQTT              = "mqtt"
   233  	TypeNanomsg           = "nanomsg"
   234  	TypeNATS              = "nats"
   235  	TypeNATSJetStream     = "nats_jetstream"
   236  	TypeNATSStream        = "nats_stream"
   237  	TypeNSQ               = "nsq"
   238  	TypePulsar            = "pulsar"
   239  	TypeReadUntil         = "read_until"
   240  	TypeRedisList         = "redis_list"
   241  	TypeRedisPubSub       = "redis_pubsub"
   242  	TypeRedisStreams      = "redis_streams"
   243  	TypeResource          = "resource"
   244  	TypeS3                = "s3"
   245  	TypeSequence          = "sequence"
   246  	TypeSFTP              = "sftp"
   247  	TypeSocket            = "socket"
   248  	TypeSocketServer      = "socket_server"
   249  	TypeSQS               = "sqs"
   250  	TypeSTDIN             = "stdin"
   251  	TypeSubprocess        = "subprocess"
   252  	TypeTCP               = "tcp"
   253  	TypeTCPServer         = "tcp_server"
   254  	TypeUDPServer         = "udp_server"
   255  	TypeWebsocket         = "websocket"
   256  	TypeZMQ4              = "zmq4"
   257  )
   258  
   259  //------------------------------------------------------------------------------
   260  
   261  // Config is the all encompassing configuration struct for all input types.
   262  // Deprecated: Do not add new components here. Instead, use the public plugin
   263  // APIs. Examples can be found in: ./internal/impl
   264  type Config struct {
   265  	Label             string                       `json:"label" yaml:"label"`
   266  	Type              string                       `json:"type" yaml:"type"`
   267  	AMQP              reader.AMQPConfig            `json:"amqp" yaml:"amqp"`
   268  	AMQP09            reader.AMQP09Config          `json:"amqp_0_9" yaml:"amqp_0_9"`
   269  	AMQP1             reader.AMQP1Config           `json:"amqp_1" yaml:"amqp_1"`
   270  	AWSKinesis        AWSKinesisConfig             `json:"aws_kinesis" yaml:"aws_kinesis"`
   271  	AWSS3             AWSS3Config                  `json:"aws_s3" yaml:"aws_s3"`
   272  	AWSSQS            AWSSQSConfig                 `json:"aws_sqs" yaml:"aws_sqs"`
   273  	AzureBlobStorage  AzureBlobStorageConfig       `json:"azure_blob_storage" yaml:"azure_blob_storage"`
   274  	AzureQueueStorage AzureQueueStorageConfig      `json:"azure_queue_storage" yaml:"azure_queue_storage"`
   275  	Bloblang          BloblangConfig               `json:"bloblang" yaml:"bloblang"`
   276  	Broker            BrokerConfig                 `json:"broker" yaml:"broker"`
   277  	CSVFile           CSVFileConfig                `json:"csv" yaml:"csv"`
   278  	Dynamic           DynamicConfig                `json:"dynamic" yaml:"dynamic"`
   279  	File              FileConfig                   `json:"file" yaml:"file"`
   280  	Files             reader.FilesConfig           `json:"files" yaml:"files"`
   281  	GCPCloudStorage   GCPCloudStorageConfig        `json:"gcp_cloud_storage" yaml:"gcp_cloud_storage"`
   282  	GCPPubSub         reader.GCPPubSubConfig       `json:"gcp_pubsub" yaml:"gcp_pubsub"`
   283  	Generate          BloblangConfig               `json:"generate" yaml:"generate"`
   284  	HDFS              reader.HDFSConfig            `json:"hdfs" yaml:"hdfs"`
   285  	HTTPClient        HTTPClientConfig             `json:"http_client" yaml:"http_client"`
   286  	HTTPServer        HTTPServerConfig             `json:"http_server" yaml:"http_server"`
   287  	Inproc            InprocConfig                 `json:"inproc" yaml:"inproc"`
   288  	Kafka             reader.KafkaConfig           `json:"kafka" yaml:"kafka"`
   289  	KafkaBalanced     reader.KafkaBalancedConfig   `json:"kafka_balanced" yaml:"kafka_balanced"`
   290  	Kinesis           reader.KinesisConfig         `json:"kinesis" yaml:"kinesis"`
   291  	KinesisBalanced   reader.KinesisBalancedConfig `json:"kinesis_balanced" yaml:"kinesis_balanced"`
   292  	MQTT              reader.MQTTConfig            `json:"mqtt" yaml:"mqtt"`
   293  	Nanomsg           reader.ScaleProtoConfig      `json:"nanomsg" yaml:"nanomsg"`
   294  	NATS              reader.NATSConfig            `json:"nats" yaml:"nats"`
   295  	NATSJetStream     NATSJetStreamConfig          `json:"nats_jetstream" yaml:"nats_jetstream"`
   296  	NATSStream        reader.NATSStreamConfig      `json:"nats_stream" yaml:"nats_stream"`
   297  	NSQ               reader.NSQConfig             `json:"nsq" yaml:"nsq"`
   298  	Plugin            interface{}                  `json:"plugin,omitempty" yaml:"plugin,omitempty"`
   299  	Pulsar            PulsarConfig                 `json:"pulsar" yaml:"pulsar"`
   300  	ReadUntil         ReadUntilConfig              `json:"read_until" yaml:"read_until"`
   301  	RedisList         reader.RedisListConfig       `json:"redis_list" yaml:"redis_list"`
   302  	RedisPubSub       reader.RedisPubSubConfig     `json:"redis_pubsub" yaml:"redis_pubsub"`
   303  	RedisStreams      reader.RedisStreamsConfig    `json:"redis_streams" yaml:"redis_streams"`
   304  	Resource          string                       `json:"resource" yaml:"resource"`
   305  	S3                reader.AmazonS3Config        `json:"s3" yaml:"s3"`
   306  	Sequence          SequenceConfig               `json:"sequence" yaml:"sequence"`
   307  	SFTP              SFTPConfig                   `json:"sftp" yaml:"sftp"`
   308  	Socket            SocketConfig                 `json:"socket" yaml:"socket"`
   309  	SocketServer      SocketServerConfig           `json:"socket_server" yaml:"socket_server"`
   310  	SQS               reader.AmazonSQSConfig       `json:"sqs" yaml:"sqs"`
   311  	STDIN             STDINConfig                  `json:"stdin" yaml:"stdin"`
   312  	Subprocess        SubprocessConfig             `json:"subprocess" yaml:"subprocess"`
   313  	TCP               TCPConfig                    `json:"tcp" yaml:"tcp"`
   314  	TCPServer         TCPServerConfig              `json:"tcp_server" yaml:"tcp_server"`
   315  	UDPServer         UDPServerConfig              `json:"udp_server" yaml:"udp_server"`
   316  	Websocket         reader.WebsocketConfig       `json:"websocket" yaml:"websocket"`
   317  	ZMQ4              *reader.ZMQ4Config           `json:"zmq4,omitempty" yaml:"zmq4,omitempty"`
   318  	Processors        []processor.Config           `json:"processors" yaml:"processors"`
   319  }
   320  
   321  // NewConfig returns a configuration struct fully populated with default values.
   322  // Deprecated: Do not add new components here. Instead, use the public plugin
   323  // APIs. Examples can be found in: ./internal/impl
   324  func NewConfig() Config {
   325  	return Config{
   326  		Label:             "",
   327  		Type:              "stdin",
   328  		AMQP:              reader.NewAMQPConfig(),
   329  		AMQP09:            reader.NewAMQP09Config(),
   330  		AMQP1:             reader.NewAMQP1Config(),
   331  		AWSKinesis:        NewAWSKinesisConfig(),
   332  		AWSS3:             NewAWSS3Config(),
   333  		AWSSQS:            NewAWSSQSConfig(),
   334  		AzureBlobStorage:  NewAzureBlobStorageConfig(),
   335  		AzureQueueStorage: NewAzureQueueStorageConfig(),
   336  		Bloblang:          NewBloblangConfig(),
   337  		Broker:            NewBrokerConfig(),
   338  		CSVFile:           NewCSVFileConfig(),
   339  		Dynamic:           NewDynamicConfig(),
   340  		File:              NewFileConfig(),
   341  		Files:             reader.NewFilesConfig(),
   342  		GCPCloudStorage:   NewGCPCloudStorageConfig(),
   343  		GCPPubSub:         reader.NewGCPPubSubConfig(),
   344  		Generate:          NewBloblangConfig(),
   345  		HDFS:              reader.NewHDFSConfig(),
   346  		HTTPClient:        NewHTTPClientConfig(),
   347  		HTTPServer:        NewHTTPServerConfig(),
   348  		Inproc:            NewInprocConfig(),
   349  		Kafka:             reader.NewKafkaConfig(),
   350  		KafkaBalanced:     reader.NewKafkaBalancedConfig(),
   351  		Kinesis:           reader.NewKinesisConfig(),
   352  		KinesisBalanced:   reader.NewKinesisBalancedConfig(),
   353  		MQTT:              reader.NewMQTTConfig(),
   354  		Nanomsg:           reader.NewScaleProtoConfig(),
   355  		NATS:              reader.NewNATSConfig(),
   356  		NATSJetStream:     NewNATSJetStreamConfig(),
   357  		NATSStream:        reader.NewNATSStreamConfig(),
   358  		NSQ:               reader.NewNSQConfig(),
   359  		Plugin:            nil,
   360  		Pulsar:            NewPulsarConfig(),
   361  		ReadUntil:         NewReadUntilConfig(),
   362  		RedisList:         reader.NewRedisListConfig(),
   363  		RedisPubSub:       reader.NewRedisPubSubConfig(),
   364  		RedisStreams:      reader.NewRedisStreamsConfig(),
   365  		Resource:          "",
   366  		S3:                reader.NewAmazonS3Config(),
   367  		Sequence:          NewSequenceConfig(),
   368  		SFTP:              NewSFTPConfig(),
   369  		Socket:            NewSocketConfig(),
   370  		SocketServer:      NewSocketServerConfig(),
   371  		SQS:               reader.NewAmazonSQSConfig(),
   372  		STDIN:             NewSTDINConfig(),
   373  		Subprocess:        NewSubprocessConfig(),
   374  		TCP:               NewTCPConfig(),
   375  		TCPServer:         NewTCPServerConfig(),
   376  		UDPServer:         NewUDPServerConfig(),
   377  		Websocket:         reader.NewWebsocketConfig(),
   378  		ZMQ4:              reader.NewZMQ4Config(),
   379  		Processors:        []processor.Config{},
   380  	}
   381  }
   382  
   383  // SanitiseConfig returns a sanitised version of the Config, meaning sections
   384  // that aren't relevant to behaviour are removed.
   385  func SanitiseConfig(conf Config) (interface{}, error) {
   386  	return conf.Sanitised(false)
   387  }
   388  
   389  // Sanitised returns a sanitised version of the config, meaning sections that
   390  // aren't relevant to behaviour are removed. Also optionally removes deprecated
   391  // fields.
   392  func (conf Config) Sanitised(removeDeprecated bool) (interface{}, error) {
   393  	outputMap, err := config.SanitizeComponent(conf)
   394  	if err != nil {
   395  		return nil, err
   396  	}
   397  	if spec, exists := pluginSpecs[conf.Type]; exists {
   398  		if spec.confSanitiser != nil {
   399  			outputMap["plugin"] = spec.confSanitiser(conf.Plugin)
   400  		}
   401  	}
   402  	if err := docs.SanitiseComponentConfig(
   403  		docs.TypeInput,
   404  		map[string]interface{}(outputMap),
   405  		docs.ShouldDropDeprecated(removeDeprecated),
   406  	); err != nil {
   407  		return nil, err
   408  	}
   409  	return outputMap, nil
   410  }
   411  
   412  //------------------------------------------------------------------------------
   413  
   414  // UnmarshalYAML ensures that when parsing configs that are in a map or slice
   415  // the default values are still applied.
   416  func (conf *Config) UnmarshalYAML(value *yaml.Node) error {
   417  	type confAlias Config
   418  	aliased := confAlias(NewConfig())
   419  
   420  	err := value.Decode(&aliased)
   421  	if err != nil {
   422  		return fmt.Errorf("line %v: %v", value.Line, err)
   423  	}
   424  
   425  	var spec docs.ComponentSpec
   426  	if aliased.Type, spec, err = docs.GetInferenceCandidateFromYAML(nil, docs.TypeInput, aliased.Type, value); err != nil {
   427  		return fmt.Errorf("line %v: %w", value.Line, err)
   428  	}
   429  
   430  	if spec.Plugin {
   431  		pluginNode, err := docs.GetPluginConfigYAML(aliased.Type, value)
   432  		if err != nil {
   433  			return fmt.Errorf("line %v: %v", value.Line, err)
   434  		}
   435  		if spec, exists := pluginSpecs[aliased.Type]; exists && spec.confConstructor != nil {
   436  			conf := spec.confConstructor()
   437  			if err = pluginNode.Decode(conf); err != nil {
   438  				return fmt.Errorf("line %v: %v", value.Line, err)
   439  			}
   440  			aliased.Plugin = conf
   441  		} else {
   442  			aliased.Plugin = &pluginNode
   443  		}
   444  	} else {
   445  		aliased.Plugin = nil
   446  	}
   447  
   448  	*conf = Config(aliased)
   449  	return nil
   450  }
   451  
   452  //------------------------------------------------------------------------------
   453  
   454  // New creates an input type based on an input configuration.
   455  func New(
   456  	conf Config,
   457  	mgr types.Manager,
   458  	log log.Modular,
   459  	stats metrics.Type,
   460  	pipelines ...types.PipelineConstructorFunc,
   461  ) (Type, error) {
   462  	return newHasBatchProcessor(false, conf, mgr, log, stats, pipelines...)
   463  }
   464  
   465  // Deprecated: This is a hack for until the batch processor is removed.
   466  // TODO: V4 Remove this.
   467  func newHasBatchProcessor(
   468  	hasBatchProc bool,
   469  	conf Config,
   470  	mgr types.Manager,
   471  	log log.Modular,
   472  	stats metrics.Type,
   473  	pipelines ...types.PipelineConstructorFunc,
   474  ) (Type, error) {
   475  	if mgrV2, ok := mgr.(interface {
   476  		NewInput(Config, bool, ...types.PipelineConstructorFunc) (types.Input, error)
   477  	}); ok {
   478  		return mgrV2.NewInput(conf, hasBatchProc, pipelines...)
   479  	}
   480  	if c, ok := Constructors[conf.Type]; ok {
   481  		return c.constructor(hasBatchProc, conf, mgr, log, stats, pipelines...)
   482  	}
   483  	if c, ok := pluginSpecs[conf.Type]; ok {
   484  		return c.constructor(hasBatchProc, conf, mgr, log, stats, pipelines...)
   485  	}
   486  	return nil, types.ErrInvalidInputType
   487  }
   488  
   489  //------------------------------------------------------------------------------