Environment variables take precedence over flags.

This commit is contained in:
Robert Jacob 2018-10-27 16:29:11 +02:00
parent 6eb05643c9
commit f0bc18ab43
3 changed files with 322 additions and 47 deletions

102
config.go Normal file
View file

@ -0,0 +1,102 @@
package main
import (
"errors"
netatmo "github.com/exzz/netatmo-api-go"
"github.com/spf13/pflag"
)
const (
envVarListenAddress = "NETATMO_EXPORTER_ADDR"
envVarNetatmoClientID = "NETATMO_CLIENT_ID"
envVarNetatmoClientSecret = "NETATMO_CLIENT_SECRET"
envVarNetatmoUsername = "NETATMO_CLIENT_USERNAME"
envVarNetatmoPassword = "NETATMO_CLIENT_PASSWORD"
flagListenAddress = "addr"
flagNetatmoClientID = "client-id"
flagNetatmoClientSecret = "client-secret"
flagNetatmoUsername = "username"
flagNetatmoPassword = "password"
)
var (
defaultConfig = config{
Addr: ":9210",
}
errNoBinaryName = errors.New("need the binary name as first argument")
errNoListenAddress = errors.New("no listen address")
errNoNetatmoClientID = errors.New("need a NetAtmo client ID")
errNoNetatmoClientSecret = errors.New("need a NetAtmo client secret")
errNoNetatmoUsername = errors.New("username can not be blank")
errNoNetatmoPassword = errors.New("password can not be blank")
)
type config struct {
Addr string
Netatmo netatmo.Config
}
func parseConfig(args []string, getenv func(string) string) (config, error) {
cfg := defaultConfig
if len(args) < 1 {
return cfg, errNoBinaryName
}
flagSet := pflag.NewFlagSet(args[0], pflag.ExitOnError)
flagSet.StringVarP(&cfg.Addr, "addr", "a", cfg.Addr, "Address to listen on.")
flagSet.StringVarP(&cfg.Netatmo.ClientID, "client-id", "i", cfg.Netatmo.ClientID, "Client ID for NetAtmo app.")
flagSet.StringVarP(&cfg.Netatmo.ClientSecret, "client-secret", "s", cfg.Netatmo.ClientSecret, "Client secret for NetAtmo app.")
flagSet.StringVarP(&cfg.Netatmo.Username, "username", "u", cfg.Netatmo.Username, "Username of NetAtmo account.")
flagSet.StringVarP(&cfg.Netatmo.Password, "password", "p", cfg.Netatmo.Password, "Password of NetAtmo account.")
flagSet.Parse(args[1:])
applyEnvironment(&cfg, getenv)
if len(cfg.Addr) == 0 {
return cfg, errNoListenAddress
}
if len(cfg.Netatmo.ClientID) == 0 {
return cfg, errNoNetatmoClientID
}
if len(cfg.Netatmo.ClientSecret) == 0 {
return cfg, errNoNetatmoClientSecret
}
if len(cfg.Netatmo.Username) == 0 {
return cfg, errNoNetatmoUsername
}
if len(cfg.Netatmo.Password) == 0 {
return cfg, errNoNetatmoPassword
}
return cfg, nil
}
func applyEnvironment(cfg *config, getenv func(string) string) {
if envAddr := getenv(envVarListenAddress); envAddr != "" {
cfg.Addr = envAddr
}
if envClientID := getenv(envVarNetatmoClientID); envClientID != "" {
cfg.Netatmo.ClientID = envClientID
}
if envClientSecret := getenv(envVarNetatmoClientSecret); envClientSecret != "" {
cfg.Netatmo.ClientSecret = envClientSecret
}
if envUsername := getenv(envVarNetatmoUsername); envUsername != "" {
cfg.Netatmo.Username = envUsername
}
if envPassword := getenv(envVarNetatmoPassword); envPassword != "" {
cfg.Netatmo.Password = envPassword
}
}

219
config_test.go Normal file
View file

