Skip to content

Commit

Permalink
Merge pull request #1727 from diggerhq/feat/status-update
Browse files Browse the repository at this point in the history
support handling job status update
  • Loading branch information
ZIJ authored Sep 30, 2024
2 parents 11ced2d + b9d9530 commit 0ab47b6
Show file tree
Hide file tree
Showing 12 changed files with 406 additions and 8 deletions.
138 changes: 138 additions & 0 deletions ee/drift/controllers/ci_jobs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package controllers

import (
"github.com/diggerhq/digger/ee/drift/dbmodels"
"github.com/diggerhq/digger/ee/drift/model"
"github.com/diggerhq/digger/libs/terraform_utils"
"github.com/gin-gonic/gin"
"log"
"net/http"
"time"
)

type SetJobStatusRequest struct {
Status string `json:"status"`
Timestamp time.Time `json:"timestamp"`
JobSummary *terraform_utils.TerraformSummary `json:"job_summary"`
Footprint *terraform_utils.TerraformPlanFootprint `json:"job_plan_footprint"`
PrCommentUrl string `json:"pr_comment_url"`
TerraformOutput string `json:"terraform_output""`
}

func (mc MainController) SetJobStatusForProject(c *gin.Context) {
jobId := c.Param("jobId")
//orgId, exists := c.Get(middleware.ORGANISATION_ID_KEY)

//if !exists {
// c.String(http.StatusForbidden, "Not allowed to access this resource")
// return
//}

var request SetJobStatusRequest

err := c.BindJSON(&request)

if err != nil {
log.Printf("Error binding JSON: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error binding JSON"})
return
}

log.Printf("settings tatus for job: %v, new status: %v, tfout: %v, job summary: %v", jobId, request.Status, request.TerraformOutput, request.JobSummary)

job, err := dbmodels.DB.GetDiggerCiJob(jobId)
if err != nil {
log.Printf("could not get digger ci job, err: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error getting digger job"})
return
}

switch request.Status {
case string(dbmodels.DiggerJobStarted):
job.Status = string(dbmodels.DiggerJobStarted)
err := dbmodels.DB.UpdateDiggerJob(job)
if err != nil {
log.Printf("Error updating job status: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error updating job status"})
return
}
case string(dbmodels.DiggerJobSucceeded):
job.Status = string(dbmodels.DiggerJobSucceeded)
job.TerraformOutput = request.TerraformOutput
job.ResourcesCreated = int32(request.JobSummary.ResourcesCreated)
job.ResourcesUpdated = int32(request.JobSummary.ResourcesUpdated)
job.ResourcesDeleted = int32(request.JobSummary.ResourcesDeleted)
err := dbmodels.DB.UpdateDiggerJob(job)
if err != nil {
log.Printf("Error updating job status: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error updating job status"})
return
}

project, err := dbmodels.DB.GetProjectById(job.ProjectID)
if err != nil {
log.Printf("Error retriving project: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving project"})
return

}
err = ProjectDriftStateMachineApply(*project, job.TerraformOutput, job.ResourcesCreated, job.ResourcesUpdated, job.ResourcesDeleted)
if err != nil {
log.Printf("error while checking drifted project")
}

case string(dbmodels.DiggerJobFailed):
job.Status = string(dbmodels.DiggerJobFailed)
job.TerraformOutput = request.TerraformOutput
err := dbmodels.DB.UpdateDiggerJob(job)
if err != nil {
log.Printf("Error updating job status: %v", request.Status)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error saving job"})
return
}

default:
log.Printf("Unexpected status %v", request.Status)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error saving job"})
return
}

job.UpdatedAt = request.Timestamp
err = dbmodels.DB.GormDB.Save(job).Error
if err != nil {
log.Printf("Error saving update job: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error saving job"})
return
}

c.JSON(http.StatusOK, gin.H{})
}

