Add minimal web interface
This commit is contained in:
98
config/config.go
Normal file
98
config/config.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
var ErrNotFound = errors.New("no config file found")
|
||||
|
||||
type Config struct {
|
||||
ListenAddr string `toml:"ListenAddr"`
|
||||
LogLevel string `toml:"LogLevel"`
|
||||
}
|
||||
|
||||
type InvalidValueError struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
func (ive *InvalidValueError) Error() string {
|
||||
return fmt.Sprintf("invalid value: config key %s has invalid value", ive.Key)
|
||||
}
|
||||
|
||||
func FromReader(r io.Reader) (*Config, error) {
|
||||
var c Config
|
||||
// Set some defaults
|
||||
c.ListenAddr = ":5566"
|
||||
c.LogLevel = "INFO"
|
||||
|
||||
decoder := toml.NewDecoder(r)
|
||||
if err := decoder.Decode(&c); err != nil {
|
||||
return nil, fmt.Errorf("error parsing config file: %w", err)
|
||||
}
|
||||
|
||||
return &c, c.Verify()
|
||||
}
|
||||
|
||||
func (c *Config) Verify() error {
|
||||
// Check that LogLevel is valid
|
||||
switch c.LogLevel {
|
||||
case "DEBUG", "INFO", "WARN", "ERROR":
|
||||
default:
|
||||
return &InvalidValueError{Key: "LogLevel"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) UpdateFromEnv() error {
|
||||
if loglevel, found := os.LookupEnv("DOGTAMER_LOGLEVEL"); found {
|
||||
c.LogLevel = loglevel
|
||||
}
|
||||
if listenAddr, found := os.LookupEnv("DOGTAMER_LISTENADDR"); found {
|
||||
c.ListenAddr = listenAddr
|
||||
}
|
||||
|
||||
return c.Verify()
|
||||
}
|
||||
|
||||
func FromFile(path string) (*Config, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error opening config file: %w", err)
|
||||
}
|
||||
|
||||
return FromReader(f)
|
||||
}
|
||||
|
||||
func FromDefaultLocations(path string) (*Config, error) {
|
||||
defaultLocations := []string{
|
||||
"dogtamer.toml",
|
||||
"/etc/dogtamer.toml",
|
||||
}
|
||||
baseConfigDir, err := os.UserConfigDir()
|
||||
if err == nil {
|
||||
userConfigFile := filepath.Join(baseConfigDir, "dogtamer", "dogtamer.toml")
|
||||
defaultLocations = append(defaultLocations, userConfigFile)
|
||||
}
|
||||
|
||||
for _, fname := range defaultLocations {
|
||||
if _, err := os.Stat(fname); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
cfg, err := FromFile(fname)
|
||||
if err != nil {
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
return cfg, cfg.UpdateFromEnv()
|
||||
}
|
||||
|
||||
return nil, ErrNotFound
|
||||
}
|
126
config/config_test.go
Normal file
126
config/config_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.uio.no/torjus/dogtamer/config"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
t.Run("TestEmpty", func(t *testing.T) {
|
||||
emptyConfig := ""
|
||||
sr := strings.NewReader(emptyConfig)
|
||||
|
||||
c, err := config.FromReader(sr)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading empty config: %s", err)
|
||||
}
|
||||
|
||||
// Ensure proper defaults are set
|
||||
if c.ListenAddr != ":5566" {
|
||||
t.Errorf("Unexpected ListenAddr: %s", c.ListenAddr)
|
||||
}
|
||||
if c.LogLevel != "INFO" {
|
||||
t.Errorf("Unexpected LogLevel: %s", c.LogLevel)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TestWithValidValues", func(t *testing.T) {
|
||||
configString := `
|
||||
# Random comment
|
||||
LogLevel = "DEBUG"
|
||||
|
||||
ListenAddr = ":5555"
|
||||
`
|
||||
sr := strings.NewReader(configString)
|
||||
|
||||
c, err := config.FromReader(sr)
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading config: %s", err)
|
||||
}
|
||||
|
||||
if c.ListenAddr != ":5555" {
|
||||
t.Errorf("Unexpected ListenAddr: %s", c.ListenAddr)
|
||||
}
|
||||
|
||||
if c.LogLevel != "DEBUG" {
|
||||
t.Errorf("Unexpected LogLevel: %s", c.LogLevel)
|
||||
}
|
||||
})
|
||||
t.Run("TestWithInValidValues", func(t *testing.T) {
|
||||
configString := `
|
||||
# Random comment
|
||||
LogLevel = "INVALID"
|
||||
|
||||
ListenAddr = ":5555"
|
||||
`
|
||||
sr := strings.NewReader(configString)
|
||||
|
||||
_, err := config.FromReader(sr)
|
||||
ive, ok := err.(*config.InvalidValueError)
|
||||
if !ok {
|
||||
t.Fatalf("Error is of wrong type: %T", ive)
|
||||
}
|
||||
if ive.Key != "LogLevel" {
|
||||
t.Errorf("Error has wrong key: %s", ive.Key)
|
||||
}
|
||||
})
|
||||
t.Run("TestFromEnv", func(t *testing.T) {
|
||||
os.Clearenv()
|
||||
|
||||
envValues := map[string]string{
|
||||
"DOGTAMER_LOGLEVEL": "DEBUG",
|
||||
"DOGTAMER_LISTENADDR": ":3333",
|
||||
}
|
||||
|
||||
for k, v := range envValues {
|
||||
if err := os.Setenv(k, v); err != nil {
|
||||
t.Fatalf("Unable to set env values: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
emptyCfg := ""
|
||||
sr := strings.NewReader(emptyCfg)
|
||||
|
||||
c, err := config.FromReader(sr)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing config string: %s", err)
|
||||
}
|
||||
if err := c.UpdateFromEnv(); err != nil {
|
||||
t.Errorf("Error updating config with environment")
|
||||
}
|
||||
|
||||
if c.ListenAddr != envValues["DOGTAMER_LISTENADDR"] {
|
||||
t.Errorf("ListenAddr has wrong value: %s", c.ListenAddr)
|
||||
}
|
||||
if c.LogLevel != envValues["DOGTAMER_LOGLEVEL"] {
|
||||
t.Errorf("LogLevel has wrong value: %s", c.LogLevel)
|
||||
}
|
||||
})
|
||||
t.Run("TestFromEnvInvalid", func(t *testing.T) {
|
||||
os.Clearenv()
|
||||
|
||||
envValues := map[string]string{
|
||||
"DOGTAMER_LOGLEVEL": "TEST",
|
||||
"DOGTAMER_LISTENADDR": ":3333",
|
||||
}
|
||||
|
||||
for k, v := range envValues {
|
||||
if err := os.Setenv(k, v); err != nil {
|
||||
t.Fatalf("Unable to set env values: %s", err)
|
||||
}
|
||||
}
|
||||
emptyCfg := ""
|
||||
sr := strings.NewReader(emptyCfg)
|
||||
|
||||
c, err := config.FromReader(sr)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing config string: %s", err)
|
||||
}
|
||||
if err := c.UpdateFromEnv(); err == nil {
|
||||
t.Errorf("No error when parsing invalid env values")
|
||||
}
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user