This commit is contained in:
Harivansh Rathi 2026-02-07 13:49:11 -05:00
commit 0595d93c49
28 changed files with 1763 additions and 0 deletions

102
internal/config/config.go Normal file
View file

@ -0,0 +1,102 @@
package config
import (
"fmt"
"os"
"gopkg.in/yaml.v3"
)
// Config is the top-level configuration parsed from agentikube.yaml.
type Config struct {
Namespace string `yaml:"namespace"`
Compute ComputeConfig `yaml:"compute"`
Storage StorageConfig `yaml:"storage"`
Sandbox SandboxConfig `yaml:"sandbox"`
}
type ComputeConfig struct {
Type string `yaml:"type"` // karpenter | fargate
InstanceTypes []string `yaml:"instanceTypes"`
CapacityTypes []string `yaml:"capacityTypes"`
MaxCPU int `yaml:"maxCpu"`
MaxMemory string `yaml:"maxMemory"`
Consolidation bool `yaml:"consolidation"`
FargateSelectors []FargateSelector `yaml:"fargateSelectors"`
}
type FargateSelector struct {
Namespace string `yaml:"namespace"`
}
type StorageConfig struct {
Type string `yaml:"type"` // efs
FilesystemID string `yaml:"filesystemId"`
BasePath string `yaml:"basePath"`
UID int `yaml:"uid"`
GID int `yaml:"gid"`
ReclaimPolicy string `yaml:"reclaimPolicy"`
}
type SandboxConfig struct {
Image string `yaml:"image"`
Ports []int `yaml:"ports"`
MountPath string `yaml:"mountPath"`
Resources ResourcesConfig `yaml:"resources"`
Env map[string]string `yaml:"env"`
SecurityContext SecurityContext `yaml:"securityContext"`
Probes ProbesConfig `yaml:"probes"`
WarmPool WarmPoolConfig `yaml:"warmPool"`
NetworkPolicy NetworkPolicy `yaml:"networkPolicy"`
}
type ResourcesConfig struct {
Requests ResourceValues `yaml:"requests"`
Limits ResourceValues `yaml:"limits"`
}
type ResourceValues struct {
CPU string `yaml:"cpu"`
Memory string `yaml:"memory"`
}
type SecurityContext struct {
RunAsUser int `yaml:"runAsUser"`
RunAsGroup int `yaml:"runAsGroup"`
RunAsNonRoot bool `yaml:"runAsNonRoot"`
}
type ProbesConfig struct {
Port int `yaml:"port"`
StartupFailureThreshold int `yaml:"startupFailureThreshold"`
}
type WarmPoolConfig struct {
Enabled bool `yaml:"enabled"`
Size int `yaml:"size"`
TTLMinutes int `yaml:"ttlMinutes"`
}
type NetworkPolicy struct {
EgressAllowAll bool `yaml:"egressAllowAll"`
IngressPorts []int `yaml:"ingressPorts"`
}
// Load reads and parses the config file at the given path.
func Load(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("reading config file: %w", err)
}
var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parsing config file: %w", err)
}
if err := Validate(&cfg); err != nil {
return nil, fmt.Errorf("validating config: %w", err)
}
return &cfg, nil
}

View file

@ -0,0 +1,99 @@
package config
import (
"fmt"
"strings"
)
// Validate checks that all required fields are present and values are valid.
func Validate(cfg *Config) error {
var errs []string
if cfg.Namespace == "" {
errs = append(errs, "namespace is required")
}
// Compute validation
switch cfg.Compute.Type {
case "karpenter":
if len(cfg.Compute.InstanceTypes) == 0 {
errs = append(errs, "compute.instanceTypes is required when type is karpenter")
}
if len(cfg.Compute.CapacityTypes) == 0 {
errs = append(errs, "compute.capacityTypes is required when type is karpenter")
}
if cfg.Compute.MaxCPU <= 0 {
errs = append(errs, "compute.maxCpu must be > 0")
}
if cfg.Compute.MaxMemory == "" {
errs = append(errs, "compute.maxMemory is required when type is karpenter")
}
case "fargate":
if len(cfg.Compute.FargateSelectors) == 0 {
errs = append(errs, "compute.fargateSelectors is required when type is fargate")
}
case "":
errs = append(errs, "compute.type is required (karpenter or fargate)")
default:
errs = append(errs, fmt.Sprintf("compute.type must be karpenter or fargate, got %q", cfg.Compute.Type))
}
// Storage validation
if cfg.Storage.Type == "" {
errs = append(errs, "storage.type is required")
} else if cfg.Storage.Type != "efs" {
errs = append(errs, fmt.Sprintf("storage.type must be efs, got %q", cfg.Storage.Type))
}
if cfg.Storage.FilesystemID == "" {
errs = append(errs, "storage.filesystemId is required")
}
if cfg.Storage.BasePath == "" {
errs = append(errs, "storage.basePath is required")
}
if cfg.Storage.ReclaimPolicy == "" {
cfg.Storage.ReclaimPolicy = "Retain"
} else if cfg.Storage.ReclaimPolicy != "Retain" && cfg.Storage.ReclaimPolicy != "Delete" {
errs = append(errs, fmt.Sprintf("storage.reclaimPolicy must be Retain or Delete, got %q", cfg.Storage.ReclaimPolicy))
}
// Storage defaults
if cfg.Storage.UID == 0 {
cfg.Storage.UID = 1000
}
if cfg.Storage.GID == 0 {
cfg.Storage.GID = 1000
}
// Sandbox validation
if cfg.Sandbox.Image == "" {
errs = append(errs, "sandbox.image is required")
}
if len(cfg.Sandbox.Ports) == 0 {
errs = append(errs, "sandbox.ports is required")
}
if cfg.Sandbox.MountPath == "" {
errs = append(errs, "sandbox.mountPath is required")
}
// Warm pool defaults
if cfg.Sandbox.WarmPool.Size == 0 && cfg.Sandbox.WarmPool.Enabled {
cfg.Sandbox.WarmPool.Size = 5
}
if cfg.Sandbox.WarmPool.TTLMinutes == 0 {
cfg.Sandbox.WarmPool.TTLMinutes = 120
}
// Probes defaults
if cfg.Sandbox.Probes.Port == 0 && len(cfg.Sandbox.Ports) > 0 {
cfg.Sandbox.Probes.Port = cfg.Sandbox.Ports[0]
}
if cfg.Sandbox.Probes.StartupFailureThreshold == 0 {
cfg.Sandbox.Probes.StartupFailureThreshold = 30
}
if len(errs) > 0 {
return fmt.Errorf("config validation errors:\n - %s", strings.Join(errs, "\n - "))
}
return nil
}