Skip to content

Commit

Permalink
refactor: add tests and restructure config
Browse files Browse the repository at this point in the history
  • Loading branch information
Felix Peters committed Apr 21, 2024
1 parent 798e0f7 commit b5d3024
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 52 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/build-snapshot.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
name: build-snapshot


# todo: replace with production build later
on:
push:
branches: [master]
Expand All @@ -10,7 +12,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
Expand Down
17 changes: 0 additions & 17 deletions .github/workflows/pre-commit-lint.yml

This file was deleted.

28 changes: 28 additions & 0 deletions .github/workflows/test-and-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: test-and-lint

on:
pull_request:
branches:
- master
push:
branches: [master]

jobs:
test-and-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v3
- uses: actions/setup-go@v5

- name: Run pre-commit with linters
uses: pre-commit/[email protected]

- name: Run tests
run: go test -json > TestResults.json

- name: Upload Go test results
uses: actions/upload-artifact@v4
with:
name: Upload Go results
path: TestResults.json
4 changes: 2 additions & 2 deletions config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ groups:
group1:
username: group1_user
password: group1_pass
# loglevel can be one of "debug", "info", "warn", "error", or "fatal"
# loglevel: info
# loglevel can be one of "debug", "info", "warn", "error"
loglevel: info
32 changes: 21 additions & 11 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,37 @@ type Config struct {

type SafeConfig struct {
sync.RWMutex
C *Config
Config *Config
}

type HostConfig struct {
Username string `yaml:"username"`
Password string `yaml:"password"`
}

func (sc *SafeConfig) ReloadConfig(configFile string) error {
var c = &Config{}

// Read exporter config from file
func NewConfigFromFile(configFile string) (*Config, error) {
var config = &Config{}
yamlFile, err := os.ReadFile(configFile)
if err != nil {
return err
return nil, err
}

if err := yaml.Unmarshal(yamlFile, config); err != nil {
return nil, err
}
if err := yaml.Unmarshal(yamlFile, c); err != nil {

return config, nil
}

func (sc *SafeConfig) ReloadConfig(configFile string) error {
var c, err = NewConfigFromFile(configFile)
if err != nil {
return err
}

sc.Lock()
sc.C = c
sc.Config = c
sc.Unlock()

return nil
Expand All @@ -46,13 +56,13 @@ func (sc *SafeConfig) ReloadConfig(configFile string) error {
func (sc *SafeConfig) HostConfigForTarget(target string) (*HostConfig, error) {
sc.Lock()
defer sc.Unlock()
if hostConfig, ok := sc.C.Hosts[target]; ok {
if hostConfig, ok := sc.Config.Hosts[target]; ok {
return &HostConfig{
Username: hostConfig.Username,
Password: hostConfig.Password,
}, nil
}
if hostConfig, ok := sc.C.Hosts["default"]; ok {
if hostConfig, ok := sc.Config.Hosts["default"]; ok {
return &HostConfig{
Username: hostConfig.Username,
Password: hostConfig.Password,
Expand All @@ -66,7 +76,7 @@ func (sc *SafeConfig) HostConfigForTarget(target string) (*HostConfig, error) {
func (sc *SafeConfig) HostConfigForGroup(group string) (*HostConfig, error) {
sc.Lock()
defer sc.Unlock()
if hostConfig, ok := sc.C.Groups[group]; ok {
if hostConfig, ok := sc.Config.Groups[group]; ok {
return &hostConfig, nil
}
return &HostConfig{}, fmt.Errorf("no credentials found for group %s", group)
Expand All @@ -75,7 +85,7 @@ func (sc *SafeConfig) HostConfigForGroup(group string) (*HostConfig, error) {
func (sc *SafeConfig) AppLogLevel() string {
sc.Lock()
defer sc.Unlock()
logLevel := sc.C.Loglevel
logLevel := sc.Config.Loglevel
if logLevel != "" {
return logLevel
}
Expand Down
19 changes: 19 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestConfigFromFile(t *testing.T) {
configFile := "config.example.yml"

config, err := NewConfigFromFile(configFile)
assert.NoError(t, err)
assert.NotNil(t, config)

assert.Equal(t, "info", config.Loglevel)
assert.Equal(t, config.Hosts["default"], HostConfig{Username: "user", Password: "pass"})
assert.Equal(t, config.Groups["group1"], HostConfig{Username: "group1_user", Password: "group1_pass"})
}
37 changes: 18 additions & 19 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,15 @@ import (
)

var (
Version string
BuildRevision string
BuildBranch string
BuildTime string
BuildHost string

webConfig = flag.String("web.config", "", "Path to web configuration file.")
configFile = flag.String("config.file", "config.yml", "Path to configuration file.")
listenAddress = flag.String(
"web.listen-address",
":9610",
"Address to listen on for web interface and telemetry.",
)
sc = &SafeConfig{
C: &Config{},
config = &SafeConfig{
Config: &Config{},
}
reloadCh chan chan error
)
Expand All @@ -43,7 +37,7 @@ func reloadHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" || r.Method == "PUT" {
slog.Info("Triggered configuration reload from /-/reload HTTP endpoint")
err := sc.ReloadConfig(*configFile)
err := config.ReloadConfig(*configFile)
if err != nil {
slog.Error("failed to reload config file", slog.Any("error", err))
http.Error(w, "failed to reload config file", http.StatusInternalServerError)
Expand Down Expand Up @@ -84,15 +78,15 @@ func metricsHandler() http.HandlerFunc {

if ok && len(group[0]) >= 1 {
// Trying to get hostConfig from group.
if hostConfig, err = sc.HostConfigForGroup(group[0]); err != nil {
if hostConfig, err = config.HostConfigForGroup(group[0]); err != nil {
slog.Error("error getting credentials", slog.Any("error", err))
return
}
}

// Always falling back to single host config when group config failed.
if hostConfig == nil {
if hostConfig, err = sc.HostConfigForTarget(target); err != nil {
if hostConfig, err = config.HostConfigForTarget(target); err != nil {
slog.Error("error getting credentials", slog.Any("error", err))
return
}
Expand All @@ -107,20 +101,25 @@ func metricsHandler() http.HandlerFunc {
// Delegate http serving to Prometheus client library, which will call collector.Collect.
h := promhttp.HandlerFor(gatherers, promhttp.HandlerOpts{})
h.ServeHTTP(w, r)

}
}

// Parse the log leven from input
func parseLogLevel(level string) slog.Level {
ret := slog.LevelInfo
if level == "debug" {
switch level {
case "debug":
ret = slog.LevelDebug
} else if level == "warning" {
case "info":
ret = slog.LevelInfo
case "warn":
ret = slog.LevelWarn
} else if level == "error" {
case "error":
ret = slog.LevelError
default:
slog.Warn("Invalid loglevel provided. Fallback to default")
}

return ret
}

Expand All @@ -132,14 +131,14 @@ func main() {
kitlogger := kitlog.NewLogfmtLogger(os.Stderr)

// load config first time
if err := sc.ReloadConfig(*configFile); err != nil {
if err := config.ReloadConfig(*configFile); err != nil {
slog.Error("Error parsing config file", slog.Any("error", err))
panic(err)
}

// Setup dinal logger from config
opts := &slog.HandlerOptions{
Level: parseLogLevel(sc.C.Loglevel),
Level: parseLogLevel(config.Config.Loglevel),
}
logger := slog.New(slog.NewTextHandler(os.Stdout, opts))
slog.SetDefault(logger)
Expand All @@ -155,13 +154,13 @@ func main() {
for {
select {
case <-hup:
if err := sc.ReloadConfig(*configFile); err != nil {
if err := config.ReloadConfig(*configFile); err != nil {
slog.Error("failed to reload config file", slog.Any("error", err))
break
}
slog.Info("config file reload", slog.String("operation", "sc.ReloadConfig"))
case rc := <-reloadCh:
if err := sc.ReloadConfig(*configFile); err != nil {
if err := config.ReloadConfig(*configFile); err != nil {
slog.Error("failed to reload config file", slog.Any("error", err))
rc <- err
break
Expand Down
5 changes: 3 additions & 2 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"fmt"
"log/slog"
"testing"

Expand All @@ -14,13 +15,13 @@ func TestParseLogLevel(t *testing.T) {
}{
{"debug", slog.LevelDebug},
{"info", slog.LevelInfo},
{"warning", slog.LevelWarn},
{"warn", slog.LevelWarn},
{"error", slog.LevelError},
{"unknown", slog.LevelInfo}, // default level
}

for _, tc := range testCases {
actual := parseLogLevel(tc.level)
assert.Equal(t, tc.expected, actual, "Unexpected log level")
assert.Equal(t, tc.expected, actual, fmt.Sprintf("Unexpected log level parsed for infot %s", tc.level))
}
}

0 comments on commit b5d3024

Please sign in to comment.