108 lines
3.1 KiB
Go
108 lines
3.1 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Load reads a kforge.yml file from disk, applies all defaults,
|
|
// and returns the parsed config ready for environment resolution.
|
|
func Load(path string) (*KforgeConfig, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading %s: %w", path, err)
|
|
}
|
|
return LoadBytes(data)
|
|
}
|
|
|
|
// LoadBytes parses kforge.yml from a byte slice. Useful in tests.
|
|
func LoadBytes(data []byte) (*KforgeConfig, error) {
|
|
cfg := &KforgeConfig{}
|
|
if err := yaml.Unmarshal(data, cfg); err != nil {
|
|
return nil, fmt.Errorf("parsing kforge.yml: %w", err)
|
|
}
|
|
if err := validate(cfg); err != nil {
|
|
return nil, err
|
|
}
|
|
ApplyDefaults(cfg)
|
|
return cfg, nil
|
|
}
|
|
|
|
// validate performs structural validation before defaults are
|
|
// applied. Returns the first error found.
|
|
func validate(cfg *KforgeConfig) error {
|
|
if cfg.Meta.Name == "" {
|
|
return fmt.Errorf("meta.name is required")
|
|
}
|
|
if cfg.Meta.Tenant == "" {
|
|
return fmt.Errorf("meta.tenant is required")
|
|
}
|
|
if len(cfg.Environments) == 0 {
|
|
return fmt.Errorf("at least one environment must be defined")
|
|
}
|
|
for envKey, env := range cfg.Environments {
|
|
if len(env.Ingress.Hosts) == 0 {
|
|
return fmt.Errorf("environments.%s: at least one ingress host is required", envKey)
|
|
}
|
|
for _, host := range env.Ingress.Hosts {
|
|
if host.Hostname == "" {
|
|
return fmt.Errorf("environments.%s: ingress host hostname cannot be empty", envKey)
|
|
}
|
|
}
|
|
for _, job := range env.CronJobs {
|
|
if job.Name == "" {
|
|
return fmt.Errorf("environments.%s: cron job is missing a name", envKey)
|
|
}
|
|
if job.Schedule == "" {
|
|
return fmt.Errorf("environments.%s.cron_jobs.%s: schedule is required", envKey, job.Name)
|
|
}
|
|
if len(job.Command) == 0 {
|
|
return fmt.Errorf("environments.%s.cron_jobs.%s: command is required", envKey, job.Name)
|
|
}
|
|
}
|
|
for _, v := range env.EnvVars {
|
|
if err := validateEnvVar(envKey, v); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
for _, v := range cfg.Defaults.EnvVars {
|
|
if err := validateEnvVar("defaults", v); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateEnvVar(context string, v EnvVarConfig) error {
|
|
if v.Name == "" {
|
|
return fmt.Errorf("%s: env var is missing a name", context)
|
|
}
|
|
switch v.Type {
|
|
case EnvVarTypePlain, "":
|
|
// value can technically be empty (empty string env var is valid)
|
|
case EnvVarTypeSecretRef:
|
|
if v.SecretName == "" || v.SecretKey == "" {
|
|
return fmt.Errorf("%s: env var %q (secret_ref) requires secret_name and secret_key", context, v.Name)
|
|
}
|
|
case EnvVarTypeConfigMapRef:
|
|
if v.ConfigMapName == "" || v.ConfigMapKey == "" {
|
|
return fmt.Errorf("%s: env var %q (configmap_ref) requires configmap_name and configmap_key", context, v.Name)
|
|
}
|
|
default:
|
|
return fmt.Errorf("%s: env var %q has unknown type %q (valid: plain, secret_ref, configmap_ref)", context, v.Name, v.Type)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EnvironmentKeys returns the sorted list of environment names.
|
|
func EnvironmentKeys(cfg *KforgeConfig) []string {
|
|
keys := make([]string, 0, len(cfg.Environments))
|
|
for k := range cfg.Environments {
|
|
keys = append(keys, k)
|
|
}
|
|
return keys
|
|
}
|