github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/k8s/encoder.go (about) 1 package k8s 2 3 import ( 4 "unsafe" 5 6 jsoniter "github.com/json-iterator/go" 7 "github.com/modern-go/reflect2" 8 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 "k8s.io/client-go/kubernetes/scheme" 10 ) 11 12 var defaultJSONIterator = createDefaultJSONIterator() 13 var specJSONIterator = createSpecJSONIterator() 14 15 func createDefaultJSONConfig() jsoniter.Config { 16 return jsoniter.Config{ 17 EscapeHTML: true, 18 SortMapKeys: true, 19 ValidateJsonRawMessage: true, 20 CaseSensitive: true, 21 } 22 } 23 24 func createDefaultJSONIterator() jsoniter.API { 25 return createDefaultJSONConfig().Froze() 26 } 27 28 // Create a JSON iterator that: 29 // - encodes "zero" metav1.Time values as empty instead of nil 30 // - encodes all status values as empty 31 func createSpecJSONIterator() jsoniter.API { 32 config := createDefaultJSONConfig().Froze() 33 config.RegisterExtension(newTimeExtension()) 34 config.RegisterExtension(alwaysEmptyExtension{ 35 typeIndex: createTypeIndex(allStatusTypes()), 36 }) 37 return config 38 } 39 40 func allStatusTypes() []reflect2.Type { 41 result := []reflect2.Type{} 42 for _, typ := range scheme.Scheme.AllKnownTypes() { 43 typ2 := reflect2.Type2(typ) 44 45 sTyp2, ok := typ2.(reflect2.StructType) 46 if !ok { 47 continue 48 } 49 50 statusField := sTyp2.FieldByName("Status") 51 if statusField == nil { 52 continue 53 } 54 55 result = append(result, statusField.Type()) 56 } 57 return result 58 } 59 60 type TypeIndex map[reflect2.Type]bool 61 62 func (idx TypeIndex) Contains(typ reflect2.Type) bool { 63 _, ok := idx[typ] 64 return ok 65 } 66 67 func createTypeIndex(ts []reflect2.Type) TypeIndex { 68 result := make(map[reflect2.Type]bool) 69 for _, t := range ts { 70 result[t] = true 71 } 72 return TypeIndex(result) 73 } 74 75 // Any type that matches this extension is considered empty, 76 // and skipped during json serialization. 77 type alwaysEmptyExtension struct { 78 *jsoniter.DummyExtension 79 typeIndex TypeIndex 80 } 81 82 func (e alwaysEmptyExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder { 83 if e.typeIndex.Contains(typ) { 84 return alwaysEmptyEncoder{} 85 } 86 return nil 87 } 88 89 type alwaysEmptyEncoder struct { 90 } 91 92 func (alwaysEmptyEncoder) IsEmpty(ptr unsafe.Pointer) bool { return true } 93 func (alwaysEmptyEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {} 94 95 type timeExtension struct { 96 *jsoniter.DummyExtension 97 timeType reflect2.Type 98 } 99 100 func newTimeExtension() timeExtension { 101 return timeExtension{ 102 // memoize the type lookup 103 timeType: reflect2.TypeOf(metav1.Time{}), 104 } 105 } 106 107 func (e timeExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder { 108 if e.timeType == typ { 109 return timeEncoder{delegate: defaultJSONIterator.EncoderOf(typ)} 110 } 111 return nil 112 } 113 114 type timeEncoder struct { 115 delegate jsoniter.ValEncoder 116 } 117 118 // Returns true if the time value is the zero value. 119 func (e timeEncoder) IsEmpty(ptr unsafe.Pointer) bool { 120 t := *((*metav1.Time)(ptr)) 121 return t == metav1.Time{} || e.delegate.IsEmpty(ptr) 122 } 123 124 func (e timeEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { 125 e.delegate.Encode(ptr, stream) 126 }