Skip to content

Commit 7cc0d63

Browse files
authored
Merge pull request #190 from apelisse/cherry-pick-166-184
Cherry pick 166 184
2 parents f5fd4ea + 65b8000 commit 7cc0d63

File tree

9 files changed

+998
-27
lines changed

9 files changed

+998
-27
lines changed

fieldpath/set.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package fieldpath
1919
import (
2020
"sort"
2121
"strings"
22+
23+
"sigs.k8s.io/structured-merge-diff/v3/schema"
2224
)
2325

2426
// Set identifies a set of fields.
@@ -94,6 +96,30 @@ func (s *Set) Difference(s2 *Set) *Set {
9496
}
9597
}
9698

99+
// EnsureNamedFieldsAreMembers returns a Set that contains all the
100+
// fields in s, as well as all the named fields that are typically not
101+
// included. For example, a set made of "a.b.c" will end-up also owning
102+
// "a" if it's a named fields but not "a.b" if it's a map.
103+
func (s *Set) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.TypeRef) *Set {
104+
members := PathElementSet{
105+
members: make(sortedPathElements, 0, s.Members.Size()+len(s.Children.members)),
106+
}
107+
atom, _ := sc.Resolve(tr)
108+
members.members = append(members.members, s.Members.members...)
109+
for _, node := range s.Children.members {
110+
// Only insert named fields.
111+
if node.pathElement.FieldName != nil && atom.Map != nil {
112+
if _, has := atom.Map.FindField(*node.pathElement.FieldName); has {
113+
members.Insert(node.pathElement)
114+
}
115+
}
116+
}
117+
return &Set{
118+
Members: members,
119+
Children: *s.Children.EnsureNamedFieldsAreMembers(sc, tr),
120+
}
121+
}
122+
97123
// Size returns the number of members of the set.
98124
func (s *Set) Size() int {
99125
return s.Members.Size() + s.Children.Size()
@@ -333,6 +359,31 @@ func (s *SetNodeMap) Difference(s2 *Set) *SetNodeMap {
333359
return out
334360
}
335361

362+
// EnsureNamedFieldsAreMembers returns a set that contains all the named fields along with the leaves.
363+
func (s *SetNodeMap) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.TypeRef) *SetNodeMap {
364+
out := make(sortedSetNode, 0, s.Size())
365+
atom, _ := sc.Resolve(tr)
366+
for _, member := range s.members {
367+
tr := schema.TypeRef{}
368+
if member.pathElement.FieldName != nil && atom.Map != nil {
369+
tr = atom.Map.ElementType
370+
if sf, ok := atom.Map.FindField(*member.pathElement.FieldName); ok {
371+
tr = sf.Type
372+
}
373+
} else if member.pathElement.Key != nil && atom.List != nil {
374+
tr = atom.List.ElementType
375+
}
376+
out = append(out, setNode{
377+
pathElement: member.pathElement,
378+
set: member.set.EnsureNamedFieldsAreMembers(sc, tr),
379+
})
380+
}
381+
382+
return &SetNodeMap{
383+
members: out,
384+
}
385+
}
386+
336387
// Iterate calls f for each PathElement in the set.
337388
func (s *SetNodeMap) Iterate(f func(PathElement)) {
338389
for _, n := range s.members {

fieldpath/set_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ import (
2121
"fmt"
2222
"math/rand"
2323
"testing"
24+
25+
"gopkg.in/yaml.v2"
26+
"sigs.k8s.io/structured-merge-diff/v3/schema"
2427
)
2528

2629
type randomPathAlphabet []PathElement
@@ -447,6 +450,86 @@ func TestSetIntersectionDifference(t *testing.T) {
447450
})
448451
}
449452

