Skip to content

Commit 787950f

Browse files
authored
Merge pull request #63 from apelisse/remove-unknown-typed-sets
Drop managed fields for obsolete versions
2 parents e5e0297 + 1ebf29f commit 787950f

File tree

3 files changed

+146
-0
lines changed

3 files changed

+146
-0
lines changed

internal/fixture/state.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ func (dummyConverter) Convert(v typed.TypedValue, version fieldpath.APIVersion)
158158
return v, nil
159159
}
160160

161+
func (dummyConverter) IsMissingVersionError(err error) bool {
162+
return false
163+
}
164+
161165
// Operation is a step that will run when building a table-driven test.
162166
type Operation interface {
163167
run(*State) error

merge/obsolete_versions_test.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
Copyright 2019 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 merge_test
18+
19+
import (
20+
"fmt"
21+
"testing"
22+
23+
"sigs.k8s.io/structured-merge-diff/fieldpath"
24+
"sigs.k8s.io/structured-merge-diff/internal/fixture"
25+
"sigs.k8s.io/structured-merge-diff/merge"
26+
"sigs.k8s.io/structured-merge-diff/typed"
27+
)
28+
29+
// specificVersionConverter doesn't convert and return the exact same
30+
// object, but only for versions that are explicitely listed.
31+
type specificVersionConverter struct {
32+
AcceptedVersions []fieldpath.APIVersion
33+
}
34+
35+
func (d *specificVersionConverter) Convert(object typed.TypedValue, version fieldpath.APIVersion) (typed.TypedValue, error) {
36+
for _, v := range d.AcceptedVersions {
37+
if v == version {
38+
return object, nil
39+
}
40+
}
41+
return nil, fmt.Errorf("Unknown version: %v", version)
42+
}
43+
44+
func (d *specificVersionConverter) IsMissingVersionError(err error) bool {
45+
return err != nil
46+
}
47+
48+
// Managers of fields in a version that no longer exist are
49+
// automatically removed. Make sure this works as intended.
50+
func TestObsoleteVersions(t *testing.T) {
51+
converter := &specificVersionConverter{
52+
AcceptedVersions: []fieldpath.APIVersion{"v1", "v2"},
53+
}
54+
state := fixture.State{
55+
Updater: &merge.Updater{Converter: converter},
56+
Parser: typed.DeducedParseableType{},
57+
}
58+
59+
if err := state.Update(typed.YAMLObject(`{"v1": 0}`), fieldpath.APIVersion("v1"), "v1"); err != nil {
60+
t.Fatalf("Failed to apply: %v", err)
61+
}
62+
if err := state.Update(typed.YAMLObject(`{"v1": 0, "v2": 0}`), fieldpath.APIVersion("v2"), "v2"); err != nil {
63+
t.Fatalf("Failed to apply: %v", err)
64+
}
65+
// Remove v1, add v3 instead.
66+
converter.AcceptedVersions = []fieldpath.APIVersion{"v2", "v3"}
67+
68+
if err := state.Update(typed.YAMLObject(`{"v1": 0, "v2": 0, "v3": 0}`), fieldpath.APIVersion("v3"), "v3"); err != nil {
69+
t.Fatalf("Failed to apply: %v", err)
70+
}
71+
72+
managers := fieldpath.ManagedFields{
73+
"v2": &fieldpath.VersionedSet{
74+
Set: _NS(
75+
_P("v2"),
76+
),
77+
APIVersion: "v2",
78+
},
79+
"v3": &fieldpath.VersionedSet{
80+
Set: _NS(
81+
_P("v3"),
82+
),
83+
APIVersion: "v3",
84+
},
85+
}
86+
if diff := state.Managers.Difference(managers); len(diff) != 0 {
87+
t.Fatalf("expected Managers to be %v, got %v", managers, state.Managers)
88+
}
89+
}
90+
91+
func TestApplyObsoleteVersion(t *testing.T) {
92+
converter := &specificVersionConverter{
93+
AcceptedVersions: []fieldpath.APIVersion{"v1"},
94+
}
95+
parser, err := typed.NewParser(`types:
96+
- name: sets
97+
struct:
98+
fields:
99+
- name: list
100+
type:
101+
list:
102+
elementType:
103+
scalar: string
104+
elementRelationship: associative`)
105+
if err != nil {
106+
t.Fatalf("Failed to create parser: %v", err)
107+
}
108+
state := fixture.State{
109+
Updater: &merge.Updater{Converter: converter},
110+
Parser: parser.Type("sets"),
111+
}
112+
113+
if err := state.Apply(typed.YAMLObject(`{"list": ["a", "b", "c", "d"]}`), fieldpath.APIVersion("v1"), "apply", false); err != nil {
114+
t.Fatalf("Failed to apply: %v", err)
115+
}
116+
// Remove v1, add v2 instead.
117+
converter.AcceptedVersions = []fieldpath.APIVersion{"v2"}
118+
119+
if err := state.Apply(typed.YAMLObject(`{"list": ["a"]}`), fieldpath.APIVersion("v2"), "apply", false); err != nil {
120+
t.Fatalf("Failed to apply: %v", err)
121+
}
122+
123+
comparison, err := state.CompareLive(`{"list": ["a", "b", "c", "d"]}`)
124+
if err != nil {
125+
t.Fatalf("Failed to compare live object: %v", err)
126+
}
127+
if !comparison.IsSame() {
128+
t.Fatalf("Unexpected object:\n%v", comparison)
129+
}
130+
}

merge/update.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
// needs to be able to convert objects from one version to another.
2525
type Converter interface {
2626
Convert(object typed.TypedValue, version fieldpath.APIVersion) (typed.TypedValue, error)
27+
IsMissingVersionError(error) bool
2728
}
2829

2930
// Updater is the object used to compute updated FieldSets and also
@@ -57,10 +58,18 @@ func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpat
5758
var err error
5859
versioned.oldObject, err = s.Converter.Convert(oldObject, managerSet.APIVersion)
5960
if err != nil {
61+
if s.Converter.IsMissingVersionError(err) {
62+
delete(managers, manager)
63+
continue
64+
}
6065
return nil, fmt.Errorf("failed to convert old object: %v", err)
6166
}
6267
versioned.newObject, err = s.Converter.Convert(newObject, managerSet.APIVersion)
6368
if err != nil {
69+
if s.Converter.IsMissingVersionError(err) {
70+
delete(managers, manager)
71+
continue
72+
}
6473
return nil, fmt.Errorf("failed to convert new object: %v", err)
6574
}
6675
versions[managerSet.APIVersion] = versioned
@@ -158,6 +167,9 @@ func (s *Updater) removeDisownedItems(merged, applied typed.TypedValue, lastSet
158167
}
159168
convertedApplied, err := s.Converter.Convert(applied, lastSet.APIVersion)
160169
if err != nil {
170+
if s.Converter.IsMissingVersionError(err) {
171+
return merged, nil
172+
}
161173
return nil, fmt.Errorf("failed to convert applied config to last applied version: %v", err)
162174
}
163175
appliedSet, err := convertedApplied.ToFieldSet()

0 commit comments

Comments
 (0)