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:
Fimeg
2025-11-04 14:25:53 -05:00
parent 253022cacd
commit e6ac0b1ec4
8 changed files with 994 additions and 16 deletions

View File

@@ -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