feat: Implement configuration management and DNS provider integration

- Added configuration management using Viper in internal/config/config.go
- Implemented ClientConfig, ServerConfig, TLSConfig, HetznerConfig, UpstreamConfig, and main Config struct.
- Created LoadConfig function to read and validate configuration files.
- Developed Hetzner DNS provider in internal/provider/hetzner/hetzner.go with methods for updating DNS records.
- Added comprehensive unit tests for configuration loading and Hetzner provider functionality.
- Established HTTP server with metrics and update endpoint in internal/server/server.go.
- Implemented request handling, authorization, and error management in the server.
- Created integration tests for the Hetzner provider API interactions.
- Removed legacy dynamic DNS integration tests in favor of the new API-based approach.
This commit is contained in:
2025-04-21 00:45:38 +02:00
parent ea62c6b396
commit adae58b7bc
19 changed files with 1188 additions and 0 deletions

33
cmd/updns/main.go Normal file
View File

@@ -0,0 +1,33 @@
package main
import (
"flag"
"fmt"
"os"
"git.cloonar.com/cloonar/updns/internal/config"
"git.cloonar.com/cloonar/updns/internal/server"
)
func run(cfgPath string) error {
if cfgPath == "" {
return fmt.Errorf("missing --config flag")
}
cfg, err := config.LoadConfig(cfgPath)
if err != nil {
return fmt.Errorf("config load error: %w", err)
}
if err := server.StartServer(cfg); err != nil {
return fmt.Errorf("server error: %w", err)
}
return nil
}
func main() {
cfgPath := flag.String("config", "", "path to config file")
flag.Parse()
if err := run(*cfgPath); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

51
cmd/updns/main_test.go Normal file
View File

@@ -0,0 +1,51 @@
package main
import (
"fmt"
"os"
"path/filepath"
"testing"
)
func TestRunMissingFlag(t *testing.T) {
err := run("")
if err == nil || err.Error() != "missing --config flag" {
t.Fatalf("expected missing flag error, got %v", err)
}
}
func TestRunNonexistentConfig(t *testing.T) {
fake := filepath.Join(os.TempDir(), "no-such-file.yaml")
err := run(fake)
if err == nil || !contains(err.Error(), "config load error:") {
t.Fatalf("expected config load error, got %v", err)
}
}
func TestRunUnsupportedProvider(t *testing.T) {
content := `
server:
bind_address: ":0"
tls:
enabled: false
upstream:
provider: unknown
clients: {}
`
tmp := filepath.Join(os.TempDir(), "updns_test.yaml")
if err := os.WriteFile(tmp, []byte(content), 0644); err != nil {
t.Fatalf("failed to write temp config: %v", err)
}
defer os.Remove(tmp)
err := run(tmp)
if err == nil || !contains(err.Error(), "server error: unsupported provider") {
t.Fatalf("expected unsupported provider error, got %v", err)
}
}
func contains(s, substr string) bool {
return fmt.Sprintf("%v", s) != "" && (substr == s || len(s) > len(substr) && fmt.Sprint(s) != "" && (func() bool {
return len(s) >= len(substr) && s[:len(substr)] == substr
})())
}