github.com/Jeffail/benthos/v3@v3.65.0/lib/output/plugin.go (about) 1 package output 2 3 import ( 4 "bytes" 5 "fmt" 6 "sort" 7 "strings" 8 9 "github.com/Jeffail/benthos/v3/internal/interop/plugins" 10 "github.com/Jeffail/benthos/v3/lib/log" 11 "github.com/Jeffail/benthos/v3/lib/metrics" 12 "github.com/Jeffail/benthos/v3/lib/types" 13 "github.com/Jeffail/benthos/v3/lib/util/config" 14 ) 15 16 //------------------------------------------------------------------------------ 17 18 // PluginConstructor is a func that constructs a Benthos output plugin. These 19 // are plugins that are specific to certain use cases, experimental, private or 20 // otherwise unfit for widespread general use. Any number of plugins can be 21 // specified when using Benthos as a framework. 22 // 23 // The configuration object will be the result of the PluginConfigConstructor 24 // after overlaying the user configuration. 25 type PluginConstructor func( 26 config interface{}, 27 manager types.Manager, 28 logger log.Modular, 29 metrics metrics.Type, 30 ) (types.Output, error) 31 32 // PluginConfigConstructor is a func that returns a pointer to a new and fully 33 // populated configuration struct for a plugin type. 34 type PluginConfigConstructor func() interface{} 35 36 // PluginConfigSanitiser is a function that takes a configuration object for a 37 // plugin and returns a sanitised (minimal) version of it for printing in 38 // examples and plugin documentation. 39 // 40 // This function is useful for when a plugins configuration struct is very large 41 // and complex, but can sometimes be expressed in a more concise way without 42 // losing the original intent. 43 type PluginConfigSanitiser func(conf interface{}) interface{} 44 45 type pluginSpec struct { 46 constructor ConstructorFunc 47 confConstructor PluginConfigConstructor 48 confSanitiser PluginConfigSanitiser 49 description string 50 } 51 52 // pluginSpecs is a map of all output plugin type specs. 53 var pluginSpecs = map[string]pluginSpec{} 54 55 // GetDeprecatedPlugin returns a constructor for an old plugin if it exists. 56 func GetDeprecatedPlugin(name string) (ConstructorFunc, bool) { 57 spec, ok := pluginSpecs[name] 58 if !ok { 59 return nil, false 60 } 61 return spec.constructor, true 62 } 63 64 // RegisterPlugin registers a plugin by a unique name so that it can be 65 // constructed similar to regular outputs. If configuration is not needed for 66 // this plugin then configConstructor can be nil. A constructor for the plugin 67 // itself must be provided. 68 func RegisterPlugin( 69 typeString string, 70 configConstructor PluginConfigConstructor, 71 constructor PluginConstructor, 72 ) { 73 spec := pluginSpecs[typeString] 74 spec.constructor = fromSimpleConstructor(func( 75 conf Config, 76 mgr types.Manager, 77 log log.Modular, 78 stats metrics.Type, 79 ) (Type, error) { 80 return constructor(conf.Plugin, mgr, log, stats) 81 }) 82 spec.confConstructor = configConstructor 83 pluginSpecs[typeString] = spec 84 plugins.Add(typeString, "output") 85 } 86 87 // DocumentPlugin adds a description and an optional configuration sanitiser 88 // function to the definition of a registered plugin. This improves the 89 // documentation generated by PluginDescriptions. 90 func DocumentPlugin( 91 typeString, description string, 92 configSanitiser PluginConfigSanitiser, 93 ) { 94 spec := pluginSpecs[typeString] 95 spec.description = description 96 spec.confSanitiser = configSanitiser 97 pluginSpecs[typeString] = spec 98 } 99 100 // PluginCount returns the number of registered plugins. This does NOT count the 101 // standard set of components. 102 func PluginCount() int { 103 return len(pluginSpecs) 104 } 105 106 //------------------------------------------------------------------------------ 107 108 var pluginHeader = "This document was generated with `benthos --list-output-plugins`." + ` 109 110 This document lists any output plugins that this flavour of Benthos offers 111 beyond the standard set.` 112 113 // PluginDescriptions generates and returns a markdown formatted document 114 // listing each registered plugin and an example configuration for it. 115 func PluginDescriptions() string { 116 // Order alphabetically 117 names := []string{} 118 for name := range pluginSpecs { 119 names = append(names, name) 120 } 121 sort.Strings(names) 122 123 buf := bytes.Buffer{} 124 buf.WriteString("Output Plugins\n") 125 buf.WriteString(strings.Repeat("=", 14)) 126 buf.WriteString("\n\n") 127 buf.WriteString(pluginHeader) 128 buf.WriteString("\n\n") 129 130 buf.WriteString("### Contents\n\n") 131 for i, name := range names { 132 buf.WriteString(fmt.Sprintf("%v. [`%v`](#%v)\n", i+1, name, name)) 133 } 134 135 if len(names) == 0 { 136 buf.WriteString("There are no plugins loaded.") 137 } else { 138 buf.WriteString("\n") 139 } 140 141 // Append each description 142 for i, name := range names { 143 var confBytes []byte 144 145 if confCtor := pluginSpecs[name].confConstructor; confCtor != nil { 146 conf := NewConfig() 147 conf.Type = name 148 conf.Plugin = confCtor() 149 if confSanit, err := SanitiseConfig(conf); err == nil { 150 confBytes, _ = config.MarshalYAML(confSanit) 151 } 152 } 153 154 buf.WriteString("## ") 155 buf.WriteString("`" + name + "`") 156 buf.WriteString("\n") 157 if confBytes != nil { 158 buf.WriteString("\n``` yaml\n") 159 buf.Write(confBytes) 160 buf.WriteString("```\n") 161 } 162 if desc := pluginSpecs[name].description; len(desc) > 0 { 163 buf.WriteString("\n") 164 buf.WriteString(desc) 165 buf.WriteString("\n") 166 } 167 if i != (len(names) - 1) { 168 buf.WriteString("\n") 169 } 170 } 171 return buf.String() 172 } 173 174 //------------------------------------------------------------------------------