Merge pull request #458 from unpoller/output-plugin-interface

Output Plugin Interface
This commit is contained in:
Cody Lee 2022-12-08 20:48:02 -06:00 committed by GitHub
commit 1c847303e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 153 additions and 39 deletions

View File

@ -69,8 +69,8 @@
# How often to poll UniFi and report to Datadog. # How often to poll UniFi and report to Datadog.
interval = "2m" interval = "2m"
# To disable this output plugin # To enable this output plugin
disable = true enable = false
# Datadog Custom Options # Datadog Custom Options
@ -84,7 +84,7 @@
# tags = [ "customer:abc_corp" ] # tags = [ "customer:abc_corp" ]
# For more advanced options for very large amount of data collected see the upstream # For more advanced options for very large amount of data collected see the upstream
# github.com/unpoller/datadogunifi repository README. # github.com/unpoller/unpoller/pkg/datadogunifi repository README.
# Unpoller has an optional web server. To turn it on, set enable to true. If you # Unpoller has an optional web server. To turn it on, set enable to true. If you

View File

@ -35,6 +35,15 @@
} }
}, },
"datadog": {
"enable": false,
"address": "localhost:8125",
"namespace": "",
"tags": [
"customer:abcde"
]
}
"unifi": { "unifi": {
"dynamic": false, "dynamic": false,
"defaults": { "defaults": {

View File

@ -36,6 +36,13 @@ webserver:
accounts: accounts:
captain: "$2a$04$mxw6i0LKH6u46oaLK2cq5eCTAAFkfNiRpzNbz.EyvJZZWNa2FzIlS" captain: "$2a$04$mxw6i0LKH6u46oaLK2cq5eCTAAFkfNiRpzNbz.EyvJZZWNa2FzIlS"
datadog:
enable: false
address: localhost:8125
namespace: ""
tags:
- customer:abcdef
unifi: unifi:
dynamic: false dynamic: false
defaults: defaults:

View File

@ -112,13 +112,15 @@ type DatadogUnifi struct {
*Datadog *Datadog
} }
var _ poller.OutputPlugin = &DatadogUnifi{}
func init() { // nolint: gochecknoinits func init() { // nolint: gochecknoinits
u := &DatadogUnifi{Datadog: &Datadog{}, LastCheck: time.Now()} u := &DatadogUnifi{Datadog: &Datadog{}, LastCheck: time.Now()}
poller.NewOutput(&poller.Output{ poller.NewOutput(&poller.Output{
Name: "datadog", Name: "datadog",
Config: u.Datadog, Config: u.Datadog,
Method: u.Run, OutputPlugin: u,
}) })
} }
@ -188,14 +190,26 @@ func (u *DatadogUnifi) setConfigDefaults() {
} }
func (u *DatadogUnifi) Enabled() bool {
if u == nil {
return false
}
if u.Enable == nil || u.Config == nil {
return false
}
if u.Collector == nil {
return false
}
return *u.Enable
}
// Run runs a ticker to poll the unifi server and update Datadog. // Run runs a ticker to poll the unifi server and update Datadog.
func (u *DatadogUnifi) Run(c poller.Collect) error { func (u *DatadogUnifi) Run(c poller.Collect) error {
disabled := u == nil || u.Enable == nil || !(*u.Enable) || u.Config == nil u.Collector = c
if disabled { if !u.Enabled() {
u.LogDebugf("Datadog config is disabled, output is disabled.") u.Logf("DataDog config missing (or disabled), DataDog output disabled!")
return nil return nil
} }
u.Collector = c
u.Logf("Datadog is configured.") u.Logf("Datadog is configured.")
u.setConfigDefaults() u.setConfigDefaults()

View File

@ -77,6 +77,8 @@ type InfluxUnifi struct {
*InfluxDB *InfluxDB
} }
var _ poller.OutputPlugin = &InfluxUnifi{}
type metric struct { type metric struct {
Table string Table string
Tags map[string]string Tags map[string]string
@ -88,9 +90,9 @@ func init() { // nolint: gochecknoinits
u := &InfluxUnifi{InfluxDB: &InfluxDB{}, LastCheck: time.Now()} u := &InfluxUnifi{InfluxDB: &InfluxDB{}, LastCheck: time.Now()}
poller.NewOutput(&poller.Output{ poller.NewOutput(&poller.Output{
Name: PluginName, Name: PluginName,
Config: u.InfluxDB, Config: u.InfluxDB,
Method: u.Run, OutputPlugin: u,
}) })
} }
@ -130,15 +132,29 @@ func (u *InfluxUnifi) PollController() {
} }
} }
func (u *InfluxUnifi) Enabled() bool {
if u == nil {
return false
}
if u.Config == nil {
return false
}
if u.Collector == nil {
return false
}
return !u.Disable
}
// Run runs a ticker to poll the unifi server and update influxdb. // Run runs a ticker to poll the unifi server and update influxdb.
func (u *InfluxUnifi) Run(c poller.Collect) error { func (u *InfluxUnifi) Run(c poller.Collect) error {
var err error u.Collector = c
if !u.Enabled() {
if u.Collector = c; u.Config == nil || u.Disable {
u.Logf("InfluxDB config missing (or disabled), InfluxDB output disabled!") u.Logf("InfluxDB config missing (or disabled), InfluxDB output disabled!")
return nil return nil
} }
var err error
u.setConfigDefaults() u.setConfigDefaults()
if u.IsVersion2 { if u.IsVersion2 {

View File

@ -46,6 +46,8 @@ type Loki struct {
last time.Time last time.Time
} }
var _ poller.OutputPlugin = &Loki{}
// init is how this modular code is initialized by the main app. // init is how this modular code is initialized by the main app.
// This module adds itself as an output module to the poller core. // This module adds itself as an output module to the poller core.
func init() { // nolint: gochecknoinits func init() { // nolint: gochecknoinits
@ -55,15 +57,26 @@ func init() { // nolint: gochecknoinits
}} }}
poller.NewOutput(&poller.Output{ poller.NewOutput(&poller.Output{
Name: PluginName, Name: PluginName,
Config: l, Config: l,
Method: l.Run, OutputPlugin: l,
}) })
} }
func (l *Loki) Enabled() bool {
if l == nil {
return false
}
if l.Config == nil {
return false
}
return !l.Disable
}
// Run is fired from the poller library after the Config is unmarshalled. // Run is fired from the poller library after the Config is unmarshalled.
func (l *Loki) Run(collect poller.Collect) error { func (l *Loki) Run(collect poller.Collect) error {
if l.Collect = collect; l.Config == nil || l.URL == "" || l.Disable { l.Collect = collect
if l.Config == nil || l.URL == "" || !l.Enabled() {
l.Logf("Loki config missing (or disabled), Loki output disabled!") l.Logf("Loki config missing (or disabled), Loki output disabled!")
return nil return nil
} }

View File

@ -13,6 +13,8 @@ type plugin struct {
poller.Collect poller.Collect
} }
var _ poller.OutputPlugin = &plugin{}
// Config represents the data that is unmarshalled from the up.conf config file for this plugins. // Config represents the data that is unmarshalled from the up.conf config file for this plugins.
// See up.conf.example.mysql for sample input data. // See up.conf.example.mysql for sample input data.
type Config struct { type Config struct {
@ -48,16 +50,30 @@ func init() {
u := &plugin{Config: &Config{}} u := &plugin{Config: &Config{}}
poller.NewOutput(&poller.Output{ poller.NewOutput(&poller.Output{
Name: "mysql", Name: "mysql",
Config: u, // pass in the struct *above* your config (so it can see the struct tags). Config: u, // pass in the struct *above* your config (so it can see the struct tags).
Method: u.Run, OutputPlugin: u,
}) })
} }
func (p *plugin) Enabled() bool {
if p == nil {
return false
}
if p.Config == nil {
return false
}
if p.Collect == nil {
return false
}
return !p.Disable
}
// Run gets called by poller core code. Return when the plugin stops working or has an error. // Run gets called by poller core code. Return when the plugin stops working or has an error.
// In other words, don't run your code in a go routine, it already is. // In other words, don't run your code in a go routine, it already is.
func (p *plugin) Run(c poller.Collect) error { func (p *plugin) Run(c poller.Collect) error {
if p.Collect = c; c == nil || p.Config == nil || p.Disable { p.Collect = c
if p.Config == nil || !p.Enabled() {
return nil // no config or disabled, bail out. return nil // no config or disabled, bail out.
} }

View File

@ -24,12 +24,17 @@ type Collect interface {
Outputs() []string Outputs() []string
} }
type OutputPlugin interface {
Run(Collect) error
Enabled() bool
}
// Output defines the output data for a metric exporter like influx or prometheus. // Output defines the output data for a metric exporter like influx or prometheus.
// Output packages should call NewOutput with this struct in init(). // Output packages should call NewOutput with this struct in init().
type Output struct { type Output struct {
Name string Name string
Config any // Each config is passed into an unmarshaller later. Config any // Each config is passed into an unmarshaller later.
Method func(Collect) error // Called on startup for each configured output. OutputPlugin
} }
// NewOutput should be called by each output package's init function. // NewOutput should be called by each output package's init function.
@ -37,8 +42,8 @@ func NewOutput(o *Output) {
outputSync.Lock() outputSync.Lock()
defer outputSync.Unlock() defer outputSync.Unlock()
if o == nil || o.Method == nil { if o == nil {
panic("nil output or method passed to poller.NewOutput") panic("nil output passed to poller.NewOutput")
} }
outputs = append(outputs, o) outputs = append(outputs, o)
@ -81,9 +86,11 @@ func (u *UnifiPoller) runOutputMethods() (int, chan error) {
defer outputSync.RUnlock() defer outputSync.RUnlock()
for _, o := range outputs { for _, o := range outputs {
go func(o *Output) { if o != nil && o.Enabled() {
err <- o.Method(u) // Run each output plugin go func(o *Output) {
}(o) err <- o.Run(u) // Run each output plugin
}(o)
}
} }
return len(outputs), err return len(outputs), err

View File

@ -46,6 +46,8 @@ type promUnifi struct {
Collector poller.Collect Collector poller.Collect
} }
var _ poller.OutputPlugin = &promUnifi{}
// Config is the input (config file) data used to initialize this output plugin. // Config is the input (config file) data used to initialize this output plugin.
type Config struct { type Config struct {
// If non-empty, each of the collected metrics is prefixed by the // If non-empty, each of the collected metrics is prefixed by the
@ -106,16 +108,30 @@ func init() { // nolint: gochecknoinits
u := &promUnifi{Config: &Config{}} u := &promUnifi{Config: &Config{}}
poller.NewOutput(&poller.Output{ poller.NewOutput(&poller.Output{
Name: PluginName, Name: PluginName,
Config: u, Config: u,
Method: u.Run, OutputPlugin: u,
}) })
} }
func (u *promUnifi) Enabled() bool {
if u == nil {
return false
}
if u.Config == nil {
return false
}
if u.Collector == nil {
return false
}
return !u.Disable
}
// Run creates the collectors and starts the web server up. // Run creates the collectors and starts the web server up.
// Should be run in a Go routine. Returns nil if not configured. // Should be run in a Go routine. Returns nil if not configured.
func (u *promUnifi) Run(c poller.Collect) error { func (u *promUnifi) Run(c poller.Collect) error {
if u.Collector = c; u.Config == nil || u.Disable { u.Collector = c
if u.Config == nil || !u.Enabled() {
u.Logf("Prometheus config missing (or disabled), Prometheus HTTP listener disabled!") u.Logf("Prometheus config missing (or disabled), Prometheus HTTP listener disabled!")
return nil return nil
} }

View File

@ -47,6 +47,8 @@ type Server struct {
start time.Time start time.Time
} }
var _ poller.OutputPlugin = &Server{}
// init is how this modular code is initialized by the main app. // init is how this modular code is initialized by the main app.
// This module adds itself as an output module to the poller core. // This module adds itself as an output module to the poller core.
func init() { // nolint: gochecknoinits func init() { // nolint: gochecknoinits
@ -58,15 +60,29 @@ func init() { // nolint: gochecknoinits
plugins.Config = s.Config plugins.Config = s.Config
poller.NewOutput(&poller.Output{ poller.NewOutput(&poller.Output{
Name: PluginName, Name: PluginName,
Config: s, Config: s,
Method: s.Run, OutputPlugin: s,
}) })
} }
func (s *Server) Enabled() bool {
if s == nil {
return false
}
if s.Config == nil {
return false
}
if s.Collect == nil {
return false
}
return s.Enable
}
// Run starts the server and gets things going. // Run starts the server and gets things going.
func (s *Server) Run(c poller.Collect) error { func (s *Server) Run(c poller.Collect) error {
if s.Collect = c; s.Config == nil || s.Port == 0 || s.HTMLPath == "" || !s.Enable { s.Collect = c
if s.Config == nil || s.Port == 0 || s.HTMLPath == "" || !s.Enabled() {
s.Logf("Internal web server disabled!") s.Logf("Internal web server disabled!")
return nil return nil
} }