2 Copyright 2017 The Kubernetes Authors.
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
17 package strategicpatch
24 "k8s.io/apimachinery/pkg/util/mergepatch"
25 forkedjson "k8s.io/apimachinery/third_party/forked/golang/json"
26 openapi "k8s.io/kube-openapi/pkg/util/proto"
29 type PatchMeta struct {
30 patchStrategies []string
34 func (pm PatchMeta) GetPatchStrategies() []string {
35 if pm.patchStrategies == nil {
38 return pm.patchStrategies
41 func (pm PatchMeta) SetPatchStrategies(ps []string) {
42 pm.patchStrategies = ps
45 func (pm PatchMeta) GetPatchMergeKey() string {
46 return pm.patchMergeKey
49 func (pm PatchMeta) SetPatchMergeKey(pmk string) {
50 pm.patchMergeKey = pmk
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
62 type PatchMetaFromStruct struct {
66 func NewPatchMetaFromStruct(dataStruct interface{}) (PatchMetaFromStruct, error) {
67 t, err := getTagStructType(dataStruct)
68 return PatchMetaFromStruct{T: t}, err
71 var _ LookupPatchMeta = PatchMetaFromStruct{}
73 func (s PatchMetaFromStruct) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
74 fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadataForStruct(s.T, key)
76 return nil, PatchMeta{}, err
79 return PatchMetaFromStruct{T: fieldType},
81 patchStrategies: fieldPatchStrategies,
82 patchMergeKey: fieldPatchMergeKey,
86 func (s PatchMetaFromStruct) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
87 subschema, patchMeta, err := s.LookupPatchMetadataForStruct(key)
89 return nil, PatchMeta{}, err
91 elemPatchMetaFromStruct := subschema.(PatchMetaFromStruct)
92 t := elemPatchMetaFromStruct.T
94 var elemType reflect.Type
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:
101 if elemType.Kind() == reflect.Array || elemType.Kind() == reflect.Slice {
102 return nil, PatchMeta{}, errors.New("unexpected slice of slice")
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.
110 if t.Kind() == reflect.Array || t.Kind() == reflect.Slice {
115 return nil, PatchMeta{}, fmt.Errorf("expected slice or array type, but got: %s", s.T.Kind().String())
118 return PatchMetaFromStruct{T: elemType}, patchMeta, nil
121 func (s PatchMetaFromStruct) Name() string {
122 return s.T.Kind().String()
125 func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
126 if dataStruct == nil {
127 return nil, mergepatch.ErrBadArgKind(struct{}{}, nil)
130 t := reflect.TypeOf(dataStruct)
131 // Get the underlying type for pointers
132 if t.Kind() == reflect.Ptr {
136 if t.Kind() != reflect.Struct {
137 return nil, mergepatch.ErrBadArgKind(struct{}{}, dataStruct)
143 func GetTagStructTypeOrDie(dataStruct interface{}) reflect.Type {
144 t, err := getTagStructType(dataStruct)
151 type PatchMetaFromOpenAPI struct {
152 Schema openapi.Schema
155 func NewPatchMetaFromOpenAPI(s openapi.Schema) PatchMetaFromOpenAPI {
156 return PatchMetaFromOpenAPI{Schema: s}
159 var _ LookupPatchMeta = PatchMetaFromOpenAPI{}
161 func (s PatchMetaFromOpenAPI) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
163 return nil, PatchMeta{}, nil
165 kindItem := NewKindItem(key, s.Schema.GetPath())
166 s.Schema.Accept(kindItem)
168 err := kindItem.Error()
170 return nil, PatchMeta{}, err
172 return PatchMetaFromOpenAPI{Schema: kindItem.subschema},
173 kindItem.patchmeta, nil
176 func (s PatchMetaFromOpenAPI) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
178 return nil, PatchMeta{}, nil
180 sliceItem := NewSliceItem(key, s.Schema.GetPath())
181 s.Schema.Accept(sliceItem)
183 err := sliceItem.Error()
185 return nil, PatchMeta{}, err
187 return PatchMetaFromOpenAPI{Schema: sliceItem.subschema},
188 sliceItem.patchmeta, nil
191 func (s PatchMetaFromOpenAPI) Name() string {
193 return schema.GetName()