1
16
17 package get
18
19 import (
20 "fmt"
21 "io"
22 "reflect"
23 "sort"
24
25 "k8s.io/klog/v2"
26
27 corev1 "k8s.io/api/core/v1"
28 "k8s.io/apimachinery/pkg/api/meta"
29 "k8s.io/apimachinery/pkg/api/resource"
30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
32 "k8s.io/apimachinery/pkg/runtime"
33 "k8s.io/cli-runtime/pkg/printers"
34 "k8s.io/client-go/util/jsonpath"
35
36 "github.com/fvbommel/sortorder"
37 )
38
39
40
41 type SortingPrinter struct {
42 SortField string
43 Delegate printers.ResourcePrinter
44 Decoder runtime.Decoder
45 }
46
47 func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
48 if table, isTable := obj.(*metav1.Table); isTable && len(table.Rows) > 1 {
49 parsedField, err := RelaxedJSONPathExpression(s.SortField)
50 if err != nil {
51 parsedField = s.SortField
52 }
53
54 if sorter, err := NewTableSorter(table, parsedField); err != nil {
55 return err
56 } else if err := sorter.Sort(); err != nil {
57 return err
58 }
59 return s.Delegate.PrintObj(table, out)
60 }
61
62 if meta.IsListType(obj) {
63 if err := s.sortObj(obj); err != nil {
64 return err
65 }
66 return s.Delegate.PrintObj(obj, out)
67 }
68
69 return s.Delegate.PrintObj(obj, out)
70 }
71
72 func (s *SortingPrinter) sortObj(obj runtime.Object) error {
73 objs, err := meta.ExtractList(obj)
74 if err != nil {
75 return err
76 }
77 if len(objs) == 0 {
78 return nil
79 }
80
81 sorter, err := SortObjects(s.Decoder, objs, s.SortField)
82 if err != nil {
83 return err
84 }
85
86 switch list := obj.(type) {
87 case *corev1.List:
88 outputList := make([]runtime.RawExtension, len(objs))
89 for ix := range objs {
90 outputList[ix] = list.Items[sorter.OriginalPosition(ix)]
91 }
92 list.Items = outputList
93 return nil
94 }
95 return meta.SetList(obj, objs)
96 }
97
98
99
100 func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput string) (*RuntimeSort, error) {
101 for ix := range objs {
102 item := objs[ix]
103 switch u := item.(type) {
104 case *runtime.Unknown:
105 var err error
106
107
108 if objs[ix], _, err = decoder.Decode(u.Raw, nil, &unstructured.Unstructured{}); err != nil {
109 return nil, err
110 }
111 }
112 }
113
114 field, err := RelaxedJSONPathExpression(fieldInput)
115 if err != nil {
116 return nil, err
117 }
118
119 parser := jsonpath.New("sorting").AllowMissingKeys(true)
120 if err := parser.Parse(field); err != nil {
121 return nil, err
122 }
123
124
125
126
127
128 var fieldFoundOnce bool
129 for _, obj := range objs {
130 values, err := findJSONPathResults(parser, obj)
131 if err != nil {
132 return nil, err
133 }
134 if len(values) > 0 && len(values[0]) > 0 {
135 fieldFoundOnce = true
136 break
137 }
138 }
139 if !fieldFoundOnce {
140 return nil, fmt.Errorf("couldn't find any field with path %q in the list of objects", field)
141 }
142
143 sorter := NewRuntimeSort(field, objs)
144 sort.Sort(sorter)
145 return sorter, nil
146 }
147
148
149
150 type RuntimeSort struct {
151 field string
152 objs []runtime.Object
153 origPosition []int
154 }
155
156
157 func NewRuntimeSort(field string, objs []runtime.Object) *RuntimeSort {
158 sorter := &RuntimeSort{field: field, objs: objs, origPosition: make([]int, len(objs))}
159 for ix := range objs {
160 sorter.origPosition[ix] = ix
161 }
162 return sorter
163 }
164
165 func (r *RuntimeSort) Len() int {
166 return len(r.objs)
167 }
168
169 func (r *RuntimeSort) Swap(i, j int) {
170 r.objs[i], r.objs[j] = r.objs[j], r.objs[i]
171 r.origPosition[i], r.origPosition[j] = r.origPosition[j], r.origPosition[i]
172 }
173
174 func isLess(i, j reflect.Value) (bool, error) {
175 switch i.Kind() {
176 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
177 return i.Int() < j.Int(), nil
178 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
179 return i.Uint() < j.Uint(), nil
180 case reflect.Float32, reflect.Float64:
181 return i.Float() < j.Float(), nil
182 case reflect.String:
183 return sortorder.NaturalLess(i.String(), j.String()), nil
184 case reflect.Pointer:
185 return isLess(i.Elem(), j.Elem())
186 case reflect.Struct:
187
188 in := i.Interface()
189 if t, ok := in.(metav1.Time); ok {
190 time := j.Interface().(metav1.Time)
191 return t.Before(&time), nil
192 }
193
194 if iQuantity, ok := in.(resource.Quantity); ok {
195 jQuantity := j.Interface().(resource.Quantity)
196 return iQuantity.Cmp(jQuantity) < 0, nil
197 }
198
199 for idx := 0; idx < i.NumField(); idx++ {
200 less, err := isLess(i.Field(idx), j.Field(idx))
201 if err != nil || !less {
202 return less, err
203 }
204 }
205 return true, nil
206 case reflect.Array, reflect.Slice:
207
208 for idx := 0; idx < min(i.Len(), j.Len()); idx++ {
209 less, err := isLess(i.Index(idx), j.Index(idx))
210 if err != nil || !less {
211 return less, err
212 }
213 }
214 return true, nil
215 case reflect.Interface:
216 if i.IsNil() && j.IsNil() {
217 return false, nil
218 } else if i.IsNil() {
219 return true, nil
220 } else if j.IsNil() {
221 return false, nil
222 }
223 switch itype := i.Interface().(type) {
224 case uint8:
225 if jtype, ok := j.Interface().(uint8); ok {
226 return itype < jtype, nil
227 }
228 case uint16:
229 if jtype, ok := j.Interface().(uint16); ok {
230 return itype < jtype, nil
231 }
232 case uint32:
233 if jtype, ok := j.Interface().(uint32); ok {
234 return itype < jtype, nil
235 }
236 case uint64:
237 if jtype, ok := j.Interface().(uint64); ok {
238 return itype < jtype, nil
239 }
240 case int8:
241 if jtype, ok := j.Interface().(int8); ok {
242 return itype < jtype, nil
243 }
244 case int16:
245 if jtype, ok := j.Interface().(int16); ok {
246 return itype < jtype, nil
247 }
248 case int32:
249 if jtype, ok := j.Interface().(int32); ok {
250 return itype < jtype, nil
251 }
252 case int64:
253 if jtype, ok := j.Interface().(int64); ok {
254 return itype < jtype, nil
255 }
256 case uint:
257 if jtype, ok := j.Interface().(uint); ok {
258 return itype < jtype, nil
259 }
260 case int:
261 if jtype, ok := j.Interface().(int); ok {
262 return itype < jtype, nil
263 }
264 case float32:
265 if jtype, ok := j.Interface().(float32); ok {
266 return itype < jtype, nil
267 }
268 case float64:
269 if jtype, ok := j.Interface().(float64); ok {
270 return itype < jtype, nil
271 }
272 case string:
273 if jtype, ok := j.Interface().(string); ok {
274
275 itypeQuantity, err := resource.ParseQuantity(itype)
276 if err != nil {
277 return sortorder.NaturalLess(itype, jtype), nil
278 }
279 jtypeQuantity, err := resource.ParseQuantity(jtype)
280 if err != nil {
281 return sortorder.NaturalLess(itype, jtype), nil
282 }
283
284 return itypeQuantity.Cmp(jtypeQuantity) < 0, nil
285 }
286 default:
287 return false, fmt.Errorf("unsortable type: %T", itype)
288 }
289 return false, fmt.Errorf("unsortable interface: %v", i.Kind())
290
291 default:
292 return false, fmt.Errorf("unsortable type: %v", i.Kind())
293 }
294 }
295
296 func (r *RuntimeSort) Less(i, j int) bool {
297 iObj := r.objs[i]
298 jObj := r.objs[j]
299
300 var iValues [][]reflect.Value
301 var jValues [][]reflect.Value
302 var err error
303
304 parser := jsonpath.New("sorting").AllowMissingKeys(true)
305 err = parser.Parse(r.field)
306 if err != nil {
307 panic(err)
308 }
309
310 iValues, err = findJSONPathResults(parser, iObj)
311 if err != nil {
312 klog.Fatalf("Failed to get i values for %#v using %s (%#v)", iObj, r.field, err)
313 }
314
315 jValues, err = findJSONPathResults(parser, jObj)
316 if err != nil {
317 klog.Fatalf("Failed to get j values for %#v using %s (%v)", jObj, r.field, err)
318 }
319
320 if len(iValues) == 0 || len(iValues[0]) == 0 {
321 return true
322 }
323 if len(jValues) == 0 || len(jValues[0]) == 0 {
324 return false
325 }
326 iField := iValues[0][0]
327 jField := jValues[0][0]
328
329 less, err := isLess(iField, jField)
330 if err != nil {
331 klog.Exitf("Field %s in %T is an unsortable type: %s, err: %v", r.field, iObj, iField.Kind().String(), err)
332 }
333 return less
334 }
335
336
337
338
339 func (r *RuntimeSort) OriginalPosition(ix int) int {
340 if ix < 0 || ix > len(r.origPosition) {
341 return -1
342 }
343 return r.origPosition[ix]
344 }
345
346 type TableSorter struct {
347 field string
348 obj *metav1.Table
349 parsedRows [][][]reflect.Value
350 }
351
352 func (t *TableSorter) Len() int {
353 return len(t.obj.Rows)
354 }
355
356 func (t *TableSorter) Swap(i, j int) {
357 t.obj.Rows[i], t.obj.Rows[j] = t.obj.Rows[j], t.obj.Rows[i]
358 t.parsedRows[i], t.parsedRows[j] = t.parsedRows[j], t.parsedRows[i]
359 }
360
361 func (t *TableSorter) Less(i, j int) bool {
362 iValues := t.parsedRows[i]
363 jValues := t.parsedRows[j]
364
365 if len(iValues) == 0 || len(iValues[0]) == 0 {
366 return true
367 }
368 if len(jValues) == 0 || len(jValues[0]) == 0 {
369 return false
370 }
371
372 iField := iValues[0][0]
373 jField := jValues[0][0]
374
375 less, err := isLess(iField, jField)
376 if err != nil {
377 klog.Exitf("Field %s in %T is an unsortable type: %s, err: %v", t.field, t.parsedRows, iField.Kind().String(), err)
378 }
379 return less
380 }
381
382 func (t *TableSorter) Sort() error {
383 sort.Sort(t)
384 return nil
385 }
386
387 func NewTableSorter(table *metav1.Table, field string) (*TableSorter, error) {
388 var parsedRows [][][]reflect.Value
389
390 parser := jsonpath.New("sorting").AllowMissingKeys(true)
391 err := parser.Parse(field)
392 if err != nil {
393 return nil, fmt.Errorf("sorting error: %v", err)
394 }
395
396 fieldFoundOnce := false
397 for i := range table.Rows {
398 parsedRow, err := findJSONPathResults(parser, table.Rows[i].Object.Object)
399 if err != nil {
400 return nil, fmt.Errorf("Failed to get values for %#v using %s (%#v)", parsedRow, field, err)
401 }
402 parsedRows = append(parsedRows, parsedRow)
403 if len(parsedRow) > 0 && len(parsedRow[0]) > 0 {
404 fieldFoundOnce = true
405 }
406 }
407
408 if len(table.Rows) > 0 && !fieldFoundOnce {
409 return nil, fmt.Errorf("couldn't find any field with path %q in the list of objects", field)
410 }
411
412 return &TableSorter{
413 obj: table,
414 field: field,
415 parsedRows: parsedRows,
416 }, nil
417 }
418 func findJSONPathResults(parser *jsonpath.JSONPath, from runtime.Object) ([][]reflect.Value, error) {
419 if unstructuredObj, ok := from.(*unstructured.Unstructured); ok {
420 return parser.FindResults(unstructuredObj.Object)
421 }
422 return parser.FindResults(reflect.ValueOf(from).Elem().Interface())
423 }
424
View as plain text