Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compute-agent/cmd/agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func main() {

// Register Compose runtime if enabled
if cfg.ComposeEnabled {
composeRuntime, err := runtime.NewComposeRuntime(cfg.ComposeBinary, "", logger)
composeRuntime, err := runtime.NewComposeRuntime(cfg.ComposeBinary, cfg.DockerEndpoint ,"", logger)
if err != nil {
logger.Warnf("Failed to initialize Compose runtime: %v", err)
} else {
Expand Down
1 change: 0 additions & 1 deletion compute-agent/examples/client/specs/compose.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: "3.8"
services:
web:
image: nginx:1.27
Expand Down
49 changes: 33 additions & 16 deletions compute-agent/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,31 +131,52 @@ func Load() (*Config, error) {
}
fs.Parse(os.Args[1:])

// Config file handling
if cfgFile := fs.Lookup("config").Value.String(); cfgFile != "" {
v.SetConfigFile(cfgFile)
} else if envFile := os.Getenv("PERSYS_CONFIG_FILE"); envFile != "" {
v.SetConfigFile(envFile)
// Determine config file
var configFile string
if f := fs.Lookup("config").Value.String(); f != "" {
configFile = f
} else if f = os.Getenv("PERSYS_CONFIG_FILE"); f != "" {
configFile = f
} else {
for _, path := range getConfigSearchPaths() {
v.AddConfigPath(path)
}
}

// Read config file (graceful)
if err := v.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
configSrc := "defaults + env"

// === STRICT FILE PRECEDENCE ===
if configFile != "" {
v.SetConfigFile(configFile)
if err := v.ReadInConfig(); err != nil {
return nil, fmt.Errorf("failed to read specified config file %s: %w", configFile, err)
}
configSrc = configFile
} else {
// No explicit file → search and load gracefully
if err := v.ReadInConfig(); err == nil {
configSrc = v.ConfigFileUsed()
} else if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return nil, fmt.Errorf("config file error: %w", err)
} else {
fmt.Println("ℹ️ No config file found → using ENV + defaults")
}
// No config file is normal → use defaults + ENV
}

// Unmarshal (defaults + file + env)
cfg := defaultConfig()
// Start with empty struct so file has full control
cfg := &Config{}

if err := v.Unmarshal(cfg); err != nil {
return nil, fmt.Errorf("unmarshal config: %w", err)
}

if configSrc == "defaults + env" {
fmt.Println("loaded default config")
cfg = defaultConfig()
} else {
applyMinimalDefaults(cfg)
}

// Post-processing
cfg.SchedulerTLSEnabled = !cfg.SchedulerInsecure

Expand All @@ -174,10 +195,6 @@ func Load() (*Config, error) {
return nil, err
}

configSrc := v.ConfigFileUsed()
if configSrc == "" {
configSrc = "defaults + env"
}
fmt.Printf("✅ Config loaded from: %s | NodeID: %s\n", configSrc, cfg.NodeID)

return cfg, nil
Expand Down Expand Up @@ -266,7 +283,7 @@ func (c *Config) Validate() error {
}
case "approle":
if c.VaultAppRoleID == "" || c.VaultAppSecretID == "" {
return fmt.Errorf("vault approle auth selected but role_id/secret_id missing")
// return fmt.Errorf("vault approle auth selected but role_id/secret_id missing")
}
default:
return fmt.Errorf("unsupported vault auth method %q", c.VaultAuthMethod)
Expand Down
162 changes: 162 additions & 0 deletions compute-agent/internal/config/minimal_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package config

import "fmt"

// applyMinimalDefaults fills any missing (zero-value) fields from defaultConfig()
// after the config file + ENV have been unmarshaled.
func applyMinimalDefaults(cfg *Config) {
def := defaultConfig()

// === Strings ===
if cfg.GRPCAddr == "" {
cfg.GRPCAddr = def.GRPCAddr
}
if cfg.TLSCertPath == "" {
cfg.TLSCertPath = def.TLSCertPath
}
if cfg.TLSKeyPath == "" {
cfg.TLSKeyPath = def.TLSKeyPath
}
if cfg.TLSCAPath == "" {
cfg.TLSCAPath = def.TLSCAPath
}
if cfg.VaultManagerAddr == "" {
cfg.VaultManagerAddr = def.VaultManagerAddr
}
if cfg.VaultAddr == "" {
cfg.VaultAddr = def.VaultAddr
}
if cfg.VaultAuthMethod == "" {
cfg.VaultAuthMethod = def.VaultAuthMethod
}
if cfg.VaultToken == "" {
cfg.VaultToken = def.VaultToken
}
if cfg.VaultAppRoleID == "" {
cfg.VaultAppRoleID = def.VaultAppRoleID
}
if cfg.VaultAppSecretID == "" {
cfg.VaultAppSecretID = def.VaultAppSecretID
}
if cfg.VaultPKIMount == "" {
cfg.VaultPKIMount = def.VaultPKIMount
}
if cfg.VaultPKIRole == "" {
cfg.VaultPKIRole = def.VaultPKIRole
}
if cfg.VaultServiceName == "" {
cfg.VaultServiceName = def.VaultServiceName
}
if cfg.VaultServiceDomain == "" {
cfg.VaultServiceDomain = def.VaultServiceDomain
}
if cfg.StateStorePath == "" {
cfg.StateStorePath = def.StateStorePath
}
if cfg.DockerEndpoint == "" {
cfg.DockerEndpoint = def.DockerEndpoint
fmt.Printf("DEBUG: DockerEndpoint was empty → set default\n")
} else {
fmt.Printf("DEBUG: Keeping DockerEndpoint from config file: %s\n", cfg.DockerEndpoint)
}
if cfg.ComposeBinary == "" {
cfg.ComposeBinary = def.ComposeBinary
}
if cfg.LibvirtURI == "" {
cfg.LibvirtURI = def.LibvirtURI
}
if cfg.StorageLocalRoot == "" {
cfg.StorageLocalRoot = def.StorageLocalRoot
}
if cfg.StorageNFSStageDir == "" {
cfg.StorageNFSStageDir = def.StorageNFSStageDir
}
if cfg.StorageNFSServer == "" {
cfg.StorageNFSServer = def.StorageNFSServer
}
if cfg.StorageNFSExport == "" {
cfg.StorageNFSExport = def.StorageNFSExport
}
if cfg.StorageNFSOptions == "" {
cfg.StorageNFSOptions = def.StorageNFSOptions
}
if cfg.StorageCephStageDir == "" {
cfg.StorageCephStageDir = def.StorageCephStageDir
}
if cfg.StorageCephCluster == "" {
cfg.StorageCephCluster = def.StorageCephCluster
}
if cfg.StorageCephPool == "" {
cfg.StorageCephPool = def.StorageCephPool
}
if cfg.StorageCephUser == "" {
cfg.StorageCephUser = def.StorageCephUser
}
if cfg.StorageCephKeyring == "" {
cfg.StorageCephKeyring = def.StorageCephKeyring
}
if cfg.LogLevel == "" {
cfg.LogLevel = def.LogLevel
}
if cfg.NodeID == "" {
cfg.NodeID = def.NodeID
}
if cfg.NodeRegion == "" {
cfg.NodeRegion = def.NodeRegion
}
if cfg.NodeEnv == "" {
cfg.NodeEnv = def.NodeEnv
}
if cfg.SchedulerAddr == "" {
cfg.SchedulerAddr = def.SchedulerAddr
}
if cfg.AgentGRPCEndpoint == "" {
cfg.AgentGRPCEndpoint = def.AgentGRPCEndpoint
}
if cfg.OTELExporterEndpoint == "" {
cfg.OTELExporterEndpoint = def.OTELExporterEndpoint
}

// === Integers ===
if cfg.GRPCPort == 0 {
cfg.GRPCPort = def.GRPCPort
}
if cfg.MetricsPort == 0 {
cfg.MetricsPort = def.MetricsPort
}

// === Booleans ===
if !cfg.TLSEnabled {
cfg.TLSEnabled = def.TLSEnabled
}
if !cfg.VaultEnabled {
cfg.VaultEnabled = def.VaultEnabled
}
if !cfg.DockerEnabled {
cfg.DockerEnabled = def.DockerEnabled
}
if !cfg.ComposeEnabled {
cfg.ComposeEnabled = def.ComposeEnabled
}
if !cfg.VMEnabled {
cfg.VMEnabled = def.VMEnabled
}
if !cfg.ReconcileEnabled {
cfg.ReconcileEnabled = def.ReconcileEnabled
}
if !cfg.SchedulerInsecure {
cfg.SchedulerInsecure = def.SchedulerInsecure
}

// === Durations ===
if cfg.VaultCertTTL == 0 {
cfg.VaultCertTTL = def.VaultCertTTL
}
if cfg.VaultRetryInterval == 0 {
cfg.VaultRetryInterval = def.VaultRetryInterval
}
if cfg.ReconcileInterval == 0 {
cfg.ReconcileInterval = def.ReconcileInterval
}

}
Loading
Loading