feat: implement agent migration system
- Fix config version inflation bug in main.go - Add dynamic subsystem checking to prevent false change detection - Implement migration detection and execution system - Add directory migration from /etc/aggregator to /etc/redflag - Update all path references across codebase to use new directories - Add configuration schema versioning and automatic migration - Implement backup and rollback capabilities - Add security feature detection and hardening - Update installation scripts and sudoers for new paths - Complete Phase 1 migration system
This commit is contained in:
@@ -47,6 +47,10 @@ type LoggingConfig struct {
|
||||
|
||||
// Config holds agent configuration
|
||||
type Config struct {
|
||||
// Version Information
|
||||
Version string `json:"version,omitempty"` // Config schema version
|
||||
AgentVersion string `json:"agent_version,omitempty"` // Agent binary version
|
||||
|
||||
// Server Configuration
|
||||
ServerURL string `json:"server_url"`
|
||||
RegistrationToken string `json:"registration_token,omitempty"` // One-time registration token
|
||||
@@ -133,8 +137,10 @@ type CLIFlags struct {
|
||||
// getDefaultConfig returns default configuration values
|
||||
func getDefaultConfig() *Config {
|
||||
return &Config{
|
||||
ServerURL: "http://localhost:8080",
|
||||
CheckInInterval: 300, // 5 minutes
|
||||
Version: "4", // Current config schema version
|
||||
AgentVersion: "", // Will be set by the agent at startup
|
||||
ServerURL: "http://localhost:8080",
|
||||
CheckInInterval: 300, // 5 minutes
|
||||
Network: NetworkConfig{
|
||||
Timeout: 30 * time.Second,
|
||||
RetryCount: 3,
|
||||
@@ -153,7 +159,7 @@ func getDefaultConfig() *Config {
|
||||
}
|
||||
}
|
||||
|
||||
// loadFromFile reads configuration from file
|
||||
// loadFromFile reads configuration from file with backward compatibility migration
|
||||
func loadFromFile(configPath string) (*Config, error) {
|
||||
// Ensure directory exists
|
||||
dir := filepath.Dir(configPath)
|
||||
@@ -170,12 +176,57 @@ func loadFromFile(configPath string) (*Config, error) {
|
||||
return nil, fmt.Errorf("failed to read config: %w", err)
|
||||
}
|
||||
|
||||
var config Config
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
// Start with latest default config
|
||||
config := getDefaultConfig()
|
||||
|
||||
// Parse the existing config into a generic map to handle missing fields
|
||||
var rawConfig map[string]interface{}
|
||||
if err := json.Unmarshal(data, &rawConfig); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config: %w", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
// Marshal back to JSON and unmarshal into our new structure
|
||||
// This ensures missing fields get default values from getDefaultConfig()
|
||||
configJSON, err := json.Marshal(rawConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to re-marshal config: %w", err)
|
||||
}
|
||||
|
||||
// Carefully merge into our config structure, preserving defaults for missing fields
|
||||
if err := json.Unmarshal(configJSON, &config); err != nil {
|
||||
return nil, fmt.Errorf("failed to merge config: %w", err)
|
||||
}
|
||||
|
||||
// Handle specific migrations for known breaking changes
|
||||
migrateConfig(config)
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// migrateConfig handles specific known migrations between config versions
|
||||
func migrateConfig(cfg *Config) {
|
||||
// Update config schema version to latest
|
||||
if cfg.Version != "4" {
|
||||
fmt.Printf("[CONFIG] Migrating config schema from version %s to 4\n", cfg.Version)
|
||||
cfg.Version = "4"
|
||||
}
|
||||
|
||||
// Migration 1: Ensure minimum check-in interval (30 seconds)
|
||||
if cfg.CheckInInterval < 30 {
|
||||
fmt.Printf("[CONFIG] Migrating check_in_interval from %d to minimum 30 seconds\n", cfg.CheckInInterval)
|
||||
cfg.CheckInInterval = 300 // Default to 5 minutes for better performance
|
||||
}
|
||||
|
||||
// Migration 2: Add missing subsystem fields with defaults
|
||||
if cfg.Subsystems.System.Timeout == 0 && cfg.Subsystems.System.CircuitBreaker.FailureThreshold == 0 {
|
||||
fmt.Printf("[CONFIG] Adding missing 'system' subsystem configuration\n")
|
||||
cfg.Subsystems.System = GetDefaultSubsystemsConfig().System
|
||||
}
|
||||
|
||||
if cfg.Subsystems.Updates.Timeout == 0 && cfg.Subsystems.Updates.CircuitBreaker.FailureThreshold == 0 {
|
||||
fmt.Printf("[CONFIG] Adding missing 'updates' subsystem configuration\n")
|
||||
cfg.Subsystems.Updates = GetDefaultSubsystemsConfig().Updates
|
||||
}
|
||||
}
|
||||
|
||||
// loadFromEnv loads configuration from environment variables
|
||||
|
||||
Reference in New Issue
Block a user