@ -0,0 +1,219 @@
package main
import (
"reflect"
"testing"
netatmo "github.com/exzz/netatmo-api-go"
)
func TestParseConfig(t *testing.T) {
tests := []struct {
name string
args []string
env map[string]string
wantConfig config
wantErr error
}{
{
name: "no args",
args: []string{},
env: map[string]string{},
wantConfig: config{},
wantErr: errNoBinaryName,
},
{
name: "success",
args: []string{
"test-cmd",
"--" + flagNetatmoClientID,
"id",
"--" + flagNetatmoClientSecret,
"secret",
"--" + flagNetatmoUsername,
"username",
"--" + flagNetatmoPassword,
"password",
},
env: map[string]string{},
wantConfig: config{
Addr: defaultConfig.Addr,
Netatmo: netatmo.Config{
ClientID: "id",
ClientSecret: "secret",
Username: "username",
Password: "password",
},
},
wantErr: nil,
},
{
name: "all env",
args: []string{
"test-cmd",
},
env: map[string]string{
envVarListenAddress: ":8080",
envVarNetatmoClientID: "id",
envVarNetatmoClientSecret: "secret",
envVarNetatmoUsername: "username",
envVarNetatmoPassword: "password",
},
wantConfig: config{
Addr: ":8080",
Netatmo: netatmo.Config{
ClientID: "id",
ClientSecret: "secret",
Username: "username",
Password: "password",
},
},
wantErr: nil,
},
{
name: "no addr",
args: []string{
"test-cmd",
"--" + flagListenAddress,
"",
"--" + flagNetatmoClientID,
"id",
"--" + flagNetatmoClientSecret,
"secret",
"--" + flagNetatmoUsername,
"username",
"--" + flagNetatmoPassword,
"password",
},
env: map[string]string{},
wantConfig: config{
Addr: defaultConfig.Addr,
Netatmo: netatmo.Config{
ClientID: "id",
ClientSecret: "secret",
Username: "username",
Password: "password",
},
},
wantErr: errNoListenAddress,
},
{
name: "no client id",
args: []string{
"test-cmd",
"--" + flagNetatmoClientSecret,
"secret",
"--" + flagNetatmoUsername,
"username",
"--" + flagNetatmoPassword,
"password",
},
env: map[string]string{},
wantConfig: config{
Addr: defaultConfig.Addr,
Netatmo: netatmo.Config{
ClientID: "id",
ClientSecret: "secret",
Username: "username",
Password: "password",
},
},
wantErr: errNoNetatmoClientID,
},
{
name: "no client secret",
args: []string{
"test-cmd",
"--" + flagNetatmoClientID,
"id",
"--" + flagNetatmoUsername,
"username",
"--" + flagNetatmoPassword,
"password",
},
env: map[string]string{},
wantConfig: config{
Addr: defaultConfig.Addr,
Netatmo: netatmo.Config{
ClientID: "id",
ClientSecret: "secret",
Username: "username",
Password: "password",
},
},
wantErr: errNoNetatmoClientSecret,
},
{
name: "no username",
args: []string{
"test-cmd",
"--" + flagNetatmoClientID,
"id",
"--" + flagNetatmoClientSecret,
"secret",
"--" + flagNetatmoPassword,
"password",
},
env: map[string]string{},
wantConfig: config{
Addr: defaultConfig.Addr,
Netatmo: netatmo.Config{
ClientID: "id",
ClientSecret: "secret",
Username: "username",
Password: "password",
},
},
wantErr: errNoNetatmoUsername,
},
{
name: "no password",
args: []string{
"test-cmd",
"--" + flagNetatmoClientID,
"id",
"--" + flagNetatmoClientSecret,
"secret",
"--" + flagNetatmoUsername,
"username",
},
env: map[string]string{},
wantConfig: config{
Addr: defaultConfig.Addr,
Netatmo: netatmo.Config{
ClientID: "id",
ClientSecret: "secret",
Username: "username",
Password: "password",
},
},
wantErr: errNoNetatmoPassword,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
getenv := func(key string) string {
return tt.env[key]
}
config, err := parseConfig(tt.args, getenv)
if err != tt.wantErr {
t.Errorf("got error %q, want %q", err, tt.wantErr)
}
if err != nil {
return
}
if !reflect.DeepEqual(config, tt.wantConfig) {
t.Errorf("got config %v, want %v", config, tt.wantConfig)
}
})
}
}

48
main.go
View file

@ -1,62 +1,16 @@
package main package main
import ( import (
"errors"
"log" "log"
"net/http" "net/http"
"os" "os"
netatmo "github.com/exzz/netatmo-api-go" netatmo "github.com/exzz/netatmo-api-go"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/spf13/pflag"
) )
type config struct {
Addr string
Netatmo netatmo.Config
}
func parseConfig() (config, error) {
cfg := config{}
// Set default port to bind to
addr := ":9210"
if envAddr := os.Getenv("NETATMO_EXPORTER_ADDR"); envAddr != "" {
addr = envAddr
}
pflag.StringVarP(&cfg.Addr, "addr", "a", addr, "Address to listen on.")
pflag.StringVarP(&cfg.Netatmo.ClientID, "client-id", "i", os.Getenv("NETATMO_CLIENT_ID"), "Client ID for NetAtmo app.")
pflag.StringVarP(&cfg.Netatmo.ClientSecret, "client-secret", "s", os.Getenv("NETATMO_CLIENT_SECRET"), "Client secret for NetAtmo app.")
pflag.StringVarP(&cfg.Netatmo.Username, "username", "u", os.Getenv("NETATMO_CLIENT_USERNAME"), "Username of NetAtmo account.")
pflag.StringVarP(&cfg.Netatmo.Password, "password", "p", os.Getenv("NETATMO_CLIENT_PASSWORD"), "Password of NetAtmo account.")
pflag.Parse()
if len(cfg.Addr) == 0 {
return cfg, errors.New("no listen address")
}
if len(cfg.Netatmo.ClientID) == 0 {
return cfg, errors.New("need a NetAtmo client ID")
}
if len(cfg.Netatmo.ClientSecret) == 0 {
return cfg, errors.New("need a NetAtmo client secret")
}
if len(cfg.Netatmo.Username) == 0 {
return cfg, errors.New("username can not be blank")
}
if len(cfg.Netatmo.Password) == 0 {
return cfg, errors.New("password can not be blank")
}
return cfg, nil
}
func main() { func main() {
cfg, err := parseConfig() cfg, err := parseConfig(os.Args, os.Getenv)
if err != nil { if err != nil {
log.Fatalf("Error in configuration: %s", err) log.Fatalf("Error in configuration: %s", err)
} }