implemented gitops controller + drift detection

This commit is contained in:
2026-05-12 19:51:59 +02:00
parent 35afa8d8f1
commit c7df53708c
17 changed files with 1064 additions and 261 deletions
+97
View File
@@ -0,0 +1,97 @@
package gitops
import (
"encoding/json"
"log"
"time"
"github.com/forgeo/forgebucket/internal/events"
"github.com/forgeo/forgebucket/internal/models"
)
// TriggerSync creates a Deployment record in "pending" state and fires
// deployment.started — the same path as a manually-triggered deployment.
// GitOps is just the trigger; actual execution is handled externally (or via CI).
func (c *Controller) TriggerSync(cfg models.GitOpsConfig, desiredSHA string) {
var env models.Environment
if found, _ := c.db.ID(cfg.EnvID).Get(&env); !found {
log.Printf("gitops: sync env %d not found", cfg.EnvID)
return
}
now := time.Now().UTC()
deploy := &models.Deployment{
EnvID: cfg.EnvID,
RepoID: cfg.RepoID,
SHA: desiredSHA,
Ref: "refs/heads/" + cfg.Branch,
Status: models.DeployStatusPending,
TriggeredBy: "gitops",
Description: "GitOps auto-sync",
StartedAt: &now,
}
if _, err := c.db.Insert(deploy); err != nil {
log.Printf("gitops: create deployment: %v", err)
return
}
cfg.SyncStatus = "syncing"
c.db.ID(cfg.ID).Cols("sync_status").Update(&cfg) //nolint:errcheck
c.bus.Publish(events.SubjectDeploymentStarted, events.DeploymentEvent{ //nolint:errcheck
DeploymentID: deploy.ID,
EnvID: env.ID,
EnvName: env.Name,
RepoID: deploy.RepoID,
SHA: deploy.SHA,
Ref: deploy.Ref,
Status: string(deploy.Status),
TriggeredBy: deploy.TriggeredBy,
})
log.Printf("gitops: triggered sync deploy %d for env %d (%s)", deploy.ID, cfg.EnvID, desiredSHA[:7])
}
// handleDeploymentSucceeded is called when any deployment.succeeded event fires.
// If the deployment was GitOps-triggered, it marks the config as synced.
func (c *Controller) handleDeploymentSucceeded(data []byte) {
var evt events.DeploymentEvent
if err := json.Unmarshal(data, &evt); err != nil {
return
}
// Only act on deployments triggered by gitops.
if evt.TriggeredBy != "gitops" {
// Still update ActualSHA and resolve drift if this env has a GitOps config —
// manual deployments also advance the state.
var cfg models.GitOpsConfig
if found, _ := c.db.Where("env_id = ?", evt.EnvID).Get(&cfg); found {
markSynced(c.db, evt.EnvID, evt.SHA)
log.Printf("gitops: env %d synced via manual deploy (%s)", evt.EnvID, sha7(evt.SHA))
}
return
}
markSynced(c.db, evt.EnvID, evt.SHA)
log.Printf("gitops: env %d synced (%s)", evt.EnvID, sha7(evt.SHA))
}
// handleDeploymentFailed is called when deployment.failed fires.
// If the deployment was GitOps-triggered, it reverts SyncStatus back to drifted.
func (c *Controller) handleDeploymentFailed(data []byte) {
var evt events.DeploymentEvent
if err := json.Unmarshal(data, &evt); err != nil {
return
}
if evt.TriggeredBy != "gitops" {
return
}
var cfg models.GitOpsConfig
if found, _ := c.db.Where("env_id = ?", evt.EnvID).Get(&cfg); !found {
return
}
cfg.SyncStatus = "drifted"
c.db.ID(cfg.ID).Cols("sync_status").Update(&cfg) //nolint:errcheck
log.Printf("gitops: env %d sync failed — reverting to drifted", evt.EnvID)
}