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 //------------------------------------------------------------------------------