Compare commits
4
Commits
master
..
aae200eeea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aae200eeea | ||
|
|
c51e4ea188 | ||
|
|
4dbe4da184 | ||
|
|
735e2052e8 |
@@ -79,9 +79,6 @@ RECONCILE_INTERVAL=60s
|
|||||||
# Coalesces rapid bursts (e.g. rolling restarts) into a single reconcile.
|
# Coalesces rapid bursts (e.g. rolling restarts) into a single reconcile.
|
||||||
DEBOUNCE_DELAY=5s
|
DEBOUNCE_DELAY=5s
|
||||||
|
|
||||||
# Log level for watcher output. Allowed: debug, info, warn, error
|
|
||||||
LOG_LEVEL=info
|
|
||||||
|
|
||||||
# ── Docker ────────────────────────────────────────────────────────────────────
|
# ── Docker ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
# Docker daemon endpoint. Leave empty to use the default Unix socket.
|
# Docker daemon endpoint. Leave empty to use the default Unix socket.
|
||||||
|
|||||||
+3
-2
@@ -13,8 +13,9 @@ traefik-dns-watcher
|
|||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
# IDE settings
|
# IDE settings (keep MCP config)
|
||||||
.vscode/
|
.vscode/*
|
||||||
|
!.vscode/mcp.json
|
||||||
|
|
||||||
# OS files
|
# OS files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
Vendored
+13
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"servers": {
|
||||||
|
"git": {
|
||||||
|
"type": "stdio",
|
||||||
|
"command": "git-mcp-go",
|
||||||
|
"args": [
|
||||||
|
"serve",
|
||||||
|
"D:\\Projects\\DevOps\\traefik-dns-watcher"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inputs": []
|
||||||
|
}
|
||||||
@@ -30,7 +30,6 @@ type Config struct {
|
|||||||
|
|
||||||
ReconcileInterval time.Duration
|
ReconcileInterval time.Duration
|
||||||
DebounceDelay time.Duration
|
DebounceDelay time.Duration
|
||||||
LogLevel string
|
|
||||||
|
|
||||||
RecordTTL int
|
RecordTTL int
|
||||||
CloudflareAutoTTL bool
|
CloudflareAutoTTL bool
|
||||||
@@ -99,13 +98,6 @@ func LoadConfig() (*Config, error) {
|
|||||||
return nil, fmt.Errorf("CF_AUTO_TTL: invalid boolean %q: %w", autoTTLStr, err)
|
return nil, fmt.Errorf("CF_AUTO_TTL: invalid boolean %q: %w", autoTTLStr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.LogLevel = strings.ToLower(envOrDefault("LOG_LEVEL", "info"))
|
|
||||||
switch cfg.LogLevel {
|
|
||||||
case "debug", "info", "warn", "error":
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("LOG_LEVEL: invalid value %q (allowed: debug, info, warn, error)", cfg.LogLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.ExcludeRouters = make(map[string]struct{})
|
cfg.ExcludeRouters = make(map[string]struct{})
|
||||||
if v := os.Getenv("EXCLUDE_ROUTERS"); v != "" {
|
if v := os.Getenv("EXCLUDE_ROUTERS"); v != "" {
|
||||||
for _, r := range strings.Split(v, ",") {
|
for _, r := range strings.Split(v, ",") {
|
||||||
|
|||||||
@@ -22,28 +22,21 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||||
|
Level: slog.LevelInfo,
|
||||||
|
})))
|
||||||
|
|
||||||
cfg, err := LoadConfig()
|
cfg, err := LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "configuration error:", err)
|
slog.Error("configuration error", "error", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
logLevel, err := parseLogLevel(cfg.LogLevel)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "configuration error:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
|
||||||
Level: logLevel,
|
|
||||||
})))
|
|
||||||
|
|
||||||
slog.Info("traefik-dns-watcher starting",
|
slog.Info("traefik-dns-watcher starting",
|
||||||
"traefik_url", cfg.TraefikURL,
|
"traefik_url", cfg.TraefikURL,
|
||||||
"zones", cfg.Zones,
|
"zones", cfg.Zones,
|
||||||
"repo_path", cfg.RepoPath,
|
"repo_path", cfg.RepoPath,
|
||||||
"dynamic_dir", cfg.DynamicDir,
|
"dynamic_dir", cfg.DynamicDir,
|
||||||
"log_level", cfg.LogLevel,
|
|
||||||
"git_https_token_enabled", cfg.GitAuthToken != "",
|
"git_https_token_enabled", cfg.GitAuthToken != "",
|
||||||
"git_auth_username", cfg.GitAuthUsername,
|
"git_auth_username", cfg.GitAuthUsername,
|
||||||
"reconcile_interval", cfg.ReconcileInterval,
|
"reconcile_interval", cfg.ReconcileInterval,
|
||||||
@@ -122,21 +115,6 @@ func main() {
|
|||||||
slog.Info("traefik-dns-watcher stopped")
|
slog.Info("traefik-dns-watcher stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseLogLevel(v string) (slog.Level, error) {
|
|
||||||
switch strings.ToLower(v) {
|
|
||||||
case "debug":
|
|
||||||
return slog.LevelDebug, nil
|
|
||||||
case "info":
|
|
||||||
return slog.LevelInfo, nil
|
|
||||||
case "warn":
|
|
||||||
return slog.LevelWarn, nil
|
|
||||||
case "error":
|
|
||||||
return slog.LevelError, nil
|
|
||||||
default:
|
|
||||||
return 0, fmt.Errorf("LOG_LEVEL: invalid value %q (allowed: debug, info, warn, error)", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// maybeHandleGitAskpass serves username/password for git HTTPS auth in non-interactive mode.
|
// maybeHandleGitAskpass serves username/password for git HTTPS auth in non-interactive mode.
|
||||||
// This process mode is only enabled for git child processes that set TDW_GIT_ASKPASS=1.
|
// This process mode is only enabled for git child processes that set TDW_GIT_ASKPASS=1.
|
||||||
func maybeHandleGitAskpass() bool {
|
func maybeHandleGitAskpass() bool {
|
||||||
@@ -195,8 +173,7 @@ type dockerEvent struct {
|
|||||||
Type string `json:"Type"`
|
Type string `json:"Type"`
|
||||||
Action string `json:"Action"`
|
Action string `json:"Action"`
|
||||||
Actor struct {
|
Actor struct {
|
||||||
ID string `json:"ID"`
|
ID string `json:"ID"`
|
||||||
Attributes map[string]string `json:"Attributes"`
|
|
||||||
} `json:"Actor"`
|
} `json:"Actor"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +187,7 @@ func runDockerEventLoop(ctx context.Context, trigger func()) error {
|
|||||||
return fmt.Errorf("build docker HTTP client: %w", err)
|
return fmt.Errorf("build docker HTTP client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
filterVal := `{"type":["container"],"event":["start","stop","die","destroy"]}`
|
filterVal := `{"type":["container"]}`
|
||||||
eventsURL := baseURL + "/events?filters=" + url.QueryEscape(filterVal)
|
eventsURL := baseURL + "/events?filters=" + url.QueryEscape(filterVal)
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, eventsURL, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, eventsURL, nil)
|
||||||
@@ -244,20 +221,13 @@ func runDockerEventLoop(ctx context.Context, trigger func()) error {
|
|||||||
if evt.Type != "container" {
|
if evt.Type != "container" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Docker may append extra details after ":" for some event kinds.
|
switch evt.Action {
|
||||||
// We only care about the base action token.
|
|
||||||
action := evt.Action
|
|
||||||
if idx := strings.Index(action, ":"); idx >= 0 {
|
|
||||||
action = strings.TrimSpace(action[:idx])
|
|
||||||
}
|
|
||||||
|
|
||||||
switch action {
|
|
||||||
case "start", "stop", "die", "destroy":
|
case "start", "stop", "die", "destroy":
|
||||||
actorID := evt.Actor.ID
|
actorID := evt.Actor.ID
|
||||||
if len(actorID) > 12 {
|
if len(actorID) > 12 {
|
||||||
actorID = actorID[:12]
|
actorID = actorID[:12]
|
||||||
}
|
}
|
||||||
slog.Debug("docker event received", "action", action, "id", actorID)
|
slog.Debug("docker event received", "action", evt.Action, "id", actorID)
|
||||||
trigger()
|
trigger()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user