1
2
3
4 package json
5
6 import (
7 "encoding/json"
8 "fmt"
9 "time"
10
11 "k8s.io/cli-runtime/pkg/genericclioptions"
12 "sigs.k8s.io/cli-utils/pkg/apply/event"
13 "sigs.k8s.io/cli-utils/pkg/common"
14 "sigs.k8s.io/cli-utils/pkg/object"
15 "sigs.k8s.io/cli-utils/pkg/object/validation"
16 "sigs.k8s.io/cli-utils/pkg/print/list"
17 "sigs.k8s.io/cli-utils/pkg/print/stats"
18 )
19
20 func NewFormatter(ioStreams genericclioptions.IOStreams,
21 _ common.DryRunStrategy) list.Formatter {
22 return &formatter{
23 ioStreams: ioStreams,
24 now: time.Now,
25 }
26 }
27
28 type formatter struct {
29 ioStreams genericclioptions.IOStreams
30 now func() time.Time
31 }
32
33 func (jf *formatter) FormatValidationEvent(ve event.ValidationEvent) error {
34
35 err := ve.Error
36 if vErr, ok := err.(*validation.Error); ok {
37 err = vErr.Unwrap()
38 }
39 if len(ve.Identifiers) == 0 {
40
41 return fmt.Errorf("invalid validation event: no identifiers: %w", err)
42 }
43 objects := make([]interface{}, len(ve.Identifiers))
44 for i, id := range ve.Identifiers {
45 objects[i] = jf.baseResourceEvent(id)
46 }
47 return jf.printEvent("validation", map[string]interface{}{
48 "objects": objects,
49 "error": err.Error(),
50 })
51 }
52
53 func (jf *formatter) FormatApplyEvent(e event.ApplyEvent) error {
54 eventInfo := jf.baseResourceEvent(e.Identifier)
55 if e.Error != nil {
56 eventInfo["error"] = e.Error.Error()
57 }
58 eventInfo["status"] = e.Status.String()
59 return jf.printEvent("apply", eventInfo)
60 }
61
62 func (jf *formatter) FormatStatusEvent(se event.StatusEvent) error {
63 return jf.printResourceStatus(se)
64 }
65
66 func (jf *formatter) printResourceStatus(se event.StatusEvent) error {
67 eventInfo := jf.baseResourceEvent(se.Identifier)
68 eventInfo["status"] = se.PollResourceInfo.Status.String()
69 eventInfo["message"] = se.PollResourceInfo.Message
70 return jf.printEvent("status", eventInfo)
71 }
72
73 func (jf *formatter) FormatPruneEvent(e event.PruneEvent) error {
74 eventInfo := jf.baseResourceEvent(e.Identifier)
75 if e.Error != nil {
76 eventInfo["error"] = e.Error.Error()
77 }
78 eventInfo["status"] = e.Status.String()
79 return jf.printEvent("prune", eventInfo)
80 }
81
82 func (jf *formatter) FormatDeleteEvent(e event.DeleteEvent) error {
83 eventInfo := jf.baseResourceEvent(e.Identifier)
84 if e.Error != nil {
85 eventInfo["error"] = e.Error.Error()
86 }
87 eventInfo["status"] = e.Status.String()
88 return jf.printEvent("delete", eventInfo)
89 }
90
91 func (jf *formatter) FormatWaitEvent(e event.WaitEvent) error {
92 eventInfo := jf.baseResourceEvent(e.Identifier)
93 eventInfo["status"] = e.Status.String()
94 return jf.printEvent("wait", eventInfo)
95 }
96
97 func (jf *formatter) FormatErrorEvent(e event.ErrorEvent) error {
98 return jf.printEvent("error", map[string]interface{}{
99 "error": e.Err.Error(),
100 })
101 }
102
103 func (jf *formatter) FormatActionGroupEvent(
104 age event.ActionGroupEvent,
105 ags []event.ActionGroup,
106 s stats.Stats,
107 _ list.Collector,
108 ) error {
109 content := map[string]interface{}{
110 "action": age.Action.String(),
111 "status": age.Status.String(),
112 }
113
114 switch age.Action {
115 case event.ApplyAction:
116 if age.Status == event.Finished {
117 as := s.ApplyStats
118 content["count"] = as.Sum()
119 content["successful"] = as.Successful
120 content["skipped"] = as.Skipped
121 content["failed"] = as.Failed
122 }
123 case event.PruneAction:
124 if age.Status == event.Finished {
125 ps := s.PruneStats
126 content["count"] = ps.Sum()
127 content["successful"] = ps.Successful
128 content["skipped"] = ps.Skipped
129 content["failed"] = ps.Failed
130 }
131 case event.DeleteAction:
132 if age.Status == event.Finished {
133 ds := s.DeleteStats
134 content["count"] = ds.Sum()
135 content["successful"] = ds.Successful
136 content["skipped"] = ds.Skipped
137 content["failed"] = ds.Failed
138 }
139 case event.WaitAction:
140 if age.Status == event.Finished {
141 ws := s.WaitStats
142 content["count"] = ws.Sum()
143 content["successful"] = ws.Successful
144 content["skipped"] = ws.Skipped
145 content["failed"] = ws.Failed
146 content["timeout"] = ws.Timeout
147 }
148 case event.InventoryAction:
149
150 default:
151 return fmt.Errorf("invalid action group action: %+v", age)
152 }
153
154 return jf.printEvent("group", content)
155 }
156
157 func (jf *formatter) FormatSummary(s stats.Stats) error {
158 if s.ApplyStats != (stats.ApplyStats{}) {
159 as := s.ApplyStats
160 err := jf.printEvent("summary", map[string]interface{}{
161 "action": event.ApplyAction.String(),
162 "count": as.Sum(),
163 "successful": as.Successful,
164 "skipped": as.Skipped,
165 "failed": as.Failed,
166 })
167 if err != nil {
168 return err
169 }
170 }
171 if s.PruneStats != (stats.PruneStats{}) {
172 ps := s.PruneStats
173 err := jf.printEvent("summary", map[string]interface{}{
174 "action": event.PruneAction.String(),
175 "count": ps.Sum(),
176 "successful": ps.Successful,
177 "skipped": ps.Skipped,
178 "failed": ps.Failed,
179 })
180 if err != nil {
181 return err
182 }
183 }
184 if s.DeleteStats != (stats.DeleteStats{}) {
185 ds := s.DeleteStats
186 err := jf.printEvent("summary", map[string]interface{}{
187 "action": event.DeleteAction.String(),
188 "count": ds.Sum(),
189 "successful": ds.Successful,
190 "skipped": ds.Skipped,
191 "failed": ds.Failed,
192 })
193 if err != nil {
194 return err
195 }
196 }
197 if s.WaitStats != (stats.WaitStats{}) {
198 ws := s.WaitStats
199 err := jf.printEvent("summary", map[string]interface{}{
200 "action": event.WaitAction.String(),
201 "count": ws.Sum(),
202 "successful": ws.Successful,
203 "skipped": ws.Skipped,
204 "failed": ws.Failed,
205 "timeout": ws.Timeout,
206 })
207 if err != nil {
208 return err
209 }
210 }
211 return nil
212 }
213
214 func (jf *formatter) baseResourceEvent(identifier object.ObjMetadata) map[string]interface{} {
215 return map[string]interface{}{
216 "group": identifier.GroupKind.Group,
217 "kind": identifier.GroupKind.Kind,
218 "namespace": identifier.Namespace,
219 "name": identifier.Name,
220 }
221 }
222
223 func (jf *formatter) printEvent(t string, content map[string]interface{}) error {
224 m := make(map[string]interface{})
225 m["timestamp"] = jf.now().UTC().Format(time.RFC3339)
226 m["type"] = t
227 for key, val := range content {
228 m[key] = val
229 }
230 b, err := json.Marshal(m)
231 if err != nil {
232 return err
233 }
234 _, err = fmt.Fprint(jf.ioStreams.Out, string(b)+"\n")
235 return err
236 }
237
View as plain text