diff --git a/db/playbooks.go b/db/playbooks.go index f3604e844..6cd57c6e4 100644 --- a/db/playbooks.go +++ b/db/playbooks.go @@ -28,6 +28,19 @@ func FindPlaybooksForEvent(ctx context.Context, eventClass, event string) ([]mod return playbooks, nil } +func FindPlaybookRun(ctx context.Context, id uuid.UUID) (*models.PlaybookRun, error) { + var p models.PlaybookRun + if err := ctx.DB().Where("id = ?", id).First(&p).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + + return nil, err + } + + return &p, nil +} + func FindPlaybook(ctx context.Context, id uuid.UUID) (*models.Playbook, error) { var p models.Playbook if err := ctx.DB().Where("id = ?", id).First(&p).Error; err != nil { diff --git a/go.mod b/go.mod index 64c949433..92df6246e 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/containrrr/shoutrrr v0.8.0 github.com/fergusstrange/embedded-postgres v1.25.0 // indirect github.com/flanksource/commons v1.29.10 - github.com/flanksource/duty v1.0.666 + github.com/flanksource/duty v1.0.667 github.com/flanksource/gomplate/v3 v3.24.32 github.com/flanksource/kopper v1.0.10 github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 @@ -359,6 +359,6 @@ require ( // replace github.com/flanksource/commons => /Users/moshe/go/src/github.com/flanksource/commons -replace github.com/flanksource/duty => ../duty +// replace github.com/flanksource/duty => ../duty // replace github.com/flanksource/gomplate/v3 => /Users/moshe/go/src/github.com/flanksource/gomplate diff --git a/go.sum b/go.sum index fc0f607eb..0d5da2599 100644 --- a/go.sum +++ b/go.sum @@ -877,8 +877,8 @@ github.com/flanksource/artifacts v1.0.14 h1:Vv70bccsae0MwGaf/uSPp34J5V1/PyKfct9z github.com/flanksource/artifacts v1.0.14/go.mod h1:qHVCnQu5k50aWNJ5UhpcAKEl7pAzqUrFFKGSm147G70= github.com/flanksource/commons v1.29.10 h1:T/S95Pl8kASEFvQjQ7fJjTUqeVdhxQXg1vfkULTYFJQ= github.com/flanksource/commons v1.29.10/go.mod h1:iTbrXOSp3Spv570Nly97D/U9cQjLZoVlmWCXqWzsvRU= -github.com/flanksource/duty v1.0.666 h1:doJ1OBKXHtb9Tlslh/LO7PSoVuqsl4R6B9cuGP+2CWw= -github.com/flanksource/duty v1.0.666/go.mod h1:/dIt7bXnQlVtVikTu1TY1syEcuGCSUBnKyKlxVR5sDw= +github.com/flanksource/duty v1.0.667 h1:um6ppCnvQF3roIduY1n6S9uPv/YkEEicPhzeFBbDzck= +github.com/flanksource/duty v1.0.667/go.mod h1:/dIt7bXnQlVtVikTu1TY1syEcuGCSUBnKyKlxVR5sDw= github.com/flanksource/gomplate/v3 v3.20.4/go.mod h1:27BNWhzzSjDed1z8YShO6W+z6G9oZXuxfNFGd/iGSdc= github.com/flanksource/gomplate/v3 v3.24.32 h1:MILauVcjIqBit4nXB5UCN/LZ2z+fiMb8n1Klwjci1Tg= github.com/flanksource/gomplate/v3 v3.24.32/go.mod h1:FdQHxnyrBSmT5zNJTDq08oXxD+eOqti4ERanSoDmQAU= diff --git a/playbook/approval.go b/playbook/approval.go index 071bd8ee1..c49e59cef 100644 --- a/playbook/approval.go +++ b/playbook/approval.go @@ -1,6 +1,7 @@ package playbook import ( + "encoding/json" "net/http" "github.com/flanksource/commons/collections" @@ -19,52 +20,45 @@ func HandlePlaybookRunApproval(c echo.Context) error { ctx := c.Request().Context().(context.Context) var ( - playbookID = c.Param("playbook_id") - runID = c.Param("run_id") + runID = c.Param("run_id") ) - playbookUUID, err := uuid.Parse(playbookID) - if err != nil { - return c.JSON(http.StatusBadRequest, dutyAPI.HTTPError{Err: err.Error(), Message: "invalid playbook id"}) - } - runUUID, err := uuid.Parse(runID) if err != nil { return c.JSON(http.StatusBadRequest, dutyAPI.HTTPError{Err: err.Error(), Message: "invalid run id"}) } - if err := ApproveRun(ctx, playbookUUID, runUUID); err != nil { + if err := ApproveRun(ctx, runUUID); err != nil { return dutyAPI.WriteError(c, err) } return c.JSON(http.StatusOK, dutyAPI.HTTPSuccess{Message: "playbook run approved"}) } -func ApproveRun(ctx context.Context, playbookID, runID uuid.UUID) error { - playbook, err := db.FindPlaybook(ctx, playbookID) +func ApproveRun(ctx context.Context, runID uuid.UUID) error { + run, err := db.FindPlaybookRun(ctx, runID) if err != nil { - return api.Errorf(api.EINTERNAL, "something went wrong while finding playbook(id=%s)", playbookID).WithDebugInfo("db.FindPlaybook(id=%s): %v", playbookID, err) - } else if playbook == nil { - return api.Errorf(api.ENOTFOUND, "playbook(id=%s) not found", playbookID) + return api.Errorf(api.EINTERNAL, "something went wrong while finding run (id=%s)", runID).WithDebugInfo("db.FindPlaybookRun(id=%s): %v", runID, err) + } else if run == nil { + return api.Errorf(api.ENOTFOUND, "playbook run (id=%s) not found", runID) } - return approveRun(ctx, playbook, runID) + return approveRun(ctx, run) } -func requiresApproval(playbook *models.Playbook) bool { - playbookV1, _ := v1.PlaybookFromModel(*playbook) - return playbookV1.Spec.Approval != nil && !playbookV1.Spec.Approval.Approvers.Empty() +func requiresApproval(spec v1.PlaybookSpec) bool { + return spec.Approval != nil && !spec.Approval.Approvers.Empty() } -func approveRun(ctx context.Context, playbook *models.Playbook, runID uuid.UUID) error { +func approveRun(ctx context.Context, run *models.PlaybookRun) error { approver := ctx.User() - playbookV1, err := v1.PlaybookFromModel(*playbook) - if err != nil { - return api.Errorf(api.EINTERNAL, "something went wrong").WithDebugInfo("v1.PlaybookFromModel: %v", err) + var spec v1.PlaybookSpec + if err := json.Unmarshal(run.Spec, &spec); err != nil { + return err } - if playbookV1.Spec.Approval == nil || playbookV1.Spec.Approval.Approvers.Empty() { + if spec.Approval == nil || spec.Approval.Approvers.Empty() { return api.Errorf(api.EINVALID, "this playbook does not require approval") } @@ -73,19 +67,19 @@ func approveRun(ctx context.Context, playbook *models.Playbook, runID uuid.UUID) } approval := models.PlaybookApproval{ - RunID: runID, + RunID: run.ID, } - if collections.Contains(playbookV1.Spec.Approval.Approvers.People, approver.Email) { + if collections.Contains(spec.Approval.Approvers.People, approver.Email) { approval.PersonID = &approver.ID } else { teams, err := db.GetTeamsForUser(ctx, approver.ID.String()) if err != nil { - return api.Errorf(api.EINTERNAL, "something went wrong").WithDebugInfo("db.GetTeamIDsForUser(id=%s): %v", approver.ID, err) + return api.Errorf(api.EINTERNAL, "something went wrong").WithDebugInfo("db.GetTeamsForUser(id=%s): %v", approver.ID, err) } for _, team := range teams { - if collections.Contains(playbookV1.Spec.Approval.Approvers.Teams, team.Name) { + if collections.Contains(spec.Approval.Approvers.Teams, team.Name) { approval.TeamID = &team.ID break } @@ -97,7 +91,7 @@ func approveRun(ctx context.Context, playbook *models.Playbook, runID uuid.UUID) } if err := db.SavePlaybookRunApproval(ctx, approval); err != nil { - return api.Errorf(api.EINTERNAL, "something went wrong while approving").WithDebugInfo("db.ApprovePlaybookRun(runID=%s, approverID=%s): %v", runID, approver.ID, err) + return api.Errorf(api.EINTERNAL, "something went wrong while approving").WithDebugInfo("db.SavePlaybookRunApproval(runID=%s, approverID=%s): %v", run.ID, approver.ID, err) } return nil diff --git a/playbook/events.go b/playbook/events.go index c8439d55c..2b71e78e7 100644 --- a/playbook/events.go +++ b/playbook/events.go @@ -175,6 +175,7 @@ func (t *playbookScheduler) Handle(ctx context.Context, event models.Event) erro run := models.PlaybookRun{ PlaybookID: p.ID, Status: models.PlaybookRunStatusPending, + Spec: p.Spec, } if playbook.Spec.Approval == nil || playbook.Spec.Approval.Approvers.Empty() { diff --git a/playbook/playbook.go b/playbook/playbook.go index db1922210..8258da587 100644 --- a/playbook/playbook.go +++ b/playbook/playbook.go @@ -182,7 +182,7 @@ func saveRunAsConfigChange(ctx context.Context, playbook *models.Playbook, run m details := map[string]any{ "parameters": parameters, - "spec": playbook.Spec, + "spec": run.Spec, } detailsJSON, err := json.Marshal(details) if err != nil { @@ -215,14 +215,18 @@ func savePlaybookRun(ctx context.Context, playbook *models.Playbook, run *models defer tx.Rollback() ctx = ctx.WithDB(tx, ctx.Pool()) - if err := ctx.DB().Create(run).Error; err != nil { return ctx.Oops("db").Wrap(err) } - if requiresApproval(playbook) { + var spec v1.PlaybookSpec + if err := json.Unmarshal(run.Spec, &spec); err != nil { + return ctx.Oops().Wrap(err) + } + + if requiresApproval(spec) { // Attempt to auto approve run - if err := approveRun(ctx, playbook, run.ID); err != nil { + if err := ApproveRun(ctx, run.ID); err != nil { switch dutyAPI.ErrorCode(err) { case dutyAPI.EFORBIDDEN, dutyAPI.EINVALID: // ignore these errors diff --git a/playbook/runner/agent.go b/playbook/runner/agent.go index afc5be585..8e5e7f8a5 100644 --- a/playbook/runner/agent.go +++ b/playbook/runner/agent.go @@ -95,7 +95,7 @@ func getActionForAgent(ctx context.Context, agent *models.Agent) (*ActionForAgen return nil, ctx.Oops().Wrapf(err, "failed to template env") } - spec, err := getActionSpec(playbook, step.Name) + spec, err := getActionSpec(run, step.Name) if err != nil { return nil, ctx.Oops().Wrap(err) } diff --git a/playbook/runner/runner.go b/playbook/runner/runner.go index f6368b2f0..7d7b9dbfd 100644 --- a/playbook/runner/runner.go +++ b/playbook/runner/runner.go @@ -87,11 +87,12 @@ func findNextActionWithFilter(actions []v1.PlaybookAction) *v1.PlaybookAction { return nil } -func getActionSpec(playbook *models.Playbook, name string) (*v1.PlaybookAction, error) { +func getActionSpec(playbook *models.PlaybookRun, name string) (*v1.PlaybookAction, error) { var spec v1.PlaybookSpec if err := json.Unmarshal(playbook.Spec, &spec); err != nil { return nil, err } + for _, action := range spec.Actions { if action.Name == name { action.PlaybookID = playbook.ID.String() @@ -197,7 +198,7 @@ func ScheduleRun(ctx context.Context, run models.PlaybookRun) error { } var playbookSpec v1.PlaybookSpec - if err := json.Unmarshal(playbook.Spec, &playbookSpec); err != nil { + if err := json.Unmarshal(run.Spec, &playbookSpec); err != nil { return ctx.Oops().Wrap(err) } diff --git a/playbook/runner/template.go b/playbook/runner/template.go index f838f8f98..35e498746 100644 --- a/playbook/runner/template.go +++ b/playbook/runner/template.go @@ -28,7 +28,7 @@ func CreateTemplateEnv(ctx context.Context, playbook *models.Playbook, run *mode oops := oops.With(models.ErrorContext(playbook, run)...) var spec v1.PlaybookSpec - if err := json.Unmarshal(playbook.Spec, &spec); err != nil { + if err := json.Unmarshal(run.Spec, &spec); err != nil { return templateEnv, oops.Wrapf(err, "invalid playbook spec") }