Files
Redflag/aggregator-server/internal/api/handlers/build_orchestrator.go
Fimeg 52c9c1a45b refactor: add AgentLifecycleService for unified agent operations
Created centralized lifecycle service to handle new, upgrade, and rebuild operations.
Added deprecation notices to old handlers (agent_setup, build_orchestrator, agent_build).
Foundation for consolidating duplicate agent lifecycle logic.
2025-11-10 22:15:03 -05:00

230 lines
6.9 KiB
Go

package handlers
import (
"fmt"
"net/http"
"github.com/Fimeg/RedFlag/aggregator-server/internal/services"
"github.com/gin-gonic/gin"
)
// NewAgentBuild handles new agent installation requests
// Deprecated: Use AgentHandler.Upgrade instead
func NewAgentBuild(c *gin.Context) {
var req services.NewBuildRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Validate registration token
if req.RegistrationToken == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "registration token is required for new installations"})
return
}
// Convert to setup request format
setupReq := services.AgentSetupRequest{
ServerURL: req.ServerURL,
Environment: req.Environment,
AgentType: req.AgentType,
Organization: req.Organization,
CustomSettings: req.CustomSettings,
DeploymentID: req.DeploymentID,
}
// Create config builder
configBuilder := services.NewConfigBuilder(req.ServerURL)
// Build agent configuration
config, err := configBuilder.BuildAgentConfig(setupReq)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Override generated agent ID if provided (for upgrades)
if req.AgentID != "" {
config.AgentID = req.AgentID
// Update public config with existing agent ID
if config.PublicConfig == nil {
config.PublicConfig = make(map[string]interface{})
}
config.PublicConfig["agent_id"] = req.AgentID
}
// Create agent builder
agentBuilder := services.NewAgentBuilder()
// Generate build artifacts
buildResult, err := agentBuilder.BuildAgentWithConfig(config)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Construct download URL
binaryURL := fmt.Sprintf("%s/api/v1/downloads/%s", req.ServerURL, config.Platform)
// Create response with native binary instructions
response := gin.H{
"agent_id": config.AgentID,
"binary_url": binaryURL,
"platform": config.Platform,
"config_version": config.ConfigVersion,
"agent_version": config.AgentVersion,
"build_time": buildResult.BuildTime,
"install_type": "new",
"consumes_seat": true,
"next_steps": []string{
"1. Download native binary: curl -sL " + binaryURL + " -o /usr/local/bin/redflag-agent",
"2. Set permissions: chmod 755 /usr/local/bin/redflag-agent",
"3. Create config directory: mkdir -p /etc/redflag",
"4. Save configuration (provided in this response) to /etc/redflag/config.json",
"5. Set config permissions: chmod 600 /etc/redflag/config.json",
"6. Start service: systemctl enable --now redflag-agent",
},
"configuration": config.PublicConfig,
}
c.JSON(http.StatusOK, response)
}
// UpgradeAgentBuild handles agent upgrade requests
func UpgradeAgentBuild(c *gin.Context) {
agentID := c.Param("agentID")
if agentID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "agent ID is required"})
return
}
var req services.UpgradeBuildRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Validate required fields
if req.ServerURL == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "server URL is required"})
return
}
// Convert to setup request format
setupReq := services.AgentSetupRequest{
ServerURL: req.ServerURL,
Environment: req.Environment,
AgentType: req.AgentType,
Organization: req.Organization,
CustomSettings: req.CustomSettings,
DeploymentID: req.DeploymentID,
}
// Create config builder
configBuilder := services.NewConfigBuilder(req.ServerURL)
// Build agent configuration
config, err := configBuilder.BuildAgentConfig(setupReq)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Override with existing agent ID (this is the key for upgrades)
config.AgentID = agentID
if config.PublicConfig == nil {
config.PublicConfig = make(map[string]interface{})
}
config.PublicConfig["agent_id"] = agentID
// For upgrades, we might want to preserve certain existing settings
if req.PreserveExisting {
// TODO: Load existing agent config and merge/override as needed
// This would involve reading the existing agent's configuration
// and selectively preserving certain fields
}
// Create agent builder
agentBuilder := services.NewAgentBuilder()
// Generate build artifacts
buildResult, err := agentBuilder.BuildAgentWithConfig(config)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Construct download URL
binaryURL := fmt.Sprintf("%s/api/v1/downloads/%s?version=%s", req.ServerURL, config.Platform, config.AgentVersion)
// Create response with native binary upgrade instructions
response := gin.H{
"agent_id": config.AgentID,
"binary_url": binaryURL,
"platform": config.Platform,
"config_version": config.ConfigVersion,
"agent_version": config.AgentVersion,
"build_time": buildResult.BuildTime,
"install_type": "upgrade",
"consumes_seat": false,
"preserves_agent_id": true,
"next_steps": []string{
"1. Stop agent service: systemctl stop redflag-agent",
"2. Download updated binary: curl -sL " + binaryURL + " -o /usr/local/bin/redflag-agent",
"3. Set permissions: chmod 755 /usr/local/bin/redflag-agent",
"4. Update config (provided in this response) to /etc/redflag/config.json if needed",
"5. Start service: systemctl start redflag-agent",
"6. Verify: systemctl status redflag-agent",
},
"configuration": config.PublicConfig,
"upgrade_notes": []string{
"This upgrade preserves the existing agent ID: " + agentID,
"No additional seat will be consumed",
"Config version: " + config.ConfigVersion,
"Agent binary version: " + config.AgentVersion,
"Agent will receive latest security enhancements and bug fixes",
},
}
c.JSON(http.StatusOK, response)
}
// DetectAgentInstallation detects existing agent installations
func DetectAgentInstallation(c *gin.Context) {
// This endpoint helps the installer determine what type of installation to perform
var req struct {
AgentID string `json:"agent_id"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Create detector service
detector := services.NewInstallationDetector()
// Detect existing installation
detection, err := detector.DetectExistingInstallation(req.AgentID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
response := gin.H{
"detection_result": detection,
"recommended_action": func() string {
if detection.HasExistingAgent {
return "upgrade"
}
return "new_installation"
}(),
"installation_type": func() string {
if detection.HasExistingAgent {
return "upgrade"
}
return "new"
}(),
}
c.JSON(http.StatusOK, response)
}