1
2
3
4 package framework
5
6 import (
7 "fmt"
8 "sort"
9 "strings"
10
11 "sigs.k8s.io/kustomize/kyaml/yaml"
12 )
13
14
15 type Severity string
16
17 const (
18
19 Error Severity = "error"
20
21 Warning Severity = "warning"
22
23 Info Severity = "info"
24 )
25
26
27 type Result struct {
28
29 Message string `yaml:"message,omitempty" json:"message,omitempty"`
30
31
32 Severity Severity `yaml:"severity,omitempty" json:"severity,omitempty"`
33
34
35
36 ResourceRef *yaml.ResourceIdentifier `yaml:"resourceRef,omitempty" json:"resourceRef,omitempty"`
37
38
39 Field *Field `yaml:"field,omitempty" json:"field,omitempty"`
40
41
42 File *File `yaml:"file,omitempty" json:"file,omitempty"`
43
44
45
46 Tags map[string]string `yaml:"tags,omitempty" json:"tags,omitempty"`
47 }
48
49
50 func (i Result) String() string {
51 identifier := i.ResourceRef
52 var idStringList []string
53 if identifier != nil {
54 if identifier.APIVersion != "" {
55 idStringList = append(idStringList, identifier.APIVersion)
56 }
57 if identifier.Kind != "" {
58 idStringList = append(idStringList, identifier.Kind)
59 }
60 if identifier.Namespace != "" {
61 idStringList = append(idStringList, identifier.Namespace)
62 }
63 if identifier.Name != "" {
64 idStringList = append(idStringList, identifier.Name)
65 }
66 }
67 formatString := "[%s]"
68 severity := i.Severity
69
70 if i.Severity == "" {
71 severity = Info
72 }
73 list := []interface{}{severity}
74 if len(idStringList) > 0 {
75 formatString += " %s"
76 list = append(list, strings.Join(idStringList, "/"))
77 }
78 if i.Field != nil {
79 formatString += " %s"
80 list = append(list, i.Field.Path)
81 }
82 formatString += ": %s"
83 list = append(list, i.Message)
84 return fmt.Sprintf(formatString, list...)
85 }
86
87
88 type File struct {
89
90
91 Path string `yaml:"path,omitempty" json:"path,omitempty"`
92
93
94
95 Index int `yaml:"index,omitempty" json:"index,omitempty"`
96 }
97
98
99 type Field struct {
100
101 Path string `yaml:"path,omitempty" json:"path,omitempty"`
102
103
104 CurrentValue interface{} `yaml:"currentValue,omitempty" json:"currentValue,omitempty"`
105
106
107 ProposedValue interface{} `yaml:"proposedValue,omitempty" json:"proposedValue,omitempty"`
108 }
109
110 type Results []*Result
111
112
113 func (e Results) Error() string {
114 var msgs []string
115 for _, i := range e {
116 msgs = append(msgs, i.String())
117 }
118 return strings.Join(msgs, "\n\n")
119 }
120
121
122 func (e Results) ExitCode() int {
123 for _, i := range e {
124 if i.Severity == Error {
125 return 1
126 }
127 }
128 return 0
129 }
130
131
132 func (e Results) Sort() {
133 sort.SliceStable(e, func(i, j int) bool {
134 if fileLess(e, i, j) != 0 {
135 return fileLess(e, i, j) < 0
136 }
137 if severityLess(e, i, j) != 0 {
138 return severityLess(e, i, j) < 0
139 }
140 return resultToString(*e[i]) < resultToString(*e[j])
141 })
142 }
143
144 func severityLess(items Results, i, j int) int {
145 severityToNumber := map[Severity]int{
146 Error: 0,
147 Warning: 1,
148 Info: 2,
149 }
150
151 severityLevelI, found := severityToNumber[items[i].Severity]
152 if !found {
153 severityLevelI = 3
154 }
155 severityLevelJ, found := severityToNumber[items[j].Severity]
156 if !found {
157 severityLevelJ = 3
158 }
159 return severityLevelI - severityLevelJ
160 }
161
162 func fileLess(items Results, i, j int) int {
163 var fileI, fileJ File
164 if items[i].File == nil {
165 fileI = File{}
166 } else {
167 fileI = *items[i].File
168 }
169 if items[j].File == nil {
170 fileJ = File{}
171 } else {
172 fileJ = *items[j].File
173 }
174 if fileI.Path != fileJ.Path {
175 if fileI.Path < fileJ.Path {
176 return -1
177 } else {
178 return 1
179 }
180 }
181 return fileI.Index - fileJ.Index
182 }
183
184 func resultToString(item Result) string {
185 return fmt.Sprintf("resource-ref:%s,field:%s,message:%s",
186 item.ResourceRef, item.Field, item.Message)
187 }
188
View as plain text