Skip to content

Commit ac39085

Browse files
Abzaekchenrui333
andauthored
fix: POST /api/plan panics when ATLANTIS_ENABLE_POLICY_CHECKS=true (#6484)
* fix: handle API plan policy checks When API plan command building expands one input project into plan and policy_check contexts, keep workflow hooks tied to plan contexts, route policy_check contexts to the policy-check runner, and return policy-check results to API callers. Load existing pull status for PR-backed API requests before building plan commands so generated policy_check contexts preserve sticky policy approvals. Stop /api/apply before apply command building when the pre-apply plan phase reports a policy-check failure. Fixes #6449 Assisted-by: OpenAI <noreply@openai.com> Signed-off-by: Rui Chen <rui@chenrui.dev> * fix: preserve API apply pull status semantics Refresh seeded API apply PullStatus metadata to the current request pull while preserving existing per-project policy status, and let multi-project API apply continue into per-project validation after mixed policy-check results. Assisted-by: OpenAI <noreply@openai.com> Signed-off-by: Rui Chen <rui@chenrui.dev> --------- Signed-off-by: Rui Chen <rui@chenrui.dev> Co-authored-by: Rui Chen <rui@chenrui.dev>
1 parent 81fcae5 commit ac39085

3 files changed

Lines changed: 402 additions & 11 deletions

File tree

server/controllers/api_controller.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ type APIController struct {
6767
// apply requirements like 'mergeable' and 'approved' evaluate against real
6868
// VCS state instead of always failing.
6969
PullReqStatusFetcher vcs.PullReqStatusFetcher
70+
// PullStatusFetcher is optional. When set and the API request supplies a PR
71+
// number, it is used to populate command.Context.PullStatus so generated
72+
// policy_check contexts can preserve existing policy approvals.
73+
PullStatusFetcher events.PullStatusFetcher
7074
// LivePullHeadFetcher is optional for tests. In production it is used for
7175
// PR-backed API requests to seed live PR identity data such as base branch.
7276
LivePullHeadFetcher events.LivePullHeadFetcher
@@ -736,15 +740,13 @@ func (a *APIController) apiPlan(request *APIRequest, ctx *command.Context) (*com
736740
var planCmds []command.ProjectContext
737741
var planCC []*events.CommentCommand
738742
var policyCmds []command.ProjectContext
739-
var policyCC []*events.CommentCommand
740743
for i, cmd := range cmds {
741744
switch cmd.CommandName {
742745
case command.Plan:
743746
planCmds = append(planCmds, cmd)
744747
planCC = append(planCC, cc[i])
745748
case command.PolicyCheck:
746749
policyCmds = append(policyCmds, cmd)
747-
policyCC = append(policyCC, cc[i])
748750
default:
749751
return nil, fmt.Errorf("%s is not supported", cmd.CommandName)
750752
}
@@ -772,15 +774,7 @@ func (a *APIController) apiPlan(request *APIRequest, ctx *command.Context) (*com
772774
return result, nil
773775
}
774776

775-
for i, cmd := range policyCmds {
776-
if !ctx.PreWorkflowHooksAlreadyRun {
777-
err = a.PreWorkflowHooksCommandRunner.RunPreHooks(ctx, policyCC[i])
778-
if err != nil {
779-
if a.FailOnPreWorkflowHookError {
780-
return nil, err
781-
}
782-
}
783-
}
777+
for _, cmd := range policyCmds {
784778
if a.ProjectPolicyCheckCommandRunner == nil {
785779
return nil, fmt.Errorf("policy check runner is not configured")
786780
}
@@ -922,6 +916,7 @@ func seedPullStatusFromPlanResult(ctx *command.Context, result *command.Result)
922916
if ctx.PullStatus == nil {
923917
ctx.PullStatus = &models.PullStatus{Pull: ctx.Pull}
924918
}
919+
ctx.PullStatus.Pull = ctx.Pull
925920
for _, projectResult := range result.ProjectResults {
926921
if projectResult.Command != command.Plan && projectResult.Command != command.PolicyCheck && projectResult.Command != command.ApprovePolicies {
927922
continue
@@ -1084,6 +1079,7 @@ func (a *APIController) apiParseAndValidate(r *http.Request) (*APIRequest, *comm
10841079
ExactProjectNameMatching: true,
10851080
}
10861081
a.populatePullRequestStatus(ctx)
1082+
a.populatePullStatus(ctx)
10871083
return &request, ctx, http.StatusOK, nil
10881084
}
10891085

@@ -1102,6 +1098,20 @@ func (a *APIController) populatePullRequestStatus(ctx *command.Context) {
11021098
ctx.PullRequestStatus = status
11031099
}
11041100

1101+
func (a *APIController) populatePullStatus(ctx *command.Context) {
1102+
if ctx.Pull.Num <= 0 || a.PullStatusFetcher == nil {
1103+
return
1104+
}
1105+
1106+
status, err := a.PullStatusFetcher.GetPullStatus(ctx.Pull)
1107+
if err != nil {
1108+
ctx.PullStatus = nil
1109+
ctx.Log.Warn("unable to fetch pull status: %s", err)
1110+
return
1111+
}
1112+
ctx.PullStatus = status
1113+
}
1114+
11051115
// Remediate handles POST /api/drift/remediate requests.
11061116
// It executes drift remediation (plan or apply) for the specified projects.
11071117
// This is an authenticated endpoint that requires the API secret.

0 commit comments

Comments
 (0)