-
Notifications
You must be signed in to change notification settings - Fork 783
/
generate.go
160 lines (137 loc) · 4.86 KB
/
generate.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
package apply
import (
"reflect"
report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha2"
client "github.com/kyverno/kyverno/pkg/dclient"
sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/log"
)
// generateCLIRaw merges all policy reports to a singe cluster policy report
func generateCLIRaw(reports []*unstructured.Unstructured) (*unstructured.Unstructured, error) {
for _, report := range reports {
if report.GetNamespace() != "" {
report.SetNamespace("")
}
}
return mergeClusterReport(reports)
}
// generateToCluster updates the existing policy reports in the cluster
// creates new report if not exist
func generateToCluster(dClient *client.Client, reports []*unstructured.Unstructured) {
var clusterReports, namespaceReports []*unstructured.Unstructured
for _, report := range reports {
if report.GetNamespace() == "" {
clusterReports = append(clusterReports, report)
} else {
namespaceReports = append(namespaceReports, report)
}
}
if clusterReport, err := mergeClusterReport(clusterReports); err != nil {
log.Log.V(3).Info("failed to merge cluster report", "error", err)
} else {
if err := updateReport(dClient, clusterReport); err != nil {
log.Log.V(3).Info("failed to update policy report", "report", clusterReport.GetName(), "error", err)
}
}
for _, report := range namespaceReports {
if err := updateReport(dClient, report); err != nil {
log.Log.V(3).Info("failed to update policy report", "report", report.GetName(), "error", err)
}
}
}
func updateReport(dClient *client.Client, new *unstructured.Unstructured) error {
old, err := dClient.GetResource(new.GetAPIVersion(), new.GetKind(), new.GetNamespace(), new.GetName())
if err != nil {
if apierrors.IsNotFound(err) {
if _, err := dClient.CreateResource(new.GetAPIVersion(), new.GetKind(), new.GetNamespace(), new, false); err != nil {
return err
}
}
return err
}
oldResults, _, err := unstructured.NestedSlice(old.UnstructuredContent(), "results")
if err != nil {
log.Log.V(3).Info("failed to get results entry", "error", err)
}
newResults, _, err := unstructured.NestedSlice(new.UnstructuredContent(), "results")
if err != nil {
log.Log.V(3).Info("failed to get results entry", "error", err)
}
if reflect.DeepEqual(oldResults, newResults) {
log.Log.V(3).Info("policy report unchanged", "name", new.GetName())
return nil
}
_, err = dClient.UpdateResource(new.GetAPIVersion(), new.GetKind(), new.GetNamespace(), new, false)
return err
}
func mergeClusterReport(reports []*unstructured.Unstructured) (*unstructured.Unstructured, error) {
var resultsEntry []interface{}
res := &unstructured.Unstructured{}
res.SetName(clusterpolicyreport)
res.SetKind("ClusterPolicyReport")
res.SetAPIVersion(report.SchemeGroupVersion.String())
for _, report := range reports {
if report.GetNamespace() != "" {
// skip namespace report
continue
}
mergeResults(report, &resultsEntry)
}
if err := unstructured.SetNestedSlice(res.Object, resultsEntry, "results"); err != nil {
return nil, sanitizederror.NewWithError("failed to set results entry", err)
}
summary := updateSummary(resultsEntry)
if err := unstructured.SetNestedField(res.Object, summary, "summary"); err != nil {
return nil, sanitizederror.NewWithError("failed to set summary", err)
}
return res, nil
}
func mergeResults(report *unstructured.Unstructured, results *[]interface{}) {
entries, ok, err := unstructured.NestedSlice(report.UnstructuredContent(), "results")
if err != nil {
log.Log.V(3).Info("failed to get results entry", "report", report.GetName(), "error", err)
}
if ok {
*results = append(*results, entries...)
}
}
func updateSummary(results []interface{}) map[string]interface{} {
summary := make(map[string]interface{})
status := []string{report.StatusPass, report.StatusFail, report.StatusError, report.StatusSkip, report.StatusWarn}
for i := 0; i < 5; i++ {
if _, ok := summary[status[i]].(int64); !ok {
summary[status[i]] = int64(0)
}
}
for _, result := range results {
typedResult, ok := result.(map[string]interface{})
if !ok {
continue
}
switch typedResult["result"].(string) {
case report.StatusPass:
pass, _ := summary[report.StatusPass].(int64)
pass++
summary[report.StatusPass] = pass
case report.StatusFail:
fail, _ := summary[report.StatusFail].(int64)
fail++
summary[report.StatusFail] = fail
case report.StatusWarn:
warn, _ := summary[report.StatusWarn].(int64)
warn++
summary[report.StatusWarn] = warn
case report.StatusError:
e, _ := summary[report.StatusError].(int64)
e++
summary[report.StatusError] = e
case report.StatusSkip:
skip, _ := summary[report.StatusSkip].(int64)
skip++
summary[report.StatusSkip] = skip
}
}
return summary
}