package config import ( "errors" "fmt" "io" "os" "path/filepath" "strings" "github.com/pelletier/go-toml" "go.uber.org/zap" ) var ErrNotFound = errors.New("no config file found") type Config struct { Hostname string `toml:"Hostname"` RTMPListenAddr string `toml:"RTMPListenAddr"` HTTPServerEnable bool `toml:"HTTPServerEnable"` HTTPListenAddr string `toml:"HTTPListenAddr"` HTTPAccessLogEnable bool `toml:"HTTPAccessLogEnable"` LogLevel string `toml:"LogLevel"` MetricsEnable bool `toml:"MetricsEnable"` } 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.RTMPListenAddr = ":5566" c.HTTPServerEnable = false c.HTTPListenAddr = ":8077" c.LogLevel = "INFO" c.Hostname = "localhost" decoder := toml.NewDecoder(r) if err := decoder.Decode(&c); err != nil { return nil, fmt.Errorf("error parsing config file: %w", err) } c.UpdateFromEnv() 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 hostname, found := os.LookupEnv("DOGTAMER_HOSTNAME"); found { c.Hostname = hostname } if loglevel, found := os.LookupEnv("DOGTAMER_LOGLEVEL"); found { c.LogLevel = loglevel } if listenAddr, found := os.LookupEnv("DOGTAMER_RTMPLISTENADDR"); found { c.RTMPListenAddr = listenAddr } if httpEnable, found := os.LookupEnv("DOGTAMER_HTTPSERVERENABLE"); found { switch strings.ToUpper(httpEnable) { case "TRUE", "YES", "ENABLE": c.HTTPServerEnable = true } } if httpListenAddr, found := os.LookupEnv("DOGTAMER_HTTPLISTENADDR"); found { c.HTTPListenAddr = httpListenAddr } if httpAccessLogEnable, found := os.LookupEnv("DOGTAMER_HTTPACCESSLOGENABLE"); found { switch strings.ToUpper(httpAccessLogEnable) { case "TRUE", "YES", "ENABLE": c.HTTPAccessLogEnable = true } } if metricsEnable, found := os.LookupEnv("DOGTAMER_METRICSENABLE"); found { switch strings.ToUpper(metricsEnable) { case "TRUE", "YES", "ENABLE": c.MetricsEnable = true } } 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() (*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() } cfg, err := FromReader(strings.NewReader("")) if err == nil { return cfg, ErrNotFound } return nil, ErrNotFound } func (c *Config) DebugLog(logger *zap.SugaredLogger) { logger.Debugw("Config", "hostname", c.Hostname, "rtmp_addr", c.RTMPListenAddr, "http_enable", c.HTTPServerEnable, "http_addr", c.HTTPListenAddr, "http_accesslog", c.HTTPAccessLogEnable, "log_level", c.LogLevel) }