...
1
16
17 package strategicpatch
18
19 import (
20 "errors"
21 "strings"
22
23 "k8s.io/apimachinery/pkg/util/mergepatch"
24 openapi "k8s.io/kube-openapi/pkg/util/proto"
25 )
26
27 const (
28 patchStrategyOpenapiextensionKey = "x-kubernetes-patch-strategy"
29 patchMergeKeyOpenapiextensionKey = "x-kubernetes-patch-merge-key"
30 )
31
32 type LookupPatchItem interface {
33 openapi.SchemaVisitor
34
35 Error() error
36 Path() *openapi.Path
37 }
38
39 type kindItem struct {
40 key string
41 path *openapi.Path
42 err error
43 patchmeta PatchMeta
44 subschema openapi.Schema
45 hasVisitKind bool
46 }
47
48 func NewKindItem(key string, path *openapi.Path) *kindItem {
49 return &kindItem{
50 key: key,
51 path: path,
52 }
53 }
54
55 var _ LookupPatchItem = &kindItem{}
56
57 func (item *kindItem) Error() error {
58 return item.err
59 }
60
61 func (item *kindItem) Path() *openapi.Path {
62 return item.path
63 }
64
65 func (item *kindItem) VisitPrimitive(schema *openapi.Primitive) {
66 item.err = errors.New("expected kind, but got primitive")
67 }
68
69 func (item *kindItem) VisitArray(schema *openapi.Array) {
70 item.err = errors.New("expected kind, but got slice")
71 }
72
73 func (item *kindItem) VisitMap(schema *openapi.Map) {
74 item.err = errors.New("expected kind, but got map")
75 }
76
77 func (item *kindItem) VisitReference(schema openapi.Reference) {
78 if !item.hasVisitKind {
79 schema.SubSchema().Accept(item)
80 }
81 }
82
83 func (item *kindItem) VisitKind(schema *openapi.Kind) {
84 subschema, ok := schema.Fields[item.key]
85 if !ok {
86 item.err = FieldNotFoundError{Path: schema.GetPath().String(), Field: item.key}
87 return
88 }
89
90 mergeKey, patchStrategies, err := parsePatchMetadata(subschema.GetExtensions())
91 if err != nil {
92 item.err = err
93 return
94 }
95 item.patchmeta = PatchMeta{
96 patchStrategies: patchStrategies,
97 patchMergeKey: mergeKey,
98 }
99 item.subschema = subschema
100 }
101
102 type sliceItem struct {
103 key string
104 path *openapi.Path
105 err error
106 patchmeta PatchMeta
107 subschema openapi.Schema
108 hasVisitKind bool
109 }
110
111 func NewSliceItem(key string, path *openapi.Path) *sliceItem {
112 return &sliceItem{
113 key: key,
114 path: path,
115 }
116 }
117
118 var _ LookupPatchItem = &sliceItem{}
119
120 func (item *sliceItem) Error() error {
121 return item.err
122 }
123
124 func (item *sliceItem) Path() *openapi.Path {
125 return item.path
126 }
127
128 func (item *sliceItem) VisitPrimitive(schema *openapi.Primitive) {
129 item.err = errors.New("expected slice, but got primitive")
130 }
131
132 func (item *sliceItem) VisitArray(schema *openapi.Array) {
133 if !item.hasVisitKind {
134 item.err = errors.New("expected visit kind first, then visit array")
135 }
136 subschema := schema.SubType
137 item.subschema = subschema
138 }
139
140 func (item *sliceItem) VisitMap(schema *openapi.Map) {
141 item.err = errors.New("expected slice, but got map")
142 }
143
144 func (item *sliceItem) VisitReference(schema openapi.Reference) {
145 if !item.hasVisitKind {
146 schema.SubSchema().Accept(item)
147 } else {
148 item.subschema = schema.SubSchema()
149 }
150 }
151
152 func (item *sliceItem) VisitKind(schema *openapi.Kind) {
153 subschema, ok := schema.Fields[item.key]
154 if !ok {
155 item.err = FieldNotFoundError{Path: schema.GetPath().String(), Field: item.key}
156 return
157 }
158
159 mergeKey, patchStrategies, err := parsePatchMetadata(subschema.GetExtensions())
160 if err != nil {
161 item.err = err
162 return
163 }
164 item.patchmeta = PatchMeta{
165 patchStrategies: patchStrategies,
166 patchMergeKey: mergeKey,
167 }
168 item.hasVisitKind = true
169 subschema.Accept(item)
170 }
171
172 func parsePatchMetadata(extensions map[string]interface{}) (string, []string, error) {
173 ps, foundPS := extensions[patchStrategyOpenapiextensionKey]
174 var patchStrategies []string
175 var mergeKey, patchStrategy string
176 var ok bool
177 if foundPS {
178 patchStrategy, ok = ps.(string)
179 if ok {
180 patchStrategies = strings.Split(patchStrategy, ",")
181 } else {
182 return "", nil, mergepatch.ErrBadArgType(patchStrategy, ps)
183 }
184 }
185 mk, foundMK := extensions[patchMergeKeyOpenapiextensionKey]
186 if foundMK {
187 mergeKey, ok = mk.(string)
188 if !ok {
189 return "", nil, mergepatch.ErrBadArgType(mergeKey, mk)
190 }
191 }
192 return mergeKey, patchStrategies, nil
193 }
194
View as plain text