Enhance configuration management by adding global config validation and network detection; refactor tests to align with updated config structure
This commit is contained in:
@@ -39,6 +39,14 @@ type API struct {
|
||||
// NewAPI creates a new API handler with all dependencies
|
||||
// Note: Setup files (cluster-services, cluster-nodes, etc.) are now embedded in the binary
|
||||
func NewAPI(dataDir, appsDir string) (*API, error) {
|
||||
// Initialize config manager
|
||||
configMgr := config.NewManager()
|
||||
|
||||
// Ensure global config exists
|
||||
if err := configMgr.EnsureGlobalConfig(dataDir); err != nil {
|
||||
return nil, fmt.Errorf("failed to ensure global config: %w", err)
|
||||
}
|
||||
|
||||
// Ensure base directories exist
|
||||
instancesDir := tools.GetInstancesPath(dataDir)
|
||||
if err := os.MkdirAll(instancesDir, 0755); err != nil {
|
||||
@@ -55,7 +63,7 @@ func NewAPI(dataDir, appsDir string) (*API, error) {
|
||||
return &API{
|
||||
dataDir: dataDir,
|
||||
appsDir: appsDir,
|
||||
config: config.NewManager(),
|
||||
config: configMgr,
|
||||
secrets: secrets.NewManager(),
|
||||
context: context.NewManager(dataDir),
|
||||
instance: instance.NewManager(dataDir),
|
||||
|
||||
@@ -10,15 +10,6 @@ import (
|
||||
|
||||
// GlobalConfig represents the main configuration structure
|
||||
type GlobalConfig struct {
|
||||
Wildcloud struct {
|
||||
Repository string `yaml:"repository,omitempty" json:"repository,omitempty"`
|
||||
CurrentPhase string `yaml:"currentPhase,omitempty" json:"currentPhase,omitempty"`
|
||||
CompletedPhases []string `yaml:"completedPhases,omitempty" json:"completedPhases,omitempty"`
|
||||
} `yaml:"wildcloud,omitempty" json:"wildcloud,omitempty"`
|
||||
Server struct {
|
||||
Port int `yaml:"port,omitempty" json:"port,omitempty"`
|
||||
Host string `yaml:"host,omitempty" json:"host,omitempty"`
|
||||
} `yaml:"server,omitempty" json:"server,omitempty"`
|
||||
Operator struct {
|
||||
Email string `yaml:"email,omitempty" json:"email,omitempty"`
|
||||
} `yaml:"operator,omitempty" json:"operator,omitempty"`
|
||||
@@ -35,14 +26,6 @@ type GlobalConfig struct {
|
||||
Interface string `yaml:"interface,omitempty" json:"interface,omitempty"`
|
||||
} `yaml:"dnsmasq,omitempty" json:"dnsmasq,omitempty"`
|
||||
} `yaml:"cloud,omitempty" json:"cloud,omitempty"`
|
||||
Cluster struct {
|
||||
EndpointIP string `yaml:"endpointIp,omitempty" json:"endpointIp,omitempty"`
|
||||
Nodes struct {
|
||||
Talos struct {
|
||||
Version string `yaml:"version,omitempty" json:"version,omitempty"`
|
||||
} `yaml:"talos,omitempty" json:"talos,omitempty"`
|
||||
} `yaml:"nodes,omitempty" json:"nodes,omitempty"`
|
||||
} `yaml:"cluster,omitempty" json:"cluster,omitempty"`
|
||||
}
|
||||
|
||||
// LoadGlobalConfig loads configuration from the specified path
|
||||
@@ -57,14 +40,6 @@ func LoadGlobalConfig(configPath string) (*GlobalConfig, error) {
|
||||
return nil, fmt.Errorf("parsing config file: %w", err)
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
if config.Server.Port == 0 {
|
||||
config.Server.Port = 5055
|
||||
}
|
||||
if config.Server.Host == "" {
|
||||
config.Server.Host = "0.0.0.0"
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
@@ -89,8 +64,8 @@ func (c *GlobalConfig) IsEmpty() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if any essential fields are empty
|
||||
return c.Cloud.DNS.IP == "" || c.Cluster.Nodes.Talos.Version == ""
|
||||
// Check if essential fields are empty
|
||||
return c.Cloud.DNS.IP == "" && c.Cloud.Router.IP == "" && c.Operator.Email == ""
|
||||
}
|
||||
|
||||
type NodeConfig struct {
|
||||
@@ -109,18 +84,7 @@ type InstanceConfig struct {
|
||||
Domain string `yaml:"domain" json:"domain"`
|
||||
InternalDomain string `yaml:"internalDomain" json:"internalDomain"`
|
||||
DHCPRange string `yaml:"dhcpRange" json:"dhcpRange"`
|
||||
DNS struct {
|
||||
IP string `yaml:"ip" json:"ip"`
|
||||
ExternalResolver string `yaml:"externalResolver" json:"externalResolver"`
|
||||
} `yaml:"dns" json:"dns"`
|
||||
Router struct {
|
||||
IP string `yaml:"ip" json:"ip"`
|
||||
DynamicDns string `yaml:"dynamicDns,omitempty" json:"dynamicDns,omitempty"`
|
||||
} `yaml:"router" json:"router"`
|
||||
Dnsmasq struct {
|
||||
Interface string `yaml:"interface" json:"interface"`
|
||||
} `yaml:"dnsmasq" json:"dnsmasq"`
|
||||
NFS struct {
|
||||
NFS struct {
|
||||
Host string `yaml:"host" json:"host"`
|
||||
MediaPath string `yaml:"mediaPath" json:"mediaPath"`
|
||||
StorageCapacity string `yaml:"storageCapacity" json:"storageCapacity"`
|
||||
|
||||
@@ -17,16 +17,7 @@ func TestLoadGlobalConfig(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "loads complete configuration",
|
||||
configYAML: `wildcloud:
|
||||
repository: "https://github.com/example/repo"
|
||||
currentPhase: "setup"
|
||||
completedPhases:
|
||||
- "phase1"
|
||||
- "phase2"
|
||||
server:
|
||||
port: 8080
|
||||
host: "localhost"
|
||||
operator:
|
||||
configYAML: `operator:
|
||||
email: "admin@example.com"
|
||||
cloud:
|
||||
dns:
|
||||
@@ -37,67 +28,43 @@ cloud:
|
||||
dynamicDns: "example.dyndns.org"
|
||||
dnsmasq:
|
||||
interface: "eth0"
|
||||
cluster:
|
||||
endpointIp: "192.168.1.100"
|
||||
nodes:
|
||||
talos:
|
||||
version: "v1.8.0"
|
||||
`,
|
||||
verify: func(t *testing.T, config *GlobalConfig) {
|
||||
if config.Wildcloud.Repository != "https://github.com/example/repo" {
|
||||
t.Error("repository not loaded correctly")
|
||||
}
|
||||
if config.Server.Port != 8080 {
|
||||
t.Error("port not loaded correctly")
|
||||
if config.Operator.Email != "admin@example.com" {
|
||||
t.Error("operator email not loaded correctly")
|
||||
}
|
||||
if config.Cloud.DNS.IP != "192.168.1.1" {
|
||||
t.Error("DNS IP not loaded correctly")
|
||||
}
|
||||
if config.Cluster.EndpointIP != "192.168.1.100" {
|
||||
t.Error("endpoint IP not loaded correctly")
|
||||
if config.Cloud.Router.IP != "192.168.1.254" {
|
||||
t.Error("router IP not loaded correctly")
|
||||
}
|
||||
if config.Cloud.Dnsmasq.Interface != "eth0" {
|
||||
t.Error("dnsmasq interface not loaded correctly")
|
||||
}
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "applies default values",
|
||||
name: "loads minimal configuration",
|
||||
configYAML: `cloud:
|
||||
dns:
|
||||
ip: "192.168.1.1"
|
||||
cluster:
|
||||
nodes:
|
||||
talos:
|
||||
version: "v1.8.0"
|
||||
`,
|
||||
verify: func(t *testing.T, config *GlobalConfig) {
|
||||
if config.Server.Port != 5055 {
|
||||
t.Errorf("default port not applied, got %d, want 5055", config.Server.Port)
|
||||
}
|
||||
if config.Server.Host != "0.0.0.0" {
|
||||
t.Errorf("default host not applied, got %q, want %q", config.Server.Host, "0.0.0.0")
|
||||
if config.Cloud.DNS.IP != "192.168.1.1" {
|
||||
t.Error("DNS IP not loaded correctly")
|
||||
}
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "preserves custom port and host",
|
||||
configYAML: `server:
|
||||
port: 9000
|
||||
host: "127.0.0.1"
|
||||
cloud:
|
||||
dns:
|
||||
ip: "192.168.1.1"
|
||||
cluster:
|
||||
nodes:
|
||||
talos:
|
||||
version: "v1.8.0"
|
||||
name: "loads empty configuration",
|
||||
configYAML: `{}
|
||||
`,
|
||||
verify: func(t *testing.T, config *GlobalConfig) {
|
||||
if config.Server.Port != 9000 {
|
||||
t.Errorf("custom port not preserved, got %d, want 9000", config.Server.Port)
|
||||
}
|
||||
if config.Server.Host != "127.0.0.1" {
|
||||
t.Errorf("custom host not preserved, got %q, want %q", config.Server.Host, "127.0.0.1")
|
||||
if config.Cloud.DNS.IP != "" {
|
||||
t.Error("expected empty DNS IP")
|
||||
}
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -189,35 +156,25 @@ func TestSaveGlobalConfig(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "saves complete configuration",
|
||||
config: &GlobalConfig{
|
||||
Wildcloud: struct {
|
||||
Repository string `yaml:"repository,omitempty" json:"repository,omitempty"`
|
||||
CurrentPhase string `yaml:"currentPhase,omitempty" json:"currentPhase,omitempty"`
|
||||
CompletedPhases []string `yaml:"completedPhases,omitempty" json:"completedPhases,omitempty"`
|
||||
}{
|
||||
Repository: "https://github.com/example/repo",
|
||||
CurrentPhase: "setup",
|
||||
CompletedPhases: []string{"phase1", "phase2"},
|
||||
},
|
||||
Server: struct {
|
||||
Port int `yaml:"port,omitempty" json:"port,omitempty"`
|
||||
Host string `yaml:"host,omitempty" json:"host,omitempty"`
|
||||
}{
|
||||
Port: 8080,
|
||||
Host: "localhost",
|
||||
},
|
||||
},
|
||||
config: func() *GlobalConfig {
|
||||
cfg := &GlobalConfig{}
|
||||
cfg.Operator.Email = "admin@example.com"
|
||||
cfg.Cloud.DNS.IP = "192.168.1.1"
|
||||
cfg.Cloud.Router.IP = "192.168.1.254"
|
||||
cfg.Cloud.Dnsmasq.Interface = "eth0"
|
||||
return cfg
|
||||
}(),
|
||||
verify: func(t *testing.T, configPath string) {
|
||||
content, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read saved config: %v", err)
|
||||
}
|
||||
contentStr := string(content)
|
||||
if !strings.Contains(contentStr, "repository") {
|
||||
t.Error("saved config missing repository field")
|
||||
if !strings.Contains(contentStr, "admin@example.com") {
|
||||
t.Error("saved config missing operator email")
|
||||
}
|
||||
if !strings.Contains(contentStr, "8080") {
|
||||
t.Error("saved config missing port value")
|
||||
if !strings.Contains(contentStr, "192.168.1.1") {
|
||||
t.Error("saved config missing DNS IP")
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -313,101 +270,41 @@ func TestGlobalConfig_IsEmpty(t *testing.T) {
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "config with only DNS IP is empty",
|
||||
config: &GlobalConfig{
|
||||
Cloud: struct {
|
||||
DNS struct {
|
||||
IP string `yaml:"ip,omitempty" json:"ip,omitempty"`
|
||||
ExternalResolver string `yaml:"externalResolver,omitempty" json:"externalResolver,omitempty"`
|
||||
} `yaml:"dns,omitempty" json:"dns,omitempty"`
|
||||
Router struct {
|
||||
IP string `yaml:"ip,omitempty" json:"ip,omitempty"`
|
||||
DynamicDns string `yaml:"dynamicDns,omitempty" json:"dynamicDns,omitempty"`
|
||||
} `yaml:"router,omitempty" json:"router,omitempty"`
|
||||
Dnsmasq struct {
|
||||
Interface string `yaml:"interface,omitempty" json:"interface,omitempty"`
|
||||
} `yaml:"dnsmasq,omitempty" json:"dnsmasq,omitempty"`
|
||||
}{
|
||||
DNS: struct {
|
||||
IP string `yaml:"ip,omitempty" json:"ip,omitempty"`
|
||||
ExternalResolver string `yaml:"externalResolver,omitempty" json:"externalResolver,omitempty"`
|
||||
}{
|
||||
IP: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
name: "config with only DNS IP is not empty",
|
||||
config: func() *GlobalConfig {
|
||||
cfg := &GlobalConfig{}
|
||||
cfg.Cloud.DNS.IP = "192.168.1.1"
|
||||
return cfg
|
||||
}(),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "config with only Talos version is empty",
|
||||
config: &GlobalConfig{
|
||||
Cluster: struct {
|
||||
EndpointIP string `yaml:"endpointIp,omitempty" json:"endpointIp,omitempty"`
|
||||
Nodes struct {
|
||||
Talos struct {
|
||||
Version string `yaml:"version,omitempty" json:"version,omitempty"`
|
||||
} `yaml:"talos,omitempty" json:"talos,omitempty"`
|
||||
} `yaml:"nodes,omitempty" json:"nodes,omitempty"`
|
||||
}{
|
||||
Nodes: struct {
|
||||
Talos struct {
|
||||
Version string `yaml:"version,omitempty" json:"version,omitempty"`
|
||||
} `yaml:"talos,omitempty" json:"talos,omitempty"`
|
||||
}{
|
||||
Talos: struct {
|
||||
Version string `yaml:"version,omitempty" json:"version,omitempty"`
|
||||
}{
|
||||
Version: "v1.8.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
name: "config with only router IP is not empty",
|
||||
config: func() *GlobalConfig {
|
||||
cfg := &GlobalConfig{}
|
||||
cfg.Cloud.Router.IP = "192.168.1.254"
|
||||
return cfg
|
||||
}(),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "config with both DNS IP and Talos version is not empty",
|
||||
config: &GlobalConfig{
|
||||
Cloud: struct {
|
||||
DNS struct {
|
||||
IP string `yaml:"ip,omitempty" json:"ip,omitempty"`
|
||||
ExternalResolver string `yaml:"externalResolver,omitempty" json:"externalResolver,omitempty"`
|
||||
} `yaml:"dns,omitempty" json:"dns,omitempty"`
|
||||
Router struct {
|
||||
IP string `yaml:"ip,omitempty" json:"ip,omitempty"`
|
||||
DynamicDns string `yaml:"dynamicDns,omitempty" json:"dynamicDns,omitempty"`
|
||||
} `yaml:"router,omitempty" json:"router,omitempty"`
|
||||
Dnsmasq struct {
|
||||
Interface string `yaml:"interface,omitempty" json:"interface,omitempty"`
|
||||
} `yaml:"dnsmasq,omitempty" json:"dnsmasq,omitempty"`
|
||||
}{
|
||||
DNS: struct {
|
||||
IP string `yaml:"ip,omitempty" json:"ip,omitempty"`
|
||||
ExternalResolver string `yaml:"externalResolver,omitempty" json:"externalResolver,omitempty"`
|
||||
}{
|
||||
IP: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
Cluster: struct {
|
||||
EndpointIP string `yaml:"endpointIp,omitempty" json:"endpointIp,omitempty"`
|
||||
Nodes struct {
|
||||
Talos struct {
|
||||
Version string `yaml:"version,omitempty" json:"version,omitempty"`
|
||||
} `yaml:"talos,omitempty" json:"talos,omitempty"`
|
||||
} `yaml:"nodes,omitempty" json:"nodes,omitempty"`
|
||||
}{
|
||||
Nodes: struct {
|
||||
Talos struct {
|
||||
Version string `yaml:"version,omitempty" json:"version,omitempty"`
|
||||
} `yaml:"talos,omitempty" json:"talos,omitempty"`
|
||||
}{
|
||||
Talos: struct {
|
||||
Version string `yaml:"version,omitempty" json:"version,omitempty"`
|
||||
}{
|
||||
Version: "v1.8.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "config with only operator email is not empty",
|
||||
config: func() *GlobalConfig {
|
||||
cfg := &GlobalConfig{}
|
||||
cfg.Operator.Email = "admin@example.com"
|
||||
return cfg
|
||||
}(),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "config with all fields is not empty",
|
||||
config: func() *GlobalConfig {
|
||||
cfg := &GlobalConfig{}
|
||||
cfg.Cloud.DNS.IP = "192.168.1.1"
|
||||
cfg.Cloud.Router.IP = "192.168.1.254"
|
||||
cfg.Operator.Email = "admin@example.com"
|
||||
return cfg
|
||||
}(),
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
@@ -433,11 +330,6 @@ func TestLoadCloudConfig(t *testing.T) {
|
||||
{
|
||||
name: "loads complete instance configuration",
|
||||
configYAML: `cloud:
|
||||
router:
|
||||
ip: "192.168.1.254"
|
||||
dns:
|
||||
ip: "192.168.1.1"
|
||||
externalResolver: "8.8.8.8"
|
||||
dhcpRange: "192.168.1.100,192.168.1.200"
|
||||
baseDomain: "example.com"
|
||||
domain: "home"
|
||||
@@ -458,6 +350,9 @@ cluster:
|
||||
if config.Cloud.BaseDomain != "example.com" {
|
||||
t.Error("base domain not loaded correctly")
|
||||
}
|
||||
if config.Cloud.DHCPRange != "192.168.1.100,192.168.1.200" {
|
||||
t.Error("DHCP range not loaded correctly")
|
||||
}
|
||||
if config.Cluster.Name != "my-cluster" {
|
||||
t.Error("cluster name not loaded correctly")
|
||||
}
|
||||
@@ -611,29 +506,13 @@ func TestGlobalConfig_RoundTrip(t *testing.T) {
|
||||
configPath := filepath.Join(tempDir, "config.yaml")
|
||||
|
||||
// Create config with all fields
|
||||
original := &GlobalConfig{
|
||||
Wildcloud: struct {
|
||||
Repository string `yaml:"repository,omitempty" json:"repository,omitempty"`
|
||||
CurrentPhase string `yaml:"currentPhase,omitempty" json:"currentPhase,omitempty"`
|
||||
CompletedPhases []string `yaml:"completedPhases,omitempty" json:"completedPhases,omitempty"`
|
||||
}{
|
||||
Repository: "https://github.com/example/repo",
|
||||
CurrentPhase: "setup",
|
||||
CompletedPhases: []string{"phase1", "phase2"},
|
||||
},
|
||||
Server: struct {
|
||||
Port int `yaml:"port,omitempty" json:"port,omitempty"`
|
||||
Host string `yaml:"host,omitempty" json:"host,omitempty"`
|
||||
}{
|
||||
Port: 8080,
|
||||
Host: "localhost",
|
||||
},
|
||||
Operator: struct {
|
||||
Email string `yaml:"email,omitempty" json:"email,omitempty"`
|
||||
}{
|
||||
Email: "admin@example.com",
|
||||
},
|
||||
}
|
||||
original := &GlobalConfig{}
|
||||
original.Operator.Email = "admin@example.com"
|
||||
original.Cloud.DNS.IP = "192.168.1.1"
|
||||
original.Cloud.DNS.ExternalResolver = "8.8.8.8"
|
||||
original.Cloud.Router.IP = "192.168.1.254"
|
||||
original.Cloud.Router.DynamicDns = "example.dyndns.org"
|
||||
original.Cloud.Dnsmasq.Interface = "eth0"
|
||||
|
||||
// Save config
|
||||
if err := SaveGlobalConfig(original, configPath); err != nil {
|
||||
@@ -647,15 +526,18 @@ func TestGlobalConfig_RoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
// Verify all fields match
|
||||
if loaded.Wildcloud.Repository != original.Wildcloud.Repository {
|
||||
t.Errorf("repository mismatch: got %q, want %q", loaded.Wildcloud.Repository, original.Wildcloud.Repository)
|
||||
}
|
||||
if loaded.Server.Port != original.Server.Port {
|
||||
t.Errorf("port mismatch: got %d, want %d", loaded.Server.Port, original.Server.Port)
|
||||
}
|
||||
if loaded.Operator.Email != original.Operator.Email {
|
||||
t.Errorf("email mismatch: got %q, want %q", loaded.Operator.Email, original.Operator.Email)
|
||||
}
|
||||
if loaded.Cloud.DNS.IP != original.Cloud.DNS.IP {
|
||||
t.Errorf("DNS IP mismatch: got %q, want %q", loaded.Cloud.DNS.IP, original.Cloud.DNS.IP)
|
||||
}
|
||||
if loaded.Cloud.Router.IP != original.Cloud.Router.IP {
|
||||
t.Errorf("router IP mismatch: got %q, want %q", loaded.Cloud.Router.IP, original.Cloud.Router.IP)
|
||||
}
|
||||
if loaded.Cloud.Dnsmasq.Interface != original.Cloud.Dnsmasq.Interface {
|
||||
t.Errorf("dnsmasq interface mismatch: got %q, want %q", loaded.Cloud.Dnsmasq.Interface, original.Cloud.Dnsmasq.Interface)
|
||||
}
|
||||
}
|
||||
|
||||
// Test: Round-trip save and load for instance config
|
||||
|
||||
@@ -2,8 +2,10 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/wild-cloud/wild-central/daemon/internal/network"
|
||||
"github.com/wild-cloud/wild-central/daemon/internal/storage"
|
||||
"github.com/wild-cloud/wild-central/daemon/internal/tools"
|
||||
)
|
||||
@@ -20,6 +22,45 @@ func NewManager() *Manager {
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureGlobalConfig ensures a global config file exists with proper structure
|
||||
func (m *Manager) EnsureGlobalConfig(dataDir string) error {
|
||||
configPath := filepath.Join(dataDir, "config.yaml")
|
||||
|
||||
// Check if config already exists
|
||||
if storage.FileExists(configPath) {
|
||||
// Validate existing config
|
||||
if err := m.yq.Validate(configPath); err != nil {
|
||||
return fmt.Errorf("invalid config file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create config structure with detected defaults
|
||||
initialConfig := &GlobalConfig{}
|
||||
|
||||
// Detect network configuration
|
||||
netInfo, err := network.DetectNetworkInfo()
|
||||
if err != nil {
|
||||
log.Printf("Warning: Could not detect network info, using empty defaults: %v", err)
|
||||
} else {
|
||||
// Set detected values
|
||||
initialConfig.Cloud.DNS.IP = netInfo.PrimaryIP
|
||||
initialConfig.Cloud.DNS.ExternalResolver = "1.1.1.1" // Default external resolver
|
||||
initialConfig.Cloud.Router.IP = netInfo.Gateway
|
||||
initialConfig.Cloud.Dnsmasq.Interface = netInfo.PrimaryInterface
|
||||
log.Printf("Detected network: IP=%s, Gateway=%s, Interface=%s",
|
||||
netInfo.PrimaryIP, netInfo.Gateway, netInfo.PrimaryInterface)
|
||||
}
|
||||
|
||||
// Ensure data directory exists
|
||||
if err := storage.EnsureDir(dataDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save config using the model's save function
|
||||
return SaveGlobalConfig(initialConfig, configPath)
|
||||
}
|
||||
|
||||
// EnsureInstanceConfig ensures an instance config file exists with proper structure
|
||||
func (m *Manager) EnsureInstanceConfig(instancePath string) error {
|
||||
configPath := filepath.Join(instancePath, "config.yaml")
|
||||
|
||||
@@ -901,9 +901,6 @@ func TestEnsureInstanceConfig_RequiredFields(t *testing.T) {
|
||||
"domain:",
|
||||
"internalDomain:",
|
||||
"dhcpRange:",
|
||||
"dns:",
|
||||
"router:",
|
||||
"dnsmasq:",
|
||||
"nfs:",
|
||||
"smtp:",
|
||||
"cluster:",
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
type NetworkInfo struct {
|
||||
PrimaryIP string `json:"primary_ip"`
|
||||
PrimaryInterface string `json:"primary_interface"`
|
||||
Gateway string `json:"gateway"`
|
||||
}
|
||||
|
||||
// DetectNetworkInfo detects the machine's primary network configuration
|
||||
@@ -28,6 +29,16 @@ func DetectNetworkInfo() (*NetworkInfo, error) {
|
||||
line := string(output)
|
||||
// Example output: "8.8.8.8 via 192.168.8.1 dev wlan0 src 192.168.8.152 uid 1000"
|
||||
|
||||
// Extract gateway (after "via")
|
||||
gateway := ""
|
||||
if idx := strings.Index(line, "via "); idx != -1 {
|
||||
rest := line[idx+4:]
|
||||
parts := strings.Fields(rest)
|
||||
if len(parts) > 0 {
|
||||
gateway = parts[0]
|
||||
}
|
||||
}
|
||||
|
||||
// Extract interface (after "dev")
|
||||
iface := ""
|
||||
if idx := strings.Index(line, "dev "); idx != -1 {
|
||||
@@ -56,6 +67,7 @@ func DetectNetworkInfo() (*NetworkInfo, error) {
|
||||
return &NetworkInfo{
|
||||
PrimaryIP: ip,
|
||||
PrimaryInterface: iface,
|
||||
Gateway: gateway,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -236,8 +236,8 @@ func fileExists(path string) bool {
|
||||
}
|
||||
|
||||
func checkDNSConfigured(instanceConfig *config.InstanceConfig, globalConfig *config.GlobalConfig) bool {
|
||||
// DNS is configured if we have DNS IP and DHCP range
|
||||
return instanceConfig.Cloud.DNS.IP != "" &&
|
||||
// DNS is configured if we have DNS IP (from global config) and DHCP range (from instance config)
|
||||
return globalConfig.Cloud.DNS.IP != "" &&
|
||||
instanceConfig.Cloud.DHCPRange != ""
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user