453+
var nestedSchema = func() (*schema.Schema, schema.TypeRef) {
454+
sc := &schema.Schema{}
455+
name := "type"
456+
err := yaml.Unmarshal([]byte(`types:
457+
- name: type
458+
map:
459+
elementType:
460+
namedType: type
461+
fields:
462+
- name: named
463+
type:
464+
namedType: type
465+
- name: list
466+
type:
467+
list:
468+
elementRelationShip: associative
469+
keys: ["name"]
470+
elementType:
471+
namedType: type
472+
- name: value
473+
type:
474+
scalar: numeric
475+
`), &sc)
476+
if err != nil {
477+
panic(err)
478+
}
479+
return sc, schema.TypeRef{NamedType: &name}
480+
}
481+
482+
var _P = MakePathOrDie
483+
484+
func TestEnsureNamedFieldsAreMembers(t *testing.T) {
485+
table := []struct {
486+
set, expected *Set
487+
}{
488+
{
489+
set: NewSet(_P("named", "named", "value")),
490+
expected: NewSet(
491+
_P("named", "named", "value"),
492+
_P("named", "named"),
493+
_P("named"),
494+
),
495+
},
496+
{
497+
set: NewSet(_P("named", "a", "named", "value"), _P("a", "named", "value"), _P("a", "b", "value")),
498+
expected: NewSet(
499+
_P("named", "a", "named", "value"),
500+
_P("named", "a", "named"),
501+
_P("named"),
502+
_P("a", "named", "value"),
503+
_P("a", "named"),
504+
_P("a", "b", "value"),
505+
),
506+
},
507+
{
508+
set: NewSet(_P("named", "list", KeyByFields("name", "a"), "named", "a", "value")),
509+
expected: NewSet(
510+
_P("named", "list", KeyByFields("name", "a"), "named", "a", "value"),
511+
_P("named", "list", KeyByFields("name", "a"), "named"),
512+
_P("named", "list"),
513+
_P("named"),
514+
),
515+
},
516+
}
517+
518+
for _, test := range table {
519+
t.Run(fmt.Sprintf("%v", test.set), func(t *testing.T) {
520+
got := test.set.EnsureNamedFieldsAreMembers(nestedSchema())
521+
if !got.Equals(test.expected) {
522+
t.Errorf("expected %v, got %v (missing: %v/superfluous: %v)",
523+
test.expected,
524+
got,
525+
test.expected.Difference(got),
526+
got.Difference(test.expected),
527+
)
528+
}
529+
})
530+
}
531+
}
532+
450533
func TestSetNodeMapIterate(t *testing.T) {
451534
set := &SetNodeMap{}
452535
toAdd := 5

merge/leaf_test.go

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ func TestUpdateLeaf(t *testing.T) {
345345
),
346346
},
347347
},
348-
"apply_twice_dangling": {
348+
"apply_twice_remove": {
349349
Ops: []Operation{
350350
Apply{
351351
Manager: "default",
@@ -365,9 +365,7 @@ func TestUpdateLeaf(t *testing.T) {
365365
},
366366
},
367367
Object: `
368-
numeric: 1
369368
string: "new string"
370-
bool: false
371369
`,
372370
APIVersion: "v1",
373371
Managed: fieldpath.ManagedFields{
@@ -380,7 +378,43 @@ func TestUpdateLeaf(t *testing.T) {
380378
),
381379
},
382380
},
383-
"apply_twice_dangling_different_version": {
381+
"update_apply_omits": {
382+
Ops: []Operation{
383+
Apply{
384+
Manager: "default",
385+
APIVersion: "v1",
386+
Object: `
387+
numeric: 2
388+
`,
389+
},
390+
Update{
391+
Manager: "controller",
392+
APIVersion: "v1",
393+
Object: `
394+
numeric: 1
395+
`,
396+
},
397+
Apply{
398+
Manager: "default",
399+
APIVersion: "v1",
400+
Object: ``,
401+
},
402+
},
403+
Object: `
404+
numeric: 1
405+
`,
406+
APIVersion: "v1",
407+
Managed: fieldpath.ManagedFields{
408+
"controller": fieldpath.NewVersionedSet(
409+
_NS(
410+
_P("numeric"),
411+
),
412+
"v1",
413+
false,
414+
),
415+
},
416+
},
417+
"apply_twice_remove_different_version": {
384418
Ops: []Operation{
385419
Apply{
386420
Manager: "default",
@@ -400,9 +434,7 @@ func TestUpdateLeaf(t *testing.T) {
400434
},
401435
},
402436
Object: `
403-
numeric: 1
404437
string: "new string"
405-
bool: false
406438
`,
407439
APIVersion: "v1",
408440
Managed: fieldpath.ManagedFields{
@@ -462,7 +494,6 @@ func TestUpdateLeaf(t *testing.T) {
462494
},
463495
},
464496
Object: `
465-
string: "string"
466497
`,
467498
APIVersion: "v1",
468499
Managed: fieldpath.ManagedFields{},

0 commit comments

Comments
 (0)