Upgrade k8s package verison (#5358)

* upgrade k8s package version

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>

* Script upgrade and code formatting.

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>
This commit is contained in:
hongzhouzi
2022-11-15 14:56:38 +08:00
committed by GitHub
parent 5f91c1663a
commit 44167aa47a
3106 changed files with 321340 additions and 172080 deletions

View File

@@ -250,35 +250,56 @@ func (s *Updater) addBackOwnedItems(merged, pruned *typed.TypedValue, managedFie
}
managedAtVersion[managerSet.APIVersion()] = managedAtVersion[managerSet.APIVersion()].Union(managerSet.Set())
}
// Add back owned items at pruned version first to avoid conversion failure
// caused by pruned fields which are required for conversion.
prunedVersion := fieldpath.APIVersion(*pruned.TypeRef().NamedType)
if managed, ok := managedAtVersion[prunedVersion]; ok {
merged, pruned, err = s.addBackOwnedItemsForVersion(merged, pruned, prunedVersion, managed)
if err != nil {
return nil, err
}
delete(managedAtVersion, prunedVersion)
}
for version, managed := range managedAtVersion {
merged, err = s.Converter.Convert(merged, version)
merged, pruned, err = s.addBackOwnedItemsForVersion(merged, pruned, version, managed)
if err != nil {
if s.Converter.IsMissingVersionError(err) {
continue
}
return nil, fmt.Errorf("failed to convert merged object at version %v: %v", version, err)
return nil, 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)
}
sc, tr := merged.Schema(), merged.TypeRef()
pruned = merged.RemoveItems(mergedSet.EnsureNamedFieldsAreMembers(sc, tr).Difference(prunedSet.EnsureNamedFieldsAreMembers(sc, tr).Union(managed.EnsureNamedFieldsAreMembers(sc, tr))))
}
return pruned, nil
}
// addBackOwnedItemsForVersion adds back any fields, list and map items that were removed by prune with specific managed field path at a version.
// It is an extracted sub-function from addBackOwnedItems for code reuse.
func (s *Updater) addBackOwnedItemsForVersion(merged, pruned *typed.TypedValue, version fieldpath.APIVersion, managed *fieldpath.Set) (*typed.TypedValue, *typed.TypedValue, error) {
var err error
merged, err = s.Converter.Convert(merged, version)
if err != nil {
if s.Converter.IsMissingVersionError(err) {
return merged, pruned, nil
}
return nil, 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) {
return merged, pruned, nil
}
return nil, nil, fmt.Errorf("failed to convert pruned object at version %v: %v", version, err)
}
mergedSet, err := merged.ToFieldSet()
if err != nil {
return nil, 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, nil, fmt.Errorf("failed to create field set from pruned object at version %v: %v", version, err)
}
sc, tr := merged.Schema(), merged.TypeRef()
pruned = merged.RemoveItems(mergedSet.EnsureNamedFieldsAreMembers(sc, tr).Difference(prunedSet.EnsureNamedFieldsAreMembers(sc, tr).Union(managed.EnsureNamedFieldsAreMembers(sc, tr))))
return merged, pruned, nil
}
// addBackDanglingItems makes sure that the fields list and map items removed by prune were
// previously owned by the currently applying manager. This will add back fields list and map items
// that are unowned or that are owned by Updaters and shouldn't be removed.

View File

