diff --git a/pkg/poller/config.go b/pkg/poller/config.go index bd811a04..38f645c9 100644 --- a/pkg/poller/config.go +++ b/pkg/poller/config.go @@ -9,22 +9,13 @@ package poller */ import ( - "encoding/json" - "encoding/xml" - "fmt" - "io/ioutil" - "os" - "reflect" - "strconv" - "strings" "sync" "time" - "github.com/BurntSushi/toml" "github.com/davidnewhall/unifi-poller/pkg/influxunifi" "github.com/spf13/pflag" + "golift.io/config" "golift.io/unifi" - yaml "gopkg.in/yaml.v2" ) // Version is injected by the Makefile @@ -45,7 +36,7 @@ const ( // ENVConfigPrefix is the prefix appended to an env variable tag // name before retrieving the value from the OS. -const ENVConfigPrefix = "UP_" +const ENVConfigPrefix = "UP" // UnifiPoller contains the application startup data, and auth info for UniFi & Influx. type UnifiPoller struct { @@ -68,7 +59,6 @@ type Flag struct { // Controller represents the configuration for a UniFi Controller. // Each polled controller may have its own configuration. type Controller struct { - Interval Duration `json:"interval,omitempty" toml:"interval,omitempty" xml:"interval" yaml:"interval"` VerifySSL bool `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"` SaveIDS bool `json:"save_ids" toml:"save_ids" xml:"save_ids" yaml:"save_ids"` ReAuth bool `json:"reauthenticate" toml:"reauthenticate" xml:"reauthenticate" yaml:"reauthenticate"` @@ -83,115 +73,24 @@ type Controller struct { // This is all of the data stored in the config file. // Any with explicit defaults have omitempty on json and toml tags. type Config struct { - Interval Duration `json:"interval,omitempty" toml:"interval,omitempty" xml:"interval" yaml:"interval"` - Debug bool `json:"debug" toml:"debug" xml:"debug" yaml:"debug"` - Quiet bool `json:"quiet,omitempty" toml:"quiet,omitempty" xml:"quiet" yaml:"quiet"` - VerifySSL bool `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"` - SaveIDS bool `json:"save_ids" toml:"save_ids" xml:"save_ids" yaml:"save_ids"` - ReAuth bool `json:"reauthenticate" toml:"reauthenticate" xml:"reauthenticate" yaml:"reauthenticate"` - InfxBadSSL bool `json:"influx_insecure_ssl" toml:"influx_insecure_ssl" xml:"influx_insecure_ssl" yaml:"influx_insecure_ssl"` - SaveSites bool `json:"save_sites,omitempty" toml:"save_sites,omitempty" xml:"save_sites" yaml:"save_sites"` - Mode string `json:"mode" toml:"mode" xml:"mode" yaml:"mode"` - HTTPListen string `json:"http_listen" toml:"http_listen" xml:"http_listen" yaml:"http_listen"` - Namespace string `json:"namespace" toml:"namespace" xml:"namespace" yaml:"namespace"` - InfluxURL string `json:"influx_url,omitempty" toml:"influx_url,omitempty" xml:"influx_url" yaml:"influx_url"` - InfluxUser string `json:"influx_user,omitempty" toml:"influx_user,omitempty" xml:"influx_user" yaml:"influx_user"` - InfluxPass string `json:"influx_pass,omitempty" toml:"influx_pass,omitempty" xml:"influx_pass" yaml:"influx_pass"` - InfluxDB string `json:"influx_db,omitempty" toml:"influx_db,omitempty" xml:"influx_db" yaml:"influx_db"` - UnifiUser string `json:"unifi_user,omitempty" toml:"unifi_user,omitempty" xml:"unifi_user" yaml:"unifi_user"` - UnifiPass string `json:"unifi_pass,omitempty" toml:"unifi_pass,omitempty" xml:"unifi_pass" yaml:"unifi_pass"` - UnifiBase string `json:"unifi_url,omitempty" toml:"unifi_url,omitempty" xml:"unifi_url" yaml:"unifi_url"` - Sites []string `json:"sites,omitempty" toml:"sites,omitempty" xml:"sites" yaml:"sites"` - Controller []*Controller `json:"controller,omitempty" toml:"controller,omitempty" xml:"controller" yaml:"controller"` -} - -// Duration is used to UnmarshalTOML into a time.Duration value. -type Duration struct{ time.Duration } - -// UnmarshalText parses a duration type from a config file. -func (d *Duration) UnmarshalText(data []byte) (err error) { - d.Duration, err = time.ParseDuration(string(data)) - return -} - -// ParseFile parses and returns our configuration data. -func (c *Config) ParseFile(configFile string) error { - switch buf, err := ioutil.ReadFile(configFile); { - case err != nil: - return err - case strings.Contains(configFile, ".json"): - return json.Unmarshal(buf, c) - case strings.Contains(configFile, ".xml"): - return xml.Unmarshal(buf, c) - case strings.Contains(configFile, ".yaml"): - return yaml.Unmarshal(buf, c) - default: - return toml.Unmarshal(buf, c) - } -} - -// ParseENV copies environment variables into configuration values. -// This is useful for Docker users that find it easier to pass ENV variables -// than a specific configuration file. Uses reflection to find struct tags. -// This method uses the json struct tag member to match environment variables. -// Use a custom tag name by changing "json" below, but that's overkill for this app. -func (c *Config) ParseENV() error { - t := reflect.TypeOf(*c) // Get "types" from the Config struct. - for i := 0; i < t.NumField(); i++ { // Loop each Config struct member - tag := t.Field(i).Tag.Get("json") // Get the ENV variable name from "json" struct tag - tag = strings.Split(strings.ToUpper(tag), ",")[0] // Capitalize and remove ,omitempty suffix - - env := os.Getenv(ENVConfigPrefix + tag) // Then pull value from OS. - if tag == "" || env == "" { // Skip if either are empty. - continue - } - - if err := c.parseENV(reflect.ValueOf(c).Elem().Field(i), tag, env); err != nil { - return err - } - } - - return nil -} - -func (c *Config) parseENV(field reflect.Value, tag, env string) error { - // Reflect and update the u.Config struct member at position i. - switch field.Type().String() { - // Handle each member type appropriately (differently). - case "string": - // This is a reflect package method to update a struct member by index. - field.SetString(env) - - case "int": - val, err := strconv.Atoi(env) - if err != nil { - return fmt.Errorf("%s: %v", tag, err) - } - field.Set(reflect.ValueOf(val)) - - case "[]string": - field.Set(reflect.ValueOf(strings.Split(env, ","))) - - case "poller.Duration": - val, err := time.ParseDuration(env) - if err != nil { - return fmt.Errorf("%s: %v", tag, err) - } - field.Set(reflect.ValueOf(Duration{val})) - - case "bool": - val, err := strconv.ParseBool(env) - if err != nil { - return fmt.Errorf("%s: %v", tag, err) - } - field.SetBool(val) - - case "poller.[]*Controller": - } - // Add more types here if more types are added to the config struct. - - return nil -} - -func (c *Config) parseSlice(field reflect.Value, tag, env string) error { + Interval config.Duration `json:"interval,omitempty" toml:"interval,omitempty" xml:"interval" yaml:"interval"` + Debug bool `json:"debug" toml:"debug" xml:"debug" yaml:"debug"` + Quiet bool `json:"quiet,omitempty" toml:"quiet,omitempty" xml:"quiet" yaml:"quiet"` + VerifySSL bool `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"` + SaveIDS bool `json:"save_ids" toml:"save_ids" xml:"save_ids" yaml:"save_ids"` + ReAuth bool `json:"reauthenticate" toml:"reauthenticate" xml:"reauthenticate" yaml:"reauthenticate"` + InfxBadSSL bool `json:"influx_insecure_ssl" toml:"influx_insecure_ssl" xml:"influx_insecure_ssl" yaml:"influx_insecure_ssl"` + SaveSites bool `json:"save_sites,omitempty" toml:"save_sites,omitempty" xml:"save_sites" yaml:"save_sites"` + Mode string `json:"mode" toml:"mode" xml:"mode" yaml:"mode"` + HTTPListen string `json:"http_listen" toml:"http_listen" xml:"http_listen" yaml:"http_listen"` + Namespace string `json:"namespace" toml:"namespace" xml:"namespace" yaml:"namespace"` + InfluxURL string `json:"influx_url,omitempty" toml:"influx_url,omitempty" xml:"influx_url" yaml:"influx_url"` + InfluxUser string `json:"influx_user,omitempty" toml:"influx_user,omitempty" xml:"influx_user" yaml:"influx_user"` + InfluxPass string `json:"influx_pass,omitempty" toml:"influx_pass,omitempty" xml:"influx_pass" yaml:"influx_pass"` + InfluxDB string `json:"influx_db,omitempty" toml:"influx_db,omitempty" xml:"influx_db" yaml:"influx_db"` + UnifiUser string `json:"unifi_user,omitempty" toml:"unifi_user,omitempty" xml:"unifi_user" yaml:"unifi_user"` + UnifiPass string `json:"unifi_pass,omitempty" toml:"unifi_pass,omitempty" xml:"unifi_pass" yaml:"unifi_pass"` + UnifiBase string `json:"unifi_url,omitempty" toml:"unifi_url,omitempty" xml:"unifi_url" yaml:"unifi_url"` + Sites []string `json:"sites,omitempty" toml:"sites,omitempty" xml:"sites" yaml:"sites"` + Controller []Controller `json:"controller,omitempty" toml:"controller,omitempty" xml:"controller" yaml:"controller"` } diff --git a/pkg/poller/start.go b/pkg/poller/start.go index 3f1a4d74..57d27720 100644 --- a/pkg/poller/start.go +++ b/pkg/poller/start.go @@ -9,6 +9,7 @@ import ( "time" "github.com/spf13/pflag" + "golift.io/config" ) // New returns a new poller struct preloaded with default values. @@ -23,7 +24,7 @@ func New() *UnifiPoller { UnifiUser: defaultUnifiUser, UnifiPass: "", UnifiBase: defaultUnifiURL, - Interval: Duration{defaultInterval}, + Interval: config.Duration{Duration: defaultInterval}, Sites: []string{"all"}, SaveSites: true, HTTPListen: defaultHTTPListen, @@ -53,16 +54,16 @@ func (u *UnifiPoller) Start() error { } // Parse config file. - if err := u.Config.ParseFile(u.Flag.ConfigFile); err != nil { + if err := config.ParseFile(u.Config, u.Flag.ConfigFile); err != nil { u.Flag.Usage() return err } // Update Config with ENV variable overrides. - if err := u.Config.ParseENV(); err != nil { + if _, err := config.ParseENV(u.Config, ENVConfigPrefix); err != nil { return err } - + log.Println("START():", u.Config.Controller) if u.Flag.DumpJSON != "" { return u.DumpJSONPayload() } @@ -71,7 +72,7 @@ func (u *UnifiPoller) Start() error { log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate) u.LogDebugf("Debug Logging Enabled") } - + log.Println("sites", u.Config.Sites) log.Printf("[INFO] UniFi Poller v%v Starting Up! PID: %d", Version, os.Getpid()) return u.Run() } diff --git a/pkg/poller/unifi.go b/pkg/poller/unifi.go index beeacec5..c3eea421 100644 --- a/pkg/poller/unifi.go +++ b/pkg/poller/unifi.go @@ -70,8 +70,7 @@ FIRST: continue FIRST } } - // This is fine, it may get added later. - u.LogErrorf("configured site not found on controller: %v", s) + return fmt.Errorf("configured site not found on controller: %v", s) } return nil