github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/strategicpatch/meta.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package strategicpatch 18 19 import ( 20 "errors" 21 "fmt" 22 "reflect" 23 24 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/mergepatch" 25 forkedjson "github.com/spotmaxtech/k8s-apimachinery-v0260/third_party/forked/golang/json" 26 openapi "k8s.io/kube-openapi/pkg/util/proto" 27 ) 28 29 type PatchMeta struct { 30 patchStrategies []string 31 patchMergeKey string 32 } 33 34 func (pm *PatchMeta) GetPatchStrategies() []string { 35 if pm.patchStrategies == nil { 36 return []string{} 37 } 38 return pm.patchStrategies 39 } 40 41 func (pm *PatchMeta) SetPatchStrategies(ps []string) { 42 pm.patchStrategies = ps 43 } 44 45 func (pm *PatchMeta) GetPatchMergeKey() string { 46 return pm.patchMergeKey 47 } 48 49 func (pm *PatchMeta) SetPatchMergeKey(pmk string) { 50 pm.patchMergeKey = pmk 51 } 52 53 type LookupPatchMeta interface { 54 // LookupPatchMetadataForStruct gets subschema and the patch metadata (e.g. patch strategy and merge key) for map. 55 LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) 56 // LookupPatchMetadataForSlice get subschema and the patch metadata for slice. 57 LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) 58 // Get the type name of the field 59 Name() string 60 } 61 62 type PatchMetaFromStruct struct { 63 T reflect.Type 64 } 65 66 func NewPatchMetaFromStruct(dataStruct interface{}) (PatchMetaFromStruct, error) { 67 t, err := getTagStructType(dataStruct) 68 return PatchMetaFromStruct{T: t}, err 69 } 70 71 var _ LookupPatchMeta = PatchMetaFromStruct{} 72 73 func (s PatchMetaFromStruct) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) { 74 fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadataForStruct(s.T, key) 75 if err != nil { 76 return nil, PatchMeta{}, err 77 } 78 79 return PatchMetaFromStruct{T: fieldType}, 80 PatchMeta{ 81 patchStrategies: fieldPatchStrategies, 82 patchMergeKey: fieldPatchMergeKey, 83 }, nil 84 } 85 86 func (s PatchMetaFromStruct) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) { 87 subschema, patchMeta, err := s.LookupPatchMetadataForStruct(key) 88 if err != nil { 89 return nil, PatchMeta{}, err 90 } 91 elemPatchMetaFromStruct := subschema.(PatchMetaFromStruct) 92 t := elemPatchMetaFromStruct.T 93 94 var elemType reflect.Type 95 switch t.Kind() { 96 // If t is an array or a slice, get the element type. 97 // If element is still an array or a slice, return an error. 98 // Otherwise, return element type. 99 case reflect.Array, reflect.Slice: 100 elemType = t.Elem() 101 if elemType.Kind() == reflect.Array || elemType.Kind() == reflect.Slice { 102 return nil, PatchMeta{}, errors.New("unexpected slice of slice") 103 } 104 // If t is an pointer, get the underlying element. 105 // If the underlying element is neither an array nor a slice, the pointer is pointing to a slice, 106 // e.g. https://github.com/kubernetes/kubernetes/blob/bc22e206c79282487ea0bf5696d5ccec7e839a76/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go#L2782-L2822 107 // If the underlying element is either an array or a slice, return its element type. 108 case reflect.Pointer: 109 t = t.Elem() 110 if t.Kind() == reflect.Array || t.Kind() == reflect.Slice { 111 t = t.Elem() 112 } 113 elemType = t 114 default: 115 return nil, PatchMeta{}, fmt.Errorf("expected slice or array type, but got: %s", s.T.Kind().String()) 116 } 117 118 return PatchMetaFromStruct{T: elemType}, patchMeta, nil 119 } 120 121 func (s PatchMetaFromStruct) Name() string { 122 return s.T.Kind().String() 123 } 124 125 func getTagStructType(dataStruct interface{}) (reflect.Type, error) { 126 if dataStruct == nil { 127 return nil, mergepatch.ErrBadArgKind(struct{}{}, nil) 128 } 129 130 t := reflect.TypeOf(dataStruct) 131 // Get the underlying type for pointers 132 if t.Kind() == reflect.Pointer { 133 t = t.Elem() 134 } 135 136 if t.Kind() != reflect.Struct { 137 return nil, mergepatch.ErrBadArgKind(struct{}{}, dataStruct) 138 } 139 140 return t, nil 141 } 142 143 func GetTagStructTypeOrDie(dataStruct interface{}) reflect.Type { 144 t, err := getTagStructType(dataStruct) 145 if err != nil { 146 panic(err) 147 } 148 return t 149 } 150 151 type PatchMetaFromOpenAPI struct { 152 Schema openapi.Schema 153 } 154 155 func NewPatchMetaFromOpenAPI(s openapi.Schema) PatchMetaFromOpenAPI { 156 return PatchMetaFromOpenAPI{Schema: s} 157 } 158 159 var _ LookupPatchMeta = PatchMetaFromOpenAPI{} 160 161 func (s PatchMetaFromOpenAPI) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) { 162 if s.Schema == nil { 163 return nil, PatchMeta{}, nil 164 } 165 kindItem := NewKindItem(key, s.Schema.GetPath()) 166 s.Schema.Accept(kindItem) 167 168 err := kindItem.Error() 169 if err != nil { 170 return nil, PatchMeta{}, err 171 } 172 return PatchMetaFromOpenAPI{Schema: kindItem.subschema}, 173 kindItem.patchmeta, nil 174 } 175 176 func (s PatchMetaFromOpenAPI) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) { 177 if s.Schema == nil { 178 return nil, PatchMeta{}, nil 179 } 180 sliceItem := NewSliceItem(key, s.Schema.GetPath()) 181 s.Schema.Accept(sliceItem) 182 183 err := sliceItem.Error() 184 if err != nil { 185 return nil, PatchMeta{}, err 186 } 187 return PatchMetaFromOpenAPI{Schema: sliceItem.subschema}, 188 sliceItem.patchmeta, nil 189 } 190 191 func (s PatchMetaFromOpenAPI) Name() string { 192 schema := s.Schema 193 return schema.GetName() 194 }