Files
Reloader/internal/pkg/config/validation.go
T

149 lines
3.7 KiB
Go

package config
import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/labels"
)
// 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
// Validate ReloadStrategy
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),
})
}
// Validate ArgoRolloutStrategy
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),
})
}
// Validate LogLevel
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),
})
}
// Validate LogFormat
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),
})
}
// Normalize IgnoredResources to lowercase for consistent comparison
c.IgnoredResources = normalizeToLower(c.IgnoredResources)
// Normalize IgnoredWorkloads to lowercase
c.IgnoredWorkloads = normalizeToLower(c.IgnoredWorkloads)
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
}
// MustParseSelectors parses selectors and panics on error.
// Use only when selectors are known to be valid (e.g., from validated config).
func MustParseSelectors(selectorStrings []string) []labels.Selector {
selectors, err := ParseSelectors(selectorStrings)
if err != nil {
panic(err)
}
return selectors
}