Files
Reloader/pkg/config/validation.go
T

161 lines
3.8 KiB
Go

package config
import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/labels"
"github.com/stakater/Reloader/internal/pkg/workload"
)
// ValidationError represents a configuration validation error.
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("config.%s: %s", e.Field, e.Message)
}
// ValidationErrors is a collection of validation errors.
type ValidationErrors []ValidationError
func (e ValidationErrors) Error() string {
if len(e) == 0 {
return ""
}
if len(e) == 1 {
return e[0].Error()
}
var b strings.Builder
b.WriteString("multiple configuration errors:\n")
for _, err := range e {
b.WriteString(" - ")
b.WriteString(err.Error())
b.WriteString("\n")
}
return b.String()
}
// Validate checks the configuration for errors and normalizes values.
func (c *Config) Validate() error {
var errs ValidationErrors
switch c.ReloadStrategy {
case ReloadStrategyEnvVars, ReloadStrategyAnnotations:
// valid
case "":
c.ReloadStrategy = ReloadStrategyEnvVars
default:
errs = append(
errs, ValidationError{
Field: "ReloadStrategy",
Message: fmt.Sprintf("invalid value %q, must be %q or %q", c.ReloadStrategy, ReloadStrategyEnvVars, ReloadStrategyAnnotations),
},
)
}
switch c.ArgoRolloutStrategy {
case ArgoRolloutStrategyRestart, ArgoRolloutStrategyRollout:
// valid
case "":
c.ArgoRolloutStrategy = ArgoRolloutStrategyRollout
default:
errs = append(
errs, ValidationError{
Field: "ArgoRolloutStrategy",
Message: fmt.Sprintf(
"invalid value %q, must be %q or %q", c.ArgoRolloutStrategy, ArgoRolloutStrategyRestart, ArgoRolloutStrategyRollout,
),
},
)
}
switch strings.ToLower(c.LogLevel) {
case "trace", "debug", "info", "warn", "warning", "error", "fatal", "panic", "":
// valid
default:
errs = append(
errs, ValidationError{
Field: "LogLevel",
Message: fmt.Sprintf("invalid log level %q", c.LogLevel),
},
)
}
switch strings.ToLower(c.LogFormat) {
case "json", "":
// valid
default:
errs = append(
errs, ValidationError{
Field: "LogFormat",
Message: fmt.Sprintf("invalid log format %q, must be \"json\" or empty", c.LogFormat),
},
)
}
c.IgnoredResources = normalizeToLower(c.IgnoredResources)
// Normalize ignored workloads to canonical Kind values (e.g., "cronjobs" -> "CronJob")
c.IgnoredWorkloads = normalizeToLower(c.IgnoredWorkloads)
normalizedWorkloads := make([]string, 0, len(c.IgnoredWorkloads))
for _, w := range c.IgnoredWorkloads {
kind, err := workload.KindFromString(w)
if err != nil {
errs = append(
errs, ValidationError{
Field: "IgnoredWorkloads",
Message: fmt.Sprintf("unknown workload type %q", w),
},
)
} else {
normalizedWorkloads = append(normalizedWorkloads, string(kind))
}
}
c.IgnoredWorkloads = normalizedWorkloads
if len(errs) > 0 {
return errs
}
return nil
}
// normalizeToLower converts all strings in the slice to lowercase and removes empty strings.
func normalizeToLower(items []string) []string {
if len(items) == 0 {
return items
}
result := make([]string, 0, len(items))
for _, item := range items {
item = strings.TrimSpace(strings.ToLower(item))
if item != "" {
result = append(result, item)
}
}
return result
}
// ParseSelectors parses a slice of selector strings into label selectors.
func ParseSelectors(selectorStrings []string) ([]labels.Selector, error) {
if len(selectorStrings) == 0 {
return nil, nil
}
selectors := make([]labels.Selector, 0, len(selectorStrings))
for _, s := range selectorStrings {
s = strings.TrimSpace(s)
if s == "" {
continue
}
selector, err := labels.Parse(s)
if err != nil {
return nil, fmt.Errorf("invalid selector %q: %w", s, err)
}
selectors = append(selectors, selector)
}
return selectors, nil
}