func ProjectDriftStateMachineApply(project model.Project, tfplan string, resourcesCreated int32, resourcesUpdated int32, resourcesDeleted int32) error {
isEmptyPlan := resourcesCreated == 0 && resourcesUpdated == 0 && resourcesDeleted == 0
wasEmptyPlan := project.ToCreate == 0 && project.ToUpdate == 0 && project.ToDelete == 0
if isEmptyPlan {
project.DriftStatus = dbmodels.DriftStatusNoDrift
}
if !isEmptyPlan && wasEmptyPlan {
project.DriftStatus = dbmodels.DriftStatusNewDrift
}
if !isEmptyPlan && !wasEmptyPlan {
if project.DriftTerraformPlan != tfplan {
if project.IsAcknowledged {
project.DriftStatus = dbmodels.DriftStatusNewDrift
}
}
}

project.DriftTerraformPlan = tfplan
project.ToCreate = resourcesCreated
project.ToUpdate = resourcesUpdated
project.ToDelete = resourcesDeleted
result := dbmodels.DB.GormDB.Save(&project)
if result.Error != nil {
return result.Error
}
log.Printf("project %v, (name: %v) has been updated successfully\n", project.ID, project.Name)
return nil
}
12 changes: 10 additions & 2 deletions ee/drift/controllers/drift.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ func (mc MainController) TriggerDriftRunForProject(c *gin.Context) {
CommentId: "",
Job: jobSpec,
Reporter: spec.ReporterSpec{
ReportingStrategy: "noop",
ReportingStrategy: "noop",
ReportTerraformOutput: true,
},
Lock: spec.LockSpec{
LockType: "noop",
Expand All @@ -128,7 +129,7 @@ func (mc MainController) TriggerDriftRunForProject(c *gin.Context) {
PolicyType: "http",
},
CommentUpdater: spec.CommentUpdaterSpec{
CommentUpdaterType: dg_configuration.CommentRenderModeBasic,
CommentUpdaterType: "noop",
},
}

Expand Down Expand Up @@ -163,6 +164,13 @@ func (mc MainController) TriggerDriftRunForProject(c *gin.Context) {

}

_, err = dbmodels.DB.CreateCiJobFromSpec(spec, *runName, project.ID)
if err != nil {
log.Printf("error creating the job: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Error creating job entry")})
return
}

err = ciBackend.TriggerWorkflow(spec, *runName, *vcsToken)
if err != nil {
log.Printf("TriggerWorkflow err: %v\n", err)
Expand Down
136 changes: 136 additions & 0 deletions ee/drift/dbmodels/ci_jobs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package dbmodels

import (
"encoding/json"
"errors"
"fmt"
"github.com/diggerhq/digger/ee/drift/model"
"github.com/diggerhq/digger/libs/spec"
"github.com/google/uuid"
"gorm.io/gorm"
"log"
"time"
)

type DiggerJobStatus string

const (
DiggerJobCreated DiggerJobStatus = "created"
DiggerJobTriggered DiggerJobStatus = "triggered"
DiggerJobFailed DiggerJobStatus = "failed"
DiggerJobStarted DiggerJobStatus = "started"
DiggerJobSucceeded DiggerJobStatus = "succeeded"
DiggerJobQueuedForRun DiggerJobStatus = "queued"
)

func (db *Database) GetDiggerCiJob(diggerJobId string) (*model.DiggerCiJob, error) {
log.Printf("GetDiggerCiJob, diggerJobId: %v", diggerJobId)
var ciJob model.DiggerCiJob

err := db.GormDB.Where("digger_job_id = ?", diggerJobId).First(&ciJob).Error

if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("ci job not found")
}
log.Printf("Unknown error occurred while fetching database, %v\n", err)
return nil, err
}

return &ciJob, nil
}

func (db *Database) UpdateDiggerJob(job *model.DiggerCiJob) error {
result := db.GormDB.Save(job)
if result.Error != nil {
return result.Error
}
log.Printf("DiggerJob %v, (id: %v) has been updated successfully\n", job.DiggerJobID, job.ID)
return nil
}

