Skip to content

Commit ded4345

Browse files
jennybuckleyapelisse
authored andcommitted
Allow defaulted keys
1 parent b84068c commit ded4345

File tree

11 files changed

+396
-13
lines changed

11 files changed

+396
-13
lines changed

go.sum

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,3 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
1717
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1818
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
1919
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
20-
sigs.k8s.io/structured-merge-diff v1.0.2 h1:WiMoyniAVAYm03w+ImfF9IE2G23GLR/SwDnQyaNZvPk=

merge/default_keys_test.go

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package merge_test
15+
16+
import (
17+
"testing"
18+
19+
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
20+
. "sigs.k8s.io/structured-merge-diff/v4/internal/fixture"
21+
"sigs.k8s.io/structured-merge-diff/v4/merge"
22+
"sigs.k8s.io/structured-merge-diff/v4/typed"
23+
)
24+
25+
// portListParser sets the default value of key "protocol" to "TCP"
26+
var portListParser = func() *typed.Parser {
27+
parser, err := typed.NewParser(`types:
28+
- name: v1
29+
map:
30+
fields:
31+
- name: containerPorts
32+
type:
33+
list:
34+
elementType:
35+
map:
36+
fields:
37+
- name: port
38+
type:
39+
scalar: numeric
40+
- name: protocol
41+
default: "TCP"
42+
type:
43+
scalar: string
44+
- name: name
45+
type:
46+
scalar: string
47+
elementRelationship: associative
48+
keys:
49+
- port
50+
- protocol
51+
`)
52+
if err != nil {
53+
panic(err)
54+
}
55+
return parser
56+
}()
57+
58+
func TestDefaultKeysFlat(t *testing.T) {
59+
tests := map[string]TestCase{
60+
"apply_missing_defaulted_key_A": {
61+
Ops: []Operation{
62+
Apply{
63+
Manager: "default",
64+
APIVersion: "v1",
65+
Object: `
66+
containerPorts:
67+
- port: 80
68+
`,
69+
},
70+
},
71+
APIVersion: "v1",
72+
Object: `
73+
containerPorts:
74+
- port: 80
75+
`,
76+
Managed: fieldpath.ManagedFields{
77+
"default": fieldpath.NewVersionedSet(
78+
_NS(
79+
_P("containerPorts", _KBF("port", 80, "protocol", "TCP")),
80+
_P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "port"),
81+
),
82+
"v1",
83+
false,
84+
),
85+
},
86+
},
87+
"apply_missing_defaulted_key_B": {
88+
Ops: []Operation{
89+
Apply{
90+
Manager: "default",
91+
APIVersion: "v1",
92+
Object: `
93+
containerPorts:
94+
- port: 80
95+
- port: 80
96+
protocol: UDP
97+
`,
98+
},
99+
},
100+
APIVersion: "v1",
101+
Object: `
102+
containerPorts:
103+
- port: 80
104+
- port: 80
105+
protocol: UDP
106+
`,
107+
Managed: fieldpath.ManagedFields{
108+
"default": fieldpath.NewVersionedSet(
109+
_NS(
110+
_P("containerPorts", _KBF("port", 80, "protocol", "TCP")),
111+
_P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "port"),
112+
_P("containerPorts", _KBF("port", 80, "protocol", "UDP")),
113+
_P("containerPorts", _KBF("port", 80, "protocol", "UDP"), "port"),
114+
_P("containerPorts", _KBF("port", 80, "protocol", "UDP"), "protocol"),
115+
),
116+
"v1",
117+
false,
118+
),
119+
},
120+
},
121+
"apply_missing_defaulted_key_with_conflict": {
122+
Ops: []Operation{
123+
Apply{
124+
Manager: "apply-one",
125+
APIVersion: "v1",
126+
Object: `
127+
containerPorts:
128+
- port: 80
129+
protocol: TCP
130+
name: foo
131+
`,
132+
},
133+
Apply{
134+
Manager: "apply-two",
135+
APIVersion: "v1",
136+
Object: `
137+
containerPorts:
138+
- port: 80
139+
name: bar
140+
`,
141+
Conflicts: merge.Conflicts{
142+
merge.Conflict{Manager: "apply-one", Path: _P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "name")},
143+
},
144+
},
145+
},
146+
APIVersion: "v1",
147+
Object: `
148+
containerPorts:
149+
- port: 80
150+
protocol: TCP
151+
name: foo
152+
`,
153+
Managed: fieldpath.ManagedFields{
154+
"apply-one": fieldpath.NewVersionedSet(
155+
_NS(
156+
_P("containerPorts", _KBF("port", 80, "protocol", "TCP")),
157+
_P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "port"),
158+
_P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "protocol"),
159+
_P("containerPorts", _KBF("port", 80, "protocol", "TCP"), "name"),
160+
),
161+
"v1",
162+
false,
163+
),
164+
},
165+
},
166+
}
167+
168+
for name, test := range tests {
169+
t.Run(name, func(t *testing.T) {
170+
if err := test.Test(portListParser); err != nil {
171+
t.Fatal(err)
172+
}
173+
})
174+
}
175+
}
176+
177+
func TestDefaultKeysFlatErrors(t *testing.T) {
178+
tests := map[string]TestCase{
179+
"apply_missing_undefaulted_defaulted_key": {
180+
Ops: []Operation{
181+
Apply{
182+
Manager: "default",
183+
APIVersion: "v1",
184+
Object: `
185+
containerPorts:
186+
- protocol: TCP
187+
`,
188+
},
189+
},
190+
},
191+
"apply_missing_defaulted_key_ambiguous_A": {
192+
Ops: []Operation{
193+
Apply{
194+
Manager: "default",
195+
APIVersion: "v1",
196+
Object: `
197+
containerPorts:
198+
- port: 80
199+
- port: 80
200+
`,
201+
},
202+
},
203+
},
204+
"apply_missing_defaulted_key_ambiguous_B": {
205+
Ops: []Operation{
206+
Apply{
207+
Manager: "default",
208+
APIVersion: "v1",
209+
Object: `
210+
containerPorts:
211+
- port: 80
212+
- port: 80
213+
protocol: TCP
214+
`,
215+
},
216+
},
217+
},
218+
}
219+
for name, test := range tests {
220+
t.Run(name, func(t *testing.T) {
221+
if test.Test(portListParser) == nil {
222+
t.Fatal("Should fail")
223+
}
224+
})
225+
}
226+
}
227+
228+
// bookParser sets the default value of key:
229+
// * "chapter" to 1
230+
// * "section" to "A"
231+
// * "page" to 2,
232+
// * "line" to 3,
233+
var bookParser = func() *typed.Parser {
234+
parser, err := typed.NewParser(`types:
235+
- name: v1
236+
map:
237+
fields:
238+
- name: book
239+
type:
240+
list:
241+
elementType:
242+
map:
243+
fields:
244+
- name: chapter
245+
default: 1
246+
type:
247+
scalar: numeric
248+
- name: section
249+
default: "A"
250+
type:
251+
scalar: string
252+
- name: sentences
253+
type:
254+
list:
255+
elementType:
256+
map:
257+
fields:
258+
- name: page
259+
default: 2
260+
type:
261+
scalar: numeric
262+
- name: line
263+
default: 3
264+
type:
265+
scalar: numeric
266+
- name: text
267+
type:
268+
scalar: string
269+
elementRelationship: associative
270+
keys:
271+
- page
272+
- line
273+
elementRelationship: associative
274+
keys:
275+
- chapter
276+
- section
277+
`)
278+
if err != nil {
279+
panic(err)
280+
}
281+
return parser
282+
}()
283+
284+
func TestDefaultKeysNested(t *testing.T) {
285+
tests := map[string]TestCase{
286+
"apply_missing_every_key_nested": {
287+
Ops: []Operation{
288+
Apply{
289+
Manager: "default",
290+
APIVersion: "v1",
291+
Object: `
292+
book:
293+
- sentences:
294+
- text: blah
295+
`,
296+
},
297+
},
298+
APIVersion: "v1",
299+
Object: `
300+
book:
301+
- sentences:
302+
- text: blah
303+
`,
304+
Managed: fieldpath.ManagedFields{
305+
"default": fieldpath.NewVersionedSet(
306+
_NS(
307+
_P(
308+
"book", _KBF("chapter", 1, "section", "A"),
309+
),
310+
_P(
311+
"book", _KBF("chapter", 1, "section", "A"),
312+
"sentences", _KBF("page", 2, "line", 3),
313+
),
314+
_P(
315+
"book", _KBF("chapter", 1, "section", "A"),
316+
"sentences", _KBF("page", 2, "line", 3),
317+
"text",
318+
),
319+
),
320+
"v1",
321+
false,
322+
),
323+
},
324+
},
325+
}
326+
327+
for name, test := range tests {
328+
t.Run(name, func(t *testing.T) {
329+
if err := test.Test(bookParser); err != nil {
330+
t.Fatal(err)
331+
}
332+
})
333+
}
334+
}

schema/elements.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ type StructField struct {
196196
Name string `yaml:"name,omitempty"`
197197
// Type is the field type.
198198
Type TypeRef `yaml:"type,omitempty"`
199+
// Default value for the field, nil if not present.
200+
Default interface{} `yaml:"default,omitempty"`
199201
}
200202

201203
// List represents a type which contains a zero or more elements, all of the

schema/equals.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ limitations under the License.
1616

1717
package schema
1818

19+
import "reflect"
20+
1921
// Equals returns true iff the two Schemas are equal.
2022
func (a *Schema) Equals(b *Schema) bool {
2123
if a == nil || b == nil {
@@ -168,6 +170,9 @@ func (a *StructField) Equals(b *StructField) bool {
168170
if a.Name != b.Name {
169171
return false
170172
}
173+
if !reflect.DeepEqual(a.Default, b.Default) {
174+
return false
175+
}
171176
return a.Type.Equals(&b.Type)
172177
}
173178

0 commit comments

Comments
 (0)