-
Notifications
You must be signed in to change notification settings - Fork 769
/
backend_taskStage_policyEvaluation.go
162 lines (137 loc) · 4.53 KB
/
backend_taskStage_policyEvaluation.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package cloud
import (
"fmt"
"strings"
"github.com/hashicorp/go-tfe"
)
type policyEvaluationSummary struct {
unreachable bool
pending int
failed int
passed int
}
type Symbol rune
const (
Tick Symbol = '\u2713'
Cross Symbol = '\u00d7'
Warning Symbol = '\u24be'
Arrow Symbol = '\u2192'
DownwardArrow Symbol = '\u21b3'
)
type policyEvaluationSummarizer struct {
finished bool
cloud *Cloud
counter int
}
func newPolicyEvaluationSummarizer(b *Cloud, ts *tfe.TaskStage) taskStageSummarizer {
if len(ts.PolicyEvaluations) == 0 {
return nil
}
return &policyEvaluationSummarizer{
finished: false,
cloud: b,
}
}
func (pes *policyEvaluationSummarizer) Summarize(context *IntegrationContext, output IntegrationOutputWriter, ts *tfe.TaskStage) (bool, *string, error) {
if pes.counter == 0 {
output.Output("[bold]OPA Policy Evaluation\n")
pes.counter++
}
if pes.finished {
return false, nil, nil
}
counts := summarizePolicyEvaluationResults(ts.PolicyEvaluations)
if counts.pending != 0 {
pendingMessage := "Evaluating ... "
return true, &pendingMessage, nil
}
if counts.unreachable {
output.Output("Skipping policy evaluation.")
output.End()
return false, nil, nil
}
// Print out the summary
if err := pes.taskStageWithPolicyEvaluation(context, output, ts.PolicyEvaluations); err != nil {
return false, nil, err
}
// Mark as finished
pes.finished = true
return false, nil, nil
}
func summarizePolicyEvaluationResults(policyEvaluations []*tfe.PolicyEvaluation) *policyEvaluationSummary {
var pendingCount, errCount, passedCount int
for _, policyEvaluation := range policyEvaluations {
switch policyEvaluation.Status {
case "unreachable":
return &policyEvaluationSummary{
unreachable: true,
}
case "running", "pending", "queued":
pendingCount++
case "passed":
passedCount++
default:
// Everything else is a failure
errCount++
}
}
return &policyEvaluationSummary{
unreachable: false,
pending: pendingCount,
failed: errCount,
passed: passedCount,
}
}
func (pes *policyEvaluationSummarizer) taskStageWithPolicyEvaluation(context *IntegrationContext, output IntegrationOutputWriter, policyEvaluation []*tfe.PolicyEvaluation) error {
var result, message string
// Currently only one policy evaluation supported : OPA
for _, polEvaluation := range policyEvaluation {
if polEvaluation.Status == tfe.PolicyEvaluationPassed {
message = "[dim] This result means that all OPA policies passed and the protected behavior is allowed"
result = fmt.Sprintf("[green]%s", strings.ToUpper(string(tfe.PolicyEvaluationPassed)))
if polEvaluation.ResultCount.AdvisoryFailed > 0 {
result += " (with advisory)"
}
} else {
message = "[dim] This result means that one or more OPA policies failed. More than likely, this was due to the discovery of violations by the main rule and other sub rules"
result = fmt.Sprintf("[red]%s", strings.ToUpper(string(tfe.PolicyEvaluationFailed)))
}
output.Output(fmt.Sprintf("[bold]%c%c Overall Result: %s", Arrow, Arrow, result))
output.Output(message)
total := getPolicyCount(polEvaluation.ResultCount)
output.Output(fmt.Sprintf("%d policies evaluated\n", total))
policyOutcomes, err := pes.cloud.client.PolicySetOutcomes.List(context.StopContext, polEvaluation.ID, nil)
if err != nil {
return err
}
for i, out := range policyOutcomes.Items {
output.Output(fmt.Sprintf("%c Policy set %d: [bold]%s (%d)", Arrow, i+1, out.PolicySetName, len(out.Outcomes)))
for _, outcome := range out.Outcomes {
output.Output(fmt.Sprintf(" %c Policy name: [bold]%s", DownwardArrow, outcome.PolicyName))
switch outcome.Status {
case "passed":
output.Output(fmt.Sprintf(" | [green][bold]%c Passed", Tick))
case "failed":
if outcome.EnforcementLevel == tfe.EnforcementAdvisory {
output.Output(fmt.Sprintf(" | [blue][bold]%c Advisory", Warning))
} else {
output.Output(fmt.Sprintf(" | [red][bold]%c Failed", Cross))
}
}
if outcome.Description != "" {
output.Output(fmt.Sprintf(" | [dim]%s", outcome.Description))
} else {
output.Output(" | [dim]No description available")
}
}
}
}
return nil
}
func getPolicyCount(resultCount *tfe.PolicyResultCount) int {
return resultCount.AdvisoryFailed + resultCount.MandatoryFailed + resultCount.Errored + resultCount.Passed
}