@@ -16,7 +16,9 @@ limitations under the License.
package schema
import "sync"
import (
"sync"
)
// Schema is a list of named types.
//
@@ -27,6 +29,11 @@ type Schema struct {
once sync.Once
m map[string]TypeDef
lock sync.Mutex
// Cached results of resolving type references to atoms. Only stores
// type references which require fields of Atom to be overriden.
resolvedTypes map[TypeRef]Atom
}
// A TypeSpecifier references a particular type in a schema.
@@ -48,6 +55,12 @@ type TypeRef struct {
// Either the name or one member of Atom should be set.
NamedType *string `yaml:"namedType,omitempty"`
Inlined Atom `yaml:",inline,omitempty"`
// If this reference refers to a map-type or list-type, this field overrides
// the `ElementRelationship` of the referred type when resolved.
// If this field is nil, then it has no effect.
// See `Map` and `List` for more information about `ElementRelationship`
ElementRelationship *ElementRelationship `yaml:"elementRelationship,omitempty"`
}
// Atom represents the smallest possible pieces of the type system.
@@ -88,11 +101,11 @@ const (
// Map is a key-value pair. Its default semantics are the same as an
// associative list, but:
// * It is serialized differently:
// - It is serialized differently:
// map: {"k": {"value": "v"}}
// list: [{"key": "k", "value": "v"}]
// * Keys must be string typed.
// * Keys can't have multiple components.
// - Keys must be string typed.
// - Keys can't have multiple components.
//
// Optionally, maps may be atomic (for example, imagine representing an RGB
// color value--it doesn't make sense to have different actors own the R and G
@@ -146,6 +159,31 @@ func (m *Map) FindField(name string) (StructField, bool) {
return sf, ok
}
// CopyInto this instance of Map into the other
// If other is nil this method does nothing.
// If other is already initialized, overwrites it with this instance
// Warning: Not thread safe
func (m *Map) CopyInto(dst *Map) {
if dst == nil {
return
}
// Map type is considered immutable so sharing references
dst.Fields = m.Fields
dst.ElementType = m.ElementType
dst.Unions = m.Unions
dst.ElementRelationship = m.ElementRelationship
if m.m != nil {
// If cache is non-nil then the once token had been consumed.
// Must reset token and use it again to ensure same semantics.
dst.once = sync.Once{}
dst.once.Do(func() {
dst.m = m.m
})
}
}
// UnionFields are mapping between the fields that are part of the union and
// their discriminated value. The discriminated value has to be set, and
// should not conflict with other discriminated value in the list.
@@ -244,18 +282,93 @@ func (s *Schema) FindNamedType(name string) (TypeDef, bool) {
return t, ok
}
func (s *Schema) resolveNoOverrides(tr TypeRef) (Atom, bool) {
result := Atom{}
if tr.NamedType != nil {
t, ok := s.FindNamedType(*tr.NamedType)
if !ok {
return Atom{}, false
}
result = t.Atom
} else {
result = tr.Inlined
}
return result, true
}
// Resolve is a convenience function which returns the atom referenced, whether
// it is inline or named. Returns (Atom{}, false) if the type can't be resolved.
//
// This allows callers to not care about the difference between a (possibly
// inlined) reference and a definition.
func (s *Schema) Resolve(tr TypeRef) (Atom, bool) {
if tr.NamedType != nil {
t, ok := s.FindNamedType(*tr.NamedType)
if !ok {
// If this is a plain reference with no overrides, just return the type
if tr.ElementRelationship == nil {
return s.resolveNoOverrides(tr)
}
s.lock.Lock()
defer s.lock.Unlock()
if s.resolvedTypes == nil {
s.resolvedTypes = make(map[TypeRef]Atom)
}
var result Atom
var exists bool
// Return cached result if available
// If not, calculate result and cache it
if result, exists = s.resolvedTypes[tr]; !exists {
if result, exists = s.resolveNoOverrides(tr); exists {
// Allow field-level electives to override the referred type's modifiers
switch {
case result.Map != nil:
mapCopy := Map{}
result.Map.CopyInto(&mapCopy)
mapCopy.ElementRelationship = *tr.ElementRelationship
result.Map = &mapCopy
case result.List != nil:
listCopy := *result.List
listCopy.ElementRelationship = *tr.ElementRelationship
result.List = &listCopy
case result.Scalar != nil:
return Atom{}, false
default:
return Atom{}, false
}
} else {
return Atom{}, false
}
return t.Atom, true
// Save result. If it is nil, that is also recorded as not existing.
s.resolvedTypes[tr] = result
}
return result, true
}
// Clones this instance of Schema into the other
// If other is nil this method does nothing.
// If other is already initialized, overwrites it with this instance
// Warning: Not thread safe
func (s *Schema) CopyInto(dst *Schema) {
if dst == nil {
return
}
// Schema type is considered immutable so sharing references
dst.Types = s.Types
if s.m != nil {
// If cache is non-nil then the once token had been consumed.
// Must reset token and use it again to ensure same semantics.
dst.once = sync.Once{}
dst.once.Do(func() {
dst.m = s.m
})
}
return tr.Inlined, true
}

View File

@@ -52,6 +52,9 @@ func (a *TypeRef) Equals(b *TypeRef) bool {
}
//return true
}
if a.ElementRelationship != b.ElementRelationship {
return false
}
return a.Inlined.Equals(&b.Inlined)
}

View File

@@ -66,6 +66,9 @@ var SchemaSchemaYAML = `types:
- name: untyped
type:
namedType: untyped
- name: elementRelationship
type:
scalar: string
- name: scalar
scalar: string
- name: map

View File

@@ -105,7 +105,11 @@ type atomHandler interface {
func resolveSchema(s *schema.Schema, tr schema.TypeRef, v value.Value, ah atomHandler) ValidationErrors {
a, ok := s.Resolve(tr)
if !ok {
return errorf("schema error: no type found matching: %v", *tr.NamedType)
typeName := "inlined type"
if tr.NamedType != nil {
typeName = *tr.NamedType
}
return errorf("schema error: no type found matching: %v", typeName)
}
a = deduceAtom(a, v)

View File

@@ -17,8 +17,6 @@ limitations under the License.
package typed
import (
"math"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
"sigs.k8s.io/structured-merge-diff/v4/schema"
"sigs.k8s.io/structured-merge-diff/v4/value"
@@ -82,7 +80,12 @@ func (w *mergingWalker) merge(prefixFn func() string) (errs ValidationErrors) {
alhs := deduceAtom(a, w.lhs)
arhs := deduceAtom(a, w.rhs)
if alhs.Equals(&arhs) {
// deduceAtom does not fix the type for nil values
// nil is a wildcard and will accept whatever form the other operand takes
if w.rhs == nil {
errs = append(errs, handleAtom(alhs, w.typeRef, w)...)
} else if w.lhs == nil || alhs.Equals(&arhs) {
errs = append(errs, handleAtom(arhs, w.typeRef, w)...)
} else {
w2 := *w
@@ -170,78 +173,94 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err
if lhs != nil {
lLen = lhs.Length()
}
out := make([]interface{}, 0, int(math.Max(float64(rLen), float64(lLen))))
outLen := lLen
if outLen < rLen {
outLen = rLen
}
out := make([]interface{}, 0, outLen)
// TODO: ordering is totally wrong.
// TODO: might as well make the map order work the same way.
rhsOrder, observedRHS, rhsErrs := w.indexListPathElements(t, rhs)
errs = append(errs, rhsErrs...)
lhsOrder, observedLHS, lhsErrs := w.indexListPathElements(t, lhs)
errs = append(errs, lhsErrs...)
// This is a cheap hack to at least make the output order stable.
rhsOrder := make([]fieldpath.PathElement, 0, rLen)
// First, collect all RHS children.
observedRHS := fieldpath.MakePathElementValueMap(rLen)
if rhs != nil {
for i := 0; i < rhs.Length(); i++ {
child := rhs.At(i)
pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child)
if err != nil {
errs = append(errs, errorf("rhs: element %v: %v", i, err.Error())...)
// If we can't construct the path element, we can't
// even report errors deeper in the schema, so bail on
// this element.
continue
}
if _, ok := observedRHS.Get(pe); ok {
errs = append(errs, errorf("rhs: duplicate entries for key %v", pe.String())...)
}
observedRHS.Insert(pe, child)
rhsOrder = append(rhsOrder, pe)
sharedOrder := make([]*fieldpath.PathElement, 0, rLen)
for i := range rhsOrder {
pe := &rhsOrder[i]
if _, ok := observedLHS.Get(*pe); ok {
sharedOrder = append(sharedOrder, pe)
}
}
// Then merge with LHS children.
observedLHS := fieldpath.MakePathElementSet(lLen)
if lhs != nil {
for i := 0; i < lhs.Length(); i++ {
child := lhs.At(i)
pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child)
if err != nil {
errs = append(errs, errorf("lhs: element %v: %v", i, err.Error())...)
// If we can't construct the path element, we can't
// even report errors deeper in the schema, so bail on
// this element.
continue
}
if observedLHS.Has(pe) {
errs = append(errs, errorf("lhs: duplicate entries for key %v", pe.String())...)
continue
}
observedLHS.Insert(pe)
w2 := w.prepareDescent(pe, t.ElementType)
w2.lhs = value.Value(child)
if rchild, ok := observedRHS.Get(pe); ok {
w2.rhs = rchild
}
errs = append(errs, w2.merge(pe.String)...)
if w2.out != nil {
out = append(out, *w2.out)
}
w.finishDescent(w2)
}
var nextShared *fieldpath.PathElement
if len(sharedOrder) > 0 {
nextShared = sharedOrder[0]
sharedOrder = sharedOrder[1:]
}
for _, pe := range rhsOrder {
if observedLHS.Has(pe) {
continue
lLen, rLen = len(lhsOrder), len(rhsOrder)
for lI, rI := 0, 0; lI < lLen || rI < rLen; {
if lI < lLen && rI < rLen {
pe := lhsOrder[lI]
if pe.Equals(rhsOrder[rI]) {
// merge LHS & RHS items
lChild, _ := observedLHS.Get(pe)
rChild, _ := observedRHS.Get(pe)
mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild)
errs = append(errs, errs...)
if mergeOut != nil {
out = append(out, *mergeOut)
}
lI++
rI++
nextShared = nil
if len(sharedOrder) > 0 {
nextShared = sharedOrder[0]
sharedOrder = sharedOrder[1:]
}
continue
}
if _, ok := observedRHS.Get(pe); ok && nextShared != nil && !nextShared.Equals(lhsOrder[lI]) {
// shared item, but not the one we want in this round
lI++
continue
}
}
value, _ := observedRHS.Get(pe)
w2 := w.prepareDescent(pe, t.ElementType)
w2.rhs = value
errs = append(errs, w2.merge(pe.String)...)
if w2.out != nil {
out = append(out, *w2.out)
if lI < lLen {
pe := lhsOrder[lI]
if _, ok := observedRHS.Get(pe); !ok {
// take LHS item
lChild, _ := observedLHS.Get(pe)
mergeOut, errs := w.mergeListItem(t, pe, lChild, nil)
errs = append(errs, errs...)
if mergeOut != nil {
out = append(out, *mergeOut)
}
lI++
continue
}
}
if rI < rLen {
// Take the RHS item, merge with matching LHS item if possible
pe := rhsOrder[rI]
lChild, _ := observedLHS.Get(pe) // may be nil
rChild, _ := observedRHS.Get(pe)
mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild)
errs = append(errs, errs...)
if mergeOut != nil {
out = append(out, *mergeOut)
}
rI++
// Advance nextShared, if we are merging nextShared.
if nextShared != nil && nextShared.Equals(pe) {
nextShared = nil
if len(sharedOrder) > 0 {
nextShared = sharedOrder[0]
sharedOrder = sharedOrder[1:]
}
}
}
w.finishDescent(w2)
}
if len(out) > 0 {
@@ -252,6 +271,46 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err
return errs
}
func (w *mergingWalker) indexListPathElements(t *schema.List, list value.List) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) {
var errs ValidationErrors
length := 0
if list != nil {
length = list.Length()
}
observed := fieldpath.MakePathElementValueMap(length)
pes := make([]fieldpath.PathElement, 0, length)
for i := 0; i < length; i++ {
child := list.At(i)
pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child)
if err != nil {
errs = append(errs, errorf("element %v: %v", i, err.Error())...)
// If we can't construct the path element, we can't
// even report errors deeper in the schema, so bail on
// this element.
continue
}
if _, found := observed.Get(pe); found {
errs = append(errs, errorf("duplicate entries for key %v", pe.String())...)
continue
}
observed.Insert(pe, child)
pes = append(pes, pe)
}
return pes, observed, errs
}
func (w *mergingWalker) mergeListItem(t *schema.List, pe fieldpath.PathElement, lChild, rChild value.Value) (out *interface{}, errs ValidationErrors) {
w2 := w.prepareDescent(pe, t.ElementType)
w2.lhs = lChild
w2.rhs = rChild
errs = append(errs, w2.merge(pe.String)...)
if w2.out != nil {
out = w2.out
}
w.finishDescent(w2)
return
}
func (w *mergingWalker) derefList(prefix string, v value.Value) (value.List, ValidationErrors) {
if v == nil {
return nil, nil

View File

@@ -110,7 +110,7 @@ func (v *reconcileWithSchemaWalker) finishDescent(v2 *reconcileWithSchemaWalker)
}
// ReconcileFieldSetWithSchema reconciles the a field set with any changes to the
//// object's schema since the field set was written. Returns the reconciled field set, or nil of
// object's schema since the field set was written. Returns the reconciled field set, or nil of
// no changes were made to the field set.
//
// Supports:
@@ -124,13 +124,6 @@ func ReconcileFieldSetWithSchema(fieldset *fieldpath.Set, tv *TypedValue) (*fiel
v.schema = tv.schema
v.typeRef = tv.typeRef
// We don't reconcile deduced types, which are primarily for use by unstructured CRDs. Deduced
// types do not support atomic or granular tags. Nor does the dynamic schema deduction
// interact well with the reconcile logic.
if v.schema == DeducedParseableType.Schema {
return nil, nil
}
defer v.finished()
errs := v.reconcile()
@@ -187,19 +180,17 @@ func (v *reconcileWithSchemaWalker) visitListItems(t *schema.List, element *fiel
}
func (v *reconcileWithSchemaWalker) doList(t *schema.List) (errs ValidationErrors) {
// reconcile lists changed from granular to atomic
// reconcile lists changed from granular to atomic.
// Note that migrations from atomic to granular are not recommended and will
// be treated as if they were always granular.
//
// In this case, the manager that owned the previously atomic field (and all subfields),
// will now own just the top-level field and none of the subfields.
if !v.isAtomic && t.ElementRelationship == schema.Atomic {
v.toRemove = fieldpath.NewSet(v.path) // remove all root and all children fields
v.toAdd = fieldpath.NewSet(v.path) // add the root of the atomic
return errs
}
// reconcile lists changed from atomic to granular
if v.isAtomic && t.ElementRelationship == schema.Associative {
v.toAdd, errs = buildGranularFieldSet(v.path, v.value)
if errs != nil {
return errs
}
}
if v.fieldSet != nil {
errs = v.visitListItems(t, v.fieldSet)
}
@@ -231,7 +222,18 @@ func (v *reconcileWithSchemaWalker) visitMapItems(t *schema.Map, element *fieldp
}
func (v *reconcileWithSchemaWalker) doMap(t *schema.Map) (errs ValidationErrors) {
// reconcile maps and structs changed from granular to atomic
// We don't currently reconcile deduced types (unstructured CRDs) or maps that contain only unknown
// fields since deduced types do not yet support atomic or granular tags.
if isUntypedDeducedMap(t) {
return errs
}
// reconcile maps and structs changed from granular to atomic.
// Note that migrations from atomic to granular are not recommended and will
// be treated as if they were always granular.
//
// In this case the manager that owned the previously atomic field (and all subfields),
// will now own just the top-level field and none of the subfields.
if !v.isAtomic && t.ElementRelationship == schema.Atomic {
if v.fieldSet != nil && v.fieldSet.Size() > 0 {
v.toRemove = fieldpath.NewSet(v.path) // remove all root and all children fields
@@ -239,34 +241,12 @@ func (v *reconcileWithSchemaWalker) doMap(t *schema.Map) (errs ValidationErrors)
}
return errs
}
// reconcile maps changed from atomic to granular
if v.isAtomic && (t.ElementRelationship == schema.Separable || t.ElementRelationship == "") {
v.toAdd, errs = buildGranularFieldSet(v.path, v.value)
if errs != nil {
return errs
}
}
if v.fieldSet != nil {
errs = v.visitMapItems(t, v.fieldSet)
}
return errs
}
func buildGranularFieldSet(path fieldpath.Path, value *TypedValue) (*fieldpath.Set, ValidationErrors) {
valueFieldSet, err := value.ToFieldSet()
if err != nil {
return nil, errorf("toFieldSet: %v", err)
}
if valueFieldSetAtPath, ok := fieldSetAtPath(valueFieldSet, path); ok {
result := fieldpath.NewSet(path)
resultAtPath := descendToPath(result, path)
*resultAtPath = *valueFieldSetAtPath
return result, nil
}
return nil, nil
}
func fieldSetAtPath(node *fieldpath.Set, path fieldpath.Path) (*fieldpath.Set, bool) {
ok := true
for _, pe := range path {
@@ -293,3 +273,18 @@ func typeRefAtPath(t *schema.Map, pe fieldpath.PathElement) (schema.TypeRef, boo
}
return tr, tr != schema.TypeRef{}
}
// isUntypedDeducedMap returns true if m has no fields defined, but allows untyped elements.
// This is equivalent to a openAPI object that has x-kubernetes-preserve-unknown-fields=true
// but does not have any properties defined on the object.
func isUntypedDeducedMap(m *schema.Map) bool {
return isUntypedDeducedRef(m.ElementType) && m.Fields == nil
}
func isUntypedDeducedRef(t schema.TypeRef) bool {
if t.NamedType != nil {
return *t.NamedType == "__untyped_deduced_"
}
atom := t.Inlined
return atom.Scalar != nil && *atom.Scalar == "untyped"
}

View File

@@ -51,10 +51,22 @@ func (w *removingWalker) doScalar(t *schema.Scalar) ValidationErrors {
}
func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) {
if !w.value.IsList() {
return nil
}
l := w.value.AsListUsing(w.allocator)
defer w.allocator.Free(l)
// If list is null, empty, or atomic just return
if l == nil || l.Length() == 0 || t.ElementRelationship == schema.Atomic {
// If list is null or empty just return
if l == nil || l.Length() == 0 {
return nil
}
// atomic lists should return everything in the case of extract
// and nothing in the case of remove (!w.shouldExtract)
if t.ElementRelationship == schema.Atomic {
if w.shouldExtract {
w.out = w.value.Unstructured()
}
return nil
}
@@ -70,7 +82,7 @@ func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) {
// but ignore them when we are removing (i.e. !w.shouldExtract)
if w.toRemove.Has(path) {
if w.shouldExtract {
newItems = append(newItems, item.Unstructured())
newItems = append(newItems, removeItemsWithSchema(item, w.toRemove, w.schema, t.ElementType, w.shouldExtract).Unstructured())
} else {
continue
}
@@ -92,12 +104,24 @@ func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) {
}
func (w *removingWalker) doMap(t *schema.Map) ValidationErrors {
if !w.value.IsMap() {
return nil
}
m := w.value.AsMapUsing(w.allocator)
if m != nil {
defer w.allocator.Free(m)
}
// If map is null, empty, or atomic just return
if m == nil || m.Empty() || t.ElementRelationship == schema.Atomic {
// If map is null or empty just return
if m == nil || m.Empty() {
return nil
}
// atomic maps should return everything in the case of extract
// and nothing in the case of remove (!w.shouldExtract)
if t.ElementRelationship == schema.Atomic {
if w.shouldExtract {
w.out = w.value.Unstructured()
}
return nil
}
@@ -118,7 +142,8 @@ func (w *removingWalker) doMap(t *schema.Map) ValidationErrors {
// but ignore them when we are removing (i.e. !w.shouldExtract)
if w.toRemove.Has(path) {
if w.shouldExtract {
newMap[k] = val.Unstructured()
newMap[k] = removeItemsWithSchema(val, w.toRemove, w.schema, fieldType, w.shouldExtract).Unstructured()
}
return true
}

View File

@@ -99,12 +99,13 @@ func (tv TypedValue) ToFieldSet() (*fieldpath.Set, error) {
// Merge returns the result of merging tv and pso ("partially specified
// object") together. Of note:
// * No fields can be removed by this operation.
// * If both tv and pso specify a given leaf field, the result will keep pso's
// value.
// * Container typed elements will have their items ordered:
// * like tv, if pso doesn't change anything in the container
// * like pso, if pso does change something in the container.
// - No fields can be removed by this operation.
// - If both tv and pso specify a given leaf field, the result will keep pso's
// value.
// - Container typed elements will have their items ordered:
// 1. like tv, if pso doesn't change anything in the container
// 2. like pso, if pso does change something in the container.
//
// tv and pso must both be of the same type (their Schema and TypeRef must
// match), or an error will be returned. Validation errors will be returned if
// the objects don't conform to the schema.