688 lines
18 KiB
Go
688 lines
18 KiB
Go
package config
|
|
|
|
// ------------------------------------------------------------
|
|
// Default values — single source of truth for every default
|
|
// referenced in the schema. Change a default here and it
|
|
// propagates everywhere automatically.
|
|
// ------------------------------------------------------------
|
|
|
|
const (
|
|
DefaultTLSIssuer = "letsencrypt-prod"
|
|
DefaultIngressClass = "nginx"
|
|
DefaultCNPGHost = "cnpg-main-rw.default.svc.cluster.local"
|
|
DefaultNamespacePattern = "${env}"
|
|
DefaultImagePullPolicy = "Always"
|
|
DefaultServiceType = "ClusterIP"
|
|
DefaultDockerfile = "Dockerfile"
|
|
DefaultImageTag = "latest"
|
|
DefaultPullSecret = "regcred"
|
|
DefaultHealthCheckPath = "/healthcheck"
|
|
DefaultRegistryURL = "registry.natelubitz.com"
|
|
|
|
DefaultPort = 3000
|
|
DefaultReplicas = 1
|
|
DefaultInitialDelaySecs = 15
|
|
DefaultPeriodSecs = 10
|
|
DefaultTimeoutSecs = 5
|
|
DefaultFailureThreshold = 3
|
|
DefaultDeleteGraceSecs = 300
|
|
DefaultSuccessfulJobsHist = 3
|
|
DefaultFailedJobsHist = 1
|
|
|
|
DefaultCacheProvider = "valkey"
|
|
DefaultCacheMode = "standalone"
|
|
DefaultStorageProvider = "minio"
|
|
DefaultStorageMode = "standalone"
|
|
DefaultQueueProvider = "nats"
|
|
DefaultSearchProvider = "meilisearch"
|
|
DefaultMonProvider = "prometheus"
|
|
DefaultMetricsPath = "/metrics"
|
|
|
|
DefaultRestartPolicy = "OnFailure"
|
|
DefaultConcurrencyPolicy = "Forbid"
|
|
)
|
|
|
|
// boolPtr / intPtr are helpers for pointer defaults.
|
|
func boolPtr(b bool) *bool { return &b }
|
|
func intPtr(i int) *int { return &i }
|
|
|
|
// isEnabled returns true if the InfraBase has no explicit Enabled
|
|
// value set (nil = inherits root default, which is true when the
|
|
// block exists) or if Enabled is explicitly true.
|
|
func isEnabled(b *bool) bool {
|
|
return b == nil || *b
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
// ApplyDefaults fills in every zero/nil value in the config
|
|
// with the canonical default. Called once after unmarshalling,
|
|
// before any environment resolution.
|
|
// ------------------------------------------------------------
|
|
|
|
func ApplyDefaults(cfg *KforgeConfig) {
|
|
applyClusterDefaults(&cfg.Cluster)
|
|
applyRegistryDefaults(&cfg.Registry, &cfg.Meta)
|
|
applyDeploymentDefaults(&cfg.Defaults)
|
|
applyRootInfraDefaults(&cfg.Infrastructure)
|
|
}
|
|
|
|
func applyClusterDefaults(c *ClusterConfig) {
|
|
if c.TLSIssuer == "" {
|
|
c.TLSIssuer = DefaultTLSIssuer
|
|
}
|
|
if c.IngressClass == "" {
|
|
c.IngressClass = DefaultIngressClass
|
|
}
|
|
if c.CNPG.Host == "" {
|
|
c.CNPG.Host = DefaultCNPGHost
|
|
}
|
|
if c.NamespacePattern == "" {
|
|
c.NamespacePattern = DefaultNamespacePattern
|
|
}
|
|
}
|
|
|
|
func applyRegistryDefaults(r *RegistryConfig, m *MetaConfig) {
|
|
if r.URL == "" {
|
|
r.URL = DefaultRegistryURL
|
|
}
|
|
if r.PullSecret == "" {
|
|
r.PullSecret = DefaultPullSecret
|
|
}
|
|
if r.Repository == "" {
|
|
r.Repository = "${tenant}/${name}"
|
|
}
|
|
}
|
|
|
|
func applyDeploymentDefaults(d *DefaultsConfig) {
|
|
if d.ImagePullPolicy == "" {
|
|
d.ImagePullPolicy = DefaultImagePullPolicy
|
|
}
|
|
if d.Replicas == nil {
|
|
d.Replicas = intPtr(DefaultReplicas)
|
|
}
|
|
if d.Port == nil {
|
|
d.Port = intPtr(DefaultPort)
|
|
}
|
|
if d.ServiceType == "" {
|
|
d.ServiceType = DefaultServiceType
|
|
}
|
|
if d.Dockerfile == "" {
|
|
d.Dockerfile = DefaultDockerfile
|
|
}
|
|
applyHealthCheckDefaults(&d.HealthCheck, d.Port)
|
|
applyResourceDefaults(&d.Resources)
|
|
}
|
|
|
|
func applyHealthCheckDefaults(h *HealthCheckConfig, defaultPort *int) {
|
|
if h.Path == "" {
|
|
h.Path = DefaultHealthCheckPath
|
|
}
|
|
if h.Port == nil {
|
|
h.Port = defaultPort
|
|
}
|
|
if h.InitialDelaySeconds == 0 {
|
|
h.InitialDelaySeconds = DefaultInitialDelaySecs
|
|
}
|
|
if h.PeriodSeconds == 0 {
|
|
h.PeriodSeconds = DefaultPeriodSecs
|
|
}
|
|
if h.TimeoutSeconds == 0 {
|
|
h.TimeoutSeconds = DefaultTimeoutSecs
|
|
}
|
|
if h.FailureThreshold == 0 {
|
|
h.FailureThreshold = DefaultFailureThreshold
|
|
}
|
|
if h.Liveness == nil {
|
|
h.Liveness = boolPtr(true)
|
|
}
|
|
if h.Readiness == nil {
|
|
h.Readiness = boolPtr(true)
|
|
}
|
|
}
|
|
|
|
func applyResourceDefaults(r *ResourceConfig) {
|
|
if r.Requests.CPU == "" {
|
|
r.Requests.CPU = "100m"
|
|
}
|
|
if r.Requests.Memory == "" {
|
|
r.Requests.Memory = "128Mi"
|
|
}
|
|
if r.Limits.CPU == "" {
|
|
r.Limits.CPU = "500m"
|
|
}
|
|
if r.Limits.Memory == "" {
|
|
r.Limits.Memory = "512Mi"
|
|
}
|
|
}
|
|
|
|
// applyRootInfraDefaults sets provider/mode defaults on the root
|
|
// infrastructure block. The enabled flag is handled by the merge
|
|
// step: if a block exists at root with no explicit enabled:false,
|
|
// it is considered enabled.
|
|
func applyRootInfraDefaults(infra *InfrastructureConfig) {
|
|
if infra.Cache != nil {
|
|
if infra.Cache.Provider == "" {
|
|
infra.Cache.Provider = DefaultCacheProvider
|
|
}
|
|
if infra.Cache.Mode == "" {
|
|
infra.Cache.Mode = DefaultCacheMode
|
|
}
|
|
if infra.Cache.Replicas == nil {
|
|
infra.Cache.Replicas = intPtr(1)
|
|
}
|
|
}
|
|
if infra.Storage != nil {
|
|
if infra.Storage.Provider == "" {
|
|
infra.Storage.Provider = DefaultStorageProvider
|
|
}
|
|
if infra.Storage.Mode == "" {
|
|
infra.Storage.Mode = DefaultStorageMode
|
|
}
|
|
}
|
|
if infra.Queue != nil {
|
|
if infra.Queue.Provider == "" {
|
|
infra.Queue.Provider = DefaultQueueProvider
|
|
}
|
|
}
|
|
if infra.Search != nil {
|
|
if infra.Search.Provider == "" {
|
|
infra.Search.Provider = DefaultSearchProvider
|
|
}
|
|
}
|
|
if infra.Monitoring != nil {
|
|
if infra.Monitoring.Provider == "" {
|
|
infra.Monitoring.Provider = DefaultMonProvider
|
|
}
|
|
if infra.Monitoring.MetricsPath == "" {
|
|
infra.Monitoring.MetricsPath = DefaultMetricsPath
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
// ResolveEnvironment produces a fully-merged, fully-defaulted
|
|
// ResolvedEnvironment for a given environment key.
|
|
//
|
|
// Merge order (last wins):
|
|
// 1. Compiled defaults (from ApplyDefaults)
|
|
// 2. Root infrastructure block
|
|
// 3. Per-environment overrides
|
|
// ------------------------------------------------------------
|
|
|
|
// ResolvedEnvironment is the flattened, ready-to-generate view
|
|
// of a single environment. Generators consume this type only —
|
|
// they never touch raw config or merge logic.
|
|
type ResolvedEnvironment struct {
|
|
// Identity
|
|
EnvKey string
|
|
EnvPrefix string
|
|
Namespace string
|
|
FullName string // ${env_prefix}-${tenant}-${name}
|
|
|
|
// Deployment
|
|
Image string
|
|
ImagePullPolicy string
|
|
ImagePullSecret string
|
|
Replicas int
|
|
Port int
|
|
ServiceType string
|
|
HealthCheck HealthCheckConfig
|
|
Resources ResourceConfig
|
|
EnvVars []EnvVarConfig
|
|
|
|
// Ingress
|
|
Ingress IngressConfig
|
|
|
|
// Infrastructure (merged root + env override)
|
|
Infrastructure ResolvedInfrastructure
|
|
|
|
// Cron jobs
|
|
CronJobs []ResolvedCronJob
|
|
|
|
// Cluster-level settings (carried through for generators)
|
|
TLSIssuer string
|
|
IngressClass string
|
|
CNPGHost string
|
|
|
|
// Lifecycle
|
|
Lifecycle LifecycleConfig
|
|
}
|
|
|
|
// ResolvedInfrastructure holds the fully-merged infra state for
|
|
// one environment. Each field is nil if the service is disabled.
|
|
type ResolvedInfrastructure struct {
|
|
Database *DatabaseInfraConfig
|
|
Cache *CacheInfraConfig
|
|
Storage *StorageInfraConfig
|
|
Queue *QueueInfraConfig
|
|
Search *SearchInfraConfig
|
|
Monitoring *MonitoringInfraConfig
|
|
}
|
|
|
|
// ResolvedCronJob is a fully-defaulted cron job ready for the
|
|
// CronJob manifest generator.
|
|
type ResolvedCronJob struct {
|
|
CronJobConfig
|
|
Image string
|
|
EnvVars []EnvVarConfig // merged: deployment vars + job-specific vars
|
|
}
|
|
|
|
// ResolveEnvironment merges root config + env overrides into a
|
|
// single ResolvedEnvironment. cfg must have had ApplyDefaults
|
|
// called on it first.
|
|
func ResolveEnvironment(cfg *KforgeConfig, envKey string) (ResolvedEnvironment, error) {
|
|
env, ok := cfg.Environments[envKey]
|
|
if !ok {
|
|
return ResolvedEnvironment{}, &UnknownEnvironmentError{Key: envKey}
|
|
}
|
|
|
|
prefix := resolveEnvPrefix(envKey, env.EnvPrefix)
|
|
namespace := resolveNamespace(envKey, env.Namespace, cfg.Cluster.NamespacePattern)
|
|
fullName := resolveFullName(cfg, prefix, env)
|
|
|
|
registry := resolveRegistry(cfg, env)
|
|
imageTag := env.ImageTag
|
|
if imageTag == "" {
|
|
imageTag = DefaultImageTag
|
|
}
|
|
image := registry.URL + "/" + registry.Repository + ":" + imageTag
|
|
|
|
replicas := *cfg.Defaults.Replicas
|
|
if env.Replicas != nil {
|
|
replicas = *env.Replicas
|
|
}
|
|
|
|
port := *cfg.Defaults.Port
|
|
hc := cfg.Defaults.HealthCheck
|
|
if hc.Port == nil || *hc.Port == 0 {
|
|
hc.Port = intPtr(port)
|
|
}
|
|
|
|
envVars := mergeEnvVars(cfg.Defaults.EnvVars, env.EnvVars)
|
|
|
|
infra := mergeInfrastructure(&cfg.Infrastructure, &env.Infrastructure)
|
|
|
|
cronJobs := resolveCronJobs(env.CronJobs, image, envVars, cfg.Defaults.Resources)
|
|
|
|
lifecycle := env.Lifecycle
|
|
if lifecycle.DeleteGraceSeconds == 0 {
|
|
lifecycle.DeleteGraceSeconds = DefaultDeleteGraceSecs
|
|
}
|
|
|
|
auth := env.Ingress.Auth
|
|
if auth.SecretName == "" {
|
|
auth.SecretName = fullName + "-basic-auth"
|
|
}
|
|
ingress := IngressConfig{
|
|
Hosts: env.Ingress.Hosts,
|
|
Auth: auth,
|
|
}
|
|
|
|
return ResolvedEnvironment{
|
|
EnvKey: envKey,
|
|
EnvPrefix: prefix,
|
|
Namespace: namespace,
|
|
FullName: fullName,
|
|
Image: image,
|
|
ImagePullPolicy: cfg.Defaults.ImagePullPolicy,
|
|
ImagePullSecret: registry.PullSecret,
|
|
Replicas: replicas,
|
|
Port: port,
|
|
ServiceType: cfg.Defaults.ServiceType,
|
|
HealthCheck: hc,
|
|
Resources: cfg.Defaults.Resources,
|
|
EnvVars: envVars,
|
|
Ingress: ingress,
|
|
Infrastructure: infra,
|
|
CronJobs: cronJobs,
|
|
TLSIssuer: cfg.Cluster.TLSIssuer,
|
|
IngressClass: cfg.Cluster.IngressClass,
|
|
CNPGHost: cfg.Cluster.CNPG.Host,
|
|
Lifecycle: lifecycle,
|
|
}, nil
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
// Internal resolution helpers
|
|
// ------------------------------------------------------------
|
|
|
|
func resolveEnvPrefix(envKey string, override *string) string {
|
|
if override != nil && *override != "" {
|
|
return *override
|
|
}
|
|
// Default: first 4 chars of envKey, or full key if shorter.
|
|
if len(envKey) <= 4 {
|
|
return envKey
|
|
}
|
|
return envKey[:4]
|
|
}
|
|
|
|
func resolveNamespace(envKey, explicit, pattern string) string {
|
|
if explicit != "" {
|
|
return explicit
|
|
}
|
|
// Apply the namespace pattern (simple token replace here;
|
|
// full interpolation runs later via the interpolate package).
|
|
result := pattern
|
|
if result == "" {
|
|
return envKey
|
|
}
|
|
return result
|
|
}
|
|
|
|
func resolveFullName(cfg *KforgeConfig, prefix string, env EnvironmentConfig) string {
|
|
if cfg.Meta.NameOverride != nil && *cfg.Meta.NameOverride != "" {
|
|
return *cfg.Meta.NameOverride
|
|
}
|
|
return prefix + "-" + cfg.Meta.Tenant + "-" + cfg.Meta.Name
|
|
}
|
|
|
|
func resolveRegistry(cfg *KforgeConfig, env EnvironmentConfig) RegistryConfig {
|
|
base := cfg.Registry
|
|
// Resolve the repository token now that we have meta values.
|
|
if base.Repository == "${tenant}/${name}" || base.Repository == "" {
|
|
base.Repository = cfg.Meta.Tenant + "/" + cfg.Meta.Name
|
|
}
|
|
if env.Registry != nil {
|
|
r := *env.Registry
|
|
if r.URL == "" {
|
|
r.URL = base.URL
|
|
}
|
|
if r.Repository == "" {
|
|
r.Repository = base.Repository
|
|
}
|
|
if r.PullSecret == "" {
|
|
r.PullSecret = base.PullSecret
|
|
}
|
|
return r
|
|
}
|
|
return base
|
|
}
|
|
|
|
// MergeEnvVars is the exported form of mergeEnvVars, used by the
|
|
// generate command to inject infrastructure env vars into deployments.
|
|
func MergeEnvVars(base, override []EnvVarConfig) []EnvVarConfig {
|
|
return mergeEnvVars(base, override)
|
|
}
|
|
|
|
// mergeEnvVars combines base and override slices. If an env var
|
|
// with the same Name appears in both, the override wins.
|
|
func mergeEnvVars(base, override []EnvVarConfig) []EnvVarConfig {
|
|
merged := make([]EnvVarConfig, 0, len(base)+len(override))
|
|
seen := make(map[string]int) // name → index in merged
|
|
|
|
for _, v := range base {
|
|
seen[v.Name] = len(merged)
|
|
merged = append(merged, v)
|
|
}
|
|
for _, v := range override {
|
|
if idx, ok := seen[v.Name]; ok {
|
|
merged[idx] = v // override
|
|
} else {
|
|
seen[v.Name] = len(merged)
|
|
merged = append(merged, v)
|
|
}
|
|
}
|
|
return merged
|
|
}
|
|
|
|
// mergeInfrastructure performs a shallow merge of root infra
|
|
// defaults with per-environment overrides.
|
|
//
|
|
// Rules:
|
|
// - If root has a service block with no explicit enabled:false,
|
|
// it is enabled in every environment.
|
|
// - An env block with enabled:false disables the service.
|
|
// - An env block with partial fields overrides only those fields;
|
|
// everything else inherits from root.
|
|
// - If root has no block for a service, env can still enable it
|
|
// by providing its own block (enabled defaults to true if present).
|
|
func mergeInfrastructure(root, env *InfrastructureConfig) ResolvedInfrastructure {
|
|
return ResolvedInfrastructure{
|
|
Database: mergeDatabase(root.Database, env.Database),
|
|
Cache: mergeCache(root.Cache, env.Cache),
|
|
Storage: mergeStorage(root.Storage, env.Storage),
|
|
Queue: mergeQueue(root.Queue, env.Queue),
|
|
Search: mergeSearch(root.Search, env.Search),
|
|
Monitoring: mergeMonitoring(root.Monitoring, env.Monitoring),
|
|
}
|
|
}
|
|
|
|
func mergeDatabase(root, env *DatabaseInfraConfig) *DatabaseInfraConfig {
|
|
if root == nil && env == nil {
|
|
return nil
|
|
}
|
|
merged := &DatabaseInfraConfig{}
|
|
if root != nil {
|
|
*merged = *root
|
|
}
|
|
if env != nil {
|
|
if env.Enabled != nil {
|
|
merged.Enabled = env.Enabled
|
|
}
|
|
if env.Provider != "" {
|
|
merged.Provider = env.Provider
|
|
}
|
|
if env.DatabaseName != "" {
|
|
merged.DatabaseName = env.DatabaseName
|
|
}
|
|
}
|
|
if !isEnabled(merged.Enabled) {
|
|
return nil
|
|
}
|
|
if merged.Provider == "" {
|
|
merged.Provider = "cnpg"
|
|
}
|
|
return merged
|
|
}
|
|
|
|
func mergeCache(root, env *CacheInfraConfig) *CacheInfraConfig {
|
|
if root == nil && env == nil {
|
|
return nil
|
|
}
|
|
merged := &CacheInfraConfig{}
|
|
if root != nil {
|
|
*merged = *root
|
|
}
|
|
if env != nil {
|
|
if env.Enabled != nil {
|
|
merged.Enabled = env.Enabled
|
|
}
|
|
if env.Provider != "" {
|
|
merged.Provider = env.Provider
|
|
}
|
|
if env.Mode != "" {
|
|
merged.Mode = env.Mode
|
|
}
|
|
if env.Replicas != nil {
|
|
merged.Replicas = env.Replicas
|
|
}
|
|
}
|
|
if !isEnabled(merged.Enabled) {
|
|
return nil
|
|
}
|
|
if merged.Provider == "" {
|
|
merged.Provider = DefaultCacheProvider
|
|
}
|
|
if merged.Mode == "" {
|
|
merged.Mode = DefaultCacheMode
|
|
}
|
|
if merged.Replicas == nil {
|
|
merged.Replicas = intPtr(1)
|
|
}
|
|
return merged
|
|
}
|
|
|
|
func mergeStorage(root, env *StorageInfraConfig) *StorageInfraConfig {
|
|
if root == nil && env == nil {
|
|
return nil
|
|
}
|
|
merged := &StorageInfraConfig{}
|
|
if root != nil {
|
|
*merged = *root
|
|
}
|
|
if env != nil {
|
|
if env.Enabled != nil {
|
|
merged.Enabled = env.Enabled
|
|
}
|
|
if env.Provider != "" {
|
|
merged.Provider = env.Provider
|
|
}
|
|
if env.Mode != "" {
|
|
merged.Mode = env.Mode
|
|
}
|
|
}
|
|
if !isEnabled(merged.Enabled) {
|
|
return nil
|
|
}
|
|
if merged.Provider == "" {
|
|
merged.Provider = DefaultStorageProvider
|
|
}
|
|
if merged.Mode == "" {
|
|
merged.Mode = DefaultStorageMode
|
|
}
|
|
return merged
|
|
}
|
|
|
|
func mergeQueue(root, env *QueueInfraConfig) *QueueInfraConfig {
|
|
if root == nil && env == nil {
|
|
return nil
|
|
}
|
|
merged := &QueueInfraConfig{}
|
|
if root != nil {
|
|
*merged = *root
|
|
}
|
|
if env != nil {
|
|
if env.Enabled != nil {
|
|
merged.Enabled = env.Enabled
|
|
}
|
|
if env.Provider != "" {
|
|
merged.Provider = env.Provider
|
|
}
|
|
}
|
|
if !isEnabled(merged.Enabled) {
|
|
return nil
|
|
}
|
|
if merged.Provider == "" {
|
|
merged.Provider = DefaultQueueProvider
|
|
}
|
|
return merged
|
|
}
|
|
|
|
func mergeSearch(root, env *SearchInfraConfig) *SearchInfraConfig {
|
|
if root == nil && env == nil {
|
|
return nil
|
|
}
|
|
merged := &SearchInfraConfig{}
|
|
if root != nil {
|
|
*merged = *root
|
|
}
|
|
if env != nil {
|
|
if env.Enabled != nil {
|
|
merged.Enabled = env.Enabled
|
|
}
|
|
if env.Provider != "" {
|
|
merged.Provider = env.Provider
|
|
}
|
|
}
|
|
if !isEnabled(merged.Enabled) {
|
|
return nil
|
|
}
|
|
if merged.Provider == "" {
|
|
merged.Provider = DefaultSearchProvider
|
|
}
|
|
return merged
|
|
}
|
|
|
|
func mergeMonitoring(root, env *MonitoringInfraConfig) *MonitoringInfraConfig {
|
|
if root == nil && env == nil {
|
|
return nil
|
|
}
|
|
merged := &MonitoringInfraConfig{}
|
|
if root != nil {
|
|
*merged = *root
|
|
}
|
|
if env != nil {
|
|
if env.Enabled != nil {
|
|
merged.Enabled = env.Enabled
|
|
}
|
|
if env.Provider != "" {
|
|
merged.Provider = env.Provider
|
|
}
|
|
if env.MetricsPath != "" {
|
|
merged.MetricsPath = env.MetricsPath
|
|
}
|
|
if env.MetricsPort != nil {
|
|
merged.MetricsPort = env.MetricsPort
|
|
}
|
|
}
|
|
if !isEnabled(merged.Enabled) {
|
|
return nil
|
|
}
|
|
if merged.Provider == "" {
|
|
merged.Provider = DefaultMonProvider
|
|
}
|
|
if merged.MetricsPath == "" {
|
|
merged.MetricsPath = DefaultMetricsPath
|
|
}
|
|
return merged
|
|
}
|
|
|
|
// resolveCronJobs applies defaults to each cron job and merges
|
|
// env vars from the parent deployment.
|
|
func resolveCronJobs(jobs []CronJobConfig, deploymentImage string, deploymentEnvVars []EnvVarConfig, defaultResources ResourceConfig) []ResolvedCronJob {
|
|
resolved := make([]ResolvedCronJob, 0, len(jobs))
|
|
for _, job := range jobs {
|
|
r := ResolvedCronJob{CronJobConfig: job}
|
|
|
|
// Image
|
|
if job.ImageOverride != nil && *job.ImageOverride != "" {
|
|
r.Image = *job.ImageOverride
|
|
} else {
|
|
r.Image = deploymentImage
|
|
}
|
|
|
|
// Env vars: deployment vars first, then job-specific (with merge)
|
|
inheritEnv := job.InheritEnv == nil || *job.InheritEnv
|
|
if inheritEnv {
|
|
r.EnvVars = mergeEnvVars(deploymentEnvVars, job.EnvVars)
|
|
} else {
|
|
r.EnvVars = job.EnvVars
|
|
}
|
|
|
|
// Resources
|
|
if job.Resources == nil {
|
|
r.Resources = &defaultResources
|
|
}
|
|
|
|
// Tuning defaults
|
|
if r.RestartPolicy == "" {
|
|
r.RestartPolicy = DefaultRestartPolicy
|
|
}
|
|
if r.ConcurrencyPolicy == "" {
|
|
r.ConcurrencyPolicy = DefaultConcurrencyPolicy
|
|
}
|
|
if r.SuccessfulJobsHistoryLimit == nil {
|
|
r.SuccessfulJobsHistoryLimit = intPtr(DefaultSuccessfulJobsHist)
|
|
}
|
|
if r.FailedJobsHistoryLimit == nil {
|
|
r.FailedJobsHistoryLimit = intPtr(DefaultFailedJobsHist)
|
|
}
|
|
|
|
resolved = append(resolved, r)
|
|
}
|
|
return resolved
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
// Errors
|
|
// ------------------------------------------------------------
|
|
|
|
type UnknownEnvironmentError struct {
|
|
Key string
|
|
}
|
|
|
|
func (e *UnknownEnvironmentError) Error() string {
|
|
return "unknown environment: " + e.Key
|
|
}
|