Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / k8s.io / apimachinery / pkg / util / strategicpatch / meta.go
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         "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"
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.Ptr:
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.Ptr {
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 }