136 lines
3.9 KiB
Go
136 lines
3.9 KiB
Go
// Команда traefik-selectel-dns: создаёт A-записи в Selectel DNS для хостов из роутеров Traefik.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/realmanual/traefik-selectel/internal/config"
|
|
"github.com/realmanual/traefik-selectel/internal/dockerevents"
|
|
"github.com/realmanual/traefik-selectel/internal/health"
|
|
"github.com/realmanual/traefik-selectel/internal/reconciler"
|
|
"github.com/realmanual/traefik-selectel/internal/selectel"
|
|
"github.com/realmanual/traefik-selectel/internal/traefik"
|
|
)
|
|
|
|
var version = "dev"
|
|
|
|
const dockerDebounce = 5 * time.Second
|
|
|
|
func main() {
|
|
os.Exit(run())
|
|
}
|
|
|
|
func run() int {
|
|
configPath := flag.String("config", os.Getenv("TSD_CONFIG"), "путь к YAML-конфигу (или TSD_CONFIG)")
|
|
healthcheck := flag.Bool("healthcheck", false, "проверить /healthz локального экземпляра и выйти (для HEALTHCHECK)")
|
|
showVersion := flag.Bool("version", false, "показать версию")
|
|
flag.Parse()
|
|
|
|
if *showVersion {
|
|
fmt.Println(version)
|
|
return 0
|
|
}
|
|
|
|
cfg, err := config.Load(*configPath, os.Getenv, config.OSReadFile)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "ошибка конфигурации:", err)
|
|
return 2
|
|
}
|
|
if *healthcheck {
|
|
if err := health.Probe(cfg.Listen, 3*time.Second); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
log := newLogger(cfg)
|
|
log.Info("запуск", "version", version, "zones", cfg.Zones, "ip", cfg.IP, "ttl", cfg.TTL,
|
|
"interval", cfg.Interval.String(), "deleteEnabled", cfg.Delete.Enabled, "dryRun", cfg.DryRun,
|
|
"traefikAPI", cfg.Traefik.APIURL, "dockerEvents", cfg.Docker.Enabled)
|
|
if cfg.DryRun {
|
|
log.Warn("включён dry-run: изменения в DNS не выполняются")
|
|
}
|
|
|
|
tokens, err := selectel.NewIAMTokenSource(cfg.Credentials, cfg.Selectel.AuthURL, cfg.Selectel.Timeout)
|
|
if err != nil {
|
|
log.Error("инициализация Selectel", "error", err)
|
|
return 2
|
|
}
|
|
dns := selectel.NewClient(cfg.Selectel.BaseURL, tokens, cfg.Selectel.Timeout)
|
|
tr, err := traefik.NewClient(cfg.Traefik.APIURL, cfg.Traefik.Timeout)
|
|
if err != nil {
|
|
log.Error("инициализация Traefik-клиента", "error", err)
|
|
return 2
|
|
}
|
|
rec := reconciler.New(reconciler.Config{
|
|
Zones: cfg.Zones, IP: cfg.IP, TTL: cfg.TTL, Comment: config.ManagedComment,
|
|
DryRun: cfg.DryRun, DeleteEnabled: cfg.Delete.Enabled, GracePeriod: cfg.Delete.GracePeriod,
|
|
}, tr, dns, log)
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
|
|
defer stop()
|
|
|
|
state := health.NewState(3*cfg.Interval + reconciler.CycleTimeout)
|
|
healthErr := make(chan error, 1)
|
|
go func() { healthErr <- health.Serve(ctx, cfg.Listen, state.Handler()) }()
|
|
|
|
trigger := make(chan struct{}, 1)
|
|
if cfg.Docker.Enabled {
|
|
w := dockerevents.New(cfg.Docker.Host, log, func() {
|
|
select {
|
|
case trigger <- struct{}{}:
|
|
default:
|
|
}
|
|
})
|
|
go w.Run(ctx)
|
|
}
|
|
|
|
loopDone := make(chan struct{})
|
|
go func() {
|
|
defer close(loopDone)
|
|
rec.Run(ctx, cfg.Interval, dockerDebounce, trigger, func(_ reconciler.Result, err error) { state.Record(err) })
|
|
}()
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
log.Info("получен сигнал завершения, останавливаемся")
|
|
case err := <-healthErr:
|
|
if err != nil {
|
|
log.Error("health-сервер остановился", "error", err)
|
|
stop()
|
|
<-loopDone
|
|
return 1
|
|
}
|
|
}
|
|
<-loopDone
|
|
log.Info("остановлено")
|
|
return 0
|
|
}
|
|
|
|
func newLogger(cfg config.Config) *slog.Logger {
|
|
var lvl slog.Level
|
|
switch cfg.LogLevel {
|
|
case "debug":
|
|
lvl = slog.LevelDebug
|
|
case "warn":
|
|
lvl = slog.LevelWarn
|
|
case "error":
|
|
lvl = slog.LevelError
|
|
default:
|
|
lvl = slog.LevelInfo
|
|
}
|
|
opts := &slog.HandlerOptions{Level: lvl}
|
|
if cfg.LogFormat == "text" {
|
|
return slog.New(slog.NewTextHandler(os.Stderr, opts))
|
|
}
|
|
return slog.New(slog.NewJSONHandler(os.Stderr, opts))
|
|
}
|