refactor: consolidate config logic into ConfigService

Created centralized ConfigService for configuration management.
Added deprecation comments to ConfigBuilder and AgentBuilder.
Platform-specific defaults centralized in one place.
Removed placeholder ConfigService from agent_lifecycle.go.
This commit is contained in:
Fimeg
2025-11-10 22:23:56 -05:00
parent 52c9c1a45b
commit e1173c9f3b
5 changed files with 182 additions and 21 deletions

View File

@@ -91,6 +91,7 @@ func NewAgentBuild(c *gin.Context) {
} }
// UpgradeAgentBuild handles agent upgrade requests // UpgradeAgentBuild handles agent upgrade requests
// Deprecated: Use ConfigService for config building
func UpgradeAgentBuild(c *gin.Context) { func UpgradeAgentBuild(c *gin.Context) {
agentID := c.Param("agentID") agentID := c.Param("agentID")
if agentID == "" { if agentID == "" {

View File

@@ -11,6 +11,7 @@ import (
) )
// AgentBuilder handles generating embedded agent configurations // AgentBuilder handles generating embedded agent configurations
// Deprecated: Configuration logic should use services.ConfigService
type AgentBuilder struct { type AgentBuilder struct {
buildContext string buildContext string
} }
@@ -21,6 +22,7 @@ func NewAgentBuilder() *AgentBuilder {
} }
// BuildAgentWithConfig generates agent configuration and prepares signed binary // BuildAgentWithConfig generates agent configuration and prepares signed binary
// Deprecated: Delegate config generation to services.ConfigService
func (ab *AgentBuilder) BuildAgentWithConfig(config *AgentConfiguration) (*BuildResult, error) { func (ab *AgentBuilder) BuildAgentWithConfig(config *AgentConfiguration) (*BuildResult, error) {
// Create temporary build directory // Create temporary build directory
buildDir, err := os.MkdirTemp("", "agent-build-") buildDir, err := os.MkdirTemp("", "agent-build-")

View File

@@ -269,27 +269,6 @@ func (s *BuildService) BuildArtifacts(ctx context.Context, cfg *AgentConfig) (*B
return &BuildArtifacts{}, nil return &BuildArtifacts{}, nil
} }
// ConfigService placeholder (to be implemented)
type ConfigService struct {
db *sqlx.DB
config *config.Config
logger *log.Logger
}
func NewConfigService(db *sqlx.DB, cfg *config.Config, logger *log.Logger) *ConfigService {
return &ConfigService{db: db, config: cfg, logger: logger}
}
func (s *ConfigService) GenerateNewConfig(cfg *AgentConfig) ([]byte, error) {
// Placeholder: Return empty JSON
return []byte("{}"), nil
}
func (s *ConfigService) LoadExistingConfig(agentID string) ([]byte, error) {
// Placeholder: Return empty JSON
return []byte("{}"), nil
}
// ArtifactService placeholder (to be implemented) // ArtifactService placeholder (to be implemented)
type ArtifactService struct { type ArtifactService struct {
db *sqlx.DB db *sqlx.DB

View File

@@ -37,6 +37,8 @@ type PublicKeyResponse struct {
} }
// ConfigBuilder handles dynamic agent configuration generation // ConfigBuilder handles dynamic agent configuration generation
// ConfigBuilder builds agent configurations
// Deprecated: Use services.ConfigService instead
type ConfigBuilder struct { type ConfigBuilder struct {
serverURL string serverURL string
templates map[string]AgentTemplate templates map[string]AgentTemplate

View File

@@ -0,0 +1,177 @@
package services
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"github.com/Fimeg/RedFlag/aggregator-server/internal/config"
"github.com/Fimeg/RedFlag/aggregator-server/internal/models"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
)
// ConfigService manages agent configuration generation and validation
type ConfigService struct {
db *sqlx.DB
config *config.Config
logger *log.Logger
}
// NewConfigService creates a new configuration service
func NewConfigService(db *sqlx.DB, cfg *config.Config, logger *log.Logger) *ConfigService {
return &ConfigService{
db: db,
config: cfg,
logger: logger,
}
}
// AgentConfigData represents agent configuration structure
type AgentConfigData struct {
AgentID string `json:"agent_id"`
Version string `json:"version"`
Platform string `json:"platform"`
ServerURL string `json:"server_url"`
LogLevel string `json:"log_level"`
Intervals map[string]int `json:"intervals"`
Subsystems map[string]interface{} `json:"subsystems"`
MaxRetries int `json:"max_retries"`
TimeoutSeconds int `json:"timeout_seconds"`
MachineID string `json:"machine_id"`
AgentType string `json:"agent_type"`
ConfigPath string `json:"config_path"`
StatePath string `json:"state_path"`
LogPath string `json:"log_path"`
ServiceName string `json:"service_name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// GenerateNewConfig creates configuration for a new agent
func (s *ConfigService) GenerateNewConfig(agentCfg *AgentConfig) ([]byte, error) {
// Base configuration
serverURL := fmt.Sprintf("http://%s:%d", s.config.Server.Host, s.config.Server.Port)
if s.config.Server.PublicURL != "" {
serverURL = s.config.Server.PublicURL
}
cfg := &AgentConfigData{
AgentID: agentCfg.AgentID,
Version: agentCfg.Version,
Platform: agentCfg.Platform,
ServerURL: serverURL,
LogLevel: "info",
Intervals: map[string]int{
"metrics": 300, // 5 minutes
"updates": 3600, // 1 hour
"commands": 30, // 30 seconds
},
Subsystems: map[string]interface{}{
"updates": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 300},
"storage": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 60},
"system": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 60},
"docker": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 120},
},
MaxRetries: 3,
TimeoutSeconds: 30,
MachineID: agentCfg.MachineID,
AgentType: agentCfg.AgentType,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
// Platform-specific customizations
s.applyPlatformDefaults(cfg)
// Validate configuration
if err := s.validateConfig(cfg); err != nil {
return nil, fmt.Errorf("validation failed: %w", err)
}
// Marshal to JSON
configJSON, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return nil, fmt.Errorf("marshal failed: %w", err)
}
return configJSON, nil
}
// LoadExistingConfig retrieves and updates existing agent configuration
func (s *ConfigService) LoadExistingConfig(agentID string) ([]byte, error) {
// Get existing agent from database
var agent models.Agent
query := `SELECT * FROM agents WHERE id = $1`
err := s.db.Get(&agent, query, agentID)
if err != nil {
return nil, fmt.Errorf("agent not found: %w", err)
}
// Generate new config based on agent data
agentCfg := &AgentConfig{
AgentID: agentID,
Version: agent.CurrentVersion,
Platform: agent.OSType,
Architecture: agent.OSArchitecture,
MachineID: "",
AgentType: "", // Could be stored in Metadata
Hostname: agent.Hostname,
}
// Use GenerateNewConfig to create config
return s.GenerateNewConfig(agentCfg)
}
// applyPlatformDefaults applies platform-specific configuration
func (s *ConfigService) applyPlatformDefaults(cfg *AgentConfigData) {
switch cfg.Platform {
case "windows-amd64", "windows-arm64", "windows-386":
// Windows-specific paths
cfg.ConfigPath = "C:\\ProgramData\\RedFlag\\config.json"
cfg.StatePath = "C:\\ProgramData\\RedFlag\\state\\"
cfg.LogPath = "C:\\ProgramData\\RedFlag\\logs\\"
cfg.ServiceName = "RedFlagAgent"
// Windows-specific subsystems
cfg.Subsystems["windows"] = map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 300}
cfg.Subsystems["winget"] = map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 180}
default:
// Linux defaults
cfg.ConfigPath = "/etc/redflag/config.json"
cfg.StatePath = "/var/lib/redflag/"
cfg.LogPath = "/var/log/redflag/"
cfg.ServiceName = "redflag-agent"
}
}
// validateConfig validates configuration
func (s *ConfigService) validateConfig(cfg *AgentConfigData) error {
if cfg.AgentID == "" {
return fmt.Errorf("agent_id is required")
}
if cfg.Version == "" {
return fmt.Errorf("version is required")
}
if cfg.Platform == "" {
return fmt.Errorf("platform is required")
}
if cfg.ServerURL == "" {
return fmt.Errorf("server_url is required")
}
return nil
}
// SaveConfig saves agent configuration to database
func (s *ConfigService) SaveConfig(ctx context.Context, agentID uuid.UUID, configJSON []byte) error {
query := `
UPDATE agents SET config = $1, updated_at = $2
WHERE id = $3
`
_, err := s.db.ExecContext(ctx, query, configJSON, time.Now(), agentID)
return err
}