func (db *Database) CreateCiJobFromSpec(spec spec.Spec, runName string, projectId string) (*model.DiggerCiJob, error) {

marshalledJobSpec, err := json.Marshal(spec.Job)
if err != nil {
log.Printf("failed to marshal job: %v", err)
return nil, err
}

marshalledReporterSpec, err := json.Marshal(spec.Reporter)
if err != nil {
log.Printf("failed to marshal reporter: %v", err)
return nil, err
}

marshalledCommentUpdaterSpec, err := json.Marshal(spec.CommentUpdater)
if err != nil {
log.Printf("failed to marshal comment updater: %v", err)
return nil, err
}

marshalledLockSpec, err := json.Marshal(spec.Lock)
if err != nil {
log.Printf("failed to marshal lockspec: %v", err)
return nil, err
}

marshalledBackendSpec, err := json.Marshal(spec.Backend)
if err != nil {
log.Printf("failed to marshal backend spec: %v", err)
return nil, err

}

marshalledVcsSpec, err := json.Marshal(spec.VCS)
if err != nil {
log.Printf("failed to marshal vcs spec: %v", err)
return nil, err
}

marshalledPolicySpec, err := json.Marshal(spec.Policy)
if err != nil {
log.Printf("failed to marshal policy spec: %v", err)
return nil, err
}

marshalledVariablesSpec, err := json.Marshal(spec.Variables)
if err != nil {
log.Printf("failed to marshal variables spec: %v", err)
return nil, err
}

job := &model.DiggerCiJob{
ID: uuid.NewString(),
DiggerJobID: spec.JobId,
Spectype: string(spec.SpecType),
Commentid: spec.CommentId,
Runname: runName,
Jobspec: marshalledJobSpec,
Reporterspec: marshalledReporterSpec,
Commentupdaterspec: marshalledCommentUpdaterSpec,
Lockspec: marshalledLockSpec,
Backendspec: marshalledBackendSpec,
Vcsspec: marshalledVcsSpec,
Policyspec: marshalledPolicySpec,
Variablesspec: marshalledVariablesSpec,
CreatedAt: time.Time{},
UpdatedAt: time.Time{},
DeletedAt: gorm.DeletedAt{},
WorkflowFile: spec.VCS.WorkflowFile,
WorkflowURL: "",
Status: string(DiggerJobCreated),
ResourcesCreated: 0,
ResourcesUpdated: 0,
ResourcesDeleted: 0,
ProjectID: projectId,
}

err = db.GormDB.Create(job).Error
if err != nil {
log.Printf("failed to create job: %v", err)
return nil, err
}

return job, nil
}
18 changes: 18 additions & 0 deletions ee/drift/dbmodels/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dbmodels

import (
"errors"
"fmt"
"github.com/diggerhq/digger/ee/drift/model"
"gorm.io/gorm"
"log"
Expand All @@ -13,6 +14,23 @@ var DriftStatusNewDrift = "new drift"
var DriftStatusNoDrift = "no drift"
var DriftStatusAcknowledgeDrift = "acknowledged drift"

func (db *Database) GetProjectById(projectId string) (*model.Project, error) {
log.Printf("GetProjectById, projectId: %v\n", projectId)
var project model.Project

err := db.GormDB.Where("id = ?", projectId).First(&project).Error

if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("could not find project")
}
log.Printf("Unknown error occurred while fetching database, %v\n", err)
return nil, err
}

return &project, nil
}

// GetProjectByName return project for specified org and repo
// if record doesn't exist return nil
func (db *Database) GetProjectByName(orgId any, repo *model.Repo, name string) (*model.Project, error) {
Expand Down
15 changes: 15 additions & 0 deletions ee/drift/dbmodels/tokens.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package dbmodels

import (
"errors"
"github.com/diggerhq/digger/ee/drift/model"
"github.com/google/uuid"
"gorm.io/gorm"
"log"
"time"
)
Expand Down Expand Up @@ -32,3 +34,16 @@ func (db *Database) CreateDiggerJobToken(organisationId string) (*model.DiggerCi
}
return jobToken, nil
}

func (db *Database) GetJobToken(tenantId any) (*model.DiggerCiJobToken, error) {
token := &model.DiggerCiJobToken{}
result := db.GormDB.Take(token, "value = ?", tenantId)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
} else {
return nil, result.Error
}
}
return token, nil
}
2 changes: 2 additions & 0 deletions ee/drift/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ func main() {
r.POST("github-app-webhook", controller.GithubAppWebHook)
r.GET("/github/callback_fe", middleware.WebhookAuth(), controller.GithubAppCallbackPage)

r.POST("/repos/:repo/projects/:projectName/jobs/:jobId/set-status", middleware.JobTokenAuth(), controller.SetJobStatusForProject)

r.POST("/_internal/trigger_drift_for_project", middleware.WebhookAuth(), controller.TriggerDriftRunForProject)

port := os.Getenv("DIGGER_PORT")
Expand Down
Loading

0 comments on commit 0ab47b6

Please sign in to comment.