autodeploy.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. package handlers
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "sync"
  8. "time"
  9. "git.linuxforward.com/byop/byop-engine/models"
  10. "github.com/gin-gonic/gin"
  11. )
  12. // AutoDeployHandler handles auto-deployment related operations
  13. type AutoDeployHandler struct {
  14. autoDeploySettings *models.AutoDeploySettings
  15. settingsMutex sync.RWMutex
  16. deploymentQueue chan *models.AutoDeployRequest
  17. }
  18. // NewAutoDeployHandler creates a new AutoDeployHandler
  19. func NewAutoDeployHandler() *AutoDeployHandler {
  20. return &AutoDeployHandler{}
  21. }
  22. // RegisterRoutes registers routes for auto-deployment operations
  23. func (h *AutoDeployHandler) RegisterRoutes(r *gin.RouterGroup) {
  24. r.POST("/client", h.AutoDeployClient)
  25. r.POST("/webhook", h.AutoDeployWebhook)
  26. r.PUT("/settings", h.UpdateAutoDeploySettings)
  27. r.GET("/settings", h.GetAutoDeploySettings)
  28. }
  29. // AutoDeployClient handles requests to deploy for a client
  30. func (h *AutoDeployHandler) AutoDeployClient(c *gin.Context) {
  31. // Parse the request
  32. var request *models.AutoDeployRequest
  33. if err := c.ShouldBindJSON(&request); err != nil {
  34. c.JSON(http.StatusBadRequest, gin.H{
  35. "error": "Invalid request body",
  36. })
  37. return
  38. }
  39. // Check if auto-deployment is enabled
  40. h.settingsMutex.RLock()
  41. enabled := h.autoDeploySettings.Enabled
  42. h.settingsMutex.RUnlock()
  43. if !enabled {
  44. c.JSON(http.StatusServiceUnavailable, gin.H{
  45. "error": "Auto-deployment is currently disabled",
  46. })
  47. return
  48. }
  49. // Queue the deployment request
  50. select {
  51. case h.deploymentQueue <- request:
  52. // Successfully queued
  53. default:
  54. // Queue is full
  55. c.JSON(http.StatusServiceUnavailable, gin.H{
  56. "error": "Deployment queue is full, try again later",
  57. })
  58. return
  59. }
  60. // Return a success response
  61. c.JSON(http.StatusAccepted, gin.H{
  62. "status": "queued",
  63. "message": "Deployment request has been queued",
  64. "clientId": request.ClientID,
  65. "queuedAt": time.Now(),
  66. })
  67. }
  68. // AutoDeployWebhook handles webhook triggers for auto-deployment
  69. func (h *AutoDeployHandler) AutoDeployWebhook(c *gin.Context) {
  70. // Parse the webhook payload
  71. var payload *models.WebhookPayload
  72. if err := c.ShouldBindJSON(&payload); err != nil {
  73. c.JSON(http.StatusBadRequest, gin.H{
  74. "error": "Invalid webhook payload",
  75. })
  76. return
  77. }
  78. // Validate the webhook signature
  79. h.settingsMutex.RLock()
  80. secret := h.autoDeploySettings.WebhookSecret
  81. enabled := h.autoDeploySettings.Enabled
  82. h.settingsMutex.RUnlock()
  83. // This is a simplified signature check, in production use HMAC
  84. if c.GetHeader("X-Webhook-Signature") != secret {
  85. c.JSON(http.StatusUnauthorized, gin.H{
  86. "error": "Invalid webhook signature",
  87. })
  88. return
  89. }
  90. if !enabled {
  91. c.JSON(http.StatusServiceUnavailable, gin.H{
  92. "error": "Auto-deployment is currently disabled",
  93. })
  94. return
  95. }
  96. // Handle different event types
  97. switch payload.Event {
  98. case "client.created":
  99. // Check if we should auto-deploy for new clients
  100. h.settingsMutex.RLock()
  101. autoDeploy := h.autoDeploySettings.AutoDeployNewClients
  102. h.settingsMutex.RUnlock()
  103. if autoDeploy && payload.ClientID != "" {
  104. // Queue a deployment request
  105. request := &models.AutoDeployRequest{
  106. ClientID: payload.ClientID,
  107. // Use defaults for other fields
  108. }
  109. select {
  110. case h.deploymentQueue <- request:
  111. // Successfully queued
  112. default:
  113. // Queue is full
  114. log.Printf("Deployment queue is full, skipping auto-deployment for client %s", payload.ClientID)
  115. }
  116. }
  117. case "deployment.requested":
  118. // Handle explicit deployment requests
  119. if payload.ClientID == "" {
  120. c.JSON(http.StatusBadRequest, gin.H{
  121. "error": "client_id is required for deployment requests",
  122. })
  123. return
  124. }
  125. // Parse the deployment data
  126. var deployData struct {
  127. TemplateID string `json:"template_id"`
  128. ProviderID string `json:"provider_id"`
  129. Region string `json:"region"`
  130. Tags map[string]string `json:"tags"`
  131. }
  132. if err := json.Unmarshal(payload.Data, &deployData); err != nil {
  133. c.JSON(http.StatusBadRequest, gin.H{
  134. "error": "Invalid deployment data",
  135. })
  136. return
  137. }
  138. // Queue the deployment
  139. request := &models.AutoDeployRequest{
  140. ClientID: payload.ClientID,
  141. TemplateID: deployData.TemplateID,
  142. ProviderID: deployData.ProviderID,
  143. Region: deployData.Region,
  144. Tags: deployData.Tags,
  145. }
  146. select {
  147. case h.deploymentQueue <- request:
  148. // Successfully queued
  149. default:
  150. // Queue is full
  151. c.JSON(http.StatusServiceUnavailable, gin.H{
  152. "error": "Deployment queue is full, try again later",
  153. })
  154. return
  155. }
  156. default:
  157. // Unknown event type
  158. c.JSON(http.StatusBadRequest, gin.H{
  159. "error": fmt.Sprintf("Unsupported event type: %s", payload.Event),
  160. })
  161. return
  162. }
  163. // Return a success response
  164. c.JSON(http.StatusOK, gin.H{
  165. "status": "received",
  166. "event": payload.Event,
  167. "timestamp": time.Now(),
  168. "requestId": payload.RequestID,
  169. })
  170. }
  171. // UpdateAutoDeploySettings updates the auto-deployment settings
  172. func (h *AutoDeployHandler) UpdateAutoDeploySettings(c *gin.Context) {
  173. // Parse the settings
  174. var settings *models.AutoDeploySettings
  175. if err := c.ShouldBindJSON(&settings); err != nil {
  176. c.JSON(http.StatusBadRequest, gin.H{
  177. "error": "Invalid settings format",
  178. })
  179. return
  180. }
  181. // Validate settings
  182. if settings.DefaultProviderID == "" {
  183. c.JSON(http.StatusBadRequest, gin.H{
  184. "error": "default_provider_id is required",
  185. })
  186. return
  187. }
  188. if settings.DefaultTemplateID == "" {
  189. c.JSON(http.StatusBadRequest, gin.H{
  190. "error": "default_template_id is required",
  191. })
  192. return
  193. }
  194. // Update the settings
  195. h.settingsMutex.Lock()
  196. // Keep the webhook secret if not provided
  197. if settings.WebhookSecret == "" {
  198. settings.WebhookSecret = h.autoDeploySettings.WebhookSecret
  199. }
  200. h.autoDeploySettings = settings
  201. h.settingsMutex.Unlock()
  202. // Return the updated settings (without sensitive fields)
  203. h.settingsMutex.RLock()
  204. responseSettings := h.autoDeploySettings
  205. responseSettings.WebhookSecret = "********" // Hide the actual secret
  206. h.settingsMutex.RUnlock()
  207. c.JSON(http.StatusOK, responseSettings)
  208. }
  209. // GetAutoDeploySettings returns the current auto-deployment settings
  210. func (h *AutoDeployHandler) GetAutoDeploySettings(c *gin.Context) {
  211. // Get a copy of the settings
  212. h.settingsMutex.RLock()
  213. settings := h.autoDeploySettings
  214. settings.WebhookSecret = "********" // Hide the actual secret
  215. h.settingsMutex.RUnlock()
  216. // Return the settings
  217. c.JSON(http.StatusOK, settings)
  218. }