Skip to content

Commit 4121d16

Browse files
authored
support for apply policies (#2538)
* support for apply policies
1 parent 31d84f9 commit 4121d16

File tree

7 files changed

+354
-1
lines changed

7 files changed

+354
-1
lines changed

backend/models/policies.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const (
66
POLICY_TYPE_ACCESS = "access"
77
POLICY_TYPE_PLAN = "plan"
88
POLICY_TYPE_DRIFT = "drift"
9+
POLICY_TYPE_APPLY = "apply"
910
)
1011

1112
type Policy struct {

cli/pkg/digger/digger.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func RunJobs(jobs []orchestrator.Job, prService ci.PullRequestService, orgServic
105105

106106
if !allowedToPerformCommand {
107107
msg := reportPolicyError(job.ProjectName, command, job.RequestedBy, reporter)
108-
slog.Warn("Skipping command ... %v for project %v", command, job.ProjectName)
108+
slog.Warn("Skipping command ...", "command", command, "projectName", job.ProjectName)
109109
slog.Warn("Received policy error", "message", msg)
110110
appliesPerProject[job.ProjectName] = false
111111
continue
@@ -429,6 +429,71 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org
429429
return nil, msg, errors.New(msg)
430430
}
431431

432+
// Check apply policy before apply
433+
// Try to retrieve the terraform plan JSON if plan storage is configured
434+
var terraformPlanJson string
435+
if os.Getenv("PLAN_UPLOAD_DESTINATION") != "" {
436+
slog.Debug("Plan storage configured, attempting to retrieve plan for apply policy check")
437+
retrievedPlanJson, err := executor.RetrievePlanJson()
438+
if err != nil {
439+
slog.Warn("Failed to retrieve plan JSON for apply policy check, proceeding without plan data", "error", err)
440+
} else {
441+
terraformPlanJson = retrievedPlanJson
442+
slog.Debug("Successfully retrieved plan JSON for apply policy check", "planLength", len(terraformPlanJson))
443+
}
444+
} else {
445+
slog.Debug("Plan storage not configured, apply policy will not have terraform plan data")
446+
}
447+
448+
slog.Debug("Calling CheckApplyPolicy",
449+
"organisation", SCMOrganisation,
450+
"repository", SCMrepository,
451+
"projectName", job.ProjectName,
452+
"projectDir", job.ProjectDir,
453+
"command", command,
454+
"requestedBy", requestedBy,
455+
"hasTerraformPlan", terraformPlanJson != "")
456+
allowedToApplyByApplyPolicy, applyPolicyViolations, err := policyChecker.CheckApplyPolicy(SCMOrganisation, SCMrepository, job.ProjectName, job.ProjectDir, command, job.PullRequestNumber, requestedBy, teams, approvals, approvalTeams, planPolicyViolations, terraformPlanJson)
457+
slog.Debug("CheckApplyPolicy result",
458+
"allowed", allowedToApplyByApplyPolicy,
459+
"violationsCount", len(applyPolicyViolations),
460+
"violations", applyPolicyViolations,
461+
"error", err)
462+
if err != nil {
463+
msg := fmt.Sprintf("Failed to run apply policy check before apply. %v", err)
464+
slog.Error("Failed to run apply policy check before apply", "error", err)
465+
return nil, msg, fmt.Errorf("%s", msg)
466+
}
467+
if !allowedToApplyByApplyPolicy {
468+
slog.Info("Apply policy check denied",
469+
"violationsCount", len(applyPolicyViolations),
470+
"violations", applyPolicyViolations)
471+
var applyPolicyFormatter func(report string) string
472+
summary := fmt.Sprintf("Policy violation for <b>%v - %v</b>", job.ProjectName, command)
473+
if reporter.SupportsMarkdown() {
474+
applyPolicyFormatter = reporting.AsCollapsibleComment(summary, false)
475+
} else {
476+
applyPolicyFormatter = reporting.AsComment(summary)
477+
}
478+
479+
applyPolicyReportMessage := "Terraform apply failed validation checks :x:<br>"
480+
if len(applyPolicyViolations) > 0 {
481+
preformattedMessages := make([]string, 0)
482+
for _, message := range applyPolicyViolations {
483+
preformattedMessages = append(preformattedMessages, fmt.Sprintf(" %v", message))
484+
}
485+
applyPolicyReportMessage = applyPolicyReportMessage + strings.Join(preformattedMessages, "<br>")
486+
}
487+
_, _, err = reporter.Report(applyPolicyReportMessage, applyPolicyFormatter)
488+
if err != nil {
489+
slog.Error("Failed to report apply policy violation.", "error", err)
490+
}
491+
492+
msg := fmt.Sprintf("Apply is not allowed due to policy violations")
493+
slog.Error(msg)
494+
return nil, msg, errors.New(msg)
495+
}
496+
432497
// Running apply
433498

434499
applySummary, applyPerformed, output, err := diggerExecutor.Apply()

ee/cli/pkg/policy/policy.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package policy
33
import (
44
"github.com/diggerhq/digger/libs/git_utils"
55
"github.com/samber/lo"
6+
"log/slog"
67
"os"
78
"path"
89
"path/filepath"
@@ -115,6 +116,25 @@ func (p DiggerRepoPolicyProvider) GetPlanPolicy(organisation string, repository
115116
return policy, nil
116117
}
117118

119+
func (p DiggerRepoPolicyProvider) GetApplyPolicy(organisation string, repository string, projectname string, projectDir string) (string, error) {
120+
slog.Debug("GetApplyPolicy called",
121+
"organisation", organisation,
122+
"repository", repository,
123+
"projectname", projectname,
124+
"projectDir", projectDir,
125+
"managementRepoUrl", p.ManagementRepoUrl)
126+
policy, err := p.getPolicyFileContents(repository, projectname, projectDir, "apply.rego")
127+
if err != nil {
128+
slog.Error("Error getting apply policy file", "error", err)
129+
return policy, err
130+
}
131+
slog.Debug("GetApplyPolicy result",
132+
"policyLength", len(policy),
133+
"policyEmpty", policy == "",
134+
"policyContent", policy)
135+
return policy, nil
136+
}
137+
118138
func (p DiggerRepoPolicyProvider) GetDriftPolicy() (string, error) {
119139
return "", nil
120140

libs/policy/core.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ type Provider interface {
44
GetAccessPolicy(organisation string, repository string, projectname string, projectDir string) (string, error)
55
GetPlanPolicy(organisation string, repository string, projectname string, projectDir string) (string, error)
66
GetDriftPolicy() (string, error)
7+
GetApplyPolicy(organisation string, repository string, projectname string, projectDir string) (string, error)
78
GetOrganisation() string // TODO: remove this method from here since out of place
89
}
910

@@ -12,6 +13,7 @@ type Checker interface {
1213
CheckAccessPolicy(SCMOrganisation string, SCMrepository string, projectName string, projectDir string, command string, prNumber *int, requestedBy string, teams []string, approvals []string, approvalTeams []string, planPolicyViolations []string) (bool, error)
1314
CheckPlanPolicy(SCMrepository string, SCMOrganisation string, projectname string, projectDir string, requestedBy string, teams []string, approvals []string, approvalTeams []string, planOutput string) (bool, []string, error)
1415
CheckDriftPolicy(SCMOrganisation string, SCMrepository string, projectname string) (bool, error)
16+
CheckApplyPolicy(SCMOrganisation string, SCMrepository string, projectName string, projectDir string, command string, prNumber *int, requestedBy string, teams []string, approvals []string, approvalTeams []string, planPolicyViolations []string, planOutput string) (bool, []string, error)
1517
}
1618

1719
type PolicyCheckerProvider interface {

libs/policy/mocks.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ func (t MockPolicyChecker) CheckPlanPolicy(SCMrepository string, SCMOrganisation
1414
func (t MockPolicyChecker) CheckDriftPolicy(SCMOrganisation string, SCMrepository string, projectname string) (bool, error) {
1515
return true, nil
1616
}
17+
18+
func (t MockPolicyChecker) CheckApplyPolicy(SCMOrganisation string, SCMrepository string, projectName string, projectDir string, command string, prNumber *int, requestedBy string, teams []string, approvals []string, approvalTeams []string, planPolicyViolations []string, planOutput string) (bool, []string, error) {
19+
return true, nil, nil
20+
}

0 commit comments

Comments
 (0)