Repository: diggerhq/digger
Pull request #463 (2023-07-20 11:30:55)

add policy plan results


Patch:
diff --git a/pkg/core/policy/policy.go b/pkg/core/policy/policy.go
index cd47571a..9d5e0615 100644
--- a/pkg/core/policy/policy.go
+++ b/pkg/core/policy/policy.go
@@ -10,5 +10,5 @@ type Provider interface {
 
 type Checker interface {
 	CheckAccessPolicy(ciService ci.OrgService, SCMOrganisation string, SCMrepository string, projectname string, command string, requestedBy string) (bool, error)
-	CheckPlanPolicy(SCMrepository string, projectname string, planOutput string) (bool, error)
+	CheckPlanPolicy(SCMrepository string, projectname string, planOutput string) (bool, []string, error)
 }
diff --git a/pkg/digger/digger.go b/pkg/digger/digger.go
index 8e562b25..ec15688c 100644
--- a/pkg/digger/digger.go
+++ b/pkg/digger/digger.go
@@ -171,19 +171,24 @@ func RunCommandsPerProject(
 						if err != nil {
 							log.Printf("Failed to report plan. %v", err)
 						}
-						planIsAllowed, err := policyChecker.CheckPlanPolicy(SCMrepository, projectCommands.ProjectName, planJsonOutput)
+						planIsAllowed, messages, err := policyChecker.CheckPlanPolicy(SCMrepository, projectCommands.ProjectName, planJsonOutput)
 						if err != nil {
 							log.Printf("failed to validate plan %v", err)
 							return false, false, fmt.Errorf("failed to validated plan %v", err)
 						}
+						planPolicyFormatter := utils.AsCollapsibleComment(fmt.Sprintf("Terraform plan validation check (%v)", projectCommands.ProjectName))
 						if !planIsAllowed {
-							formatter := utils.AsCollapsibleComment("Terraform plan failed validation checks")
-							err = reporter.Report("Terraform plan failed validation checks", formatter)
+							planReportMessage := "Terraform plan failed validation checks :x:\n"
+							planReportMessage = planReportMessage + "    " + strings.Join(messages, "   \n")
+							err = reporter.Report(planReportMessage, planPolicyFormatter)
+
 							if err != nil {
 								log.Printf("Failed to report plan. %v", err)
 							}
 							log.Printf("Plan is not allowed")
 							return false, false, fmt.Errorf("Plan is not allowed")
+						} else {
+							reporter.Report("Terraform plan validation checks succeeded :white_check_mark:", planPolicyFormatter)
 						}
 					}
 					err := prService.SetStatus(prNumber, "success", projectCommands.ProjectName+"/plan")
@@ -352,7 +357,8 @@ func RunCommandForProject(
 				log.Printf("Failed to run digger plan command. %v", err)
 				return fmt.Errorf("failed to run digger plan command. %v", err)
 			}
-			planIsAllowed, err := policyChecker.CheckPlanPolicy(SCMrepository, commands.ProjectName, planJsonOutput)
+			planIsAllowed, messages, err := policyChecker.CheckPlanPolicy(SCMrepository, commands.ProjectName, planJsonOutput)
+			fmt.Printf(strings.Join(messages, "\n"))
 			if err != nil {
 				log.Printf("failed to validate plan %v", err)
 				return fmt.Errorf("failed to validated plan %v", err)
diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go
index 16d98dfe..e05b53c0 100644
--- a/pkg/policy/policy.go
+++ b/pkg/policy/policy.go
@@ -28,8 +28,8 @@ func (p NoOpPolicyChecker) CheckAccessPolicy(_ ci.OrgService, _ string, _ string
 	return true, nil
 }
 
-func (p NoOpPolicyChecker) CheckPlanPolicy(_ string, _ string, _ string) (bool, error) {
-	return true, nil
+func (p NoOpPolicyChecker) CheckPlanPolicy(_ string, _ string, _ string) (bool, []string, error) {
+	return true, nil, nil
 }
 
 func getAccessPolicyForOrganisation(p *DiggerHttpPolicyProvider) (string, *http.Response, error) {
@@ -267,17 +267,17 @@ func (p DiggerPolicyChecker) CheckAccessPolicy(ciService ci.OrgService, SCMOrgan
 	return true, nil
 }
 
-func (p DiggerPolicyChecker) CheckPlanPolicy(SCMrepository string, projectName string, planOutput string) (bool, error) {
+func (p DiggerPolicyChecker) CheckPlanPolicy(SCMrepository string, projectName string, planOutput string) (bool, []string, error) {
 	// TODO: Get rid of organisation if its not needed
 	organisation := p.PolicyProvider.GetOrganisation()
 	policy, err := p.PolicyProvider.GetPlanPolicy(organisation, SCMrepository, projectName)
 	if err != nil {
-		return false, fmt.Errorf("failed get plan policy: %v", err)
+		return false, nil, fmt.Errorf("failed get plan policy: %v", err)
 	}
 	var parsedPlanOutput map[string]interface{}
 	err = json.Unmarshal([]byte(planOutput), &parsedPlanOutput)
 	if err != nil {
-		return false, fmt.Errorf("failed to parse json terraform output to map: %v", err)
+		return false, nil, fmt.Errorf("failed to parse json terraform output to map: %v", err)
 	}
 
 	input := map[string]interface{}{
@@ -285,7 +285,8 @@ func (p DiggerPolicyChecker) CheckPlanPolicy(SCMrepository string, projectName s
 	}
 
 	if policy == "" {
-		return true, nil
+		fmt.Printf("No plan policies found, succeeding")
+		return true, nil, nil
 	}
 
 	ctx := context.Background()
@@ -296,28 +297,36 @@ func (p DiggerPolicyChecker) CheckPlanPolicy(SCMrepository string, projectName s
 	).PrepareForEval(ctx)
 
 	if err != nil {
-		return false, err
+		return false, nil, err
 	}
 
 	results, err := query.Eval(ctx, rego.EvalInput(input))
 	if len(results) == 0 || len(results[0].Expressions) == 0 {
-		return false, fmt.Errorf("no result found")
+		return false, nil, fmt.Errorf("no result found")
 	}
 
 	expressions := results[0].Expressions
 
+	decisionsResult := make([]string, 0)
 	for _, expression := range expressions {
 		decisions, ok := expression.Value.([]interface{})
+
 		if !ok {
-			return false, fmt.Errorf("decision is not a slice of interfaces")
+			return false, nil, fmt.Errorf("decision is not a slice of interfaces")
 		}
 		if len(decisions) > 0 {
 			for _, d := range decisions {
+				decisionsResult = append(decisionsResult, d.(string))
 				fmt.Printf("denied: %v\n", d)
 			}
-			return false, nil
+
 		}
+
 	}
 
-	return true, nil
+	if len(decisionsResult) > 0 {
+		return false, decisionsResult, nil
+	}
+
+	return true, []string{}, nil
 }
diff --git a/pkg/utils/mocks.go b/pkg/utils/mocks.go
index 72606113..26a04fc4 100644
--- a/pkg/utils/mocks.go
+++ b/pkg/utils/mocks.go
@@ -48,8 +48,8 @@ func (t MockPolicyChecker) CheckAccessPolicy(ciService ci.OrgService, SCMOrganis
 	return false, nil
 }
 
-func (t MockPolicyChecker) CheckPlanPolicy(projectName string, command string, requestedBy string) (bool, error) {
-	return false, nil
+func (t MockPolicyChecker) CheckPlanPolicy(projectName string, command string, requestedBy string) (bool, []string, error) {
+	return false, nil, nil
 }
 
 type MockPullRequestManager struct {


Test patch:
diff --git a/pkg/policy/policy_test.go b/pkg/policy/policy_test.go
index 6ce493f7..7526f83a 100644
--- a/pkg/policy/policy_test.go
+++ b/pkg/policy/policy_test.go
@@ -220,7 +220,7 @@ func TestDiggerPlanPolicyChecker_Check(t *testing.T) {
 			var p = &DiggerPolicyChecker{
 				PolicyProvider: tt.fields.PolicyProvider,
 			}
-			got, err := p.CheckPlanPolicy("", "", tt.planJsonOutput)
+			got, _, err := p.CheckPlanPolicy("", "", tt.planJsonOutput)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("DiggerPolicyChecker.CheckPlanPolicy() error = %v, wantErr %v", err, tt.wantErr)
 				return