diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index c843bf3396..8eacee9a4c 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -4453,23 +4453,23 @@ }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/fieldpath", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/merge", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/schema", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/typed", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/value", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/yaml", diff --git a/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json b/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json index 3e8fdd9ebf..cf1d905fe3 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json @@ -2600,23 +2600,23 @@ }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/fieldpath", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/merge", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/schema", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/typed", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/value", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/yaml", diff --git a/staging/src/k8s.io/apiserver/Godeps/Godeps.json b/staging/src/k8s.io/apiserver/Godeps/Godeps.json index 0ebf89c7ed..cc7bc60f99 100644 --- a/staging/src/k8s.io/apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/apiserver/Godeps/Godeps.json @@ -2116,23 +2116,23 @@ }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/fieldpath", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/merge", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/schema", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/typed", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/value", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/yaml", diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go index 6dbc840d01..744da85b65 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go @@ -114,6 +114,9 @@ func BuildManagerIdentifier(encodedManager *metav1.ManagedFieldsEntry) (manager func decodeVersionedSet(encodedVersionedSet *metav1.ManagedFieldsEntry) (versionedSet *fieldpath.VersionedSet, err error) { versionedSet = &fieldpath.VersionedSet{} versionedSet.APIVersion = fieldpath.APIVersion(encodedVersionedSet.APIVersion) + if encodedVersionedSet.Operation == metav1.ManagedFieldsOperationApply { + versionedSet.Applied = true + } fields := metav1.Fields{} if encodedVersionedSet.Fields != nil { @@ -179,8 +182,11 @@ func encodeManagerVersionedSet(manager string, versionedSet *fieldpath.Versioned return nil, fmt.Errorf("error unmarshalling manager identifier %v: %v", manager, err) } - // Get the APIVersion and Fields from the VersionedSet + // Get the APIVersion, Operation, and Fields from the VersionedSet encodedVersionedSet.APIVersion = string(versionedSet.APIVersion) + if versionedSet.Applied { + encodedVersionedSet.Operation = metav1.ManagedFieldsOperationApply + } fields, err := SetToFields(*versionedSet.Set) if err != nil { return nil, fmt.Errorf("error encoding set: %v", err) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/versionconverter.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/versionconverter.go index 05770b0aeb..66c63c3877 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/versionconverter.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/versionconverter.go @@ -81,3 +81,8 @@ func (v *versionConverter) Convert(object typed.TypedValue, version fieldpath.AP // Convert the object back to a smd typed value and return it. return v.typeConverter.ObjectToTyped(convertedObject) } + +// IsMissingVersionError +func (v *versionConverter) IsMissingVersionError(err error) bool { + return runtime.IsNotRegisteredError(err) +} diff --git a/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json b/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json index 1c0252238e..add44449b7 100644 --- a/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json +++ b/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json @@ -1856,23 +1856,23 @@ }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/fieldpath", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/merge", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/schema", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/typed", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/value", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/yaml", diff --git a/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json b/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json index 6a7a3e5dae..c32cfe9b19 100644 --- a/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json @@ -1808,23 +1808,23 @@ }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/fieldpath", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/merge", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/schema", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/typed", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/value", - "Rev": "e5e029740eb81ee0217ecf9d950c25a0eeb9688a" + "Rev": "e85c7b244fd2cc57bb829d73a061f93a441e63ce" }, { "ImportPath": "sigs.k8s.io/yaml", diff --git a/vendor/sigs.k8s.io/structured-merge-diff/fieldpath/managers.go b/vendor/sigs.k8s.io/structured-merge-diff/fieldpath/managers.go index a6adb0e016..eaff3171c2 100644 --- a/vendor/sigs.k8s.io/structured-merge-diff/fieldpath/managers.go +++ b/vendor/sigs.k8s.io/structured-merge-diff/fieldpath/managers.go @@ -20,6 +20,7 @@ type APIVersion string type VersionedSet struct { *Set APIVersion APIVersion + Applied bool } // ManagedFields is a map from manager to VersionedSet (what they own in diff --git a/vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go b/vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go index a4290c4d94..e561093b6e 100644 --- a/vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go +++ b/vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go @@ -80,7 +80,6 @@ func (s *Set) Intersection(s2 *Set) *Set { // Difference returns a Set containing elements which: // * appear in s // * do not appear in s2 -// * and are not children of elements that appear in s2. // // In other words, for leaf fields, this acts like a regular set difference // operation. When non leaf fields are compared with leaf fields ("parents" @@ -284,9 +283,6 @@ func (s *SetNodeMap) Difference(s2 *Set) *SetNodeMap { out := &SetNodeMap{} for k, sn := range s.members { pe := sn.pathElement - if s2.Members.Has(pe) { - continue - } if sn2, ok := s2.Children.members[k]; ok { diff := *sn.set.Difference(sn2.set) // We aren't permitted to add nodes with no elements. diff --git a/vendor/sigs.k8s.io/structured-merge-diff/merge/update.go b/vendor/sigs.k8s.io/structured-merge-diff/merge/update.go index 5572258f23..b4543674e6 100644 --- a/vendor/sigs.k8s.io/structured-merge-diff/merge/update.go +++ b/vendor/sigs.k8s.io/structured-merge-diff/merge/update.go @@ -24,6 +24,7 @@ import ( // needs to be able to convert objects from one version to another. type Converter interface { Convert(object typed.TypedValue, version fieldpath.APIVersion) (typed.TypedValue, error) + IsMissingVersionError(error) bool } // Updater is the object used to compute updated FieldSets and also @@ -33,10 +34,8 @@ type Updater struct { } func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, workflow string, force bool) (fieldpath.ManagedFields, error) { - if managers == nil { - managers = fieldpath.ManagedFields{} - } conflicts := fieldpath.ManagedFields{} + removed := fieldpath.ManagedFields{} type Versioned struct { oldObject typed.TypedValue newObject typed.TypedValue @@ -57,10 +56,18 @@ func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpat var err error versioned.oldObject, err = s.Converter.Convert(oldObject, managerSet.APIVersion) if err != nil { + if s.Converter.IsMissingVersionError(err) { + delete(managers, manager) + continue + } return nil, fmt.Errorf("failed to convert old object: %v", err) } versioned.newObject, err = s.Converter.Convert(newObject, managerSet.APIVersion) if err != nil { + if s.Converter.IsMissingVersionError(err) { + delete(managers, manager) + continue + } return nil, fmt.Errorf("failed to convert new object: %v", err) } versions[managerSet.APIVersion] = versioned @@ -77,6 +84,13 @@ func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpat APIVersion: managerSet.APIVersion, } } + + if !compare.Removed.Empty() { + removed[manager] = &fieldpath.VersionedSet{ + Set: compare.Removed, + APIVersion: managerSet.APIVersion, + } + } } if !force && len(conflicts) != 0 { @@ -85,14 +99,15 @@ func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpat for manager, conflictSet := range conflicts { managers[manager].Set = managers[manager].Set.Difference(conflictSet.Set) - if managers[manager].Set.Empty() { - delete(managers, manager) - } } - if _, ok := managers[workflow]; !ok { - managers[workflow] = &fieldpath.VersionedSet{ - Set: fieldpath.NewSet(), + for manager, removedSet := range removed { + managers[manager].Set = managers[manager].Set.Difference(removedSet.Set) + } + + for manager := range managers { + if managers[manager].Set.Empty() { + delete(managers, manager) } } @@ -106,6 +121,7 @@ func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpat // this is a CREATE call). func (s *Updater) Update(liveObject, newObject typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string) (fieldpath.ManagedFields, error) { var err error + managers = shallowCopyManagers(managers) managers, err = s.update(liveObject, newObject, version, managers, manager, true) if err != nil { return fieldpath.ManagedFields{}, err @@ -114,6 +130,11 @@ func (s *Updater) Update(liveObject, newObject typed.TypedValue, version fieldpa if err != nil { return fieldpath.ManagedFields{}, fmt.Errorf("failed to compare live and new objects: %v", err) } + if _, ok := managers[manager]; !ok { + managers[manager] = &fieldpath.VersionedSet{ + Set: fieldpath.NewSet(), + } + } managers[manager].Set = managers[manager].Set.Union(compare.Modified).Union(compare.Added).Difference(compare.Removed) managers[manager].APIVersion = version if managers[manager].Set.Empty() { @@ -126,18 +147,12 @@ func (s *Updater) Update(liveObject, newObject typed.TypedValue, version fieldpa // well as the configuration that is applied. This will merge the object // and return it. func (s *Updater) Apply(liveObject, configObject typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string, force bool) (typed.TypedValue, fieldpath.ManagedFields, error) { + managers = shallowCopyManagers(managers) newObject, err := liveObject.Merge(configObject) if err != nil { return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to merge config: %v", err) } - managers, err = s.update(liveObject, newObject, version, managers, manager, force) - if err != nil { - return nil, fieldpath.ManagedFields{}, err - } - newObject, err = s.removeDisownedItems(newObject, configObject, managers[manager]) - if err != nil { - return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to remove fields: %v", err) - } + lastSet := managers[manager] set, err := configObject.ToFieldSet() if err != nil { return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to get field set: %v", err) @@ -145,24 +160,114 @@ func (s *Updater) Apply(liveObject, configObject typed.TypedValue, version field managers[manager] = &fieldpath.VersionedSet{ Set: set, APIVersion: version, + Applied: true, } - if managers[manager].Set.Empty() { - delete(managers, manager) + newObject, err = s.prune(newObject, managers, manager, lastSet) + if err != nil { + return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to prune fields: %v", err) + } + managers, err = s.update(liveObject, newObject, version, managers, manager, force) + if err != nil { + return nil, fieldpath.ManagedFields{}, err } return newObject, managers, nil } -func (s *Updater) removeDisownedItems(merged, applied typed.TypedValue, lastSet *fieldpath.VersionedSet) (typed.TypedValue, error) { - if lastSet.Set.Empty() { +func shallowCopyManagers(managers fieldpath.ManagedFields) fieldpath.ManagedFields { + newManagers := fieldpath.ManagedFields{} + for manager, set := range managers { + newManagers[manager] = set + } + return newManagers +} + +// prune will remove a list or map item, iff: +// * applyingManager applied it last time +// * applyingManager didn't apply it this time +// * no other applier claims to manage it +func (s *Updater) prune(merged typed.TypedValue, managers fieldpath.ManagedFields, applyingManager string, lastSet *fieldpath.VersionedSet) (typed.TypedValue, error) { + if lastSet == nil || lastSet.Set.Empty() { return merged, nil } - convertedApplied, err := s.Converter.Convert(applied, lastSet.APIVersion) + convertedMerged, err := s.Converter.Convert(merged, lastSet.APIVersion) if err != nil { - return nil, fmt.Errorf("failed to convert applied config to last applied version: %v", err) + if s.Converter.IsMissingVersionError(err) { + return merged, nil + } + return nil, fmt.Errorf("failed to convert merged object to last applied version: %v", err) } - appliedSet, err := convertedApplied.ToFieldSet() + pruned := convertedMerged.RemoveItems(lastSet.Set) + pruned, err = s.addBackOwnedItems(convertedMerged, pruned, managers, applyingManager) if err != nil { - return nil, fmt.Errorf("failed to create field set from applied config in last applied version: %v", err) + return nil, fmt.Errorf("failed add back owned items: %v", err) } - return merged.RemoveItems(lastSet.Set.Difference(appliedSet)), nil + pruned, err = s.addBackDanglingItems(convertedMerged, pruned, lastSet) + if err != nil { + return nil, fmt.Errorf("failed add back dangling items: %v", err) + } + return s.Converter.Convert(pruned, managers[applyingManager].APIVersion) +} + +// addBackOwnedItems adds back any list and map items that were removed by prune, +// but other appliers (or the current applier's new config) claim to own. +func (s *Updater) addBackOwnedItems(merged, pruned typed.TypedValue, managedFields fieldpath.ManagedFields, applyingManager string) (typed.TypedValue, error) { + var err error + managedAtVersion := map[fieldpath.APIVersion]*fieldpath.Set{} + for _, managerSet := range managedFields { + if managerSet.Applied { + if _, ok := managedAtVersion[managerSet.APIVersion]; !ok { + managedAtVersion[managerSet.APIVersion] = fieldpath.NewSet() + } + managedAtVersion[managerSet.APIVersion] = managedAtVersion[managerSet.APIVersion].Union(managerSet.Set) + } + } + for version, managed := range managedAtVersion { + merged, err = s.Converter.Convert(merged, version) + if err != nil { + if s.Converter.IsMissingVersionError(err) { + continue + } + return nil, fmt.Errorf("failed to convert merged object at version %v: %v", version, err) + } + pruned, err = s.Converter.Convert(pruned, version) + if err != nil { + if s.Converter.IsMissingVersionError(err) { + continue + } + return nil, fmt.Errorf("failed to convert pruned object at version %v: %v", version, err) + } + mergedSet, err := merged.ToFieldSet() + if err != nil { + return nil, fmt.Errorf("failed to create field set from merged object at version %v: %v", version, err) + } + prunedSet, err := pruned.ToFieldSet() + if err != nil { + return nil, fmt.Errorf("failed to create field set from pruned object at version %v: %v", version, err) + } + pruned = merged.RemoveItems(mergedSet.Difference(prunedSet.Union(managed))) + } + return pruned, nil +} + + +// addBackDanglingItems makes sure that the only items removed by prune are items that were +// previously owned by the currently applying manager. This will add back unowned items and items +// which are owned by Updaters that shouldn't be removed. +func (s *Updater) addBackDanglingItems(merged, pruned typed.TypedValue, lastSet *fieldpath.VersionedSet) (typed.TypedValue, error) { + convertedPruned, err := s.Converter.Convert(pruned, lastSet.APIVersion) + if err != nil { + if s.Converter.IsMissingVersionError(err) { + return merged, nil + } + return nil, fmt.Errorf("failed to convert pruned object to last applied version: %v", err) + } + prunedSet, err := convertedPruned.ToFieldSet() + if err != nil { + return nil, fmt.Errorf("failed to create field set from pruned object in last applied version: %v", err) + } + mergedSet, err := merged.ToFieldSet() + if err != nil { + return nil, fmt.Errorf("failed to create field set from merged object in last applied version: %v", err) + } + return merged.RemoveItems(mergedSet.Difference(prunedSet).Intersection(lastSet.Set)), nil } diff --git a/vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go b/vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go index 657fd1467f..f3ba803d83 100644 --- a/vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go +++ b/vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go @@ -86,6 +86,10 @@ func (w *removingWalker) doList(t schema.List) (errs ValidationErrors) { newItems = append(newItems, l.Items[i]) } l.Items = newItems + if len(l.Items) == 0 { + w.value.ListValue = nil + w.value.Null = true + } return nil } @@ -111,6 +115,10 @@ func (w *removingWalker) doMap(t schema.Map) ValidationErrors { newMap.Set(item.Name, m.Items[i].Value) } w.value.MapValue = newMap + if len(w.value.MapValue.Items) == 0 { + w.value.MapValue = nil + w.value.Null = true + } return nil } diff --git a/vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go b/vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go index f3134b00d1..d7705c986c 100644 --- a/vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go +++ b/vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go @@ -31,7 +31,7 @@ type TypedValue interface { AsValue() *value.Value // Validate returns an error with a list of every spec violation. Validate() error - // ToFieldSet creates a set containing every leaf field mentioned, or + // ToFieldSet creates a set containing every leaf field and item mentioned, or // validation errors, if any were encountered. ToFieldSet() (*fieldpath.Set, error) // Merge returns the result of merging tv and pso ("partially specified @@ -109,6 +109,7 @@ func (tv typedValue) ToFieldSet() (*fieldpath.Set, error) { s := fieldpath.NewSet() w := tv.walker() w.leafFieldCallback = func(p fieldpath.Path) { s.Insert(p) } + w.nodeFieldCallback = func(p fieldpath.Path) { s.Insert(p) } if errs := w.validate(); len(errs) != 0 { return nil, errs } @@ -163,8 +164,10 @@ func (tv typedValue) Compare(rhs TypedValue) (c *Comparison, err error) { // RemoveItems removes each provided list or map item from the value. func (tv typedValue) RemoveItems(items *fieldpath.Set) TypedValue { - removeItemsWithSchema(&tv.value, items, tv.schema, tv.typeRef) - return tv + copied := tv + copied.value, _ = value.FromUnstructured(tv.value.ToUnstructured(true)) + removeItemsWithSchema(&copied.value, items, copied.schema, copied.typeRef) + return copied } func merge(lhs, rhs typedValue, rule, postRule mergeRule) (TypedValue, error) { diff --git a/vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go b/vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go index 9edf6fb64d..25807c9f3f 100644 --- a/vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go +++ b/vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go @@ -42,6 +42,11 @@ type validatingObjectWalker struct { // * untyped fields leafFieldCallback func(fieldpath.Path) + // If set, this is called on "node fields": + // * list items + // * map items + nodeFieldCallback func(fieldpath.Path) + // internal housekeeping--don't set when constructing. inLeaf bool // Set to true if we're in a "big leaf"--atomic map/list } @@ -67,6 +72,21 @@ func (v *validatingObjectWalker) doLeaf() { } } +// doNode should be called on nodes after descending into children +func (v *validatingObjectWalker) doNode() { + if v.inLeaf { + // We're in a "big leaf", an atomic map or list. Ignore + // subsequent leaves. + return + } + + if v.nodeFieldCallback != nil { + // At the moment, this is only used to build fieldsets; we can + // add more than the path in here if needed. + v.nodeFieldCallback(v.path) + } +} + func (v validatingObjectWalker) doScalar(t schema.Scalar) ValidationErrors { if errs := v.validateScalar(t, &v.value, ""); len(errs) > 0 { return errs @@ -144,6 +164,8 @@ func (v validatingObjectWalker) visitListItems(t schema.List, list *value.List) v2.value = child v2.typeRef = t.ElementType errs = append(errs, v2.validate()...) + + v2.doNode() } return errs } @@ -175,6 +197,8 @@ func (v validatingObjectWalker) visitMapItems(t schema.Map, m *value.Map) (errs v2.value = item.Value v2.typeRef = t.ElementType errs = append(errs, v2.validate()...) + + v2.doNode() } return errs }