chore: include validation errors for user-defined Caddy configs as a comment in Caddyfile

This commit is contained in:
Pasha Sviderski
2025-08-21 14:01:24 +10:00
parent b437659678
commit 11949eeb3b
2 changed files with 90 additions and 7 deletions
+18 -4
View File
@@ -114,6 +114,8 @@ func (g *CaddyfileGenerator) Generate(ctx context.Context, records []store.Conta
}
upstreams := serviceUpstreams(containers)
// Track validation errors for reporting.
var configErrors []string
// Find the 'caddy' service container on this machine. Use the most recent one if multiple exist.
var caddyCtr *api.ServiceContainer
@@ -136,6 +138,8 @@ func (g *CaddyfileGenerator) Generate(ctx context.Context, records []store.Conta
if err != nil {
g.log.Error("Failed to render template directives in user-defined global Caddy config, skipping it.",
"service", caddyCtr.ServiceName(), "container", caddyCtr.ID, "err", err)
configErrors = append(configErrors,
fmt.Sprintf("service '%s': failed to render template: %v", caddyCtr.ServiceName(), err))
} else {
caddyfileCandidate := fmt.Sprintf("# User-defined global config from service '%s'.\n%s\n\n%s",
caddyCtr.ServiceName(), renderedConfig, caddyfile)
@@ -143,6 +147,8 @@ func (g *CaddyfileGenerator) Generate(ctx context.Context, records []store.Conta
if err = g.validator.Validate(ctx, caddyfileCandidate); err != nil {
g.log.Error("User-defined global Caddy config is invalid, skipping it.",
"service", caddyCtr.ServiceName(), "container", caddyCtr.ID, "err", err)
configErrors = append(configErrors,
fmt.Sprintf("service '%s': validation failed: %v", caddyCtr.ServiceName(), err))
} else {
caddyfile = caddyfileCandidate
}
@@ -186,6 +192,8 @@ func (g *CaddyfileGenerator) Generate(ctx context.Context, records []store.Conta
if err != nil {
g.log.Error("Failed to render template directives in user-defined Caddy config for service, skipping it.",
"service", serviceName, "err", err)
configErrors = append(configErrors,
fmt.Sprintf("service '%s': failed to render template: %v", serviceName, err))
continue
}
@@ -194,15 +202,21 @@ func (g *CaddyfileGenerator) Generate(ctx context.Context, records []store.Conta
if err = g.validator.Validate(ctx, caddyfileCandidate); err != nil {
g.log.Error("User-defined Caddy config for service is invalid, skipping it.",
"service", serviceName, "err", err)
configErrors = append(configErrors, fmt.Sprintf("service '%s': validation failed: %v", serviceName, err))
} else {
caddyfile = caddyfileCandidate
}
}
// TODO: add ac omment with invalid configs and their validation errors:
// # Skipped invalid configs:
// # - Service 'api': Template error in x-caddy
// # - Service 'web': Invalid Caddyfile syntax
// Append error summary as comment if there were any invalid configs.
if len(configErrors) > 0 {
errorsComment := "# Skipped invalid user-defined configs:\n"
for _, e := range configErrors {
errorsComment += fmt.Sprintf("# - %s\n", e)
}
caddyfile += "\n" + errorsComment
}
return caddyfileHeader + "\n" + caddyfile, nil
}
+72 -3
View File
@@ -297,7 +297,10 @@ bad.config.com {
time.Now(),
),
},
want: caddyfileBase,
want: caddyfileBase + `
# Skipped invalid user-defined configs:
# - service 'bad-service': validation failed: invalid config detected
`,
},
{
name: "service with invalid config template is skipped",
@@ -313,7 +316,10 @@ bad.template.com {
time.Now(),
),
},
want: caddyfileBase,
want: caddyfileBase + `
# Skipped invalid user-defined configs:
# - service 'bad-template': failed to render template: parse config as Go template: template: Caddyfile:3: unexpected "}" in operand
`,
},
{
name: "caddy service with invalid global config is skipped",
@@ -329,7 +335,10 @@ localhost {
time.Now(),
),
},
want: caddyfileBase,
want: caddyfileBase + `
# Skipped invalid user-defined configs:
# - service 'caddy': validation failed: invalid config detected
`,
},
{
name: "caddy service on different machine is ignored",
@@ -389,6 +398,9 @@ api.example.com {
web.example.com {
reverse_proxy web:3000
}
# Skipped invalid user-defined configs:
# - service 'invalid-svc': validation failed: invalid config detected
`,
},
{
@@ -756,6 +768,63 @@ gateway.example.com {
web-v2.example.com {
reverse_proxy 10.210.1.3:8080 10.210.3.3:8080 10.210.2.3:8080
}
# Skipped invalid user-defined configs:
# - service 'invalid': validation failed: invalid config detected
`,
},
{
name: "multiple errors: invalid global, template error, and validation error",
containers: []store.ContainerRecord{
newContainerRecordWithCaddyConfig(
"caddy",
"10.210.0.1",
`# test:invalid
{
invalid global
}`,
"test-machine-id",
time.Now(),
),
newContainerRecordWithCaddyConfig(
"broken-template",
"10.210.0.2",
`broken.example.com {
reverse_proxy {{upstreams "missing
}`,
"test-machine-id",
time.Now(),
),
newContainerRecordWithCaddyConfig(
"invalid",
"10.210.0.3",
`# test:invalid
invalid.example.com {
respond "Invalid config"
}`,
"test-machine-id",
time.Now(),
),
newContainerRecordWithCaddyConfig(
"valid",
"10.210.0.4",
`valid.example.com {
respond "Valid config"
}`,
"test-machine-id",
time.Now(),
),
},
want: caddyfileBase + `
# User-defined config for service 'valid'.
valid.example.com {
respond "Valid config"
}
# Skipped invalid user-defined configs:
# - service 'caddy': validation failed: invalid config detected
# - service 'broken-template': failed to render template: parse config as Go template: template: Caddyfile:2: unterminated quoted string
# - service 'invalid': validation failed: invalid config detected
`,
},
}