mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
build: move e2e dependencies into e2e/go.mod
Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
committed by
mergify[bot]
parent
15da101b1b
commit
bec6090996
201
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/LICENSE
generated
vendored
Normal file
201
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
21
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/doc.go
generated
vendored
Normal file
21
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/doc.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package fieldpath defines a way for referencing path elements (e.g., an
|
||||
// index in an array, or a key in a map). It provides types for arranging these
|
||||
// into paths for referencing nested fields, and for grouping those into sets,
|
||||
// for referencing multiple nested fields.
|
||||
package fieldpath
|
317
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/element.go
generated
vendored
Normal file
317
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/element.go
generated
vendored
Normal file
@ -0,0 +1,317 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
// PathElement describes how to select a child field given a containing object.
|
||||
type PathElement struct {
|
||||
// Exactly one of the following fields should be non-nil.
|
||||
|
||||
// FieldName selects a single field from a map (reminder: this is also
|
||||
// how structs are represented). The containing object must be a map.
|
||||
FieldName *string
|
||||
|
||||
// Key selects the list element which has fields matching those given.
|
||||
// The containing object must be an associative list with map typed
|
||||
// elements. They are sorted alphabetically.
|
||||
Key *value.FieldList
|
||||
|
||||
// Value selects the list element with the given value. The containing
|
||||
// object must be an associative list with a primitive typed element
|
||||
// (i.e., a set).
|
||||
Value *value.Value
|
||||
|
||||
// Index selects a list element by its index number. The containing
|
||||
// object must be an atomic list.
|
||||
Index *int
|
||||
}
|
||||
|
||||
// Less provides an order for path elements.
|
||||
func (e PathElement) Less(rhs PathElement) bool {
|
||||
return e.Compare(rhs) < 0
|
||||
}
|
||||
|
||||
// Compare provides an order for path elements.
|
||||
func (e PathElement) Compare(rhs PathElement) int {
|
||||
if e.FieldName != nil {
|
||||
if rhs.FieldName == nil {
|
||||
return -1
|
||||
}
|
||||
return strings.Compare(*e.FieldName, *rhs.FieldName)
|
||||
} else if rhs.FieldName != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
if e.Key != nil {
|
||||
if rhs.Key == nil {
|
||||
return -1
|
||||
}
|
||||
return e.Key.Compare(*rhs.Key)
|
||||
} else if rhs.Key != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
if e.Value != nil {
|
||||
if rhs.Value == nil {
|
||||
return -1
|
||||
}
|
||||
return value.Compare(*e.Value, *rhs.Value)
|
||||
} else if rhs.Value != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
if e.Index != nil {
|
||||
if rhs.Index == nil {
|
||||
return -1
|
||||
}
|
||||
if *e.Index < *rhs.Index {
|
||||
return -1
|
||||
} else if *e.Index == *rhs.Index {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
} else if rhs.Index != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Equals returns true if both path elements are equal.
|
||||
func (e PathElement) Equals(rhs PathElement) bool {
|
||||
if e.FieldName != nil {
|
||||
if rhs.FieldName == nil {
|
||||
return false
|
||||
}
|
||||
return *e.FieldName == *rhs.FieldName
|
||||
} else if rhs.FieldName != nil {
|
||||
return false
|
||||
}
|
||||
if e.Key != nil {
|
||||
if rhs.Key == nil {
|
||||
return false
|
||||
}
|
||||
return e.Key.Equals(*rhs.Key)
|
||||
} else if rhs.Key != nil {
|
||||
return false
|
||||
}
|
||||
if e.Value != nil {
|
||||
if rhs.Value == nil {
|
||||
return false
|
||||
}
|
||||
return value.Equals(*e.Value, *rhs.Value)
|
||||
} else if rhs.Value != nil {
|
||||
return false
|
||||
}
|
||||
if e.Index != nil {
|
||||
if rhs.Index == nil {
|
||||
return false
|
||||
}
|
||||
return *e.Index == *rhs.Index
|
||||
} else if rhs.Index != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// String presents the path element as a human-readable string.
|
||||
func (e PathElement) String() string {
|
||||
switch {
|
||||
case e.FieldName != nil:
|
||||
return "." + *e.FieldName
|
||||
case e.Key != nil:
|
||||
strs := make([]string, len(*e.Key))
|
||||
for i, k := range *e.Key {
|
||||
strs[i] = fmt.Sprintf("%v=%v", k.Name, value.ToString(k.Value))
|
||||
}
|
||||
// Keys are supposed to be sorted.
|
||||
return "[" + strings.Join(strs, ",") + "]"
|
||||
case e.Value != nil:
|
||||
return fmt.Sprintf("[=%v]", value.ToString(*e.Value))
|
||||
case e.Index != nil:
|
||||
return fmt.Sprintf("[%v]", *e.Index)
|
||||
default:
|
||||
return "{{invalid path element}}"
|
||||
}
|
||||
}
|
||||
|
||||
// KeyByFields is a helper function which constructs a key for an associative
|
||||
// list type. `nameValues` must have an even number of entries, alternating
|
||||
// names (type must be string) with values (type must be value.Value). If these
|
||||
// conditions are not met, KeyByFields will panic--it's intended for static
|
||||
// construction and shouldn't have user-produced values passed to it.
|
||||
func KeyByFields(nameValues ...interface{}) *value.FieldList {
|
||||
if len(nameValues)%2 != 0 {
|
||||
panic("must have a value for every name")
|
||||
}
|
||||
out := value.FieldList{}
|
||||
for i := 0; i < len(nameValues)-1; i += 2 {
|
||||
out = append(out, value.Field{Name: nameValues[i].(string), Value: value.NewValueInterface(nameValues[i+1])})
|
||||
}
|
||||
out.Sort()
|
||||
return &out
|
||||
}
|
||||
|
||||
// PathElementSet is a set of path elements.
|
||||
// TODO: serialize as a list.
|
||||
type PathElementSet struct {
|
||||
members sortedPathElements
|
||||
}
|
||||
|
||||
func MakePathElementSet(size int) PathElementSet {
|
||||
return PathElementSet{
|
||||
members: make(sortedPathElements, 0, size),
|
||||
}
|
||||
}
|
||||
|
||||
type sortedPathElements []PathElement
|
||||
|
||||
// Implement the sort interface; this would permit bulk creation, which would
|
||||
// be faster than doing it one at a time via Insert.
|
||||
func (spe sortedPathElements) Len() int { return len(spe) }
|
||||
func (spe sortedPathElements) Less(i, j int) bool { return spe[i].Less(spe[j]) }
|
||||
func (spe sortedPathElements) Swap(i, j int) { spe[i], spe[j] = spe[j], spe[i] }
|
||||
|
||||
// Insert adds pe to the set.
|
||||
func (s *PathElementSet) Insert(pe PathElement) {
|
||||
loc := sort.Search(len(s.members), func(i int) bool {
|
||||
return !s.members[i].Less(pe)
|
||||
})
|
||||
if loc == len(s.members) {
|
||||
s.members = append(s.members, pe)
|
||||
return
|
||||
}
|
||||
if s.members[loc].Equals(pe) {
|
||||
return
|
||||
}
|
||||
s.members = append(s.members, PathElement{})
|
||||
copy(s.members[loc+1:], s.members[loc:])
|
||||
s.members[loc] = pe
|
||||
}
|
||||
|
||||
// Union returns a set containing elements that appear in either s or s2.
|
||||
func (s *PathElementSet) Union(s2 *PathElementSet) *PathElementSet {
|
||||
out := &PathElementSet{}
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(s.members) && j < len(s2.members) {
|
||||
if s.members[i].Less(s2.members[j]) {
|
||||
out.members = append(out.members, s.members[i])
|
||||
i++
|
||||
} else {
|
||||
out.members = append(out.members, s2.members[j])
|
||||
if !s2.members[j].Less(s.members[i]) {
|
||||
i++
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if i < len(s.members) {
|
||||
out.members = append(out.members, s.members[i:]...)
|
||||
}
|
||||
if j < len(s2.members) {
|
||||
out.members = append(out.members, s2.members[j:]...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Intersection returns a set containing elements which appear in both s and s2.
|
||||
func (s *PathElementSet) Intersection(s2 *PathElementSet) *PathElementSet {
|
||||
out := &PathElementSet{}
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(s.members) && j < len(s2.members) {
|
||||
if s.members[i].Less(s2.members[j]) {
|
||||
i++
|
||||
} else {
|
||||
if !s2.members[j].Less(s.members[i]) {
|
||||
out.members = append(out.members, s.members[i])
|
||||
i++
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Difference returns a set containing elements which appear in s but not in s2.
|
||||
func (s *PathElementSet) Difference(s2 *PathElementSet) *PathElementSet {
|
||||
out := &PathElementSet{}
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(s.members) && j < len(s2.members) {
|
||||
if s.members[i].Less(s2.members[j]) {
|
||||
out.members = append(out.members, s.members[i])
|
||||
i++
|
||||
} else {
|
||||
if !s2.members[j].Less(s.members[i]) {
|
||||
i++
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
if i < len(s.members) {
|
||||
out.members = append(out.members, s.members[i:]...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Size retuns the number of elements in the set.
|
||||
func (s *PathElementSet) Size() int { return len(s.members) }
|
||||
|
||||
// Has returns true if pe is a member of the set.
|
||||
func (s *PathElementSet) Has(pe PathElement) bool {
|
||||
loc := sort.Search(len(s.members), func(i int) bool {
|
||||
return !s.members[i].Less(pe)
|
||||
})
|
||||
if loc == len(s.members) {
|
||||
return false
|
||||
}
|
||||
if s.members[loc].Equals(pe) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals returns true if s and s2 have exactly the same members.
|
||||
func (s *PathElementSet) Equals(s2 *PathElementSet) bool {
|
||||
if len(s.members) != len(s2.members) {
|
||||
return false
|
||||
}
|
||||
for k := range s.members {
|
||||
if !s.members[k].Equals(s2.members[k]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Iterate calls f for each PathElement in the set. The order is deterministic.
|
||||
func (s *PathElementSet) Iterate(f func(PathElement)) {
|
||||
for _, pe := range s.members {
|
||||
f(pe)
|
||||
}
|
||||
}
|
134
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/fromvalue.go
generated
vendored
Normal file
134
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/fromvalue.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
// SetFromValue creates a set containing every leaf field mentioned in v.
|
||||
func SetFromValue(v value.Value) *Set {
|
||||
s := NewSet()
|
||||
|
||||
w := objectWalker{
|
||||
path: Path{},
|
||||
value: v,
|
||||
allocator: value.NewFreelistAllocator(),
|
||||
do: func(p Path) { s.Insert(p) },
|
||||
}
|
||||
|
||||
w.walk()
|
||||
return s
|
||||
}
|
||||
|
||||
type objectWalker struct {
|
||||
path Path
|
||||
value value.Value
|
||||
allocator value.Allocator
|
||||
|
||||
do func(Path)
|
||||
}
|
||||
|
||||
func (w *objectWalker) walk() {
|
||||
switch {
|
||||
case w.value.IsNull():
|
||||
case w.value.IsFloat():
|
||||
case w.value.IsInt():
|
||||
case w.value.IsString():
|
||||
case w.value.IsBool():
|
||||
// All leaf fields handled the same way (after the switch
|
||||
// statement).
|
||||
|
||||
// Descend
|
||||
case w.value.IsList():
|
||||
// If the list were atomic, we'd break here, but we don't have
|
||||
// a schema, so we can't tell.
|
||||
l := w.value.AsListUsing(w.allocator)
|
||||
defer w.allocator.Free(l)
|
||||
iter := l.RangeUsing(w.allocator)
|
||||
defer w.allocator.Free(iter)
|
||||
for iter.Next() {
|
||||
i, value := iter.Item()
|
||||
w2 := *w
|
||||
w2.path = append(w.path, w.GuessBestListPathElement(i, value))
|
||||
w2.value = value
|
||||
w2.walk()
|
||||
}
|
||||
return
|
||||
case w.value.IsMap():
|
||||
// If the map/struct were atomic, we'd break here, but we don't
|
||||
// have a schema, so we can't tell.
|
||||
|
||||
m := w.value.AsMapUsing(w.allocator)
|
||||
defer w.allocator.Free(m)
|
||||
m.IterateUsing(w.allocator, func(k string, val value.Value) bool {
|
||||
w2 := *w
|
||||
w2.path = append(w.path, PathElement{FieldName: &k})
|
||||
w2.value = val
|
||||
w2.walk()
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Leaf fields get added to the set.
|
||||
if len(w.path) > 0 {
|
||||
w.do(w.path)
|
||||
}
|
||||
}
|
||||
|
||||
// AssociativeListCandidateFieldNames lists the field names which are
|
||||
// considered keys if found in a list element.
|
||||
var AssociativeListCandidateFieldNames = []string{
|
||||
"key",
|
||||
"id",
|
||||
"name",
|
||||
}
|
||||
|
||||
// GuessBestListPathElement guesses whether item is an associative list
|
||||
// element, which should be referenced by key(s), or if it is not and therefore
|
||||
// referencing by index is acceptable. Currently this is done by checking
|
||||
// whether item has any of the fields listed in
|
||||
// AssociativeListCandidateFieldNames which have scalar values.
|
||||
func (w *objectWalker) GuessBestListPathElement(index int, item value.Value) PathElement {
|
||||
if !item.IsMap() {
|
||||
// Non map items could be parts of sets or regular "atomic"
|
||||
// lists. We won't try to guess whether something should be a
|
||||
// set or not.
|
||||
return PathElement{Index: &index}
|
||||
}
|
||||
|
||||
m := item.AsMapUsing(w.allocator)
|
||||
defer w.allocator.Free(m)
|
||||
var keys value.FieldList
|
||||
for _, name := range AssociativeListCandidateFieldNames {
|
||||
f, ok := m.Get(name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// only accept primitive/scalar types as keys.
|
||||
if f.IsNull() || f.IsMap() || f.IsList() {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, value.Field{Name: name, Value: f})
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
keys.Sort()
|
||||
return PathElement{Key: &keys}
|
||||
}
|
||||
return PathElement{Index: &index}
|
||||
}
|
144
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/managers.go
generated
vendored
Normal file
144
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/managers.go
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// APIVersion describes the version of an object or of a fieldset.
|
||||
type APIVersion string
|
||||
|
||||
type VersionedSet interface {
|
||||
Set() *Set
|
||||
APIVersion() APIVersion
|
||||
Applied() bool
|
||||
}
|
||||
|
||||
// VersionedSet associates a version to a set.
|
||||
type versionedSet struct {
|
||||
set *Set
|
||||
apiVersion APIVersion
|
||||
applied bool
|
||||
}
|
||||
|
||||
func NewVersionedSet(set *Set, apiVersion APIVersion, applied bool) VersionedSet {
|
||||
return versionedSet{
|
||||
set: set,
|
||||
apiVersion: apiVersion,
|
||||
applied: applied,
|
||||
}
|
||||
}
|
||||
|
||||
func (v versionedSet) Set() *Set {
|
||||
return v.set
|
||||
}
|
||||
|
||||
func (v versionedSet) APIVersion() APIVersion {
|
||||
return v.apiVersion
|
||||
}
|
||||
|
||||
func (v versionedSet) Applied() bool {
|
||||
return v.applied
|
||||
}
|
||||
|
||||
// ManagedFields is a map from manager to VersionedSet (what they own in
|
||||
// what version).
|
||||
type ManagedFields map[string]VersionedSet
|
||||
|
||||
// Equals returns true if the two managedfields are the same, false
|
||||
// otherwise.
|
||||
func (lhs ManagedFields) Equals(rhs ManagedFields) bool {
|
||||
if len(lhs) != len(rhs) {
|
||||
return false
|
||||
}
|
||||
|
||||
for manager, left := range lhs {
|
||||
right, ok := rhs[manager]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if left.APIVersion() != right.APIVersion() || left.Applied() != right.Applied() {
|
||||
return false
|
||||
}
|
||||
if !left.Set().Equals(right.Set()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Copy the list, this is mostly a shallow copy.
|
||||
func (lhs ManagedFields) Copy() ManagedFields {
|
||||
copy := ManagedFields{}
|
||||
for manager, set := range lhs {
|
||||
copy[manager] = set
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
// Difference returns a symmetric difference between two Managers. If a
|
||||
// given user's entry has version X in lhs and version Y in rhs, then
|
||||
// the return value for that user will be from rhs. If the difference for
|
||||
// a user is an empty set, that user will not be inserted in the map.
|
||||
func (lhs ManagedFields) Difference(rhs ManagedFields) ManagedFields {
|
||||
diff := ManagedFields{}
|
||||
|
||||
for manager, left := range lhs {
|
||||
right, ok := rhs[manager]
|
||||
if !ok {
|
||||
if !left.Set().Empty() {
|
||||
diff[manager] = left
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// If we have sets in both but their version
|
||||
// differs, we don't even diff and keep the
|
||||
// entire thing.
|
||||
if left.APIVersion() != right.APIVersion() {
|
||||
diff[manager] = right
|
||||
continue
|
||||
}
|
||||
|
||||
newSet := left.Set().Difference(right.Set()).Union(right.Set().Difference(left.Set()))
|
||||
if !newSet.Empty() {
|
||||
diff[manager] = NewVersionedSet(newSet, right.APIVersion(), false)
|
||||
}
|
||||
}
|
||||
|
||||
for manager, set := range rhs {
|
||||
if _, ok := lhs[manager]; ok {
|
||||
// Already done
|
||||
continue
|
||||
}
|
||||
if !set.Set().Empty() {
|
||||
diff[manager] = set
|
||||
}
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
func (lhs ManagedFields) String() string {
|
||||
s := strings.Builder{}
|
||||
for k, v := range lhs {
|
||||
fmt.Fprintf(&s, "%s:\n", k)
|
||||
fmt.Fprintf(&s, "- Applied: %v\n", v.Applied())
|
||||
fmt.Fprintf(&s, "- APIVersion: %v\n", v.APIVersion())
|
||||
fmt.Fprintf(&s, "- Set: %v\n", v.Set())
|
||||
}
|
||||
return s.String()
|
||||
}
|
118
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/path.go
generated
vendored
Normal file
118
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/path.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
// Path describes how to select a potentially deeply-nested child field given a
|
||||
// containing object.
|
||||
type Path []PathElement
|
||||
|
||||
func (fp Path) String() string {
|
||||
strs := make([]string, len(fp))
|
||||
for i := range fp {
|
||||
strs[i] = fp[i].String()
|
||||
}
|
||||
return strings.Join(strs, "")
|
||||
}
|
||||
|
||||
// Equals returns true if the two paths are equivalent.
|
||||
func (fp Path) Equals(fp2 Path) bool {
|
||||
if len(fp) != len(fp2) {
|
||||
return false
|
||||
}
|
||||
for i := range fp {
|
||||
if !fp[i].Equals(fp2[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Less provides a lexical order for Paths.
|
||||
func (fp Path) Compare(rhs Path) int {
|
||||
i := 0
|
||||
for {
|
||||
if i >= len(fp) && i >= len(rhs) {
|
||||
// Paths are the same length and all items are equal.
|
||||
return 0
|
||||
}
|
||||
if i >= len(fp) {
|
||||
// LHS is shorter.
|
||||
return -1
|
||||
}
|
||||
if i >= len(rhs) {
|
||||
// RHS is shorter.
|
||||
return 1
|
||||
}
|
||||
if c := fp[i].Compare(rhs[i]); c != 0 {
|
||||
return c
|
||||
}
|
||||
// The items are equal; continue.
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func (fp Path) Copy() Path {
|
||||
new := make(Path, len(fp))
|
||||
copy(new, fp)
|
||||
return new
|
||||
}
|
||||
|
||||
// MakePath constructs a Path. The parts may be PathElements, ints, strings.
|
||||
func MakePath(parts ...interface{}) (Path, error) {
|
||||
var fp Path
|
||||
for _, p := range parts {
|
||||
switch t := p.(type) {
|
||||
case PathElement:
|
||||
fp = append(fp, t)
|
||||
case int:
|
||||
// TODO: Understand schema and object and convert this to the
|
||||
// FieldSpecifier below if appropriate.
|
||||
fp = append(fp, PathElement{Index: &t})
|
||||
case string:
|
||||
fp = append(fp, PathElement{FieldName: &t})
|
||||
case *value.FieldList:
|
||||
if len(*t) == 0 {
|
||||
return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)")
|
||||
}
|
||||
fp = append(fp, PathElement{Key: t})
|
||||
case value.Value:
|
||||
// TODO: understand schema and verify that this is a set type
|
||||
// TODO: make a copy of t
|
||||
fp = append(fp, PathElement{Value: &t})
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to make %#v into a path element", p)
|
||||
}
|
||||
}
|
||||
return fp, nil
|
||||
}
|
||||
|
||||
// MakePathOrDie panics if parts can't be turned into a path. Good for things
|
||||
// that are known at complie time.
|
||||
func MakePathOrDie(parts ...interface{}) Path {
|
||||
fp, err := MakePath(parts...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return fp
|
||||
}
|
114
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/pathelementmap.go
generated
vendored
Normal file
114
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/pathelementmap.go
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
// PathElementValueMap is a map from PathElement to value.Value.
|
||||
//
|
||||
// TODO(apelisse): We have multiple very similar implementation of this
|
||||
// for PathElementSet and SetNodeMap, so we could probably share the
|
||||
// code.
|
||||
type PathElementValueMap struct {
|
||||
valueMap PathElementMap
|
||||
}
|
||||
|
||||
func MakePathElementValueMap(size int) PathElementValueMap {
|
||||
return PathElementValueMap{
|
||||
valueMap: MakePathElementMap(size),
|
||||
}
|
||||
}
|
||||
|
||||
type sortedPathElementValues []pathElementValue
|
||||
|
||||
// Implement the sort interface; this would permit bulk creation, which would
|
||||
// be faster than doing it one at a time via Insert.
|
||||
func (spev sortedPathElementValues) Len() int { return len(spev) }
|
||||
func (spev sortedPathElementValues) Less(i, j int) bool {
|
||||
return spev[i].PathElement.Less(spev[j].PathElement)
|
||||
}
|
||||
func (spev sortedPathElementValues) Swap(i, j int) { spev[i], spev[j] = spev[j], spev[i] }
|
||||
|
||||
// Insert adds the pathelement and associated value in the map.
|
||||
// If insert is called twice with the same PathElement, the value is replaced.
|
||||
func (s *PathElementValueMap) Insert(pe PathElement, v value.Value) {
|
||||
s.valueMap.Insert(pe, v)
|
||||
}
|
||||
|
||||
// Get retrieves the value associated with the given PathElement from the map.
|
||||
// (nil, false) is returned if there is no such PathElement.
|
||||
func (s *PathElementValueMap) Get(pe PathElement) (value.Value, bool) {
|
||||
v, ok := s.valueMap.Get(pe)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return v.(value.Value), true
|
||||
}
|
||||
|
||||
// PathElementValueMap is a map from PathElement to interface{}.
|
||||
type PathElementMap struct {
|
||||
members sortedPathElementValues
|
||||
}
|
||||
|
||||
type pathElementValue struct {
|
||||
PathElement PathElement
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func MakePathElementMap(size int) PathElementMap {
|
||||
return PathElementMap{
|
||||
members: make(sortedPathElementValues, 0, size),
|
||||
}
|
||||
}
|
||||
|
||||
// Insert adds the pathelement and associated value in the map.
|
||||
// If insert is called twice with the same PathElement, the value is replaced.
|
||||
func (s *PathElementMap) Insert(pe PathElement, v interface{}) {
|
||||
loc := sort.Search(len(s.members), func(i int) bool {
|
||||
return !s.members[i].PathElement.Less(pe)
|
||||
})
|
||||
if loc == len(s.members) {
|
||||
s.members = append(s.members, pathElementValue{pe, v})
|
||||
return
|
||||
}
|
||||
if s.members[loc].PathElement.Equals(pe) {
|
||||
s.members[loc].Value = v
|
||||
return
|
||||
}
|
||||
s.members = append(s.members, pathElementValue{})
|
||||
copy(s.members[loc+1:], s.members[loc:])
|
||||
s.members[loc] = pathElementValue{pe, v}
|
||||
}
|
||||
|
||||
// Get retrieves the value associated with the given PathElement from the map.
|
||||
// (nil, false) is returned if there is no such PathElement.
|
||||
func (s *PathElementMap) Get(pe PathElement) (interface{}, bool) {
|
||||
loc := sort.Search(len(s.members), func(i int) bool {
|
||||
return !s.members[i].PathElement.Less(pe)
|
||||
})
|
||||
if loc == len(s.members) {
|
||||
return nil, false
|
||||
}
|
||||
if s.members[loc].PathElement.Equals(pe) {
|
||||
return s.members[loc].Value, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
168
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/serialize-pe.go
generated
vendored
Normal file
168
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/serialize-pe.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
var ErrUnknownPathElementType = errors.New("unknown path element type")
|
||||
|
||||
const (
|
||||
// Field indicates that the content of this path element is a field's name
|
||||
peField = "f"
|
||||
|
||||
// Value indicates that the content of this path element is a field's value
|
||||
peValue = "v"
|
||||
|
||||
// Index indicates that the content of this path element is an index in an array
|
||||
peIndex = "i"
|
||||
|
||||
// Key indicates that the content of this path element is a key value map
|
||||
peKey = "k"
|
||||
|
||||
// Separator separates the type of a path element from the contents
|
||||
peSeparator = ":"
|
||||
)
|
||||
|
||||
var (
|
||||
peFieldSepBytes = []byte(peField + peSeparator)
|
||||
peValueSepBytes = []byte(peValue + peSeparator)
|
||||
peIndexSepBytes = []byte(peIndex + peSeparator)
|
||||
peKeySepBytes = []byte(peKey + peSeparator)
|
||||
peSepBytes = []byte(peSeparator)
|
||||
)
|
||||
|
||||
// DeserializePathElement parses a serialized path element
|
||||
func DeserializePathElement(s string) (PathElement, error) {
|
||||
b := []byte(s)
|
||||
if len(b) < 2 {
|
||||
return PathElement{}, errors.New("key must be 2 characters long:")
|
||||
}
|
||||
typeSep, b := b[:2], b[2:]
|
||||
if typeSep[1] != peSepBytes[0] {
|
||||
return PathElement{}, fmt.Errorf("missing colon: %v", s)
|
||||
}
|
||||
switch typeSep[0] {
|
||||
case peFieldSepBytes[0]:
|
||||
// Slice s rather than convert b, to save on
|
||||
// allocations.
|
||||
str := s[2:]
|
||||
return PathElement{
|
||||
FieldName: &str,
|
||||
}, nil
|
||||
case peValueSepBytes[0]:
|
||||
iter := readPool.BorrowIterator(b)
|
||||
defer readPool.ReturnIterator(iter)
|
||||
v, err := value.ReadJSONIter(iter)
|
||||
if err != nil {
|
||||
return PathElement{}, err
|
||||
}
|
||||
return PathElement{Value: &v}, nil
|
||||
case peKeySepBytes[0]:
|
||||
iter := readPool.BorrowIterator(b)
|
||||
defer readPool.ReturnIterator(iter)
|
||||
fields := value.FieldList{}
|
||||
|
||||
iter.ReadObjectCB(func(iter *jsoniter.Iterator, key string) bool {
|
||||
v, err := value.ReadJSONIter(iter)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
return false
|
||||
}
|
||||
fields = append(fields, value.Field{Name: key, Value: v})
|
||||
return true
|
||||
})
|
||||
fields.Sort()
|
||||
return PathElement{Key: &fields}, iter.Error
|
||||
case peIndexSepBytes[0]:
|
||||
i, err := strconv.Atoi(s[2:])
|
||||
if err != nil {
|
||||
return PathElement{}, err
|
||||
}
|
||||
return PathElement{
|
||||
Index: &i,
|
||||
}, nil
|
||||
default:
|
||||
return PathElement{}, ErrUnknownPathElementType
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
readPool = jsoniter.NewIterator(jsoniter.ConfigCompatibleWithStandardLibrary).Pool()
|
||||
writePool = jsoniter.NewStream(jsoniter.ConfigCompatibleWithStandardLibrary, nil, 1024).Pool()
|
||||
)
|
||||
|
||||
// SerializePathElement serializes a path element
|
||||
func SerializePathElement(pe PathElement) (string, error) {
|
||||
buf := strings.Builder{}
|
||||
err := serializePathElementToWriter(&buf, pe)
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
func serializePathElementToWriter(w io.Writer, pe PathElement) error {
|
||||
stream := writePool.BorrowStream(w)
|
||||
defer writePool.ReturnStream(stream)
|
||||
switch {
|
||||
case pe.FieldName != nil:
|
||||
if _, err := stream.Write(peFieldSepBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteRaw(*pe.FieldName)
|
||||
case pe.Key != nil:
|
||||
if _, err := stream.Write(peKeySepBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectStart()
|
||||
|
||||
for i, field := range *pe.Key {
|
||||
if i > 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteObjectField(field.Name)
|
||||
value.WriteJSONStream(field.Value, stream)
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
case pe.Value != nil:
|
||||
if _, err := stream.Write(peValueSepBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
value.WriteJSONStream(*pe.Value, stream)
|
||||
case pe.Index != nil:
|
||||
if _, err := stream.Write(peIndexSepBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteInt(*pe.Index)
|
||||
default:
|
||||
return errors.New("invalid PathElement")
|
||||
}
|
||||
b := stream.Buffer()
|
||||
err := stream.Flush()
|
||||
// Help jsoniter manage its buffers--without this, the next
|
||||
// use of the stream is likely to require an allocation. Look
|
||||
// at the jsoniter stream code to understand why. They were probably
|
||||
// optimizing for folks using the buffer directly.
|
||||
stream.SetBuffer(b[:0])
|
||||
return err
|
||||
}
|
238
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/serialize.go
generated
vendored
Normal file
238
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/serialize.go
generated
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"unsafe"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
func (s *Set) ToJSON() ([]byte, error) {
|
||||
buf := bytes.Buffer{}
|
||||
err := s.ToJSONStream(&buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (s *Set) ToJSONStream(w io.Writer) error {
|
||||
stream := writePool.BorrowStream(w)
|
||||
defer writePool.ReturnStream(stream)
|
||||
|
||||
var r reusableBuilder
|
||||
|
||||
stream.WriteObjectStart()
|
||||
err := s.emitContentsV1(false, stream, &r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
return stream.Flush()
|
||||
}
|
||||
|
||||
func manageMemory(stream *jsoniter.Stream) error {
|
||||
// Help jsoniter manage its buffers--without this, it does a bunch of
|
||||
// alloctaions that are not necessary. They were probably optimizing
|
||||
// for folks using the buffer directly.
|
||||
b := stream.Buffer()
|
||||
if len(b) > 4096 || cap(b)-len(b) < 2048 {
|
||||
if err := stream.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.SetBuffer(b[:0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type reusableBuilder struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (r *reusableBuilder) unsafeString() string {
|
||||
b := r.Bytes()
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
func (r *reusableBuilder) reset() *bytes.Buffer {
|
||||
r.Reset()
|
||||
return &r.Buffer
|
||||
}
|
||||
|
||||
func (s *Set) emitContentsV1(includeSelf bool, stream *jsoniter.Stream, r *reusableBuilder) error {
|
||||
mi, ci := 0, 0
|
||||
first := true
|
||||
preWrite := func() {
|
||||
if first {
|
||||
first = false
|
||||
return
|
||||
}
|
||||
stream.WriteMore()
|
||||
}
|
||||
|
||||
if includeSelf && !(len(s.Members.members) == 0 && len(s.Children.members) == 0) {
|
||||
preWrite()
|
||||
stream.WriteObjectField(".")
|
||||
stream.WriteEmptyObject()
|
||||
}
|
||||
|
||||
for mi < len(s.Members.members) && ci < len(s.Children.members) {
|
||||
mpe := s.Members.members[mi]
|
||||
cpe := s.Children.members[ci].pathElement
|
||||
|
||||
if c := mpe.Compare(cpe); c < 0 {
|
||||
preWrite()
|
||||
if err := serializePathElementToWriter(r.reset(), mpe); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectField(r.unsafeString())
|
||||
stream.WriteEmptyObject()
|
||||
mi++
|
||||
} else if c > 0 {
|
||||
preWrite()
|
||||
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectField(r.unsafeString())
|
||||
stream.WriteObjectStart()
|
||||
if err := s.Children.members[ci].set.emitContentsV1(false, stream, r); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
ci++
|
||||
} else {
|
||||
preWrite()
|
||||
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectField(r.unsafeString())
|
||||
stream.WriteObjectStart()
|
||||
if err := s.Children.members[ci].set.emitContentsV1(true, stream, r); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
mi++
|
||||
ci++
|
||||
}
|
||||
}
|
||||
|
||||
for mi < len(s.Members.members) {
|
||||
mpe := s.Members.members[mi]
|
||||
|
||||
preWrite()
|
||||
if err := serializePathElementToWriter(r.reset(), mpe); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectField(r.unsafeString())
|
||||
stream.WriteEmptyObject()
|
||||
mi++
|
||||
}
|
||||
|
||||
for ci < len(s.Children.members) {
|
||||
cpe := s.Children.members[ci].pathElement
|
||||
|
||||
preWrite()
|
||||
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectField(r.unsafeString())
|
||||
stream.WriteObjectStart()
|
||||
if err := s.Children.members[ci].set.emitContentsV1(false, stream, r); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
ci++
|
||||
}
|
||||
|
||||
return manageMemory(stream)
|
||||
}
|
||||
|
||||
// FromJSON clears s and reads a JSON formatted set structure.
|
||||
func (s *Set) FromJSON(r io.Reader) error {
|
||||
// The iterator pool is completely useless for memory management, grrr.
|
||||
iter := jsoniter.Parse(jsoniter.ConfigCompatibleWithStandardLibrary, r, 4096)
|
||||
|
||||
found, _ := readIterV1(iter)
|
||||
if found == nil {
|
||||
*s = Set{}
|
||||
} else {
|
||||
*s = *found
|
||||
}
|
||||
return iter.Error
|
||||
}
|
||||
|
||||
// returns true if this subtree is also (or only) a member of parent; s is nil
|
||||
// if there are no further children.
|
||||
func readIterV1(iter *jsoniter.Iterator) (children *Set, isMember bool) {
|
||||
iter.ReadMapCB(func(iter *jsoniter.Iterator, key string) bool {
|
||||
if key == "." {
|
||||
isMember = true
|
||||
iter.Skip()
|
||||
return true
|
||||
}
|
||||
pe, err := DeserializePathElement(key)
|
||||
if err == ErrUnknownPathElementType {
|
||||
// Ignore these-- a future version maybe knows what
|
||||
// they are. We drop these completely rather than try
|
||||
// to preserve things we don't understand.
|
||||
iter.Skip()
|
||||
return true
|
||||
} else if err != nil {
|
||||
iter.ReportError("parsing key as path element", err.Error())
|
||||
iter.Skip()
|
||||
return true
|
||||
}
|
||||
grandchildren, childIsMember := readIterV1(iter)
|
||||
if childIsMember {
|
||||
if children == nil {
|
||||
children = &Set{}
|
||||
}
|
||||
m := &children.Members.members
|
||||
// Since we expect that most of the time these will have been
|
||||
// serialized in the right order, we just verify that and append.
|
||||
appendOK := len(*m) == 0 || (*m)[len(*m)-1].Less(pe)
|
||||
if appendOK {
|
||||
*m = append(*m, pe)
|
||||
} else {
|
||||
children.Members.Insert(pe)
|
||||
}
|
||||
}
|
||||
if grandchildren != nil {
|
||||
if children == nil {
|
||||
children = &Set{}
|
||||
}
|
||||
// Since we expect that most of the time these will have been
|
||||
// serialized in the right order, we just verify that and append.
|
||||
m := &children.Children.members
|
||||
appendOK := len(*m) == 0 || (*m)[len(*m)-1].pathElement.Less(pe)
|
||||
if appendOK {
|
||||
*m = append(*m, setNode{pe, grandchildren})
|
||||
} else {
|
||||
*children.Children.Descend(pe) = *grandchildren
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if children == nil {
|
||||
isMember = true
|
||||
}
|
||||
|
||||
return children, isMember
|
||||
}
|
782
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/set.go
generated
vendored
Normal file
782
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/set.go
generated
vendored
Normal file
@ -0,0 +1,782 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/schema"
|
||||
)
|
||||
|
||||
// Set identifies a set of fields.
|
||||
type Set struct {
|
||||
// Members lists fields that are part of the set.
|
||||
// TODO: will be serialized as a list of path elements.
|
||||
Members PathElementSet
|
||||
|
||||
// Children lists child fields which themselves have children that are
|
||||
// members of the set. Appearance in this list does not imply membership.
|
||||
// Note: this is a tree, not an arbitrary graph.
|
||||
Children SetNodeMap
|
||||
}
|
||||
|
||||
// NewSet makes a set from a list of paths.
|
||||
func NewSet(paths ...Path) *Set {
|
||||
s := &Set{}
|
||||
for _, p := range paths {
|
||||
s.Insert(p)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Insert adds the field identified by `p` to the set. Important: parent fields
|
||||
// are NOT added to the set; if that is desired, they must be added separately.
|
||||
func (s *Set) Insert(p Path) {
|
||||
if len(p) == 0 {
|
||||
// Zero-length path identifies the entire object; we don't
|
||||
// track top-level ownership.
|
||||
return
|
||||
}
|
||||
for {
|
||||
if len(p) == 1 {
|
||||
s.Members.Insert(p[0])
|
||||
return
|
||||
}
|
||||
s = s.Children.Descend(p[0])
|
||||
p = p[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Union returns a Set containing elements which appear in either s or s2.
|
||||
func (s *Set) Union(s2 *Set) *Set {
|
||||
return &Set{
|
||||
Members: *s.Members.Union(&s2.Members),
|
||||
Children: *s.Children.Union(&s2.Children),
|
||||
}
|
||||
}
|
||||
|
||||
// Intersection returns a Set containing leaf elements which appear in both s
|
||||
// and s2. Intersection can be constructed from Union and Difference operations
|
||||
// (example in the tests) but it's much faster to do it in one pass.
|
||||
func (s *Set) Intersection(s2 *Set) *Set {
|
||||
return &Set{
|
||||
Members: *s.Members.Intersection(&s2.Members),
|
||||
Children: *s.Children.Intersection(&s2.Children),
|
||||
}
|
||||
}
|
||||
|
||||
// Difference returns a Set containing elements which:
|
||||
// * appear in s
|
||||
// * do not 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"
|
||||
// which contain "children"), the effect is:
|
||||
// * parent - child = parent
|
||||
// * child - parent = {empty set}
|
||||
func (s *Set) Difference(s2 *Set) *Set {
|
||||
return &Set{
|
||||
Members: *s.Members.Difference(&s2.Members),
|
||||
Children: *s.Children.Difference(s2),
|
||||
}
|
||||
}
|
||||
|
||||
// RecursiveDifference returns a Set containing elements which:
|
||||
// * appear in s
|
||||
// * do not appear in s2
|
||||
//
|
||||
// Compared to a regular difference,
|
||||
// this removes every field **and its children** from s that is contained in s2.
|
||||
//
|
||||
// For example, with s containing `a.b.c` and s2 containing `a.b`,
|
||||
// a RecursiveDifference will result in `a`, as the entire node `a.b` gets removed.
|
||||
func (s *Set) RecursiveDifference(s2 *Set) *Set {
|
||||
return &Set{
|
||||
Members: *s.Members.Difference(&s2.Members),
|
||||
Children: *s.Children.RecursiveDifference(s2),
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureNamedFieldsAreMembers returns a Set that contains all the
|
||||
// fields in s, as well as all the named fields that are typically not
|
||||
// included. For example, a set made of "a.b.c" will end-up also owning
|
||||
// "a" if it's a named fields but not "a.b" if it's a map.
|
||||
func (s *Set) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.TypeRef) *Set {
|
||||
members := PathElementSet{
|
||||
members: make(sortedPathElements, 0, s.Members.Size()+len(s.Children.members)),
|
||||
}
|
||||
atom, _ := sc.Resolve(tr)
|
||||
members.members = append(members.members, s.Members.members...)
|
||||
for _, node := range s.Children.members {
|
||||
// Only insert named fields.
|
||||
if node.pathElement.FieldName != nil && atom.Map != nil {
|
||||
if _, has := atom.Map.FindField(*node.pathElement.FieldName); has {
|
||||
members.Insert(node.pathElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
return &Set{
|
||||
Members: members,
|
||||
Children: *s.Children.EnsureNamedFieldsAreMembers(sc, tr),
|
||||
}
|
||||
}
|
||||
|
||||
// MakePrefixMatcherOrDie is the same as PrefixMatcher except it panics if parts can't be
|
||||
// turned into a SetMatcher.
|
||||
func MakePrefixMatcherOrDie(parts ...interface{}) *SetMatcher {
|
||||
result, err := PrefixMatcher(parts...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// PrefixMatcher creates a SetMatcher that matches all field paths prefixed by the given list of matcher path parts.
|
||||
// The matcher parts may any of:
|
||||
//
|
||||
// - PathElementMatcher - for wildcards, `MatchAnyPathElement()` can be used as well.
|
||||
// - PathElement - for any path element
|
||||
// - value.FieldList - for listMap keys
|
||||
// - value.Value - for scalar list elements
|
||||
// - string - For field names
|
||||
// - int - for array indices
|
||||
func PrefixMatcher(parts ...interface{}) (*SetMatcher, error) {
|
||||
current := MatchAnySet() // match all field path suffixes
|
||||
for i := len(parts) - 1; i >= 0; i-- {
|
||||
part := parts[i]
|
||||
var pattern PathElementMatcher
|
||||
switch t := part.(type) {
|
||||
case PathElementMatcher:
|
||||
// any path matcher, including wildcard
|
||||
pattern = t
|
||||
case PathElement:
|
||||
// any path element
|
||||
pattern = PathElementMatcher{PathElement: t}
|
||||
case *value.FieldList:
|
||||
// a listMap key
|
||||
if len(*t) == 0 {
|
||||
return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)")
|
||||
}
|
||||
pattern = PathElementMatcher{PathElement: PathElement{Key: t}}
|
||||
case value.Value:
|
||||
// a scalar or set-type list element
|
||||
pattern = PathElementMatcher{PathElement: PathElement{Value: &t}}
|
||||
case string:
|
||||
// a plain field name
|
||||
pattern = PathElementMatcher{PathElement: PathElement{FieldName: &t}}
|
||||
case int:
|
||||
// a plain list index
|
||||
pattern = PathElementMatcher{PathElement: PathElement{Index: &t}}
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected type %T", t)
|
||||
}
|
||||
current = &SetMatcher{
|
||||
members: []*SetMemberMatcher{{
|
||||
Path: pattern,
|
||||
Child: current,
|
||||
}},
|
||||
}
|
||||
}
|
||||
return current, nil
|
||||
}
|
||||
|
||||
// MatchAnyPathElement returns a PathElementMatcher that matches any path element.
|
||||
func MatchAnyPathElement() PathElementMatcher {
|
||||
return PathElementMatcher{Wildcard: true}
|
||||
}
|
||||
|
||||
// MatchAnySet returns a SetMatcher that matches any set.
|
||||
func MatchAnySet() *SetMatcher {
|
||||
return &SetMatcher{wildcard: true}
|
||||
}
|
||||
|
||||
// NewSetMatcher returns a new SetMatcher.
|
||||
// Wildcard members take precedent over non-wildcard members;
|
||||
// all non-wildcard members are ignored if there is a wildcard members.
|
||||
func NewSetMatcher(wildcard bool, members ...*SetMemberMatcher) *SetMatcher {
|
||||
sort.Sort(sortedMemberMatcher(members))
|
||||
return &SetMatcher{wildcard: wildcard, members: members}
|
||||
}
|
||||
|
||||
// SetMatcher defines a matcher that matches fields in a Set.
|
||||
// SetMatcher is structured much like a Set but with wildcard support.
|
||||
type SetMatcher struct {
|
||||
// wildcard indicates that all members and children are included in the match.
|
||||
// If set, the members field is ignored.
|
||||
wildcard bool
|
||||
// members provides patterns to match the members of a Set.
|
||||
// Wildcard members are sorted before non-wildcards and take precedent over
|
||||
// non-wildcard members.
|
||||
members sortedMemberMatcher
|
||||
}
|
||||
|
||||
type sortedMemberMatcher []*SetMemberMatcher
|
||||
|
||||
func (s sortedMemberMatcher) Len() int { return len(s) }
|
||||
func (s sortedMemberMatcher) Less(i, j int) bool { return s[i].Path.Less(s[j].Path) }
|
||||
func (s sortedMemberMatcher) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s sortedMemberMatcher) Find(p PathElementMatcher) (location int, ok bool) {
|
||||
return sort.Find(len(s), func(i int) int {
|
||||
return s[i].Path.Compare(p)
|
||||
})
|
||||
}
|
||||
|
||||
// Merge merges s and s2 and returns a SetMatcher that matches all field paths matched by either s or s2.
|
||||
// During the merge, members of s and s2 with the same PathElementMatcher merged into a single member
|
||||
// with the children of each merged by calling this function recursively.
|
||||
func (s *SetMatcher) Merge(s2 *SetMatcher) *SetMatcher {
|
||||
if s.wildcard || s2.wildcard {
|
||||
return NewSetMatcher(true)
|
||||
}
|
||||
merged := make(sortedMemberMatcher, len(s.members), len(s.members)+len(s2.members))
|
||||
copy(merged, s.members)
|
||||
for _, m := range s2.members {
|
||||
if i, ok := s.members.Find(m.Path); ok {
|
||||
// since merged is a shallow copy, do not modify elements in place
|
||||
merged[i] = &SetMemberMatcher{
|
||||
Path: merged[i].Path,
|
||||
Child: merged[i].Child.Merge(m.Child),
|
||||
}
|
||||
} else {
|
||||
merged = append(merged, m)
|
||||
}
|
||||
}
|
||||
return NewSetMatcher(false, merged...) // sort happens here
|
||||
}
|
||||
|
||||
// SetMemberMatcher defines a matcher that matches the members of a Set.
|
||||
// SetMemberMatcher is structured much like the elements of a SetNodeMap, but
|
||||
// with wildcard support.
|
||||
type SetMemberMatcher struct {
|
||||
// Path provides a matcher to match members of a Set.
|
||||
// If Path is a wildcard, all members of a Set are included in the match.
|
||||
// Otherwise, if any Path is Equal to a member of a Set, that member is
|
||||
// included in the match and the children of that member are matched
|
||||
// against the Child matcher.
|
||||
Path PathElementMatcher
|
||||
|
||||
// Child provides a matcher to use for the children of matched members of a Set.
|
||||
Child *SetMatcher
|
||||
}
|
||||
|
||||
// PathElementMatcher defined a path matcher for a PathElement.
|
||||
type PathElementMatcher struct {
|
||||
// Wildcard indicates that all PathElements are matched by this matcher.
|
||||
// If set, PathElement is ignored.
|
||||
Wildcard bool
|
||||
|
||||
// PathElement indicates that a PathElement is matched if it is Equal
|
||||
// to this PathElement.
|
||||
PathElement
|
||||
}
|
||||
|
||||
func (p PathElementMatcher) Equals(p2 PathElementMatcher) bool {
|
||||
return p.Wildcard != p2.Wildcard && p.PathElement.Equals(p2.PathElement)
|
||||
}
|
||||
|
||||
func (p PathElementMatcher) Less(p2 PathElementMatcher) bool {
|
||||
if p.Wildcard && !p2.Wildcard {
|
||||
return true
|
||||
} else if p2.Wildcard {
|
||||
return false
|
||||
}
|
||||
return p.PathElement.Less(p2.PathElement)
|
||||
}
|
||||
|
||||
func (p PathElementMatcher) Compare(p2 PathElementMatcher) int {
|
||||
if p.Wildcard && !p2.Wildcard {
|
||||
return -1
|
||||
} else if p2.Wildcard {
|
||||
return 1
|
||||
}
|
||||
return p.PathElement.Compare(p2.PathElement)
|
||||
}
|
||||
|
||||
// FilterIncludeMatches returns a Set with only the field paths that match.
|
||||
func (s *Set) FilterIncludeMatches(pattern *SetMatcher) *Set {
|
||||
if pattern.wildcard {
|
||||
return s
|
||||
}
|
||||
|
||||
members := PathElementSet{}
|
||||
for _, m := range s.Members.members {
|
||||
for _, pm := range pattern.members {
|
||||
if pm.Path.Wildcard || pm.Path.PathElement.Equals(m) {
|
||||
members.Insert(m)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return &Set{
|
||||
Members: members,
|
||||
Children: *s.Children.FilterIncludeMatches(pattern),
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the number of members of the set.
|
||||
func (s *Set) Size() int {
|
||||
return s.Members.Size() + s.Children.Size()
|
||||
}
|
||||
|
||||
// Empty returns true if there are no members of the set. It is a separate
|
||||
// function from Size since it's common to check whether size > 0, and
|
||||
// potentially much faster to return as soon as a single element is found.
|
||||
func (s *Set) Empty() bool {
|
||||
if s.Members.Size() > 0 {
|
||||
return false
|
||||
}
|
||||
return s.Children.Empty()
|
||||
}
|
||||
|
||||
// Has returns true if the field referenced by `p` is a member of the set.
|
||||
func (s *Set) Has(p Path) bool {
|
||||
if len(p) == 0 {
|
||||
// No one owns "the entire object"
|
||||
return false
|
||||
}
|
||||
for {
|
||||
if len(p) == 1 {
|
||||
return s.Members.Has(p[0])
|
||||
}
|
||||
var ok bool
|
||||
s, ok = s.Children.Get(p[0])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
p = p[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Equals returns true if s and s2 have exactly the same members.
|
||||
func (s *Set) Equals(s2 *Set) bool {
|
||||
return s.Members.Equals(&s2.Members) && s.Children.Equals(&s2.Children)
|
||||
}
|
||||
|
||||
// String returns the set one element per line.
|
||||
func (s *Set) String() string {
|
||||
elements := []string{}
|
||||
s.Iterate(func(p Path) {
|
||||
elements = append(elements, p.String())
|
||||
})
|
||||
return strings.Join(elements, "\n")
|
||||
}
|
||||
|
||||
// Iterate calls f once for each field that is a member of the set (preorder
|
||||
// DFS). The path passed to f will be reused so make a copy if you wish to keep
|
||||
// it.
|
||||
func (s *Set) Iterate(f func(Path)) {
|
||||
s.iteratePrefix(Path{}, f)
|
||||
}
|
||||
|
||||
func (s *Set) iteratePrefix(prefix Path, f func(Path)) {
|
||||
s.Members.Iterate(func(pe PathElement) { f(append(prefix, pe)) })
|
||||
s.Children.iteratePrefix(prefix, f)
|
||||
}
|
||||
|
||||
// WithPrefix returns the subset of paths which begin with the given prefix,
|
||||
// with the prefix not included.
|
||||
func (s *Set) WithPrefix(pe PathElement) *Set {
|
||||
subset, ok := s.Children.Get(pe)
|
||||
if !ok {
|
||||
return NewSet()
|
||||
}
|
||||
return subset
|
||||
}
|
||||
|
||||
// Leaves returns a set containing only the leaf paths
|
||||
// of a set.
|
||||
func (s *Set) Leaves() *Set {
|
||||
leaves := PathElementSet{}
|
||||
im := 0
|
||||
ic := 0
|
||||
|
||||
// any members that are not also children are leaves
|
||||
outer:
|
||||
for im < len(s.Members.members) {
|
||||
member := s.Members.members[im]
|
||||
|
||||
for ic < len(s.Children.members) {
|
||||
d := member.Compare(s.Children.members[ic].pathElement)
|
||||
if d == 0 {
|
||||
ic++
|
||||
im++
|
||||
continue outer
|
||||
} else if d < 0 {
|
||||
break
|
||||
} else /* if d > 0 */ {
|
||||
ic++
|
||||
}
|
||||
}
|
||||
leaves.members = append(leaves.members, member)
|
||||
im++
|
||||
}
|
||||
|
||||
return &Set{
|
||||
Members: leaves,
|
||||
Children: *s.Children.Leaves(),
|
||||
}
|
||||
}
|
||||
|
||||
// setNode is a pair of PathElement / Set, for the purpose of expressing
|
||||
// nested set membership.
|
||||
type setNode struct {
|
||||
pathElement PathElement
|
||||
set *Set
|
||||
}
|
||||
|
||||
// SetNodeMap is a map of PathElement to subset.
|
||||
type SetNodeMap struct {
|
||||
members sortedSetNode
|
||||
}
|
||||
|
||||
type sortedSetNode []setNode
|
||||
|
||||
// Implement the sort interface; this would permit bulk creation, which would
|
||||
// be faster than doing it one at a time via Insert.
|
||||
func (s sortedSetNode) Len() int { return len(s) }
|
||||
func (s sortedSetNode) Less(i, j int) bool { return s[i].pathElement.Less(s[j].pathElement) }
|
||||
func (s sortedSetNode) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// Descend adds pe to the set if necessary, returning the associated subset.
|
||||
func (s *SetNodeMap) Descend(pe PathElement) *Set {
|
||||
loc := sort.Search(len(s.members), func(i int) bool {
|
||||
return !s.members[i].pathElement.Less(pe)
|
||||
})
|
||||
if loc == len(s.members) {
|
||||
s.members = append(s.members, setNode{pathElement: pe, set: &Set{}})
|
||||
return s.members[loc].set
|
||||
}
|
||||
if s.members[loc].pathElement.Equals(pe) {
|
||||
return s.members[loc].set
|
||||
}
|
||||
s.members = append(s.members, setNode{})
|
||||
copy(s.members[loc+1:], s.members[loc:])
|
||||
s.members[loc] = setNode{pathElement: pe, set: &Set{}}
|
||||
return s.members[loc].set
|
||||
}
|
||||
|
||||
// Size returns the sum of the number of members of all subsets.
|
||||
func (s *SetNodeMap) Size() int {
|
||||
count := 0
|
||||
for _, v := range s.members {
|
||||
count += v.set.Size()
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Empty returns false if there's at least one member in some child set.
|
||||
func (s *SetNodeMap) Empty() bool {
|
||||
for _, n := range s.members {
|
||||
if !n.set.Empty() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Get returns (the associated set, true) or (nil, false) if there is none.
|
||||
func (s *SetNodeMap) Get(pe PathElement) (*Set, bool) {
|
||||
loc := sort.Search(len(s.members), func(i int) bool {
|
||||
return !s.members[i].pathElement.Less(pe)
|
||||
})
|
||||
if loc == len(s.members) {
|
||||
return nil, false
|
||||
}
|
||||
if s.members[loc].pathElement.Equals(pe) {
|
||||
return s.members[loc].set, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Equals returns true if s and s2 have the same structure (same nested
|
||||
// child sets).
|
||||
func (s *SetNodeMap) Equals(s2 *SetNodeMap) bool {
|
||||
if len(s.members) != len(s2.members) {
|
||||
return false
|
||||
}
|
||||
for i := range s.members {
|
||||
if !s.members[i].pathElement.Equals(s2.members[i].pathElement) {
|
||||
return false
|
||||
}
|
||||
if !s.members[i].set.Equals(s2.members[i].set) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Union returns a SetNodeMap with members that appear in either s or s2.
|
||||
func (s *SetNodeMap) Union(s2 *SetNodeMap) *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(s.members) && j < len(s2.members) {
|
||||
if s.members[i].pathElement.Less(s2.members[j].pathElement) {
|
||||
out.members = append(out.members, s.members[i])
|
||||
i++
|
||||
} else {
|
||||
if !s2.members[j].pathElement.Less(s.members[i].pathElement) {
|
||||
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: s.members[i].set.Union(s2.members[j].set)})
|
||||
i++
|
||||
} else {
|
||||
out.members = append(out.members, s2.members[j])
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if i < len(s.members) {
|
||||
out.members = append(out.members, s.members[i:]...)
|
||||
}
|
||||
if j < len(s2.members) {
|
||||
out.members = append(out.members, s2.members[j:]...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Intersection returns a SetNodeMap with members that appear in both s and s2.
|
||||
func (s *SetNodeMap) Intersection(s2 *SetNodeMap) *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(s.members) && j < len(s2.members) {
|
||||
if s.members[i].pathElement.Less(s2.members[j].pathElement) {
|
||||
i++
|
||||
} else {
|
||||
if !s2.members[j].pathElement.Less(s.members[i].pathElement) {
|
||||
res := s.members[i].set.Intersection(s2.members[j].set)
|
||||
if !res.Empty() {
|
||||
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: res})
|
||||
}
|
||||
i++
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Difference returns a SetNodeMap with members that appear in s but not in s2.
|
||||
func (s *SetNodeMap) Difference(s2 *Set) *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(s.members) && j < len(s2.Children.members) {
|
||||
if s.members[i].pathElement.Less(s2.Children.members[j].pathElement) {
|
||||
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: s.members[i].set})
|
||||
i++
|
||||
} else {
|
||||
if !s2.Children.members[j].pathElement.Less(s.members[i].pathElement) {
|
||||
|
||||
diff := s.members[i].set.Difference(s2.Children.members[j].set)
|
||||
// We aren't permitted to add nodes with no elements.
|
||||
if !diff.Empty() {
|
||||
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: diff})
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if i < len(s.members) {
|
||||
out.members = append(out.members, s.members[i:]...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// RecursiveDifference returns a SetNodeMap with members that appear in s but not in s2.
|
||||
//
|
||||
// Compared to a regular difference,
|
||||
// this removes every field **and its children** from s that is contained in s2.
|
||||
//
|
||||
// For example, with s containing `a.b.c` and s2 containing `a.b`,
|
||||
// a RecursiveDifference will result in `a`, as the entire node `a.b` gets removed.
|
||||
func (s *SetNodeMap) RecursiveDifference(s2 *Set) *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(s.members) && j < len(s2.Children.members) {
|
||||
if s.members[i].pathElement.Less(s2.Children.members[j].pathElement) {
|
||||
if !s2.Members.Has(s.members[i].pathElement) {
|
||||
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: s.members[i].set})
|
||||
}
|
||||
i++
|
||||
} else {
|
||||
if !s2.Children.members[j].pathElement.Less(s.members[i].pathElement) {
|
||||
if !s2.Members.Has(s.members[i].pathElement) {
|
||||
diff := s.members[i].set.RecursiveDifference(s2.Children.members[j].set)
|
||||
if !diff.Empty() {
|
||||
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: diff})
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if i < len(s.members) {
|
||||
for _, c := range s.members[i:] {
|
||||
if !s2.Members.Has(c.pathElement) {
|
||||
out.members = append(out.members, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// EnsureNamedFieldsAreMembers returns a set that contains all the named fields along with the leaves.
|
||||
func (s *SetNodeMap) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.TypeRef) *SetNodeMap {
|
||||
out := make(sortedSetNode, 0, s.Size())
|
||||
atom, _ := sc.Resolve(tr)
|
||||
for _, member := range s.members {
|
||||
tr := schema.TypeRef{}
|
||||
if member.pathElement.FieldName != nil && atom.Map != nil {
|
||||
tr = atom.Map.ElementType
|
||||
if sf, ok := atom.Map.FindField(*member.pathElement.FieldName); ok {
|
||||
tr = sf.Type
|
||||
}
|
||||
} else if member.pathElement.Key != nil && atom.List != nil {
|
||||
tr = atom.List.ElementType
|
||||
}
|
||||
out = append(out, setNode{
|
||||
pathElement: member.pathElement,
|
||||
set: member.set.EnsureNamedFieldsAreMembers(sc, tr),
|
||||
})
|
||||
}
|
||||
|
||||
return &SetNodeMap{
|
||||
members: out,
|
||||
}
|
||||
}
|
||||
|
||||
// FilterIncludeMatches returns a SetNodeMap with only the field paths that match the matcher.
|
||||
func (s *SetNodeMap) FilterIncludeMatches(pattern *SetMatcher) *SetNodeMap {
|
||||
if pattern.wildcard {
|
||||
return s
|
||||
}
|
||||
|
||||
var out sortedSetNode
|
||||
for _, member := range s.members {
|
||||
for _, c := range pattern.members {
|
||||
if c.Path.Wildcard || c.Path.PathElement.Equals(member.pathElement) {
|
||||
childSet := member.set.FilterIncludeMatches(c.Child)
|
||||
if childSet.Size() > 0 {
|
||||
out = append(out, setNode{
|
||||
pathElement: member.pathElement,
|
||||
set: childSet,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &SetNodeMap{
|
||||
members: out,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate calls f for each PathElement in the set.
|
||||
func (s *SetNodeMap) Iterate(f func(PathElement)) {
|
||||
for _, n := range s.members {
|
||||
f(n.pathElement)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SetNodeMap) iteratePrefix(prefix Path, f func(Path)) {
|
||||
for _, n := range s.members {
|
||||
pe := n.pathElement
|
||||
n.set.iteratePrefix(append(prefix, pe), f)
|
||||
}
|
||||
}
|
||||
|
||||
// Leaves returns a SetNodeMap containing
|
||||
// only setNodes with leaf PathElements.
|
||||
func (s *SetNodeMap) Leaves() *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
out.members = make(sortedSetNode, len(s.members))
|
||||
for i, n := range s.members {
|
||||
out.members[i] = setNode{
|
||||
pathElement: n.pathElement,
|
||||
set: n.set.Leaves(),
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Filter defines an interface for excluding field paths from a set.
|
||||
// NewExcludeSetFilter can be used to create a filter that removes
|
||||
// specific field paths and all of their children.
|
||||
// NewIncludeMatcherFilter can be used to create a filter that removes all fields except
|
||||
// the fields that match a field path matcher. PrefixMatcher and MakePrefixMatcherOrDie
|
||||
// can be used to define field path patterns.
|
||||
type Filter interface {
|
||||
// Filter returns a filtered copy of the set.
|
||||
Filter(*Set) *Set
|
||||
}
|
||||
|
||||
// NewExcludeSetFilter returns a filter that removes field paths in the exclude set.
|
||||
func NewExcludeSetFilter(exclude *Set) Filter {
|
||||
return excludeFilter{exclude}
|
||||
}
|
||||
|
||||
// NewExcludeFilterSetMap converts a map of APIVersion to exclude set to a map of APIVersion to exclude filters.
|
||||
func NewExcludeFilterSetMap(resetFields map[APIVersion]*Set) map[APIVersion]Filter {
|
||||
result := make(map[APIVersion]Filter)
|
||||
for k, v := range resetFields {
|
||||
result[k] = excludeFilter{v}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type excludeFilter struct {
|
||||
excludeSet *Set
|
||||
}
|
||||
|
||||
func (t excludeFilter) Filter(set *Set) *Set {
|
||||
return set.RecursiveDifference(t.excludeSet)
|
||||
}
|
||||
|
||||
// NewIncludeMatcherFilter returns a filter that only includes field paths that match.
|
||||
// If no matchers are provided, the filter includes all field paths.
|
||||
// PrefixMatcher and MakePrefixMatcherOrDie can help create basic matcher.
|
||||
func NewIncludeMatcherFilter(matchers ...*SetMatcher) Filter {
|
||||
if len(matchers) == 0 {
|
||||
return includeMatcherFilter{&SetMatcher{wildcard: true}}
|
||||
}
|
||||
matcher := matchers[0]
|
||||
for i := 1; i < len(matchers); i++ {
|
||||
matcher = matcher.Merge(matchers[i])
|
||||
}
|
||||
|
||||
return includeMatcherFilter{matcher}
|
||||
}
|
||||
|
||||
type includeMatcherFilter struct {
|
||||
matcher *SetMatcher
|
||||
}
|
||||
|
||||
func (pf includeMatcherFilter) Filter(set *Set) *Set {
|
||||
return set.FilterIncludeMatches(pf.matcher)
|
||||
}
|
121
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/conflict.go
generated
vendored
Normal file
121
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/conflict.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package merge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// Conflict is a conflict on a specific field with the current manager of
|
||||
// that field. It does implement the error interface so that it can be
|
||||
// used as an error.
|
||||
type Conflict struct {
|
||||
Manager string
|
||||
Path fieldpath.Path
|
||||
}
|
||||
|
||||
// Conflict is an error.
|
||||
var _ error = Conflict{}
|
||||
|
||||
// Error formats the conflict as an error.
|
||||
func (c Conflict) Error() string {
|
||||
return fmt.Sprintf("conflict with %q: %v", c.Manager, c.Path)
|
||||
}
|
||||
|
||||
// Equals returns true if c == c2
|
||||
func (c Conflict) Equals(c2 Conflict) bool {
|
||||
if c.Manager != c2.Manager {
|
||||
return false
|
||||
}
|
||||
return c.Path.Equals(c2.Path)
|
||||
}
|
||||
|
||||
// Conflicts accumulates multiple conflicts and aggregates them by managers.
|
||||
type Conflicts []Conflict
|
||||
|
||||
var _ error = Conflicts{}
|
||||
|
||||
// Error prints the list of conflicts, grouped by sorted managers.
|
||||
func (conflicts Conflicts) Error() string {
|
||||
if len(conflicts) == 1 {
|
||||
return conflicts[0].Error()
|
||||
}
|
||||
|
||||
m := map[string][]fieldpath.Path{}
|
||||
for _, conflict := range conflicts {
|
||||
m[conflict.Manager] = append(m[conflict.Manager], conflict.Path)
|
||||
}
|
||||
|
||||
managers := []string{}
|
||||
for manager := range m {
|
||||
managers = append(managers, manager)
|
||||
}
|
||||
|
||||
// Print conflicts by sorted managers.
|
||||
sort.Strings(managers)
|
||||
|
||||
messages := []string{}
|
||||
for _, manager := range managers {
|
||||
messages = append(messages, fmt.Sprintf("conflicts with %q:", manager))
|
||||
for _, path := range m[manager] {
|
||||
messages = append(messages, fmt.Sprintf("- %v", path))
|
||||
}
|
||||
}
|
||||
return strings.Join(messages, "\n")
|
||||
}
|
||||
|
||||
// Equals returns true if the lists of conflicts are the same.
|
||||
func (c Conflicts) Equals(c2 Conflicts) bool {
|
||||
if len(c) != len(c2) {
|
||||
return false
|
||||
}
|
||||
for i := range c {
|
||||
if !c[i].Equals(c2[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ToSet aggregates conflicts for all managers into a single Set.
|
||||
func (c Conflicts) ToSet() *fieldpath.Set {
|
||||
set := fieldpath.NewSet()
|
||||
for _, conflict := range []Conflict(c) {
|
||||
set.Insert(conflict.Path)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// ConflictsFromManagers creates a list of conflicts given Managers sets.
|
||||
func ConflictsFromManagers(sets fieldpath.ManagedFields) Conflicts {
|
||||
conflicts := []Conflict{}
|
||||
|
||||
for manager, set := range sets {
|
||||
set.Set().Iterate(func(p fieldpath.Path) {
|
||||
conflicts = append(conflicts, Conflict{
|
||||
Manager: manager,
|
||||
Path: p.Copy(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return conflicts
|
||||
}
|
394
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go
generated
vendored
Normal file
394
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go
generated
vendored
Normal file
@ -0,0 +1,394 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package merge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/typed"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
// Converter is an interface to the conversion logic. The converter
|
||||
// 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
|
||||
}
|
||||
|
||||
// UpdateBuilder allows you to create a new Updater by exposing all of
|
||||
// the options and setting them once.
|
||||
type UpdaterBuilder struct {
|
||||
Converter Converter
|
||||
IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter
|
||||
|
||||
// IgnoredFields provides a set of fields to ignore for each
|
||||
IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set
|
||||
|
||||
// Stop comparing the new object with old object after applying.
|
||||
// This was initially used to avoid spurious etcd update, but
|
||||
// since that's vastly inefficient, we've come-up with a better
|
||||
// way of doing that. Create this flag to stop it.
|
||||
// Comparing has become more expensive too now that we're not using
|
||||
// `Compare` but `value.Equals` so this gives an option to avoid it.
|
||||
ReturnInputOnNoop bool
|
||||
}
|
||||
|
||||
func (u *UpdaterBuilder) BuildUpdater() *Updater {
|
||||
return &Updater{
|
||||
Converter: u.Converter,
|
||||
IgnoreFilter: u.IgnoreFilter,
|
||||
IgnoredFields: u.IgnoredFields,
|
||||
returnInputOnNoop: u.ReturnInputOnNoop,
|
||||
}
|
||||
}
|
||||
|
||||
// Updater is the object used to compute updated FieldSets and also
|
||||
// merge the object on Apply.
|
||||
type Updater struct {
|
||||
// Deprecated: This will eventually become private.
|
||||
Converter Converter
|
||||
|
||||
// Deprecated: This will eventually become private.
|
||||
IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set
|
||||
|
||||
// Deprecated: This will eventually become private.
|
||||
IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter
|
||||
|
||||
returnInputOnNoop bool
|
||||
}
|
||||
|
||||
func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, workflow string, force bool) (fieldpath.ManagedFields, *typed.Comparison, error) {
|
||||
conflicts := fieldpath.ManagedFields{}
|
||||
removed := fieldpath.ManagedFields{}
|
||||
compare, err := oldObject.Compare(newObject)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to compare objects: %v", err)
|
||||
}
|
||||
|
||||
var versions map[fieldpath.APIVersion]*typed.Comparison
|
||||
|
||||
if s.IgnoredFields != nil && s.IgnoreFilter != nil {
|
||||
return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set")
|
||||
}
|
||||
if s.IgnoredFields != nil {
|
||||
versions = map[fieldpath.APIVersion]*typed.Comparison{
|
||||
version: compare.ExcludeFields(s.IgnoredFields[version]),
|
||||
}
|
||||
} else {
|
||||
versions = map[fieldpath.APIVersion]*typed.Comparison{
|
||||
version: compare.FilterFields(s.IgnoreFilter[version]),
|
||||
}
|
||||
}
|
||||
|
||||
for manager, managerSet := range managers {
|
||||
if manager == workflow {
|
||||
continue
|
||||
}
|
||||
compare, ok := versions[managerSet.APIVersion()]
|
||||
if !ok {
|
||||
var err error
|
||||
versionedOldObject, err := s.Converter.Convert(oldObject, managerSet.APIVersion())
|
||||
if err != nil {
|
||||
if s.Converter.IsMissingVersionError(err) {
|
||||
delete(managers, manager)
|
||||
continue
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to convert old object: %v", err)
|
||||
}
|
||||
versionedNewObject, err := s.Converter.Convert(newObject, managerSet.APIVersion())
|
||||
if err != nil {
|
||||
if s.Converter.IsMissingVersionError(err) {
|
||||
delete(managers, manager)
|
||||
continue
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to convert new object: %v", err)
|
||||
}
|
||||
compare, err = versionedOldObject.Compare(versionedNewObject)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to compare objects: %v", err)
|
||||
}
|
||||
|
||||
if s.IgnoredFields != nil {
|
||||
versions[managerSet.APIVersion()] = compare.ExcludeFields(s.IgnoredFields[managerSet.APIVersion()])
|
||||
} else {
|
||||
versions[managerSet.APIVersion()] = compare.FilterFields(s.IgnoreFilter[managerSet.APIVersion()])
|
||||
}
|
||||
}
|
||||
|
||||
conflictSet := managerSet.Set().Intersection(compare.Modified.Union(compare.Added))
|
||||
if !conflictSet.Empty() {
|
||||
conflicts[manager] = fieldpath.NewVersionedSet(conflictSet, managerSet.APIVersion(), false)
|
||||
}
|
||||
|
||||
if !compare.Removed.Empty() {
|
||||
removed[manager] = fieldpath.NewVersionedSet(compare.Removed, managerSet.APIVersion(), false)
|
||||
}
|
||||
}
|
||||
|
||||
if !force && len(conflicts) != 0 {
|
||||
return nil, nil, ConflictsFromManagers(conflicts)
|
||||
}
|
||||
|
||||
for manager, conflictSet := range conflicts {
|
||||
managers[manager] = fieldpath.NewVersionedSet(managers[manager].Set().Difference(conflictSet.Set()), managers[manager].APIVersion(), managers[manager].Applied())
|
||||
}
|
||||
|
||||
for manager, removedSet := range removed {
|
||||
managers[manager] = fieldpath.NewVersionedSet(managers[manager].Set().Difference(removedSet.Set()), managers[manager].APIVersion(), managers[manager].Applied())
|
||||
}
|
||||
|
||||
for manager := range managers {
|
||||
if managers[manager].Set().Empty() {
|
||||
delete(managers, manager)
|
||||
}
|
||||
}
|
||||
|
||||
return managers, compare, nil
|
||||
}
|
||||
|
||||
// Update is the method you should call once you've merged your final
|
||||
// object on CREATE/UPDATE/PATCH verbs. newObject must be the object
|
||||
// that you intend to persist (after applying the patch if this is for a
|
||||
// PATCH call), and liveObject must be the original object (empty if
|
||||
// this is a CREATE call).
|
||||
func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string) (*typed.TypedValue, fieldpath.ManagedFields, error) {
|
||||
var err error
|
||||
managers, err = s.reconcileManagedFieldsWithSchemaChanges(liveObject, managers)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
managers, compare, err := s.update(liveObject, newObject, version, managers, manager, true)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
if _, ok := managers[manager]; !ok {
|
||||
managers[manager] = fieldpath.NewVersionedSet(fieldpath.NewSet(), version, false)
|
||||
}
|
||||
set := managers[manager].Set().Difference(compare.Removed).Union(compare.Modified).Union(compare.Added)
|
||||
|
||||
if s.IgnoredFields != nil && s.IgnoreFilter != nil {
|
||||
return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set")
|
||||
}
|
||||
var ignoreFilter fieldpath.Filter
|
||||
if s.IgnoredFields != nil {
|
||||
ignoreFilter = fieldpath.NewExcludeSetFilter(s.IgnoredFields[version])
|
||||
} else {
|
||||
ignoreFilter = s.IgnoreFilter[version]
|
||||
}
|
||||
if ignoreFilter != nil {
|
||||
set = ignoreFilter.Filter(set)
|
||||
}
|
||||
|
||||
managers[manager] = fieldpath.NewVersionedSet(
|
||||
set,
|
||||
version,
|
||||
false,
|
||||
)
|
||||
if managers[manager].Set().Empty() {
|
||||
delete(managers, manager)
|
||||
}
|
||||
return newObject, managers, nil
|
||||
}
|
||||
|
||||
// Apply should be called when Apply is run, given the current object as
|
||||
// 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) {
|
||||
var err error
|
||||
managers, err = s.reconcileManagedFieldsWithSchemaChanges(liveObject, managers)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
newObject, err := liveObject.Merge(configObject)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to merge config: %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)
|
||||
}
|
||||
|
||||
if s.IgnoredFields != nil && s.IgnoreFilter != nil {
|
||||
return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set")
|
||||
}
|
||||
var ignoreFilter fieldpath.Filter
|
||||
if s.IgnoredFields != nil {
|
||||
ignoreFilter = fieldpath.NewExcludeSetFilter(s.IgnoredFields[version])
|
||||
} else {
|
||||
ignoreFilter = s.IgnoreFilter[version]
|
||||
}
|
||||
if ignoreFilter != nil {
|
||||
set = ignoreFilter.Filter(set)
|
||||
}
|
||||
managers[manager] = fieldpath.NewVersionedSet(set, version, true)
|
||||
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
|
||||
}
|
||||
if !s.returnInputOnNoop && value.EqualsUsing(value.NewFreelistAllocator(), liveObject.AsValue(), newObject.AsValue()) {
|
||||
newObject = nil
|
||||
}
|
||||
return newObject, managers, nil
|
||||
}
|
||||
|
||||
// prune will remove a field, 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
|
||||
}
|
||||
version := lastSet.APIVersion()
|
||||
convertedMerged, err := s.Converter.Convert(merged, version)
|
||||
if err != nil {
|
||||
if s.Converter.IsMissingVersionError(err) {
|
||||
return merged, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to convert merged object to last applied version: %v", err)
|
||||
}
|
||||
|
||||
sc, tr := convertedMerged.Schema(), convertedMerged.TypeRef()
|
||||
pruned := convertedMerged.RemoveItems(lastSet.Set().EnsureNamedFieldsAreMembers(sc, tr))
|
||||
pruned, err = s.addBackOwnedItems(convertedMerged, pruned, version, managers, applyingManager)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed add back owned items: %v", err)
|
||||
}
|
||||
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 fields, list and map items that were removed by prune,
|
||||
// but other appliers or updaters (or the current applier's new config) claim to own.
|
||||
func (s *Updater) addBackOwnedItems(merged, pruned *typed.TypedValue, prunedVersion fieldpath.APIVersion, managedFields fieldpath.ManagedFields, applyingManager string) (*typed.TypedValue, error) {
|
||||
var err error
|
||||
managedAtVersion := map[fieldpath.APIVersion]*fieldpath.Set{}
|
||||
for _, managerSet := range managedFields {
|
||||
if _, ok := managedAtVersion[managerSet.APIVersion()]; !ok {
|
||||
managedAtVersion[managerSet.APIVersion()] = fieldpath.NewSet()
|
||||
}
|
||||
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.
|
||||
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, pruned, err = s.addBackOwnedItemsForVersion(merged, pruned, version, managed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
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.
|
||||
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)
|
||||
}
|
||||
sc, tr := merged.Schema(), merged.TypeRef()
|
||||
prunedSet = prunedSet.EnsureNamedFieldsAreMembers(sc, tr)
|
||||
mergedSet = mergedSet.EnsureNamedFieldsAreMembers(sc, tr)
|
||||
last := lastSet.Set().EnsureNamedFieldsAreMembers(sc, tr)
|
||||
return merged.RemoveItems(mergedSet.Difference(prunedSet).Intersection(last)), nil
|
||||
}
|
||||
|
||||
// reconcileManagedFieldsWithSchemaChanges reconciles the managed fields with any changes to the
|
||||
// object's schema since the managed fields were written.
|
||||
//
|
||||
// Supports:
|
||||
// - changing types from atomic to granular
|
||||
// - changing types from granular to atomic
|
||||
func (s *Updater) reconcileManagedFieldsWithSchemaChanges(liveObject *typed.TypedValue, managers fieldpath.ManagedFields) (fieldpath.ManagedFields, error) {
|
||||
result := fieldpath.ManagedFields{}
|
||||
for manager, versionedSet := range managers {
|
||||
tv, err := s.Converter.Convert(liveObject, versionedSet.APIVersion())
|
||||
if s.Converter.IsMissingVersionError(err) { // okay to skip, obsolete versions will be deleted automatically anyway
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reconciled, err := typed.ReconcileFieldSetWithSchema(versionedSet.Set(), tv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reconciled != nil {
|
||||
result[manager] = fieldpath.NewVersionedSet(reconciled, versionedSet.APIVersion(), versionedSet.Applied())
|
||||
} else {
|
||||
result[manager] = versionedSet
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
28
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/schema/doc.go
generated
vendored
Normal file
28
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/schema/doc.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package schema defines a targeted schema language which allows one to
|
||||
// represent all the schema information necessary to perform "structured"
|
||||
// merges and diffs.
|
||||
//
|
||||
// Due to the targeted nature of the data model, the schema language can fit in
|
||||
// just a few hundred lines of go code, making it much more understandable and
|
||||
// concise than e.g. OpenAPI.
|
||||
//
|
||||
// This schema was derived by observing the API objects used by Kubernetes, and
|
||||
// formalizing a model which allows certain operations ("apply") to be more
|
||||
// well defined. It is currently missing one feature: one-of ("unions").
|
||||
package schema
|
375
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/schema/elements.go
generated
vendored
Normal file
375
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/schema/elements.go
generated
vendored
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Schema is a list of named types.
|
||||
//
|
||||
// Schema types are indexed in a map before the first search so this type
|
||||
// should be considered immutable.
|
||||
type Schema struct {
|
||||
Types []TypeDef `yaml:"types,omitempty"`
|
||||
|
||||
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.
|
||||
type TypeSpecifier struct {
|
||||
Type TypeRef `yaml:"type,omitempty"`
|
||||
Schema Schema `yaml:"schema,omitempty"`
|
||||
}
|
||||
|
||||
// TypeDef represents a named type in a schema.
|
||||
type TypeDef struct {
|
||||
// Top level types should be named. Every type must have a unique name.
|
||||
Name string `yaml:"name,omitempty"`
|
||||
|
||||
Atom `yaml:"atom,omitempty,inline"`
|
||||
}
|
||||
|
||||
// TypeRef either refers to a named type or declares an inlined type.
|
||||
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.
|
||||
// Each set field in the Atom represents a possible type for the object.
|
||||
// If none of the fields are set, any object will fail validation against the atom.
|
||||
type Atom struct {
|
||||
*Scalar `yaml:"scalar,omitempty"`
|
||||
*List `yaml:"list,omitempty"`
|
||||
*Map `yaml:"map,omitempty"`
|
||||
}
|
||||
|
||||
// Scalar (AKA "primitive") represents a type which has a single value which is
|
||||
// either numeric, string, or boolean, or untyped for any of them.
|
||||
//
|
||||
// TODO: split numeric into float/int? Something even more fine-grained?
|
||||
type Scalar string
|
||||
|
||||
const (
|
||||
Numeric = Scalar("numeric")
|
||||
String = Scalar("string")
|
||||
Boolean = Scalar("boolean")
|
||||
Untyped = Scalar("untyped")
|
||||
)
|
||||
|
||||
// ElementRelationship is an enum of the different possible relationships
|
||||
// between the elements of container types (maps, lists).
|
||||
type ElementRelationship string
|
||||
|
||||
const (
|
||||
// Associative only applies to lists (see the documentation there).
|
||||
Associative = ElementRelationship("associative")
|
||||
// Atomic makes container types (lists, maps) behave
|
||||
// as scalars / leaf fields
|
||||
Atomic = ElementRelationship("atomic")
|
||||
// Separable means the items of the container type have no particular
|
||||
// relationship (default behavior for maps).
|
||||
Separable = ElementRelationship("separable")
|
||||
)
|
||||
|
||||
// Map is a key-value pair. Its default semantics are the same as an
|
||||
// associative list, but:
|
||||
// - It is serialized differently:
|
||||
// map: {"k": {"value": "v"}}
|
||||
// list: [{"key": "k", "value": "v"}]
|
||||
// - 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
|
||||
// values).
|
||||
//
|
||||
// Maps may also represent a type which is composed of a number of different fields.
|
||||
// Each field has a name and a type.
|
||||
//
|
||||
// Fields are indexed in a map before the first search so this type
|
||||
// should be considered immutable.
|
||||
type Map struct {
|
||||
// Each struct field appears exactly once in this list. The order in
|
||||
// this list defines the canonical field ordering.
|
||||
Fields []StructField `yaml:"fields,omitempty"`
|
||||
|
||||
// A Union is a grouping of fields with special rules. It may refer to
|
||||
// one or more fields in the above list. A given field from the above
|
||||
// list may be referenced in exactly 0 or 1 places in the below list.
|
||||
// One can have multiple unions in the same struct, but the fields can't
|
||||
// overlap between unions.
|
||||
Unions []Union `yaml:"unions,omitempty"`
|
||||
|
||||
// ElementType is the type of the structs's unknown fields.
|
||||
ElementType TypeRef `yaml:"elementType,omitempty"`
|
||||
|
||||
// ElementRelationship states the relationship between the map's items.
|
||||
// * `separable` (or unset) implies that each element is 100% independent.
|
||||
// * `atomic` implies that all elements depend on each other, and this
|
||||
// is effectively a scalar / leaf field; it doesn't make sense for
|
||||
// separate actors to set the elements. Example: an RGB color struct;
|
||||
// it would never make sense to "own" only one component of the
|
||||
// color.
|
||||
// The default behavior for maps is `separable`; it's permitted to
|
||||
// leave this unset to get the default behavior.
|
||||
ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
|
||||
|
||||
once sync.Once
|
||||
m map[string]StructField
|
||||
}
|
||||
|
||||
// FindField is a convenience function that returns the referenced StructField,
|
||||
// if it exists, or (nil, false) if it doesn't.
|
||||
func (m *Map) FindField(name string) (StructField, bool) {
|
||||
m.once.Do(func() {
|
||||
m.m = make(map[string]StructField, len(m.Fields))
|
||||
for _, field := range m.Fields {
|
||||
m.m[field.Name] = field
|
||||
}
|
||||
})
|
||||
sf, ok := m.m[name]
|
||||
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.
|
||||
type UnionField struct {
|
||||
// FieldName is the name of the field that is part of the union. This
|
||||
// is the serialized form of the field.
|
||||
FieldName string `yaml:"fieldName"`
|
||||
// Discriminatorvalue is the value of the discriminator to
|
||||
// select that field. If the union doesn't have a discriminator,
|
||||
// this field is ignored.
|
||||
DiscriminatorValue string `yaml:"discriminatorValue"`
|
||||
}
|
||||
|
||||
// Union, or oneof, means that only one of multiple fields of a structure can be
|
||||
// set at a time. Setting the discriminator helps clearing oher fields:
|
||||
// - If discriminator changed to non-nil, and a new field has been added
|
||||
// that doesn't match, an error is returned,
|
||||
// - If discriminator hasn't changed and two fields or more are set, an
|
||||
// error is returned,
|
||||
// - If discriminator changed to non-nil, all other fields but the
|
||||
// discriminated one will be cleared,
|
||||
// - Otherwise, If only one field is left, update discriminator to that value.
|
||||
type Union struct {
|
||||
// Discriminator, if present, is the name of the field that
|
||||
// discriminates fields in the union. The mapping between the value of
|
||||
// the discriminator and the field is done by using the Fields list
|
||||
// below.
|
||||
Discriminator *string `yaml:"discriminator,omitempty"`
|
||||
|
||||
// DeduceInvalidDiscriminator indicates if the discriminator
|
||||
// should be updated automatically based on the fields set. This
|
||||
// typically defaults to false since we don't want to deduce by
|
||||
// default (the behavior exists to maintain compatibility on
|
||||
// existing types and shouldn't be used for new types).
|
||||
DeduceInvalidDiscriminator bool `yaml:"deduceInvalidDiscriminator,omitempty"`
|
||||
|
||||
// This is the list of fields that belong to this union. All the
|
||||
// fields present in here have to be part of the parent
|
||||
// structure. Discriminator (if oneOf has one), is NOT included in
|
||||
// this list. The value for field is how we map the name of the field
|
||||
// to actual value for discriminator.
|
||||
Fields []UnionField `yaml:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// StructField pairs a field name with a field type.
|
||||
type StructField struct {
|
||||
// Name is the field name.
|
||||
Name string `yaml:"name,omitempty"`
|
||||
// Type is the field type.
|
||||
Type TypeRef `yaml:"type,omitempty"`
|
||||
// Default value for the field, nil if not present.
|
||||
Default interface{} `yaml:"default,omitempty"`
|
||||
}
|
||||
|
||||
// List represents a type which contains a zero or more elements, all of the
|
||||
// same subtype. Lists may be either associative: each element is more or less
|
||||
// independent and could be managed by separate entities in the system; or
|
||||
// atomic, where the elements are heavily dependent on each other: it is not
|
||||
// sensible to change one element without considering the ramifications on all
|
||||
// the other elements.
|
||||
type List struct {
|
||||
// ElementType is the type of the list's elements.
|
||||
ElementType TypeRef `yaml:"elementType,omitempty"`
|
||||
|
||||
// ElementRelationship states the relationship between the list's elements
|
||||
// and must have one of these values:
|
||||
// * `atomic`: the list is treated as a single entity, like a scalar.
|
||||
// * `associative`:
|
||||
// - If the list element is a scalar, the list is treated as a set.
|
||||
// - If the list element is a map, the list is treated as a map.
|
||||
// There is no default for this value for lists; all schemas must
|
||||
// explicitly state the element relationship for all lists.
|
||||
ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
|
||||
|
||||
// Iff ElementRelationship is `associative`, and the element type is
|
||||
// map, then Keys must have non-zero length, and it lists the fields
|
||||
// of the element's map type which are to be used as the keys of the
|
||||
// list.
|
||||
//
|
||||
// TODO: change this to "non-atomic struct" above and make the code reflect this.
|
||||
//
|
||||
// Each key must refer to a single field name (no nesting, not JSONPath).
|
||||
Keys []string `yaml:"keys,omitempty"`
|
||||
}
|
||||
|
||||
// FindNamedType is a convenience function that returns the referenced TypeDef,
|
||||
// if it exists, or (nil, false) if it doesn't.
|
||||
func (s *Schema) FindNamedType(name string) (TypeDef, bool) {
|
||||
s.once.Do(func() {
|
||||
s.m = make(map[string]TypeDef, len(s.Types))
|
||||
for _, t := range s.Types {
|
||||
s.m[t.Name] = t
|
||||
}
|
||||
})
|
||||
t, ok := s.m[name]
|
||||
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 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
|
||||
}
|
||||
|
||||
// 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
|
||||
})
|
||||
}
|
||||
}
|
202
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/schema/equals.go
generated
vendored
Normal file
202
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/schema/equals.go
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Equals returns true iff the two Schemas are equal.
|
||||
func (a *Schema) Equals(b *Schema) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
|
||||
if len(a.Types) != len(b.Types) {
|
||||
return false
|
||||
}
|
||||
for i := range a.Types {
|
||||
if !a.Types[i].Equals(&b.Types[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true iff the two TypeRefs are equal.
|
||||
//
|
||||
// Note that two typerefs that have an equivalent type but where one is
|
||||
// inlined and the other is named, are not considered equal.
|
||||
func (a *TypeRef) Equals(b *TypeRef) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
if (a.NamedType == nil) != (b.NamedType == nil) {
|
||||
return false
|
||||
}
|
||||
if a.NamedType != nil {
|
||||
if *a.NamedType != *b.NamedType {
|
||||
return false
|
||||
}
|
||||
//return true
|
||||
}
|
||||
if a.ElementRelationship != b.ElementRelationship {
|
||||
return false
|
||||
}
|
||||
return a.Inlined.Equals(&b.Inlined)
|
||||
}
|
||||
|
||||
// Equals returns true iff the two TypeDefs are equal.
|
||||
func (a *TypeDef) Equals(b *TypeDef) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
if a.Name != b.Name {
|
||||
return false
|
||||
}
|
||||
return a.Atom.Equals(&b.Atom)
|
||||
}
|
||||
|
||||
// Equals returns true iff the two Atoms are equal.
|
||||
func (a *Atom) Equals(b *Atom) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
if (a.Scalar == nil) != (b.Scalar == nil) {
|
||||
return false
|
||||
}
|
||||
if (a.List == nil) != (b.List == nil) {
|
||||
return false
|
||||
}
|
||||
if (a.Map == nil) != (b.Map == nil) {
|
||||
return false
|
||||
}
|
||||
switch {
|
||||
case a.Scalar != nil:
|
||||
return *a.Scalar == *b.Scalar
|
||||
case a.List != nil:
|
||||
return a.List.Equals(b.List)
|
||||
case a.Map != nil:
|
||||
return a.Map.Equals(b.Map)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true iff the two Maps are equal.
|
||||
func (a *Map) Equals(b *Map) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
if !a.ElementType.Equals(&b.ElementType) {
|
||||
return false
|
||||
}
|
||||
if a.ElementRelationship != b.ElementRelationship {
|
||||
return false
|
||||
}
|
||||
if len(a.Fields) != len(b.Fields) {
|
||||
return false
|
||||
}
|
||||
for i := range a.Fields {
|
||||
if !a.Fields[i].Equals(&b.Fields[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(a.Unions) != len(b.Unions) {
|
||||
return false
|
||||
}
|
||||
for i := range a.Unions {
|
||||
if !a.Unions[i].Equals(&b.Unions[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true iff the two Unions are equal.
|
||||
func (a *Union) Equals(b *Union) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
if (a.Discriminator == nil) != (b.Discriminator == nil) {
|
||||
return false
|
||||
}
|
||||
if a.Discriminator != nil {
|
||||
if *a.Discriminator != *b.Discriminator {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if a.DeduceInvalidDiscriminator != b.DeduceInvalidDiscriminator {
|
||||
return false
|
||||
}
|
||||
if len(a.Fields) != len(b.Fields) {
|
||||
return false
|
||||
}
|
||||
for i := range a.Fields {
|
||||
if !a.Fields[i].Equals(&b.Fields[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true iff the two UnionFields are equal.
|
||||
func (a *UnionField) Equals(b *UnionField) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
if a.FieldName != b.FieldName {
|
||||
return false
|
||||
}
|
||||
if a.DiscriminatorValue != b.DiscriminatorValue {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true iff the two StructFields are equal.
|
||||
func (a *StructField) Equals(b *StructField) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
if a.Name != b.Name {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(a.Default, b.Default) {
|
||||
return false
|
||||
}
|
||||
return a.Type.Equals(&b.Type)
|
||||
}
|
||||
|
||||
// Equals returns true iff the two Lists are equal.
|
||||
func (a *List) Equals(b *List) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
if !a.ElementType.Equals(&b.ElementType) {
|
||||
return false
|
||||
}
|
||||
if a.ElementRelationship != b.ElementRelationship {
|
||||
return false
|
||||
}
|
||||
if len(a.Keys) != len(b.Keys) {
|
||||
return false
|
||||
}
|
||||
for i := range a.Keys {
|
||||
if a.Keys[i] != b.Keys[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
165
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/schema/schemaschema.go
generated
vendored
Normal file
165
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/schema/schemaschema.go
generated
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// SchemaSchemaYAML is a schema against which you can validate other schemas.
|
||||
// It will validate itself. It can be unmarshalled into a Schema type.
|
||||
var SchemaSchemaYAML = `types:
|
||||
- name: schema
|
||||
map:
|
||||
fields:
|
||||
- name: types
|
||||
type:
|
||||
list:
|
||||
elementRelationship: associative
|
||||
elementType:
|
||||
namedType: typeDef
|
||||
keys:
|
||||
- name
|
||||
- name: typeDef
|
||||
map:
|
||||
fields:
|
||||
- name: name
|
||||
type:
|
||||
scalar: string
|
||||
- name: scalar
|
||||
type:
|
||||
scalar: string
|
||||
- name: map
|
||||
type:
|
||||
namedType: map
|
||||
- name: list
|
||||
type:
|
||||
namedType: list
|
||||
- name: untyped
|
||||
type:
|
||||
namedType: untyped
|
||||
- name: typeRef
|
||||
map:
|
||||
fields:
|
||||
- name: namedType
|
||||
type:
|
||||
scalar: string
|
||||
- name: scalar
|
||||
type:
|
||||
scalar: string
|
||||
- name: map
|
||||
type:
|
||||
namedType: map
|
||||
- name: list
|
||||
type:
|
||||
namedType: list
|
||||
- name: untyped
|
||||
type:
|
||||
namedType: untyped
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
- name: scalar
|
||||
scalar: string
|
||||
- name: map
|
||||
map:
|
||||
fields:
|
||||
- name: fields
|
||||
type:
|
||||
list:
|
||||
elementType:
|
||||
namedType: structField
|
||||
elementRelationship: associative
|
||||
keys: [ "name" ]
|
||||
- name: unions
|
||||
type:
|
||||
list:
|
||||
elementType:
|
||||
namedType: union
|
||||
elementRelationship: atomic
|
||||
- name: elementType
|
||||
type:
|
||||
namedType: typeRef
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
- name: unionField
|
||||
map:
|
||||
fields:
|
||||
- name: fieldName
|
||||
type:
|
||||
scalar: string
|
||||
- name: discriminatorValue
|
||||
type:
|
||||
scalar: string
|
||||
- name: union
|
||||
map:
|
||||
fields:
|
||||
- name: discriminator
|
||||
type:
|
||||
scalar: string
|
||||
- name: deduceInvalidDiscriminator
|
||||
type:
|
||||
scalar: boolean
|
||||
- name: fields
|
||||
type:
|
||||
list:
|
||||
elementRelationship: associative
|
||||
elementType:
|
||||
namedType: unionField
|
||||
keys:
|
||||
- fieldName
|
||||
- name: structField
|
||||
map:
|
||||
fields:
|
||||
- name: name
|
||||
type:
|
||||
scalar: string
|
||||
- name: type
|
||||
type:
|
||||
namedType: typeRef
|
||||
- name: default
|
||||
type:
|
||||
namedType: __untyped_atomic_
|
||||
- name: list
|
||||
map:
|
||||
fields:
|
||||
- name: elementType
|
||||
type:
|
||||
namedType: typeRef
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
- name: keys
|
||||
type:
|
||||
list:
|
||||
elementType:
|
||||
scalar: string
|
||||
elementRelationship: atomic
|
||||
- name: untyped
|
||||
map:
|
||||
fields:
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
- name: __untyped_atomic_
|
||||
scalar: untyped
|
||||
list:
|
||||
elementType:
|
||||
namedType: __untyped_atomic_
|
||||
elementRelationship: atomic
|
||||
map:
|
||||
elementType:
|
||||
namedType: __untyped_atomic_
|
||||
elementRelationship: atomic
|
||||
`
|
470
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/compare.go
generated
vendored
Normal file
470
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/compare.go
generated
vendored
Normal file
@ -0,0 +1,470 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
// Comparison is the return value of a TypedValue.Compare() operation.
|
||||
//
|
||||
// No field will appear in more than one of the three fieldsets. If all of the
|
||||
// fieldsets are empty, then the objects must have been equal.
|
||||
type Comparison struct {
|
||||
// Removed contains any fields removed by rhs (the right-hand-side
|
||||
// object in the comparison).
|
||||
Removed *fieldpath.Set
|
||||
// Modified contains fields present in both objects but different.
|
||||
Modified *fieldpath.Set
|
||||
// Added contains any fields added by rhs.
|
||||
Added *fieldpath.Set
|
||||
}
|
||||
|
||||
// IsSame returns true if the comparison returned no changes (the two
|
||||
// compared objects are similar).
|
||||
func (c *Comparison) IsSame() bool {
|
||||
return c.Removed.Empty() && c.Modified.Empty() && c.Added.Empty()
|
||||
}
|
||||
|
||||
// String returns a human readable version of the comparison.
|
||||
func (c *Comparison) String() string {
|
||||
bld := strings.Builder{}
|
||||
if !c.Modified.Empty() {
|
||||
bld.WriteString(fmt.Sprintf("- Modified Fields:\n%v\n", c.Modified))
|
||||
}
|
||||
if !c.Added.Empty() {
|
||||
bld.WriteString(fmt.Sprintf("- Added Fields:\n%v\n", c.Added))
|
||||
}
|
||||
if !c.Removed.Empty() {
|
||||
bld.WriteString(fmt.Sprintf("- Removed Fields:\n%v\n", c.Removed))
|
||||
}
|
||||
return bld.String()
|
||||
}
|
||||
|
||||
// ExcludeFields fields from the compare recursively removes the fields
|
||||
// from the entire comparison
|
||||
func (c *Comparison) ExcludeFields(fields *fieldpath.Set) *Comparison {
|
||||
if fields == nil || fields.Empty() {
|
||||
return c
|
||||
}
|
||||
c.Removed = c.Removed.RecursiveDifference(fields)
|
||||
c.Modified = c.Modified.RecursiveDifference(fields)
|
||||
c.Added = c.Added.RecursiveDifference(fields)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Comparison) FilterFields(filter fieldpath.Filter) *Comparison {
|
||||
if filter == nil {
|
||||
return c
|
||||
}
|
||||
c.Removed = filter.Filter(c.Removed)
|
||||
c.Modified = filter.Filter(c.Modified)
|
||||
c.Added = filter.Filter(c.Added)
|
||||
return c
|
||||
}
|
||||
|
||||
type compareWalker struct {
|
||||
lhs value.Value
|
||||
rhs value.Value
|
||||
schema *schema.Schema
|
||||
typeRef schema.TypeRef
|
||||
|
||||
// Current path that we are comparing
|
||||
path fieldpath.Path
|
||||
|
||||
// Resulting comparison.
|
||||
comparison *Comparison
|
||||
|
||||
// internal housekeeping--don't set when constructing.
|
||||
inLeaf bool // Set to true if we're in a "big leaf"--atomic map/list
|
||||
|
||||
// Allocate only as many walkers as needed for the depth by storing them here.
|
||||
spareWalkers *[]*compareWalker
|
||||
|
||||
allocator value.Allocator
|
||||
}
|
||||
|
||||
// compare compares stuff.
|
||||
func (w *compareWalker) compare(prefixFn func() string) (errs ValidationErrors) {
|
||||
if w.lhs == nil && w.rhs == nil {
|
||||
// check this condidition here instead of everywhere below.
|
||||
return errorf("at least one of lhs and rhs must be provided")
|
||||
}
|
||||
a, ok := w.schema.Resolve(w.typeRef)
|
||||
if !ok {
|
||||
return errorf("schema error: no type found matching: %v", *w.typeRef.NamedType)
|
||||
}
|
||||
|
||||
alhs := deduceAtom(a, w.lhs)
|
||||
arhs := deduceAtom(a, w.rhs)
|
||||
|
||||
// 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
|
||||
errs = append(errs, handleAtom(alhs, w.typeRef, &w2)...)
|
||||
errs = append(errs, handleAtom(arhs, w.typeRef, w)...)
|
||||
}
|
||||
|
||||
if !w.inLeaf {
|
||||
if w.lhs == nil {
|
||||
w.comparison.Added.Insert(w.path)
|
||||
} else if w.rhs == nil {
|
||||
w.comparison.Removed.Insert(w.path)
|
||||
}
|
||||
}
|
||||
return errs.WithLazyPrefix(prefixFn)
|
||||
}
|
||||
|
||||
// doLeaf should be called on leaves before descending into children, if there
|
||||
// will be a descent. It modifies w.inLeaf.
|
||||
func (w *compareWalker) doLeaf() {
|
||||
if w.inLeaf {
|
||||
// We're in a "big leaf", an atomic map or list. Ignore
|
||||
// subsequent leaves.
|
||||
return
|
||||
}
|
||||
w.inLeaf = true
|
||||
|
||||
// We don't recurse into leaf fields for merging.
|
||||
if w.lhs == nil {
|
||||
w.comparison.Added.Insert(w.path)
|
||||
} else if w.rhs == nil {
|
||||
w.comparison.Removed.Insert(w.path)
|
||||
} else if !value.EqualsUsing(w.allocator, w.rhs, w.lhs) {
|
||||
// TODO: Equality is not sufficient for this.
|
||||
// Need to implement equality check on the value type.
|
||||
w.comparison.Modified.Insert(w.path)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *compareWalker) doScalar(t *schema.Scalar) ValidationErrors {
|
||||
// Make sure at least one side is a valid scalar.
|
||||
lerrs := validateScalar(t, w.lhs, "lhs: ")
|
||||
rerrs := validateScalar(t, w.rhs, "rhs: ")
|
||||
if len(lerrs) > 0 && len(rerrs) > 0 {
|
||||
return append(lerrs, rerrs...)
|
||||
}
|
||||
|
||||
// All scalars are leaf fields.
|
||||
w.doLeaf()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *compareWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef, cmp *Comparison) *compareWalker {
|
||||
if w.spareWalkers == nil {
|
||||
// first descent.
|
||||
w.spareWalkers = &[]*compareWalker{}
|
||||
}
|
||||
var w2 *compareWalker
|
||||
if n := len(*w.spareWalkers); n > 0 {
|
||||
w2, *w.spareWalkers = (*w.spareWalkers)[n-1], (*w.spareWalkers)[:n-1]
|
||||
} else {
|
||||
w2 = &compareWalker{}
|
||||
}
|
||||
*w2 = *w
|
||||
w2.typeRef = tr
|
||||
w2.path = append(w2.path, pe)
|
||||
w2.lhs = nil
|
||||
w2.rhs = nil
|
||||
w2.comparison = cmp
|
||||
return w2
|
||||
}
|
||||
|
||||
func (w *compareWalker) finishDescent(w2 *compareWalker) {
|
||||
// if the descent caused a realloc, ensure that we reuse the buffer
|
||||
// for the next sibling.
|
||||
w.path = w2.path[:len(w2.path)-1]
|
||||
*w.spareWalkers = append(*w.spareWalkers, w2)
|
||||
}
|
||||
|
||||
func (w *compareWalker) derefMap(prefix string, v value.Value) (value.Map, ValidationErrors) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
m, err := mapValue(w.allocator, v)
|
||||
if err != nil {
|
||||
return nil, errorf("%v: %v", prefix, err)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (errs ValidationErrors) {
|
||||
rLen := 0
|
||||
if rhs != nil {
|
||||
rLen = rhs.Length()
|
||||
}
|
||||
lLen := 0
|
||||
if lhs != nil {
|
||||
lLen = lhs.Length()
|
||||
}
|
||||
|
||||
maxLength := rLen
|
||||
if lLen > maxLength {
|
||||
maxLength = lLen
|
||||
}
|
||||
// Contains all the unique PEs between lhs and rhs, exactly once.
|
||||
// Order doesn't matter since we're just tracking ownership in a set.
|
||||
allPEs := make([]fieldpath.PathElement, 0, maxLength)
|
||||
|
||||
// Gather all the elements from lhs, indexed by PE, in a list for duplicates.
|
||||
lValues := fieldpath.MakePathElementMap(lLen)
|
||||
for i := 0; i < lLen; i++ {
|
||||
child := lhs.At(i)
|
||||
pe, err := listItemToPathElement(w.allocator, w.schema, t, 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 v, found := lValues.Get(pe); found {
|
||||
list := v.([]value.Value)
|
||||
lValues.Insert(pe, append(list, child))
|
||||
} else {
|
||||
lValues.Insert(pe, []value.Value{child})
|
||||
allPEs = append(allPEs, pe)
|
||||
}
|
||||
}
|
||||
|
||||
// Gather all the elements from rhs, indexed by PE, in a list for duplicates.
|
||||
rValues := fieldpath.MakePathElementMap(rLen)
|
||||
for i := 0; i < rLen; i++ {
|
||||
rValue := rhs.At(i)
|
||||
pe, err := listItemToPathElement(w.allocator, w.schema, t, rValue)
|
||||
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 v, found := rValues.Get(pe); found {
|
||||
list := v.([]value.Value)
|
||||
rValues.Insert(pe, append(list, rValue))
|
||||
} else {
|
||||
rValues.Insert(pe, []value.Value{rValue})
|
||||
if _, found := lValues.Get(pe); !found {
|
||||
allPEs = append(allPEs, pe)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pe := range allPEs {
|
||||
lList := []value.Value(nil)
|
||||
if l, ok := lValues.Get(pe); ok {
|
||||
lList = l.([]value.Value)
|
||||
}
|
||||
rList := []value.Value(nil)
|
||||
if l, ok := rValues.Get(pe); ok {
|
||||
rList = l.([]value.Value)
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(lList) == 0 && len(rList) == 0:
|
||||
// We shouldn't be here anyway.
|
||||
return
|
||||
// Normal use-case:
|
||||
// We have no duplicates for this PE, compare items one-to-one.
|
||||
case len(lList) <= 1 && len(rList) <= 1:
|
||||
lValue := value.Value(nil)
|
||||
if len(lList) != 0 {
|
||||
lValue = lList[0]
|
||||
}
|
||||
rValue := value.Value(nil)
|
||||
if len(rList) != 0 {
|
||||
rValue = rList[0]
|
||||
}
|
||||
errs = append(errs, w.compareListItem(t, pe, lValue, rValue)...)
|
||||
// Duplicates before & after use-case:
|
||||
// Compare the duplicates lists as if they were atomic, mark modified if they changed.
|
||||
case len(lList) >= 2 && len(rList) >= 2:
|
||||
listEqual := func(lList, rList []value.Value) bool {
|
||||
if len(lList) != len(rList) {
|
||||
return false
|
||||
}
|
||||
for i := range lList {
|
||||
if !value.Equals(lList[i], rList[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
if !listEqual(lList, rList) {
|
||||
w.comparison.Modified.Insert(append(w.path, pe))
|
||||
}
|
||||
// Duplicates before & not anymore use-case:
|
||||
// Rcursively add new non-duplicate items, Remove duplicate marker,
|
||||
case len(lList) >= 2:
|
||||
if len(rList) != 0 {
|
||||
errs = append(errs, w.compareListItem(t, pe, nil, rList[0])...)
|
||||
}
|
||||
w.comparison.Removed.Insert(append(w.path, pe))
|
||||
// New duplicates use-case:
|
||||
// Recursively remove old non-duplicate items, add duplicate marker.
|
||||
case len(rList) >= 2:
|
||||
if len(lList) != 0 {
|
||||
errs = append(errs, w.compareListItem(t, pe, lList[0], nil)...)
|
||||
}
|
||||
w.comparison.Added.Insert(append(w.path, pe))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (w *compareWalker) 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, 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
|
||||
}
|
||||
// Ignore repeated occurences of `pe`.
|
||||
if _, found := observed.Get(pe); found {
|
||||
continue
|
||||
}
|
||||
observed.Insert(pe, child)
|
||||
pes = append(pes, pe)
|
||||
}
|
||||
return pes, observed, errs
|
||||
}
|
||||
|
||||
func (w *compareWalker) compareListItem(t *schema.List, pe fieldpath.PathElement, lChild, rChild value.Value) ValidationErrors {
|
||||
w2 := w.prepareDescent(pe, t.ElementType, w.comparison)
|
||||
w2.lhs = lChild
|
||||
w2.rhs = rChild
|
||||
errs := w2.compare(pe.String)
|
||||
w.finishDescent(w2)
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *compareWalker) derefList(prefix string, v value.Value) (value.List, ValidationErrors) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
l, err := listValue(w.allocator, v)
|
||||
if err != nil {
|
||||
return nil, errorf("%v: %v", prefix, err)
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (w *compareWalker) doList(t *schema.List) (errs ValidationErrors) {
|
||||
lhs, _ := w.derefList("lhs: ", w.lhs)
|
||||
if lhs != nil {
|
||||
defer w.allocator.Free(lhs)
|
||||
}
|
||||
rhs, _ := w.derefList("rhs: ", w.rhs)
|
||||
if rhs != nil {
|
||||
defer w.allocator.Free(rhs)
|
||||
}
|
||||
|
||||
// If both lhs and rhs are empty/null, treat it as a
|
||||
// leaf: this helps preserve the empty/null
|
||||
// distinction.
|
||||
emptyPromoteToLeaf := (lhs == nil || lhs.Length() == 0) && (rhs == nil || rhs.Length() == 0)
|
||||
|
||||
if t.ElementRelationship == schema.Atomic || emptyPromoteToLeaf {
|
||||
w.doLeaf()
|
||||
return nil
|
||||
}
|
||||
|
||||
if lhs == nil && rhs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = w.visitListItems(t, lhs, rhs)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *compareWalker) visitMapItem(t *schema.Map, out map[string]interface{}, key string, lhs, rhs value.Value) (errs ValidationErrors) {
|
||||
fieldType := t.ElementType
|
||||
if sf, ok := t.FindField(key); ok {
|
||||
fieldType = sf.Type
|
||||
}
|
||||
pe := fieldpath.PathElement{FieldName: &key}
|
||||
w2 := w.prepareDescent(pe, fieldType, w.comparison)
|
||||
w2.lhs = lhs
|
||||
w2.rhs = rhs
|
||||
errs = append(errs, w2.compare(pe.String)...)
|
||||
w.finishDescent(w2)
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *compareWalker) visitMapItems(t *schema.Map, lhs, rhs value.Map) (errs ValidationErrors) {
|
||||
out := map[string]interface{}{}
|
||||
|
||||
value.MapZipUsing(w.allocator, lhs, rhs, value.Unordered, func(key string, lhsValue, rhsValue value.Value) bool {
|
||||
errs = append(errs, w.visitMapItem(t, out, key, lhsValue, rhsValue)...)
|
||||
return true
|
||||
})
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *compareWalker) doMap(t *schema.Map) (errs ValidationErrors) {
|
||||
lhs, _ := w.derefMap("lhs: ", w.lhs)
|
||||
if lhs != nil {
|
||||
defer w.allocator.Free(lhs)
|
||||
}
|
||||
rhs, _ := w.derefMap("rhs: ", w.rhs)
|
||||
if rhs != nil {
|
||||
defer w.allocator.Free(rhs)
|
||||
}
|
||||
// If both lhs and rhs are empty/null, treat it as a
|
||||
// leaf: this helps preserve the empty/null
|
||||
// distinction.
|
||||
emptyPromoteToLeaf := (lhs == nil || lhs.Empty()) && (rhs == nil || rhs.Empty())
|
||||
|
||||
if t.ElementRelationship == schema.Atomic || emptyPromoteToLeaf {
|
||||
w.doLeaf()
|
||||
return nil
|
||||
}
|
||||
|
||||
if lhs == nil && rhs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = append(errs, w.visitMapItems(t, lhs, rhs)...)
|
||||
|
||||
return errs
|
||||
}
|
18
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/doc.go
generated
vendored
Normal file
18
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package typed contains logic for operating on values with given schemas.
|
||||
package typed
|
259
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/helpers.go
generated
vendored
Normal file
259
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/helpers.go
generated
vendored
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
// ValidationError reports an error about a particular field
|
||||
type ValidationError struct {
|
||||
Path string
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
// Error returns a human readable error message.
|
||||
func (ve ValidationError) Error() string {
|
||||
if len(ve.Path) == 0 {
|
||||
return ve.ErrorMessage
|
||||
}
|
||||
return fmt.Sprintf("%s: %v", ve.Path, ve.ErrorMessage)
|
||||
}
|
||||
|
||||
// ValidationErrors accumulates multiple validation error messages.
|
||||
type ValidationErrors []ValidationError
|
||||
|
||||
// Error returns a human readable error message reporting each error in the
|
||||
// list.
|
||||
func (errs ValidationErrors) Error() string {
|
||||
if len(errs) == 1 {
|
||||
return errs[0].Error()
|
||||
}
|
||||
messages := []string{"errors:"}
|
||||
for _, e := range errs {
|
||||
messages = append(messages, " "+e.Error())
|
||||
}
|
||||
return strings.Join(messages, "\n")
|
||||
}
|
||||
|
||||
// Set the given path to all the validation errors.
|
||||
func (errs ValidationErrors) WithPath(p string) ValidationErrors {
|
||||
for i := range errs {
|
||||
errs[i].Path = p
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// WithPrefix prefixes all errors path with the given pathelement. This
|
||||
// is useful when unwinding the stack on errors.
|
||||
func (errs ValidationErrors) WithPrefix(prefix string) ValidationErrors {
|
||||
for i := range errs {
|
||||
errs[i].Path = prefix + errs[i].Path
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// WithLazyPrefix prefixes all errors path with the given pathelement.
|
||||
// This is useful when unwinding the stack on errors. Prefix is
|
||||
// computed lazily only if there is an error.
|
||||
func (errs ValidationErrors) WithLazyPrefix(fn func() string) ValidationErrors {
|
||||
if len(errs) == 0 {
|
||||
return errs
|
||||
}
|
||||
prefix := ""
|
||||
if fn != nil {
|
||||
prefix = fn()
|
||||
}
|
||||
for i := range errs {
|
||||
errs[i].Path = prefix + errs[i].Path
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func errorf(format string, args ...interface{}) ValidationErrors {
|
||||
return ValidationErrors{{
|
||||
ErrorMessage: fmt.Sprintf(format, args...),
|
||||
}}
|
||||
}
|
||||
|
||||
type atomHandler interface {
|
||||
doScalar(*schema.Scalar) ValidationErrors
|
||||
doList(*schema.List) ValidationErrors
|
||||
doMap(*schema.Map) ValidationErrors
|
||||
}
|
||||
|
||||
func resolveSchema(s *schema.Schema, tr schema.TypeRef, v value.Value, ah atomHandler) ValidationErrors {
|
||||
a, ok := s.Resolve(tr)
|
||||
if !ok {
|
||||
typeName := "inlined type"
|
||||
if tr.NamedType != nil {
|
||||
typeName = *tr.NamedType
|
||||
}
|
||||
return errorf("schema error: no type found matching: %v", typeName)
|
||||
}
|
||||
|
||||
a = deduceAtom(a, v)
|
||||
return handleAtom(a, tr, ah)
|
||||
}
|
||||
|
||||
// deduceAtom determines which of the possible types in atom 'atom' applies to value 'val'.
|
||||
// If val is of a type allowed by atom, return a copy of atom with all other types set to nil.
|
||||
// if val is nil, or is not of a type allowed by atom, just return the original atom,
|
||||
// and validation will fail at a later stage. (with a more useful error)
|
||||
func deduceAtom(atom schema.Atom, val value.Value) schema.Atom {
|
||||
switch {
|
||||
case val == nil:
|
||||
case val.IsFloat(), val.IsInt(), val.IsString(), val.IsBool():
|
||||
if atom.Scalar != nil {
|
||||
return schema.Atom{Scalar: atom.Scalar}
|
||||
}
|
||||
case val.IsList():
|
||||
if atom.List != nil {
|
||||
return schema.Atom{List: atom.List}
|
||||
}
|
||||
case val.IsMap():
|
||||
if atom.Map != nil {
|
||||
return schema.Atom{Map: atom.Map}
|
||||
}
|
||||
}
|
||||
return atom
|
||||
}
|
||||
|
||||
func handleAtom(a schema.Atom, tr schema.TypeRef, ah atomHandler) ValidationErrors {
|
||||
switch {
|
||||
case a.Map != nil:
|
||||
return ah.doMap(a.Map)
|
||||
case a.Scalar != nil:
|
||||
return ah.doScalar(a.Scalar)
|
||||
case a.List != nil:
|
||||
return ah.doList(a.List)
|
||||
}
|
||||
|
||||
name := "inlined"
|
||||
if tr.NamedType != nil {
|
||||
name = "named type: " + *tr.NamedType
|
||||
}
|
||||
|
||||
return errorf("schema error: invalid atom: %v", name)
|
||||
}
|
||||
|
||||
// Returns the list, or an error. Reminder: nil is a valid list and might be returned.
|
||||
func listValue(a value.Allocator, val value.Value) (value.List, error) {
|
||||
if val.IsNull() {
|
||||
// Null is a valid list.
|
||||
return nil, nil
|
||||
}
|
||||
if !val.IsList() {
|
||||
return nil, fmt.Errorf("expected list, got %v", val)
|
||||
}
|
||||
return val.AsListUsing(a), nil
|
||||
}
|
||||
|
||||
// Returns the map, or an error. Reminder: nil is a valid map and might be returned.
|
||||
func mapValue(a value.Allocator, val value.Value) (value.Map, error) {
|
||||
if val == nil {
|
||||
return nil, fmt.Errorf("expected map, got nil")
|
||||
}
|
||||
if val.IsNull() {
|
||||
// Null is a valid map.
|
||||
return nil, nil
|
||||
}
|
||||
if !val.IsMap() {
|
||||
return nil, fmt.Errorf("expected map, got %v", val)
|
||||
}
|
||||
return val.AsMapUsing(a), nil
|
||||
}
|
||||
|
||||
func getAssociativeKeyDefault(s *schema.Schema, list *schema.List, fieldName string) (interface{}, error) {
|
||||
atom, ok := s.Resolve(list.ElementType)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid elementType for list")
|
||||
}
|
||||
if atom.Map == nil {
|
||||
return nil, errors.New("associative list may not have non-map types")
|
||||
}
|
||||
// If the field is not found, we can assume there is no default.
|
||||
field, _ := atom.Map.FindField(fieldName)
|
||||
return field.Default, nil
|
||||
}
|
||||
|
||||
func keyedAssociativeListItemToPathElement(a value.Allocator, s *schema.Schema, list *schema.List, child value.Value) (fieldpath.PathElement, error) {
|
||||
pe := fieldpath.PathElement{}
|
||||
if child.IsNull() {
|
||||
// null entries are illegal.
|
||||
return pe, errors.New("associative list with keys may not have a null element")
|
||||
}
|
||||
if !child.IsMap() {
|
||||
return pe, errors.New("associative list with keys may not have non-map elements")
|
||||
}
|
||||
keyMap := value.FieldList{}
|
||||
m := child.AsMapUsing(a)
|
||||
defer a.Free(m)
|
||||
for _, fieldName := range list.Keys {
|
||||
if val, ok := m.Get(fieldName); ok {
|
||||
keyMap = append(keyMap, value.Field{Name: fieldName, Value: val})
|
||||
} else if def, err := getAssociativeKeyDefault(s, list, fieldName); err != nil {
|
||||
return pe, fmt.Errorf("couldn't find default value for %v: %v", fieldName, err)
|
||||
} else if def != nil {
|
||||
keyMap = append(keyMap, value.Field{Name: fieldName, Value: value.NewValueInterface(def)})
|
||||
} else {
|
||||
return pe, fmt.Errorf("associative list with keys has an element that omits key field %q (and doesn't have default value)", fieldName)
|
||||
}
|
||||
}
|
||||
keyMap.Sort()
|
||||
pe.Key = &keyMap
|
||||
return pe, nil
|
||||
}
|
||||
|
||||
func setItemToPathElement(child value.Value) (fieldpath.PathElement, error) {
|
||||
pe := fieldpath.PathElement{}
|
||||
switch {
|
||||
case child.IsMap():
|
||||
// TODO: atomic maps should be acceptable.
|
||||
return pe, errors.New("associative list without keys has an element that's a map type")
|
||||
case child.IsList():
|
||||
// Should we support a set of lists? For the moment
|
||||
// let's say we don't.
|
||||
// TODO: atomic lists should be acceptable.
|
||||
return pe, errors.New("not supported: associative list with lists as elements")
|
||||
case child.IsNull():
|
||||
return pe, errors.New("associative list without keys has an element that's an explicit null")
|
||||
default:
|
||||
// We are a set type.
|
||||
pe.Value = &child
|
||||
return pe, nil
|
||||
}
|
||||
}
|
||||
|
||||
func listItemToPathElement(a value.Allocator, s *schema.Schema, list *schema.List, child value.Value) (fieldpath.PathElement, error) {
|
||||
if list.ElementRelationship != schema.Associative {
|
||||
return fieldpath.PathElement{}, errors.New("invalid indexing of non-associative list")
|
||||
}
|
||||
|
||||
if len(list.Keys) > 0 {
|
||||
return keyedAssociativeListItemToPathElement(a, s, list, child)
|
||||
}
|
||||
|
||||
// If there's no keys, then we must be a set of primitives.
|
||||
return setItemToPathElement(child)
|
||||
}
|
427
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/merge.go
generated
vendored
Normal file
427
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/merge.go
generated
vendored
Normal file
@ -0,0 +1,427 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
type mergingWalker struct {
|
||||
lhs value.Value
|
||||
rhs value.Value
|
||||
schema *schema.Schema
|
||||
typeRef schema.TypeRef
|
||||
|
||||
// Current path that we are merging
|
||||
path fieldpath.Path
|
||||
|
||||
// How to merge. Called after schema validation for all leaf fields.
|
||||
rule mergeRule
|
||||
|
||||
// If set, called after non-leaf items have been merged. (`out` is
|
||||
// probably already set.)
|
||||
postItemHook mergeRule
|
||||
|
||||
// output of the merge operation (nil if none)
|
||||
out *interface{}
|
||||
|
||||
// internal housekeeping--don't set when constructing.
|
||||
inLeaf bool // Set to true if we're in a "big leaf"--atomic map/list
|
||||
|
||||
// Allocate only as many walkers as needed for the depth by storing them here.
|
||||
spareWalkers *[]*mergingWalker
|
||||
|
||||
allocator value.Allocator
|
||||
}
|
||||
|
||||
// merge rules examine w.lhs and w.rhs (up to one of which may be nil) and
|
||||
// optionally set w.out. If lhs and rhs are both set, they will be of
|
||||
// comparable type.
|
||||
type mergeRule func(w *mergingWalker)
|
||||
|
||||
var (
|
||||
ruleKeepRHS = mergeRule(func(w *mergingWalker) {
|
||||
if w.rhs != nil {
|
||||
v := w.rhs.Unstructured()
|
||||
w.out = &v
|
||||
} else if w.lhs != nil {
|
||||
v := w.lhs.Unstructured()
|
||||
w.out = &v
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
// merge sets w.out.
|
||||
func (w *mergingWalker) merge(prefixFn func() string) (errs ValidationErrors) {
|
||||
if w.lhs == nil && w.rhs == nil {
|
||||
// check this condidition here instead of everywhere below.
|
||||
return errorf("at least one of lhs and rhs must be provided")
|
||||
}
|
||||
a, ok := w.schema.Resolve(w.typeRef)
|
||||
if !ok {
|
||||
return errorf("schema error: no type found matching: %v", *w.typeRef.NamedType)
|
||||
}
|
||||
|
||||
alhs := deduceAtom(a, w.lhs)
|
||||
arhs := deduceAtom(a, w.rhs)
|
||||
|
||||
// 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
|
||||
errs = append(errs, handleAtom(alhs, w.typeRef, &w2)...)
|
||||
errs = append(errs, handleAtom(arhs, w.typeRef, w)...)
|
||||
}
|
||||
|
||||
if !w.inLeaf && w.postItemHook != nil {
|
||||
w.postItemHook(w)
|
||||
}
|
||||
return errs.WithLazyPrefix(prefixFn)
|
||||
}
|
||||
|
||||
// doLeaf should be called on leaves before descending into children, if there
|
||||
// will be a descent. It modifies w.inLeaf.
|
||||
func (w *mergingWalker) doLeaf() {
|
||||
if w.inLeaf {
|
||||
// We're in a "big leaf", an atomic map or list. Ignore
|
||||
// subsequent leaves.
|
||||
return
|
||||
}
|
||||
w.inLeaf = true
|
||||
|
||||
// We don't recurse into leaf fields for merging.
|
||||
w.rule(w)
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doScalar(t *schema.Scalar) ValidationErrors {
|
||||
// Make sure at least one side is a valid scalar.
|
||||
lerrs := validateScalar(t, w.lhs, "lhs: ")
|
||||
rerrs := validateScalar(t, w.rhs, "rhs: ")
|
||||
if len(lerrs) > 0 && len(rerrs) > 0 {
|
||||
return append(lerrs, rerrs...)
|
||||
}
|
||||
|
||||
// All scalars are leaf fields.
|
||||
w.doLeaf()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *mergingWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef) *mergingWalker {
|
||||
if w.spareWalkers == nil {
|
||||
// first descent.
|
||||
w.spareWalkers = &[]*mergingWalker{}
|
||||
}
|
||||
var w2 *mergingWalker
|
||||
if n := len(*w.spareWalkers); n > 0 {
|
||||
w2, *w.spareWalkers = (*w.spareWalkers)[n-1], (*w.spareWalkers)[:n-1]
|
||||
} else {
|
||||
w2 = &mergingWalker{}
|
||||
}
|
||||
*w2 = *w
|
||||
w2.typeRef = tr
|
||||
w2.path = append(w2.path, pe)
|
||||
w2.lhs = nil
|
||||
w2.rhs = nil
|
||||
w2.out = nil
|
||||
return w2
|
||||
}
|
||||
|
||||
func (w *mergingWalker) finishDescent(w2 *mergingWalker) {
|
||||
// if the descent caused a realloc, ensure that we reuse the buffer
|
||||
// for the next sibling.
|
||||
w.path = w2.path[:len(w2.path)-1]
|
||||
*w.spareWalkers = append(*w.spareWalkers, w2)
|
||||
}
|
||||
|
||||
func (w *mergingWalker) derefMap(prefix string, v value.Value) (value.Map, ValidationErrors) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
m, err := mapValue(w.allocator, v)
|
||||
if err != nil {
|
||||
return nil, errorf("%v: %v", prefix, err)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (errs ValidationErrors) {
|
||||
rLen := 0
|
||||
if rhs != nil {
|
||||
rLen = rhs.Length()
|
||||
}
|
||||
lLen := 0
|
||||
if lhs != nil {
|
||||
lLen = lhs.Length()
|
||||
}
|
||||
outLen := lLen
|
||||
if outLen < rLen {
|
||||
outLen = rLen
|
||||
}
|
||||
out := make([]interface{}, 0, outLen)
|
||||
|
||||
rhsPEs, observedRHS, rhsErrs := w.indexListPathElements(t, rhs, false)
|
||||
errs = append(errs, rhsErrs...)
|
||||
lhsPEs, observedLHS, lhsErrs := w.indexListPathElements(t, lhs, true)
|
||||
errs = append(errs, lhsErrs...)
|
||||
|
||||
if len(errs) != 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
sharedOrder := make([]*fieldpath.PathElement, 0, rLen)
|
||||
for i := range rhsPEs {
|
||||
pe := &rhsPEs[i]
|
||||
if _, ok := observedLHS.Get(*pe); ok {
|
||||
sharedOrder = append(sharedOrder, pe)
|
||||
}
|
||||
}
|
||||
|
||||
var nextShared *fieldpath.PathElement
|
||||
if len(sharedOrder) > 0 {
|
||||
nextShared = sharedOrder[0]
|
||||
sharedOrder = sharedOrder[1:]
|
||||
}
|
||||
|
||||
mergedRHS := fieldpath.MakePathElementMap(len(rhsPEs))
|
||||
lLen, rLen = len(lhsPEs), len(rhsPEs)
|
||||
for lI, rI := 0, 0; lI < lLen || rI < rLen; {
|
||||
if lI < lLen && rI < rLen {
|
||||
pe := lhsPEs[lI]
|
||||
if pe.Equals(rhsPEs[rI]) {
|
||||
// merge LHS & RHS items
|
||||
mergedRHS.Insert(pe, struct{}{})
|
||||
lChild, _ := observedLHS.Get(pe) // may be nil if the PE is duplicaated.
|
||||
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(lhsPEs[lI]) {
|
||||
// shared item, but not the one we want in this round
|
||||
lI++
|
||||
continue
|
||||
}
|
||||
}
|
||||
if lI < lLen {
|
||||
pe := lhsPEs[lI]
|
||||
if _, ok := observedRHS.Get(pe); !ok {
|
||||
// take LHS item using At to make sure we get the right item (observed may not contain the right item).
|
||||
lChild := lhs.AtUsing(w.allocator, lI)
|
||||
mergeOut, errs := w.mergeListItem(t, pe, lChild, nil)
|
||||
errs = append(errs, errs...)
|
||||
if mergeOut != nil {
|
||||
out = append(out, *mergeOut)
|
||||
}
|
||||
lI++
|
||||
continue
|
||||
} else if _, ok := mergedRHS.Get(pe); ok {
|
||||
// we've already merged it with RHS, we don't want to duplicate it, skip it.
|
||||
lI++
|
||||
}
|
||||
}
|
||||
if rI < rLen {
|
||||
// Take the RHS item, merge with matching LHS item if possible
|
||||
pe := rhsPEs[rI]
|
||||
mergedRHS.Insert(pe, struct{}{})
|
||||
lChild, _ := observedLHS.Get(pe) // may be nil if absent or duplicaated.
|
||||
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:]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(out) > 0 {
|
||||
i := interface{}(out)
|
||||
w.out = &i
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) indexListPathElements(t *schema.List, list value.List, allowDuplicates bool) ([]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, 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 && !allowDuplicates {
|
||||
errs = append(errs, errorf("duplicate entries for key %v", pe.String())...)
|
||||
continue
|
||||
} else if !found {
|
||||
observed.Insert(pe, child)
|
||||
} else {
|
||||
// Duplicated items are not merged with the new value, make them nil.
|
||||
observed.Insert(pe, value.NewValueInterface(nil))
|
||||
}
|
||||
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
|
||||
}
|
||||
l, err := listValue(w.allocator, v)
|
||||
if err != nil {
|
||||
return nil, errorf("%v: %v", prefix, err)
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doList(t *schema.List) (errs ValidationErrors) {
|
||||
lhs, _ := w.derefList("lhs: ", w.lhs)
|
||||
if lhs != nil {
|
||||
defer w.allocator.Free(lhs)
|
||||
}
|
||||
rhs, _ := w.derefList("rhs: ", w.rhs)
|
||||
if rhs != nil {
|
||||
defer w.allocator.Free(rhs)
|
||||
}
|
||||
|
||||
// If both lhs and rhs are empty/null, treat it as a
|
||||
// leaf: this helps preserve the empty/null
|
||||
// distinction.
|
||||
emptyPromoteToLeaf := (lhs == nil || lhs.Length() == 0) && (rhs == nil || rhs.Length() == 0)
|
||||
|
||||
if t.ElementRelationship == schema.Atomic || emptyPromoteToLeaf {
|
||||
w.doLeaf()
|
||||
return nil
|
||||
}
|
||||
|
||||
if lhs == nil && rhs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = w.visitListItems(t, lhs, rhs)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) visitMapItem(t *schema.Map, out map[string]interface{}, key string, lhs, rhs value.Value) (errs ValidationErrors) {
|
||||
fieldType := t.ElementType
|
||||
if sf, ok := t.FindField(key); ok {
|
||||
fieldType = sf.Type
|
||||
}
|
||||
pe := fieldpath.PathElement{FieldName: &key}
|
||||
w2 := w.prepareDescent(pe, fieldType)
|
||||
w2.lhs = lhs
|
||||
w2.rhs = rhs
|
||||
errs = append(errs, w2.merge(pe.String)...)
|
||||
if w2.out != nil {
|
||||
out[key] = *w2.out
|
||||
}
|
||||
w.finishDescent(w2)
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) visitMapItems(t *schema.Map, lhs, rhs value.Map) (errs ValidationErrors) {
|
||||
out := map[string]interface{}{}
|
||||
|
||||
value.MapZipUsing(w.allocator, lhs, rhs, value.Unordered, func(key string, lhsValue, rhsValue value.Value) bool {
|
||||
errs = append(errs, w.visitMapItem(t, out, key, lhsValue, rhsValue)...)
|
||||
return true
|
||||
})
|
||||
if len(out) > 0 {
|
||||
i := interface{}(out)
|
||||
w.out = &i
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doMap(t *schema.Map) (errs ValidationErrors) {
|
||||
lhs, _ := w.derefMap("lhs: ", w.lhs)
|
||||
if lhs != nil {
|
||||
defer w.allocator.Free(lhs)
|
||||
}
|
||||
rhs, _ := w.derefMap("rhs: ", w.rhs)
|
||||
if rhs != nil {
|
||||
defer w.allocator.Free(rhs)
|
||||
}
|
||||
// If both lhs and rhs are empty/null, treat it as a
|
||||
// leaf: this helps preserve the empty/null
|
||||
// distinction.
|
||||
emptyPromoteToLeaf := (lhs == nil || lhs.Empty()) && (rhs == nil || rhs.Empty())
|
||||
|
||||
if t.ElementRelationship == schema.Atomic || emptyPromoteToLeaf {
|
||||
w.doLeaf()
|
||||
return nil
|
||||
}
|
||||
|
||||
if lhs == nil && rhs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = append(errs, w.visitMapItems(t, lhs, rhs)...)
|
||||
|
||||
return errs
|
||||
}
|
151
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/parser.go
generated
vendored
Normal file
151
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/parser.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
yaml "sigs.k8s.io/yaml/goyaml.v2"
|
||||
)
|
||||
|
||||
// YAMLObject is an object encoded in YAML.
|
||||
type YAMLObject string
|
||||
|
||||
// Parser implements YAMLParser and allows introspecting the schema.
|
||||
type Parser struct {
|
||||
Schema schema.Schema
|
||||
}
|
||||
|
||||
// create builds an unvalidated parser.
|
||||
func create(s YAMLObject) (*Parser, error) {
|
||||
p := Parser{}
|
||||
err := yaml.Unmarshal([]byte(s), &p.Schema)
|
||||
return &p, err
|
||||
}
|
||||
|
||||
func createOrDie(schema YAMLObject) *Parser {
|
||||
p, err := create(schema)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to create parser: %v", err))
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
var ssParser = createOrDie(YAMLObject(schema.SchemaSchemaYAML))
|
||||
|
||||
// NewParser will build a YAMLParser from a schema. The schema is validated.
|
||||
func NewParser(schema YAMLObject) (*Parser, error) {
|
||||
_, err := ssParser.Type("schema").FromYAML(schema)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to validate schema: %v", err)
|
||||
}
|
||||
p, err := create(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// TypeNames returns a list of types this parser understands.
|
||||
func (p *Parser) TypeNames() (names []string) {
|
||||
for _, td := range p.Schema.Types {
|
||||
names = append(names, td.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// Type returns a helper which can produce objects of the given type. Any
|
||||
// errors are deferred until a further function is called.
|
||||
func (p *Parser) Type(name string) ParseableType {
|
||||
return ParseableType{
|
||||
Schema: &p.Schema,
|
||||
TypeRef: schema.TypeRef{NamedType: &name},
|
||||
}
|
||||
}
|
||||
|
||||
// ParseableType allows for easy production of typed objects.
|
||||
type ParseableType struct {
|
||||
TypeRef schema.TypeRef
|
||||
Schema *schema.Schema
|
||||
}
|
||||
|
||||
// IsValid return true if p's schema and typename are valid.
|
||||
func (p ParseableType) IsValid() bool {
|
||||
_, ok := p.Schema.Resolve(p.TypeRef)
|
||||
return ok
|
||||
}
|
||||
|
||||
// FromYAML parses a yaml string into an object with the current schema
|
||||
// and the type "typename" or an error if validation fails.
|
||||
func (p ParseableType) FromYAML(object YAMLObject, opts ...ValidationOptions) (*TypedValue, error) {
|
||||
var v interface{}
|
||||
err := yaml.Unmarshal([]byte(object), &v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AsTyped(value.NewValueInterface(v), p.Schema, p.TypeRef, opts...)
|
||||
}
|
||||
|
||||
// FromUnstructured converts a go "interface{}" type, typically an
|
||||
// unstructured object in Kubernetes world, to a TypedValue. It returns an
|
||||
// error if the resulting object fails schema validation.
|
||||
// The provided interface{} must be one of: map[string]interface{},
|
||||
// map[interface{}]interface{}, []interface{}, int types, float types,
|
||||
// string or boolean. Nested interface{} must also be one of these types.
|
||||
func (p ParseableType) FromUnstructured(in interface{}, opts ...ValidationOptions) (*TypedValue, error) {
|
||||
return AsTyped(value.NewValueInterface(in), p.Schema, p.TypeRef, opts...)
|
||||
}
|
||||
|
||||
// FromStructured converts a go "interface{}" type, typically an structured object in
|
||||
// Kubernetes, to a TypedValue. It will return an error if the resulting object fails
|
||||
// schema validation. The provided "interface{}" value must be a pointer so that the
|
||||
// value can be modified via reflection. The provided "interface{}" may contain structs
|
||||
// and types that are converted to Values by the jsonMarshaler interface.
|
||||
func (p ParseableType) FromStructured(in interface{}, opts ...ValidationOptions) (*TypedValue, error) {
|
||||
v, err := value.NewValueReflect(in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating struct value reflector: %v", err)
|
||||
}
|
||||
return AsTyped(v, p.Schema, p.TypeRef, opts...)
|
||||
}
|
||||
|
||||
// DeducedParseableType is a ParseableType that deduces the type from
|
||||
// the content of the object.
|
||||
var DeducedParseableType ParseableType = createOrDie(YAMLObject(`types:
|
||||
- name: __untyped_atomic_
|
||||
scalar: untyped
|
||||
list:
|
||||
elementType:
|
||||
namedType: __untyped_atomic_
|
||||
elementRelationship: atomic
|
||||
map:
|
||||
elementType:
|
||||
namedType: __untyped_atomic_
|
||||
elementRelationship: atomic
|
||||
- name: __untyped_deduced_
|
||||
scalar: untyped
|
||||
list:
|
||||
elementType:
|
||||
namedType: __untyped_atomic_
|
||||
elementRelationship: atomic
|
||||
map:
|
||||
elementType:
|
||||
namedType: __untyped_deduced_
|
||||
elementRelationship: separable
|
||||
`)).Type("__untyped_deduced_")
|
290
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/reconcile_schema.go
generated
vendored
Normal file
290
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/reconcile_schema.go
generated
vendored
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/schema"
|
||||
)
|
||||
|
||||
var fmPool = sync.Pool{
|
||||
New: func() interface{} { return &reconcileWithSchemaWalker{} },
|
||||
}
|
||||
|
||||
func (v *reconcileWithSchemaWalker) finished() {
|
||||
v.fieldSet = nil
|
||||
v.schema = nil
|
||||
v.value = nil
|
||||
v.typeRef = schema.TypeRef{}
|
||||
v.path = nil
|
||||
v.toRemove = nil
|
||||
v.toAdd = nil
|
||||
fmPool.Put(v)
|
||||
}
|
||||
|
||||
type reconcileWithSchemaWalker struct {
|
||||
value *TypedValue // root of the live object
|
||||
schema *schema.Schema // root of the live schema
|
||||
|
||||
// state of node being visited by walker
|
||||
fieldSet *fieldpath.Set
|
||||
typeRef schema.TypeRef
|
||||
path fieldpath.Path
|
||||
isAtomic bool
|
||||
|
||||
// the accumulated diff to perform to apply reconciliation
|
||||
toRemove *fieldpath.Set // paths to remove recursively
|
||||
toAdd *fieldpath.Set // paths to add after any removals
|
||||
|
||||
// Allocate only as many walkers as needed for the depth by storing them here.
|
||||
spareWalkers *[]*reconcileWithSchemaWalker
|
||||
}
|
||||
|
||||
func (v *reconcileWithSchemaWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef) *reconcileWithSchemaWalker {
|
||||
if v.spareWalkers == nil {
|
||||
// first descent.
|
||||
v.spareWalkers = &[]*reconcileWithSchemaWalker{}
|
||||
}
|
||||
var v2 *reconcileWithSchemaWalker
|
||||
if n := len(*v.spareWalkers); n > 0 {
|
||||
v2, *v.spareWalkers = (*v.spareWalkers)[n-1], (*v.spareWalkers)[:n-1]
|
||||
} else {
|
||||
v2 = &reconcileWithSchemaWalker{}
|
||||
}
|
||||
*v2 = *v
|
||||
v2.typeRef = tr
|
||||
v2.path = append(v.path, pe)
|
||||
v2.value = v.value
|
||||
return v2
|
||||
}
|
||||
|
||||
func (v *reconcileWithSchemaWalker) finishDescent(v2 *reconcileWithSchemaWalker) {
|
||||
v2.fieldSet = nil
|
||||
v2.schema = nil
|
||||
v2.value = nil
|
||||
v2.typeRef = schema.TypeRef{}
|
||||
if cap(v2.path) < 20 { // recycle slices that do not have unexpectedly high capacity
|
||||
v2.path = v2.path[:0]
|
||||
} else {
|
||||
v2.path = nil
|
||||
}
|
||||
|
||||
// merge any accumulated changes into parent walker
|
||||
if v2.toRemove != nil {
|
||||
if v.toRemove == nil {
|
||||
v.toRemove = v2.toRemove
|
||||
} else {
|
||||
v.toRemove = v.toRemove.Union(v2.toRemove)
|
||||
}
|
||||
}
|
||||
if v2.toAdd != nil {
|
||||
if v.toAdd == nil {
|
||||
v.toAdd = v2.toAdd
|
||||
} else {
|
||||
v.toAdd = v.toAdd.Union(v2.toAdd)
|
||||
}
|
||||
}
|
||||
v2.toRemove = nil
|
||||
v2.toAdd = nil
|
||||
|
||||
// if the descent caused a realloc, ensure that we reuse the buffer
|
||||
// for the next sibling.
|
||||
*v.spareWalkers = append(*v.spareWalkers, v2)
|
||||
}
|
||||
|
||||
// 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
|
||||
// no changes were made to the field set.
|
||||
//
|
||||
// Supports:
|
||||
// - changing types from atomic to granular
|
||||
// - changing types from granular to atomic
|
||||
func ReconcileFieldSetWithSchema(fieldset *fieldpath.Set, tv *TypedValue) (*fieldpath.Set, error) {
|
||||
v := fmPool.Get().(*reconcileWithSchemaWalker)
|
||||
v.fieldSet = fieldset
|
||||
v.value = tv
|
||||
|
||||
v.schema = tv.schema
|
||||
v.typeRef = tv.typeRef
|
||||
|
||||
defer v.finished()
|
||||
errs := v.reconcile()
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, fmt.Errorf("errors reconciling field set with schema: %s", errs.Error())
|
||||
}
|
||||
|
||||
// If there are any accumulated changes, apply them
|
||||
if v.toAdd != nil || v.toRemove != nil {
|
||||
out := v.fieldSet
|
||||
if v.toRemove != nil {
|
||||
out = out.RecursiveDifference(v.toRemove)
|
||||
}
|
||||
if v.toAdd != nil {
|
||||
out = out.Union(v.toAdd)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (v *reconcileWithSchemaWalker) reconcile() (errs ValidationErrors) {
|
||||
a, ok := v.schema.Resolve(v.typeRef)
|
||||
if !ok {
|
||||
errs = append(errs, errorf("could not resolve %v", v.typeRef)...)
|
||||
return
|
||||
}
|
||||
return handleAtom(a, v.typeRef, v)
|
||||
}
|
||||
|
||||
func (v *reconcileWithSchemaWalker) doScalar(_ *schema.Scalar) (errs ValidationErrors) {
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *reconcileWithSchemaWalker) visitListItems(t *schema.List, element *fieldpath.Set) (errs ValidationErrors) {
|
||||
handleElement := func(pe fieldpath.PathElement, isMember bool) {
|
||||
var hasChildren bool
|
||||
v2 := v.prepareDescent(pe, t.ElementType)
|
||||
v2.fieldSet, hasChildren = element.Children.Get(pe)
|
||||
v2.isAtomic = isMember && !hasChildren
|
||||
errs = append(errs, v2.reconcile()...)
|
||||
v.finishDescent(v2)
|
||||
}
|
||||
element.Children.Iterate(func(pe fieldpath.PathElement) {
|
||||
if element.Members.Has(pe) {
|
||||
return
|
||||
}
|
||||
handleElement(pe, false)
|
||||
})
|
||||
element.Members.Iterate(func(pe fieldpath.PathElement) {
|
||||
handleElement(pe, true)
|
||||
})
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *reconcileWithSchemaWalker) doList(t *schema.List) (errs ValidationErrors) {
|
||||
// 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
|
||||
}
|
||||
if v.fieldSet != nil {
|
||||
errs = v.visitListItems(t, v.fieldSet)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *reconcileWithSchemaWalker) visitMapItems(t *schema.Map, element *fieldpath.Set) (errs ValidationErrors) {
|
||||
handleElement := func(pe fieldpath.PathElement, isMember bool) {
|
||||
var hasChildren bool
|
||||
if tr, ok := typeRefAtPath(t, pe); ok { // ignore fields not in the schema
|
||||
v2 := v.prepareDescent(pe, tr)
|
||||
v2.fieldSet, hasChildren = element.Children.Get(pe)
|
||||
v2.isAtomic = isMember && !hasChildren
|
||||
errs = append(errs, v2.reconcile()...)
|
||||
v.finishDescent(v2)
|
||||
}
|
||||
}
|
||||
element.Children.Iterate(func(pe fieldpath.PathElement) {
|
||||
if element.Members.Has(pe) {
|
||||
return
|
||||
}
|
||||
handleElement(pe, false)
|
||||
})
|
||||
element.Members.Iterate(func(pe fieldpath.PathElement) {
|
||||
handleElement(pe, true)
|
||||
})
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *reconcileWithSchemaWalker) doMap(t *schema.Map) (errs ValidationErrors) {
|
||||
// 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
|
||||
v.toAdd = fieldpath.NewSet(v.path) // add the root of the atomic
|
||||
}
|
||||
return errs
|
||||
}
|
||||
if v.fieldSet != nil {
|
||||
errs = v.visitMapItems(t, v.fieldSet)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func fieldSetAtPath(node *fieldpath.Set, path fieldpath.Path) (*fieldpath.Set, bool) {
|
||||
ok := true
|
||||
for _, pe := range path {
|
||||
if node, ok = node.Children.Get(pe); !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
return node, ok
|
||||
}
|
||||
|
||||
func descendToPath(node *fieldpath.Set, path fieldpath.Path) *fieldpath.Set {
|
||||
for _, pe := range path {
|
||||
node = node.Children.Descend(pe)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
func typeRefAtPath(t *schema.Map, pe fieldpath.PathElement) (schema.TypeRef, bool) {
|
||||
tr := t.ElementType
|
||||
if pe.FieldName != nil {
|
||||
if sf, ok := t.FindField(*pe.FieldName); ok {
|
||||
tr = sf.Type
|
||||
}
|
||||
}
|
||||
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"
|
||||
}
|
165
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/remove.go
generated
vendored
Normal file
165
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/remove.go
generated
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
type removingWalker struct {
|
||||
value value.Value
|
||||
out interface{}
|
||||
schema *schema.Schema
|
||||
toRemove *fieldpath.Set
|
||||
allocator value.Allocator
|
||||
shouldExtract bool
|
||||
}
|
||||
|
||||
// removeItemsWithSchema will walk the given value and look for items from the toRemove set.
|
||||
// Depending on whether shouldExtract is set true or false, it will return a modified version
|
||||
// of the input value with either:
|
||||
// 1. only the items in the toRemove set (when shouldExtract is true) or
|
||||
// 2. the items from the toRemove set removed from the value (when shouldExtract is false).
|
||||
func removeItemsWithSchema(val value.Value, toRemove *fieldpath.Set, schema *schema.Schema, typeRef schema.TypeRef, shouldExtract bool) value.Value {
|
||||
w := &removingWalker{
|
||||
value: val,
|
||||
schema: schema,
|
||||
toRemove: toRemove,
|
||||
allocator: value.NewFreelistAllocator(),
|
||||
shouldExtract: shouldExtract,
|
||||
}
|
||||
resolveSchema(schema, typeRef, val, w)
|
||||
return value.NewValueInterface(w.out)
|
||||
}
|
||||
|
||||
func (w *removingWalker) doScalar(t *schema.Scalar) ValidationErrors {
|
||||
w.out = w.value.Unstructured()
|
||||
return nil
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
var newItems []interface{}
|
||||
iter := l.RangeUsing(w.allocator)
|
||||
defer w.allocator.Free(iter)
|
||||
for iter.Next() {
|
||||
_, item := iter.Item()
|
||||
// Ignore error because we have already validated this list
|
||||
pe, _ := listItemToPathElement(w.allocator, w.schema, t, item)
|
||||
path, _ := fieldpath.MakePath(pe)
|
||||
// save items on the path when we shouldExtract
|
||||
// but ignore them when we are removing (i.e. !w.shouldExtract)
|
||||
if w.toRemove.Has(path) {
|
||||
if w.shouldExtract {
|
||||
newItems = append(newItems, removeItemsWithSchema(item, w.toRemove, w.schema, t.ElementType, w.shouldExtract).Unstructured())
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
item = removeItemsWithSchema(item, subset, w.schema, t.ElementType, w.shouldExtract)
|
||||
} else {
|
||||
// don't save items not on the path when we shouldExtract.
|
||||
if w.shouldExtract {
|
||||
continue
|
||||
}
|
||||
}
|
||||
newItems = append(newItems, item.Unstructured())
|
||||
}
|
||||
if len(newItems) > 0 {
|
||||
w.out = newItems
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
fieldTypes := map[string]schema.TypeRef{}
|
||||
for _, structField := range t.Fields {
|
||||
fieldTypes[structField.Name] = structField.Type
|
||||
}
|
||||
|
||||
newMap := map[string]interface{}{}
|
||||
m.Iterate(func(k string, val value.Value) bool {
|
||||
pe := fieldpath.PathElement{FieldName: &k}
|
||||
path, _ := fieldpath.MakePath(pe)
|
||||
fieldType := t.ElementType
|
||||
if ft, ok := fieldTypes[k]; ok {
|
||||
fieldType = ft
|
||||
}
|
||||
// save values on the path when we shouldExtract
|
||||
// but ignore them when we are removing (i.e. !w.shouldExtract)
|
||||
if w.toRemove.Has(path) {
|
||||
if w.shouldExtract {
|
||||
newMap[k] = removeItemsWithSchema(val, w.toRemove, w.schema, fieldType, w.shouldExtract).Unstructured()
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
val = removeItemsWithSchema(val, subset, w.schema, fieldType, w.shouldExtract)
|
||||
} else {
|
||||
// don't save values not on the path when we shouldExtract.
|
||||
if w.shouldExtract {
|
||||
return true
|
||||
}
|
||||
}
|
||||
newMap[k] = val.Unstructured()
|
||||
return true
|
||||
})
|
||||
if len(newMap) > 0 {
|
||||
w.out = newMap
|
||||
}
|
||||
return nil
|
||||
}
|
190
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/tofieldset.go
generated
vendored
Normal file
190
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/tofieldset.go
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
var tPool = sync.Pool{
|
||||
New: func() interface{} { return &toFieldSetWalker{} },
|
||||
}
|
||||
|
||||
func (tv TypedValue) toFieldSetWalker() *toFieldSetWalker {
|
||||
v := tPool.Get().(*toFieldSetWalker)
|
||||
v.value = tv.value
|
||||
v.schema = tv.schema
|
||||
v.typeRef = tv.typeRef
|
||||
v.set = &fieldpath.Set{}
|
||||
v.allocator = value.NewFreelistAllocator()
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *toFieldSetWalker) finished() {
|
||||
v.schema = nil
|
||||
v.typeRef = schema.TypeRef{}
|
||||
v.path = nil
|
||||
v.set = nil
|
||||
tPool.Put(v)
|
||||
}
|
||||
|
||||
type toFieldSetWalker struct {
|
||||
value value.Value
|
||||
schema *schema.Schema
|
||||
typeRef schema.TypeRef
|
||||
|
||||
set *fieldpath.Set
|
||||
path fieldpath.Path
|
||||
|
||||
// Allocate only as many walkers as needed for the depth by storing them here.
|
||||
spareWalkers *[]*toFieldSetWalker
|
||||
allocator value.Allocator
|
||||
}
|
||||
|
||||
func (v *toFieldSetWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef) *toFieldSetWalker {
|
||||
if v.spareWalkers == nil {
|
||||
// first descent.
|
||||
v.spareWalkers = &[]*toFieldSetWalker{}
|
||||
}
|
||||
var v2 *toFieldSetWalker
|
||||
if n := len(*v.spareWalkers); n > 0 {
|
||||
v2, *v.spareWalkers = (*v.spareWalkers)[n-1], (*v.spareWalkers)[:n-1]
|
||||
} else {
|
||||
v2 = &toFieldSetWalker{}
|
||||
}
|
||||
*v2 = *v
|
||||
v2.typeRef = tr
|
||||
v2.path = append(v2.path, pe)
|
||||
return v2
|
||||
}
|
||||
|
||||
func (v *toFieldSetWalker) finishDescent(v2 *toFieldSetWalker) {
|
||||
// if the descent caused a realloc, ensure that we reuse the buffer
|
||||
// for the next sibling.
|
||||
v.path = v2.path[:len(v2.path)-1]
|
||||
*v.spareWalkers = append(*v.spareWalkers, v2)
|
||||
}
|
||||
|
||||
func (v *toFieldSetWalker) toFieldSet() ValidationErrors {
|
||||
return resolveSchema(v.schema, v.typeRef, v.value, v)
|
||||
}
|
||||
|
||||
func (v *toFieldSetWalker) doScalar(t *schema.Scalar) ValidationErrors {
|
||||
v.set.Insert(v.path)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *toFieldSetWalker) visitListItems(t *schema.List, list value.List) (errs ValidationErrors) {
|
||||
// Keeps track of the PEs we've seen
|
||||
seen := fieldpath.MakePathElementSet(list.Length())
|
||||
// Keeps tracks of the PEs we've counted as duplicates
|
||||
duplicates := fieldpath.MakePathElementSet(list.Length())
|
||||
for i := 0; i < list.Length(); i++ {
|
||||
child := list.At(i)
|
||||
pe, _ := listItemToPathElement(v.allocator, v.schema, t, child)
|
||||
if seen.Has(pe) {
|
||||
if duplicates.Has(pe) {
|
||||
// do nothing
|
||||
} else {
|
||||
v.set.Insert(append(v.path, pe))
|
||||
duplicates.Insert(pe)
|
||||
}
|
||||
} else {
|
||||
seen.Insert(pe)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < list.Length(); i++ {
|
||||
child := list.At(i)
|
||||
pe, _ := listItemToPathElement(v.allocator, v.schema, t, child)
|
||||
if duplicates.Has(pe) {
|
||||
continue
|
||||
}
|
||||
v2 := v.prepareDescent(pe, t.ElementType)
|
||||
v2.value = child
|
||||
errs = append(errs, v2.toFieldSet()...)
|
||||
|
||||
v2.set.Insert(v2.path)
|
||||
v.finishDescent(v2)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *toFieldSetWalker) doList(t *schema.List) (errs ValidationErrors) {
|
||||
list, _ := listValue(v.allocator, v.value)
|
||||
if list != nil {
|
||||
defer v.allocator.Free(list)
|
||||
}
|
||||
if t.ElementRelationship == schema.Atomic {
|
||||
v.set.Insert(v.path)
|
||||
return nil
|
||||
}
|
||||
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = v.visitListItems(t, list)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *toFieldSetWalker) visitMapItems(t *schema.Map, m value.Map) (errs ValidationErrors) {
|
||||
m.Iterate(func(key string, val value.Value) bool {
|
||||
pe := fieldpath.PathElement{FieldName: &key}
|
||||
|
||||
tr := t.ElementType
|
||||
if sf, ok := t.FindField(key); ok {
|
||||
tr = sf.Type
|
||||
}
|
||||
v2 := v.prepareDescent(pe, tr)
|
||||
v2.value = val
|
||||
errs = append(errs, v2.toFieldSet()...)
|
||||
if val.IsNull() || (val.IsMap() && val.AsMap().Length() == 0) {
|
||||
v2.set.Insert(v2.path)
|
||||
} else if _, ok := t.FindField(key); !ok {
|
||||
v2.set.Insert(v2.path)
|
||||
}
|
||||
v.finishDescent(v2)
|
||||
return true
|
||||
})
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *toFieldSetWalker) doMap(t *schema.Map) (errs ValidationErrors) {
|
||||
m, _ := mapValue(v.allocator, v.value)
|
||||
if m != nil {
|
||||
defer v.allocator.Free(m)
|
||||
}
|
||||
if t.ElementRelationship == schema.Atomic {
|
||||
v.set.Insert(v.path)
|
||||
return nil
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = v.visitMapItems(t, m)
|
||||
|
||||
return errs
|
||||
}
|
294
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/typed.go
generated
vendored
Normal file
294
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/typed.go
generated
vendored
Normal file
@ -0,0 +1,294 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
// ValidationOptions is the list of all the options available when running the validation.
|
||||
type ValidationOptions int
|
||||
|
||||
const (
|
||||
// AllowDuplicates means that sets and associative lists can have duplicate similar items.
|
||||
AllowDuplicates ValidationOptions = iota
|
||||
)
|
||||
|
||||
// extractItemsOptions is the options available when extracting items.
|
||||
type extractItemsOptions struct {
|
||||
appendKeyFields bool
|
||||
}
|
||||
|
||||
type ExtractItemsOption func(*extractItemsOptions)
|
||||
|
||||
// WithAppendKeyFields configures ExtractItems to include key fields.
|
||||
// It is exported for use in configuring ExtractItems.
|
||||
func WithAppendKeyFields() ExtractItemsOption {
|
||||
return func(opts *extractItemsOptions) {
|
||||
opts.appendKeyFields = true
|
||||
}
|
||||
}
|
||||
|
||||
// AsTyped accepts a value and a type and returns a TypedValue. 'v' must have
|
||||
// type 'typeName' in the schema. An error is returned if the v doesn't conform
|
||||
// to the schema.
|
||||
func AsTyped(v value.Value, s *schema.Schema, typeRef schema.TypeRef, opts ...ValidationOptions) (*TypedValue, error) {
|
||||
tv := &TypedValue{
|
||||
value: v,
|
||||
typeRef: typeRef,
|
||||
schema: s,
|
||||
}
|
||||
if err := tv.Validate(opts...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tv, nil
|
||||
}
|
||||
|
||||
// AsTypeUnvalidated is just like AsTyped, but doesn't validate that the type
|
||||
// conforms to the schema, for cases where that has already been checked or
|
||||
// where you're going to call a method that validates as a side-effect (like
|
||||
// ToFieldSet).
|
||||
//
|
||||
// Deprecated: This function was initially created because validation
|
||||
// was expensive. Now that this has been solved, objects should always
|
||||
// be created as validated, using `AsTyped`.
|
||||
func AsTypedUnvalidated(v value.Value, s *schema.Schema, typeRef schema.TypeRef) *TypedValue {
|
||||
tv := &TypedValue{
|
||||
value: v,
|
||||
typeRef: typeRef,
|
||||
schema: s,
|
||||
}
|
||||
return tv
|
||||
}
|
||||
|
||||
// TypedValue is a value of some specific type.
|
||||
type TypedValue struct {
|
||||
value value.Value
|
||||
typeRef schema.TypeRef
|
||||
schema *schema.Schema
|
||||
}
|
||||
|
||||
// TypeRef is the type of the value.
|
||||
func (tv TypedValue) TypeRef() schema.TypeRef {
|
||||
return tv.typeRef
|
||||
}
|
||||
|
||||
// AsValue removes the type from the TypedValue and only keeps the value.
|
||||
func (tv TypedValue) AsValue() value.Value {
|
||||
return tv.value
|
||||
}
|
||||
|
||||
// Schema gets the schema from the TypedValue.
|
||||
func (tv TypedValue) Schema() *schema.Schema {
|
||||
return tv.schema
|
||||
}
|
||||
|
||||
// Validate returns an error with a list of every spec violation.
|
||||
func (tv TypedValue) Validate(opts ...ValidationOptions) error {
|
||||
w := tv.walker()
|
||||
for _, opt := range opts {
|
||||
switch opt {
|
||||
case AllowDuplicates:
|
||||
w.allowDuplicates = true
|
||||
}
|
||||
}
|
||||
defer w.finished()
|
||||
if errs := w.validate(nil); len(errs) != 0 {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToFieldSet creates a set containing every leaf field and item mentioned, or
|
||||
// validation errors, if any were encountered.
|
||||
func (tv TypedValue) ToFieldSet() (*fieldpath.Set, error) {
|
||||
w := tv.toFieldSetWalker()
|
||||
defer w.finished()
|
||||
if errs := w.toFieldSet(); len(errs) != 0 {
|
||||
return nil, errs
|
||||
}
|
||||
return w.set, nil
|
||||
}
|
||||
|
||||
// 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:
|
||||
// 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.
|
||||
func (tv TypedValue) Merge(pso *TypedValue) (*TypedValue, error) {
|
||||
return merge(&tv, pso, ruleKeepRHS, nil)
|
||||
}
|
||||
|
||||
var cmpwPool = sync.Pool{
|
||||
New: func() interface{} { return &compareWalker{} },
|
||||
}
|
||||
|
||||
// Compare compares the two objects. See the comments on the `Comparison`
|
||||
// struct for details on the return value.
|
||||
//
|
||||
// tv and rhs 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.
|
||||
func (tv TypedValue) Compare(rhs *TypedValue) (c *Comparison, err error) {
|
||||
lhs := tv
|
||||
if lhs.schema != rhs.schema {
|
||||
return nil, errorf("expected objects with types from the same schema")
|
||||
}
|
||||
if !lhs.typeRef.Equals(&rhs.typeRef) {
|
||||
return nil, errorf("expected objects of the same type, but got %v and %v", lhs.typeRef, rhs.typeRef)
|
||||
}
|
||||
|
||||
cmpw := cmpwPool.Get().(*compareWalker)
|
||||
defer func() {
|
||||
cmpw.lhs = nil
|
||||
cmpw.rhs = nil
|
||||
cmpw.schema = nil
|
||||
cmpw.typeRef = schema.TypeRef{}
|
||||
cmpw.comparison = nil
|
||||
cmpw.inLeaf = false
|
||||
|
||||
cmpwPool.Put(cmpw)
|
||||
}()
|
||||
|
||||
cmpw.lhs = lhs.value
|
||||
cmpw.rhs = rhs.value
|
||||
cmpw.schema = lhs.schema
|
||||
cmpw.typeRef = lhs.typeRef
|
||||
cmpw.comparison = &Comparison{
|
||||
Removed: fieldpath.NewSet(),
|
||||
Modified: fieldpath.NewSet(),
|
||||
Added: fieldpath.NewSet(),
|
||||
}
|
||||
if cmpw.allocator == nil {
|
||||
cmpw.allocator = value.NewFreelistAllocator()
|
||||
}
|
||||
|
||||
errs := cmpw.compare(nil)
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
return cmpw.comparison, nil
|
||||
}
|
||||
|
||||
// RemoveItems removes each provided list or map item from the value.
|
||||
func (tv TypedValue) RemoveItems(items *fieldpath.Set) *TypedValue {
|
||||
tv.value = removeItemsWithSchema(tv.value, items, tv.schema, tv.typeRef, false)
|
||||
return &tv
|
||||
}
|
||||
|
||||
// ExtractItems returns a value with only the provided list or map items extracted from the value.
|
||||
func (tv TypedValue) ExtractItems(items *fieldpath.Set, opts ...ExtractItemsOption) *TypedValue {
|
||||
options := &extractItemsOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
if options.appendKeyFields {
|
||||
tvPathSet, err := tv.ToFieldSet()
|
||||
if err == nil {
|
||||
keyFieldPathSet := fieldpath.NewSet()
|
||||
items.Iterate(func(path fieldpath.Path) {
|
||||
if !tvPathSet.Has(path) {
|
||||
return
|
||||
}
|
||||
for i, pe := range path {
|
||||
if pe.Key == nil {
|
||||
continue
|
||||
}
|
||||
for _, keyField := range *pe.Key {
|
||||
keyName := keyField.Name
|
||||
// Create a new slice with the same elements as path[:i+1], but set its capacity to len(path[:i+1]).
|
||||
// This ensures that appending to keyFieldPath creates a new underlying array, avoiding accidental
|
||||
// modification of the original slice (path).
|
||||
keyFieldPath := append(path[:i+1:i+1], fieldpath.PathElement{FieldName: &keyName})
|
||||
keyFieldPathSet.Insert(keyFieldPath)
|
||||
}
|
||||
}
|
||||
})
|
||||
items = items.Union(keyFieldPathSet)
|
||||
}
|
||||
}
|
||||
|
||||
tv.value = removeItemsWithSchema(tv.value, items, tv.schema, tv.typeRef, true)
|
||||
return &tv
|
||||
}
|
||||
|
||||
func (tv TypedValue) Empty() *TypedValue {
|
||||
tv.value = value.NewValueInterface(nil)
|
||||
return &tv
|
||||
}
|
||||
|
||||
var mwPool = sync.Pool{
|
||||
New: func() interface{} { return &mergingWalker{} },
|
||||
}
|
||||
|
||||
func merge(lhs, rhs *TypedValue, rule, postRule mergeRule) (*TypedValue, error) {
|
||||
if lhs.schema != rhs.schema {
|
||||
return nil, errorf("expected objects with types from the same schema")
|
||||
}
|
||||
if !lhs.typeRef.Equals(&rhs.typeRef) {
|
||||
return nil, errorf("expected objects of the same type, but got %v and %v", lhs.typeRef, rhs.typeRef)
|
||||
}
|
||||
|
||||
mw := mwPool.Get().(*mergingWalker)
|
||||
defer func() {
|
||||
mw.lhs = nil
|
||||
mw.rhs = nil
|
||||
mw.schema = nil
|
||||
mw.typeRef = schema.TypeRef{}
|
||||
mw.rule = nil
|
||||
mw.postItemHook = nil
|
||||
mw.out = nil
|
||||
mw.inLeaf = false
|
||||
|
||||
mwPool.Put(mw)
|
||||
}()
|
||||
|
||||
mw.lhs = lhs.value
|
||||
mw.rhs = rhs.value
|
||||
mw.schema = lhs.schema
|
||||
mw.typeRef = lhs.typeRef
|
||||
mw.rule = rule
|
||||
mw.postItemHook = postRule
|
||||
if mw.allocator == nil {
|
||||
mw.allocator = value.NewFreelistAllocator()
|
||||
}
|
||||
|
||||
errs := mw.merge(nil)
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
out := &TypedValue{
|
||||
schema: lhs.schema,
|
||||
typeRef: lhs.typeRef,
|
||||
}
|
||||
if mw.out != nil {
|
||||
out.value = value.NewValueInterface(*mw.out)
|
||||
}
|
||||
return out, nil
|
||||
}
|
205
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/validate.go
generated
vendored
Normal file
205
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/validate.go
generated
vendored
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
)
|
||||
|
||||
var vPool = sync.Pool{
|
||||
New: func() interface{} { return &validatingObjectWalker{} },
|
||||
}
|
||||
|
||||
func (tv TypedValue) walker() *validatingObjectWalker {
|
||||
v := vPool.Get().(*validatingObjectWalker)
|
||||
v.value = tv.value
|
||||
v.schema = tv.schema
|
||||
v.typeRef = tv.typeRef
|
||||
v.allowDuplicates = false
|
||||
if v.allocator == nil {
|
||||
v.allocator = value.NewFreelistAllocator()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) finished() {
|
||||
v.schema = nil
|
||||
v.typeRef = schema.TypeRef{}
|
||||
vPool.Put(v)
|
||||
}
|
||||
|
||||
type validatingObjectWalker struct {
|
||||
value value.Value
|
||||
schema *schema.Schema
|
||||
typeRef schema.TypeRef
|
||||
// If set to true, duplicates will be allowed in
|
||||
// associativeLists/sets.
|
||||
allowDuplicates bool
|
||||
|
||||
// Allocate only as many walkers as needed for the depth by storing them here.
|
||||
spareWalkers *[]*validatingObjectWalker
|
||||
allocator value.Allocator
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) prepareDescent(tr schema.TypeRef) *validatingObjectWalker {
|
||||
if v.spareWalkers == nil {
|
||||
// first descent.
|
||||
v.spareWalkers = &[]*validatingObjectWalker{}
|
||||
}
|
||||
var v2 *validatingObjectWalker
|
||||
if n := len(*v.spareWalkers); n > 0 {
|
||||
v2, *v.spareWalkers = (*v.spareWalkers)[n-1], (*v.spareWalkers)[:n-1]
|
||||
} else {
|
||||
v2 = &validatingObjectWalker{}
|
||||
}
|
||||
*v2 = *v
|
||||
v2.typeRef = tr
|
||||
return v2
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) finishDescent(v2 *validatingObjectWalker) {
|
||||
// if the descent caused a realloc, ensure that we reuse the buffer
|
||||
// for the next sibling.
|
||||
*v.spareWalkers = append(*v.spareWalkers, v2)
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) validate(prefixFn func() string) ValidationErrors {
|
||||
return resolveSchema(v.schema, v.typeRef, v.value, v).WithLazyPrefix(prefixFn)
|
||||
}
|
||||
|
||||
func validateScalar(t *schema.Scalar, v value.Value, prefix string) (errs ValidationErrors) {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
if v.IsNull() {
|
||||
return nil
|
||||
}
|
||||
switch *t {
|
||||
case schema.Numeric:
|
||||
if !v.IsFloat() && !v.IsInt() {
|
||||
// TODO: should the schema separate int and float?
|
||||
return errorf("%vexpected numeric (int or float), got %T", prefix, v.Unstructured())
|
||||
}
|
||||
case schema.String:
|
||||
if !v.IsString() {
|
||||
return errorf("%vexpected string, got %#v", prefix, v)
|
||||
}
|
||||
case schema.Boolean:
|
||||
if !v.IsBool() {
|
||||
return errorf("%vexpected boolean, got %v", prefix, v)
|
||||
}
|
||||
case schema.Untyped:
|
||||
if !v.IsFloat() && !v.IsInt() && !v.IsString() && !v.IsBool() {
|
||||
return errorf("%vexpected any scalar, got %v", prefix, v)
|
||||
}
|
||||
default:
|
||||
return errorf("%vunexpected scalar type in schema: %v", prefix, *t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) doScalar(t *schema.Scalar) ValidationErrors {
|
||||
if errs := validateScalar(t, v.value, ""); len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) visitListItems(t *schema.List, list value.List) (errs ValidationErrors) {
|
||||
observedKeys := fieldpath.MakePathElementSet(list.Length())
|
||||
for i := 0; i < list.Length(); i++ {
|
||||
child := list.AtUsing(v.allocator, i)
|
||||
defer v.allocator.Free(child)
|
||||
var pe fieldpath.PathElement
|
||||
if t.ElementRelationship != schema.Associative {
|
||||
pe.Index = &i
|
||||
} else {
|
||||
var err error
|
||||
pe, err = listItemToPathElement(v.allocator, v.schema, t, 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.
|
||||
return
|
||||
}
|
||||
if observedKeys.Has(pe) && !v.allowDuplicates {
|
||||
errs = append(errs, errorf("duplicate entries for key %v", pe.String())...)
|
||||
}
|
||||
observedKeys.Insert(pe)
|
||||
}
|
||||
v2 := v.prepareDescent(t.ElementType)
|
||||
v2.value = child
|
||||
errs = append(errs, v2.validate(pe.String)...)
|
||||
v.finishDescent(v2)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) doList(t *schema.List) (errs ValidationErrors) {
|
||||
list, err := listValue(v.allocator, v.value)
|
||||
if err != nil {
|
||||
return errorf(err.Error())
|
||||
}
|
||||
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer v.allocator.Free(list)
|
||||
errs = v.visitListItems(t, list)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) visitMapItems(t *schema.Map, m value.Map) (errs ValidationErrors) {
|
||||
m.IterateUsing(v.allocator, func(key string, val value.Value) bool {
|
||||
pe := fieldpath.PathElement{FieldName: &key}
|
||||
tr := t.ElementType
|
||||
if sf, ok := t.FindField(key); ok {
|
||||
tr = sf.Type
|
||||
} else if (t.ElementType == schema.TypeRef{}) {
|
||||
errs = append(errs, errorf("field not declared in schema").WithPrefix(pe.String())...)
|
||||
return false
|
||||
}
|
||||
v2 := v.prepareDescent(tr)
|
||||
v2.value = val
|
||||
// Giving pe.String as a parameter actually increases the allocations.
|
||||
errs = append(errs, v2.validate(func() string { return pe.String() })...)
|
||||
v.finishDescent(v2)
|
||||
return true
|
||||
})
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) doMap(t *schema.Map) (errs ValidationErrors) {
|
||||
m, err := mapValue(v.allocator, v.value)
|
||||
if err != nil {
|
||||
return errorf(err.Error())
|
||||
}
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
defer v.allocator.Free(m)
|
||||
errs = v.visitMapItems(t, m)
|
||||
|
||||
return errs
|
||||
}
|
203
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/allocator.go
generated
vendored
Normal file
203
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/allocator.go
generated
vendored
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
// Allocator provides a value object allocation strategy.
|
||||
// Value objects can be allocated by passing an allocator to the "Using"
|
||||
// receiver functions on the value interfaces, e.g. Map.ZipUsing(allocator, ...).
|
||||
// Value objects returned from "Using" functions should be given back to the allocator
|
||||
// once longer needed by calling Allocator.Free(Value).
|
||||
type Allocator interface {
|
||||
// Free gives the allocator back any value objects returned by the "Using"
|
||||
// receiver functions on the value interfaces.
|
||||
// interface{} may be any of: Value, Map, List or Range.
|
||||
Free(interface{})
|
||||
|
||||
// The unexported functions are for "Using" receiver functions of the value types
|
||||
// to request what they need from the allocator.
|
||||
allocValueUnstructured() *valueUnstructured
|
||||
allocListUnstructuredRange() *listUnstructuredRange
|
||||
allocValueReflect() *valueReflect
|
||||
allocMapReflect() *mapReflect
|
||||
allocStructReflect() *structReflect
|
||||
allocListReflect() *listReflect
|
||||
allocListReflectRange() *listReflectRange
|
||||
}
|
||||
|
||||
// HeapAllocator simply allocates objects to the heap. It is the default
|
||||
// allocator used receiver functions on the value interfaces that do not accept
|
||||
// an allocator and should be used whenever allocating objects that will not
|
||||
// be given back to an allocator by calling Allocator.Free(Value).
|
||||
var HeapAllocator = &heapAllocator{}
|
||||
|
||||
type heapAllocator struct{}
|
||||
|
||||
func (p *heapAllocator) allocValueUnstructured() *valueUnstructured {
|
||||
return &valueUnstructured{}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocListUnstructuredRange() *listUnstructuredRange {
|
||||
return &listUnstructuredRange{vv: &valueUnstructured{}}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocValueReflect() *valueReflect {
|
||||
return &valueReflect{}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocStructReflect() *structReflect {
|
||||
return &structReflect{}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocMapReflect() *mapReflect {
|
||||
return &mapReflect{}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocListReflect() *listReflect {
|
||||
return &listReflect{}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocListReflectRange() *listReflectRange {
|
||||
return &listReflectRange{vr: &valueReflect{}}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) Free(_ interface{}) {}
|
||||
|
||||
// NewFreelistAllocator creates freelist based allocator.
|
||||
// This allocator provides fast allocation and freeing of short lived value objects.
|
||||
//
|
||||
// The freelists are bounded in size by freelistMaxSize. If more than this amount of value objects is
|
||||
// allocated at once, the excess will be returned to the heap for garbage collection when freed.
|
||||
//
|
||||
// This allocator is unsafe and must not be accessed concurrently by goroutines.
|
||||
//
|
||||
// This allocator works well for traversal of value data trees. Typical usage is to acquire
|
||||
// a freelist at the beginning of the traversal and use it through out
|
||||
// for all temporary value access.
|
||||
func NewFreelistAllocator() Allocator {
|
||||
return &freelistAllocator{
|
||||
valueUnstructured: &freelist{new: func() interface{} {
|
||||
return &valueUnstructured{}
|
||||
}},
|
||||
listUnstructuredRange: &freelist{new: func() interface{} {
|
||||
return &listUnstructuredRange{vv: &valueUnstructured{}}
|
||||
}},
|
||||
valueReflect: &freelist{new: func() interface{} {
|
||||
return &valueReflect{}
|
||||
}},
|
||||
mapReflect: &freelist{new: func() interface{} {
|
||||
return &mapReflect{}
|
||||
}},
|
||||
structReflect: &freelist{new: func() interface{} {
|
||||
return &structReflect{}
|
||||
}},
|
||||
listReflect: &freelist{new: func() interface{} {
|
||||
return &listReflect{}
|
||||
}},
|
||||
listReflectRange: &freelist{new: func() interface{} {
|
||||
return &listReflectRange{vr: &valueReflect{}}
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
// Bound memory usage of freelists. This prevents the processing of very large lists from leaking memory.
|
||||
// This limit is large enough for endpoints objects containing 1000 IP address entries. Freed objects
|
||||
// that don't fit into the freelist are orphaned on the heap to be garbage collected.
|
||||
const freelistMaxSize = 1000
|
||||
|
||||
type freelistAllocator struct {
|
||||
valueUnstructured *freelist
|
||||
listUnstructuredRange *freelist
|
||||
valueReflect *freelist
|
||||
mapReflect *freelist
|
||||
structReflect *freelist
|
||||
listReflect *freelist
|
||||
listReflectRange *freelist
|
||||
}
|
||||
|
||||
type freelist struct {
|
||||
list []interface{}
|
||||
new func() interface{}
|
||||
}
|
||||
|
||||
func (f *freelist) allocate() interface{} {
|
||||
var w2 interface{}
|
||||
if n := len(f.list); n > 0 {
|
||||
w2, f.list = f.list[n-1], f.list[:n-1]
|
||||
} else {
|
||||
w2 = f.new()
|
||||
}
|
||||
return w2
|
||||
}
|
||||
|
||||
func (f *freelist) free(v interface{}) {
|
||||
if len(f.list) < freelistMaxSize {
|
||||
f.list = append(f.list, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) Free(value interface{}) {
|
||||
switch v := value.(type) {
|
||||
case *valueUnstructured:
|
||||
v.Value = nil // don't hold references to unstructured objects
|
||||
w.valueUnstructured.free(v)
|
||||
case *listUnstructuredRange:
|
||||
v.vv.Value = nil // don't hold references to unstructured objects
|
||||
w.listUnstructuredRange.free(v)
|
||||
case *valueReflect:
|
||||
v.ParentMapKey = nil
|
||||
v.ParentMap = nil
|
||||
w.valueReflect.free(v)
|
||||
case *mapReflect:
|
||||
w.mapReflect.free(v)
|
||||
case *structReflect:
|
||||
w.structReflect.free(v)
|
||||
case *listReflect:
|
||||
w.listReflect.free(v)
|
||||
case *listReflectRange:
|
||||
v.vr.ParentMapKey = nil
|
||||
v.vr.ParentMap = nil
|
||||
w.listReflectRange.free(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocValueUnstructured() *valueUnstructured {
|
||||
return w.valueUnstructured.allocate().(*valueUnstructured)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocListUnstructuredRange() *listUnstructuredRange {
|
||||
return w.listUnstructuredRange.allocate().(*listUnstructuredRange)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocValueReflect() *valueReflect {
|
||||
return w.valueReflect.allocate().(*valueReflect)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocStructReflect() *structReflect {
|
||||
return w.structReflect.allocate().(*structReflect)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocMapReflect() *mapReflect {
|
||||
return w.mapReflect.allocate().(*mapReflect)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocListReflect() *listReflect {
|
||||
return w.listReflect.allocate().(*listReflect)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocListReflectRange() *listReflectRange {
|
||||
return w.listReflectRange.allocate().(*listReflectRange)
|
||||
}
|
21
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/doc.go
generated
vendored
Normal file
21
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/doc.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package value defines types for an in-memory representation of yaml or json
|
||||
// objects, organized for convenient comparison with a schema (as defined by
|
||||
// the sibling schema package). Functions for reading and writing the objects
|
||||
// are also provided.
|
||||
package value
|
97
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/fields.go
generated
vendored
Normal file
97
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/fields.go
generated
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Field is an individual key-value pair.
|
||||
type Field struct {
|
||||
Name string
|
||||
Value Value
|
||||
}
|
||||
|
||||
// FieldList is a list of key-value pairs. Each field is expected to
|
||||
// have a different name.
|
||||
type FieldList []Field
|
||||
|
||||
// Sort sorts the field list by Name.
|
||||
func (f FieldList) Sort() {
|
||||
if len(f) < 2 {
|
||||
return
|
||||
}
|
||||
if len(f) == 2 {
|
||||
if f[1].Name < f[0].Name {
|
||||
f[0], f[1] = f[1], f[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
sort.SliceStable(f, func(i, j int) bool {
|
||||
return f[i].Name < f[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
// Less compares two lists lexically.
|
||||
func (f FieldList) Less(rhs FieldList) bool {
|
||||
return f.Compare(rhs) == -1
|
||||
}
|
||||
|
||||
// Compare compares two lists lexically. The result will be 0 if f==rhs, -1
|
||||
// if f < rhs, and +1 if f > rhs.
|
||||
func (f FieldList) Compare(rhs FieldList) int {
|
||||
i := 0
|
||||
for {
|
||||
if i >= len(f) && i >= len(rhs) {
|
||||
// Maps are the same length and all items are equal.
|
||||
return 0
|
||||
}
|
||||
if i >= len(f) {
|
||||
// F is shorter.
|
||||
return -1
|
||||
}
|
||||
if i >= len(rhs) {
|
||||
// RHS is shorter.
|
||||
return 1
|
||||
}
|
||||
if c := strings.Compare(f[i].Name, rhs[i].Name); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := Compare(f[i].Value, rhs[i].Value); c != 0 {
|
||||
return c
|
||||
}
|
||||
// The items are equal; continue.
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// Equals returns true if the two fieldslist are equals, false otherwise.
|
||||
func (f FieldList) Equals(rhs FieldList) bool {
|
||||
if len(f) != len(rhs) {
|
||||
return false
|
||||
}
|
||||
for i := range f {
|
||||
if f[i].Name != rhs[i].Name {
|
||||
return false
|
||||
}
|
||||
if !Equals(f[i].Value, rhs[i].Value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
91
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/jsontagutil.go
generated
vendored
Normal file
91
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/jsontagutil.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TODO: This implements the same functionality as https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L236
|
||||
// but is based on the highly efficient approach from https://golang.org/src/encoding/json/encode.go
|
||||
|
||||
func lookupJsonTags(f reflect.StructField) (name string, omit bool, inline bool, omitempty bool) {
|
||||
tag := f.Tag.Get("json")
|
||||
if tag == "-" {
|
||||
return "", true, false, false
|
||||
}
|
||||
name, opts := parseTag(tag)
|
||||
if name == "" {
|
||||
name = f.Name
|
||||
}
|
||||
return name, false, opts.Contains("inline"), opts.Contains("omitempty")
|
||||
}
|
||||
|
||||
func isZero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
case reflect.Chan, reflect.Func:
|
||||
panic(fmt.Sprintf("unsupported type: %v", v.Type()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type tagOptions string
|
||||
|
||||
// parseTag splits a struct field's json tag into its name and
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
if idx := strings.Index(tag, ","); idx != -1 {
|
||||
return tag[:idx], tagOptions(tag[idx+1:])
|
||||
}
|
||||
return tag, tagOptions("")
|
||||
}
|
||||
|
||||
// Contains reports whether a comma-separated list of options
|
||||
// contains a particular substr flag. substr must be surrounded by a
|
||||
// string boundary or commas.
|
||||
func (o tagOptions) Contains(optionName string) bool {
|
||||
if len(o) == 0 {
|
||||
return false
|
||||
}
|
||||
s := string(o)
|
||||
for s != "" {
|
||||
var next string
|
||||
i := strings.Index(s, ",")
|
||||
if i >= 0 {
|
||||
s, next = s[:i], s[i+1:]
|
||||
}
|
||||
if s == optionName {
|
||||
return true
|
||||
}
|
||||
s = next
|
||||
}
|
||||
return false
|
||||
}
|
139
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/list.go
generated
vendored
Normal file
139
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/list.go
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
// List represents a list object.
|
||||
type List interface {
|
||||
// Length returns how many items can be found in the map.
|
||||
Length() int
|
||||
// At returns the item at the given position in the map. It will
|
||||
// panic if the index is out of range.
|
||||
At(int) Value
|
||||
// AtUsing uses the provided allocator and returns the item at the given
|
||||
// position in the map. It will panic if the index is out of range.
|
||||
// The returned Value should be given back to the Allocator when no longer needed
|
||||
// by calling Allocator.Free(Value).
|
||||
AtUsing(Allocator, int) Value
|
||||
// Range returns a ListRange for iterating over the items in the list.
|
||||
Range() ListRange
|
||||
// RangeUsing uses the provided allocator and returns a ListRange for
|
||||
// iterating over the items in the list.
|
||||
// The returned Range should be given back to the Allocator when no longer needed
|
||||
// by calling Allocator.Free(Value).
|
||||
RangeUsing(Allocator) ListRange
|
||||
// Equals compares the two lists, and return true if they are the same, false otherwise.
|
||||
// Implementations can use ListEquals as a general implementation for this methods.
|
||||
Equals(List) bool
|
||||
// EqualsUsing uses the provided allocator and compares the two lists, and return true if
|
||||
// they are the same, false otherwise. Implementations can use ListEqualsUsing as a general
|
||||
// implementation for this methods.
|
||||
EqualsUsing(Allocator, List) bool
|
||||
}
|
||||
|
||||
// ListRange represents a single iteration across the items of a list.
|
||||
type ListRange interface {
|
||||
// Next increments to the next item in the range, if there is one, and returns true, or returns false if there are no more items.
|
||||
Next() bool
|
||||
// Item returns the index and value of the current item in the range. or panics if there is no current item.
|
||||
// For efficiency, Item may reuse the values returned by previous Item calls. Callers should be careful avoid holding
|
||||
// pointers to the value returned by Item() that escape the iteration loop since they become invalid once either
|
||||
// Item() or Allocator.Free() is called.
|
||||
Item() (index int, value Value)
|
||||
}
|
||||
|
||||
var EmptyRange = &emptyRange{}
|
||||
|
||||
type emptyRange struct{}
|
||||
|
||||
func (_ *emptyRange) Next() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ *emptyRange) Item() (index int, value Value) {
|
||||
panic("Item called on empty ListRange")
|
||||
}
|
||||
|
||||
// ListEquals compares two lists lexically.
|
||||
// WARN: This is a naive implementation, calling lhs.Equals(rhs) is typically the most efficient.
|
||||
func ListEquals(lhs, rhs List) bool {
|
||||
return ListEqualsUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// ListEqualsUsing uses the provided allocator and compares two lists lexically.
|
||||
// WARN: This is a naive implementation, calling lhs.EqualsUsing(allocator, rhs) is typically the most efficient.
|
||||
func ListEqualsUsing(a Allocator, lhs, rhs List) bool {
|
||||
if lhs.Length() != rhs.Length() {
|
||||
return false
|
||||
}
|
||||
|
||||
lhsRange := lhs.RangeUsing(a)
|
||||
defer a.Free(lhsRange)
|
||||
rhsRange := rhs.RangeUsing(a)
|
||||
defer a.Free(rhsRange)
|
||||
|
||||
for lhsRange.Next() && rhsRange.Next() {
|
||||
_, lv := lhsRange.Item()
|
||||
_, rv := rhsRange.Item()
|
||||
if !EqualsUsing(a, lv, rv) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ListLess compares two lists lexically.
|
||||
func ListLess(lhs, rhs List) bool {
|
||||
return ListCompare(lhs, rhs) == -1
|
||||
}
|
||||
|
||||
// ListCompare compares two lists lexically. The result will be 0 if l==rhs, -1
|
||||
// if l < rhs, and +1 if l > rhs.
|
||||
func ListCompare(lhs, rhs List) int {
|
||||
return ListCompareUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// ListCompareUsing uses the provided allocator and compares two lists lexically. The result will be 0 if l==rhs, -1
|
||||
// if l < rhs, and +1 if l > rhs.
|
||||
func ListCompareUsing(a Allocator, lhs, rhs List) int {
|
||||
lhsRange := lhs.RangeUsing(a)
|
||||
defer a.Free(lhsRange)
|
||||
rhsRange := rhs.RangeUsing(a)
|
||||
defer a.Free(rhsRange)
|
||||
|
||||
for {
|
||||
lhsOk := lhsRange.Next()
|
||||
rhsOk := rhsRange.Next()
|
||||
if !lhsOk && !rhsOk {
|
||||
// Lists are the same length and all items are equal.
|
||||
return 0
|
||||
}
|
||||
if !lhsOk {
|
||||
// LHS is shorter.
|
||||
return -1
|
||||
}
|
||||
if !rhsOk {
|
||||
// RHS is shorter.
|
||||
return 1
|
||||
}
|
||||
_, lv := lhsRange.Item()
|
||||
_, rv := rhsRange.Item()
|
||||
if c := CompareUsing(a, lv, rv); c != 0 {
|
||||
return c
|
||||
}
|
||||
// The items are equal; continue.
|
||||
}
|
||||
}
|
98
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/listreflect.go
generated
vendored
Normal file
98
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/listreflect.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type listReflect struct {
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
func (r listReflect) Length() int {
|
||||
val := r.Value
|
||||
return val.Len()
|
||||
}
|
||||
|
||||
func (r listReflect) At(i int) Value {
|
||||
val := r.Value
|
||||
return mustWrapValueReflect(val.Index(i), nil, nil)
|
||||
}
|
||||
|
||||
func (r listReflect) AtUsing(a Allocator, i int) Value {
|
||||
val := r.Value
|
||||
return a.allocValueReflect().mustReuse(val.Index(i), nil, nil, nil)
|
||||
}
|
||||
|
||||
func (r listReflect) Unstructured() interface{} {
|
||||
l := r.Length()
|
||||
result := make([]interface{}, l)
|
||||
for i := 0; i < l; i++ {
|
||||
result[i] = r.At(i).Unstructured()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (r listReflect) Range() ListRange {
|
||||
return r.RangeUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (r listReflect) RangeUsing(a Allocator) ListRange {
|
||||
length := r.Value.Len()
|
||||
if length == 0 {
|
||||
return EmptyRange
|
||||
}
|
||||
rr := a.allocListReflectRange()
|
||||
rr.list = r.Value
|
||||
rr.i = -1
|
||||
rr.entry = TypeReflectEntryOf(r.Value.Type().Elem())
|
||||
return rr
|
||||
}
|
||||
|
||||
func (r listReflect) Equals(other List) bool {
|
||||
return r.EqualsUsing(HeapAllocator, other)
|
||||
}
|
||||
func (r listReflect) EqualsUsing(a Allocator, other List) bool {
|
||||
if otherReflectList, ok := other.(*listReflect); ok {
|
||||
return reflect.DeepEqual(r.Value.Interface(), otherReflectList.Value.Interface())
|
||||
}
|
||||
return ListEqualsUsing(a, &r, other)
|
||||
}
|
||||
|
||||
type listReflectRange struct {
|
||||
list reflect.Value
|
||||
vr *valueReflect
|
||||
i int
|
||||
entry *TypeReflectCacheEntry
|
||||
}
|
||||
|
||||
func (r *listReflectRange) Next() bool {
|
||||
r.i += 1
|
||||
return r.i < r.list.Len()
|
||||
}
|
||||
|
||||
func (r *listReflectRange) Item() (index int, value Value) {
|
||||
if r.i < 0 {
|
||||
panic("Item() called before first calling Next()")
|
||||
}
|
||||
if r.i >= r.list.Len() {
|
||||
panic("Item() called on ListRange with no more items")
|
||||
}
|
||||
v := r.list.Index(r.i)
|
||||
return r.i, r.vr.mustReuse(v, r.entry, nil, nil)
|
||||
}
|
74
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/listunstructured.go
generated
vendored
Normal file
74
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/listunstructured.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
type listUnstructured []interface{}
|
||||
|
||||
func (l listUnstructured) Length() int {
|
||||
return len(l)
|
||||
}
|
||||
|
||||
func (l listUnstructured) At(i int) Value {
|
||||
return NewValueInterface(l[i])
|
||||
}
|
||||
|
||||
func (l listUnstructured) AtUsing(a Allocator, i int) Value {
|
||||
return a.allocValueUnstructured().reuse(l[i])
|
||||
}
|
||||
|
||||
func (l listUnstructured) Equals(other List) bool {
|
||||
return l.EqualsUsing(HeapAllocator, other)
|
||||
}
|
||||
|
||||
func (l listUnstructured) EqualsUsing(a Allocator, other List) bool {
|
||||
return ListEqualsUsing(a, &l, other)
|
||||
}
|
||||
|
||||
func (l listUnstructured) Range() ListRange {
|
||||
return l.RangeUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (l listUnstructured) RangeUsing(a Allocator) ListRange {
|
||||
if len(l) == 0 {
|
||||
return EmptyRange
|
||||
}
|
||||
r := a.allocListUnstructuredRange()
|
||||
r.list = l
|
||||
r.i = -1
|
||||
return r
|
||||
}
|
||||
|
||||
type listUnstructuredRange struct {
|
||||
list listUnstructured
|
||||
vv *valueUnstructured
|
||||
i int
|
||||
}
|
||||
|
||||
func (r *listUnstructuredRange) Next() bool {
|
||||
r.i += 1
|
||||
return r.i < len(r.list)
|
||||
}
|
||||
|
||||
func (r *listUnstructuredRange) Item() (index int, value Value) {
|
||||
if r.i < 0 {
|
||||
panic("Item() called before first calling Next()")
|
||||
}
|
||||
if r.i >= len(r.list) {
|
||||
panic("Item() called on ListRange with no more items")
|
||||
}
|
||||
return r.i, r.vv.reuse(r.list[r.i])
|
||||
}
|
270
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/map.go
generated
vendored
Normal file
270
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/map.go
generated
vendored
Normal file
@ -0,0 +1,270 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Map represents a Map or go structure.
|
||||
type Map interface {
|
||||
// Set changes or set the value of the given key.
|
||||
Set(key string, val Value)
|
||||
// Get returns the value for the given key, if present, or (nil, false) otherwise.
|
||||
Get(key string) (Value, bool)
|
||||
// GetUsing uses the provided allocator and returns the value for the given key,
|
||||
// if present, or (nil, false) otherwise.
|
||||
// The returned Value should be given back to the Allocator when no longer needed
|
||||
// by calling Allocator.Free(Value).
|
||||
GetUsing(a Allocator, key string) (Value, bool)
|
||||
// Has returns true if the key is present, or false otherwise.
|
||||
Has(key string) bool
|
||||
// Delete removes the key from the map.
|
||||
Delete(key string)
|
||||
// Equals compares the two maps, and return true if they are the same, false otherwise.
|
||||
// Implementations can use MapEquals as a general implementation for this methods.
|
||||
Equals(other Map) bool
|
||||
// EqualsUsing uses the provided allocator and compares the two maps, and return true if
|
||||
// they are the same, false otherwise. Implementations can use MapEqualsUsing as a general
|
||||
// implementation for this methods.
|
||||
EqualsUsing(a Allocator, other Map) bool
|
||||
// Iterate runs the given function for each key/value in the
|
||||
// map. Returning false in the closure prematurely stops the
|
||||
// iteration.
|
||||
Iterate(func(key string, value Value) bool) bool
|
||||
// IterateUsing uses the provided allocator and runs the given function for each key/value
|
||||
// in the map. Returning false in the closure prematurely stops the iteration.
|
||||
IterateUsing(Allocator, func(key string, value Value) bool) bool
|
||||
// Length returns the number of items in the map.
|
||||
Length() int
|
||||
// Empty returns true if the map is empty.
|
||||
Empty() bool
|
||||
// Zip iterates over the entries of two maps together. If both maps contain a value for a given key, fn is called
|
||||
// with the values from both maps, otherwise it is called with the value of the map that contains the key and nil
|
||||
// for the map that does not contain the key. Returning false in the closure prematurely stops the iteration.
|
||||
Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool
|
||||
// ZipUsing uses the provided allocator and iterates over the entries of two maps together. If both maps
|
||||
// contain a value for a given key, fn is called with the values from both maps, otherwise it is called with
|
||||
// the value of the map that contains the key and nil for the map that does not contain the key. Returning
|
||||
// false in the closure prematurely stops the iteration.
|
||||
ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool
|
||||
}
|
||||
|
||||
// MapTraverseOrder defines the map traversal ordering available.
|
||||
type MapTraverseOrder int
|
||||
|
||||
const (
|
||||
// Unordered indicates that the map traversal has no ordering requirement.
|
||||
Unordered = iota
|
||||
// LexicalKeyOrder indicates that the map traversal is ordered by key, lexically.
|
||||
LexicalKeyOrder
|
||||
)
|
||||
|
||||
// MapZip iterates over the entries of two maps together. If both maps contain a value for a given key, fn is called
|
||||
// with the values from both maps, otherwise it is called with the value of the map that contains the key and nil
|
||||
// for the other map. Returning false in the closure prematurely stops the iteration.
|
||||
func MapZip(lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return MapZipUsing(HeapAllocator, lhs, rhs, order, fn)
|
||||
}
|
||||
|
||||
// MapZipUsing uses the provided allocator and iterates over the entries of two maps together. If both maps
|
||||
// contain a value for a given key, fn is called with the values from both maps, otherwise it is called with
|
||||
// the value of the map that contains the key and nil for the other map. Returning false in the closure
|
||||
// prematurely stops the iteration.
|
||||
func MapZipUsing(a Allocator, lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
if lhs != nil {
|
||||
return lhs.ZipUsing(a, rhs, order, fn)
|
||||
}
|
||||
if rhs != nil {
|
||||
return rhs.ZipUsing(a, lhs, order, func(key string, rhs, lhs Value) bool { // arg positions of lhs and rhs deliberately swapped
|
||||
return fn(key, lhs, rhs)
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// defaultMapZip provides a default implementation of Zip for implementations that do not need to provide
|
||||
// their own optimized implementation.
|
||||
func defaultMapZip(a Allocator, lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
switch order {
|
||||
case Unordered:
|
||||
return unorderedMapZip(a, lhs, rhs, fn)
|
||||
case LexicalKeyOrder:
|
||||
return lexicalKeyOrderedMapZip(a, lhs, rhs, fn)
|
||||
default:
|
||||
panic("Unsupported map order")
|
||||
}
|
||||
}
|
||||
|
||||
func unorderedMapZip(a Allocator, lhs, rhs Map, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
if (lhs == nil || lhs.Empty()) && (rhs == nil || rhs.Empty()) {
|
||||
return true
|
||||
}
|
||||
|
||||
if lhs != nil {
|
||||
ok := lhs.IterateUsing(a, func(key string, lhsValue Value) bool {
|
||||
var rhsValue Value
|
||||
if rhs != nil {
|
||||
if item, ok := rhs.GetUsing(a, key); ok {
|
||||
rhsValue = item
|
||||
defer a.Free(rhsValue)
|
||||
}
|
||||
}
|
||||
return fn(key, lhsValue, rhsValue)
|
||||
})
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if rhs != nil {
|
||||
return rhs.IterateUsing(a, func(key string, rhsValue Value) bool {
|
||||
if lhs == nil || !lhs.Has(key) {
|
||||
return fn(key, nil, rhsValue)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func lexicalKeyOrderedMapZip(a Allocator, lhs, rhs Map, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
var lhsLength, rhsLength int
|
||||
var orderedLength int // rough estimate of length of union of map keys
|
||||
if lhs != nil {
|
||||
lhsLength = lhs.Length()
|
||||
orderedLength = lhsLength
|
||||
}
|
||||
if rhs != nil {
|
||||
rhsLength = rhs.Length()
|
||||
if rhsLength > orderedLength {
|
||||
orderedLength = rhsLength
|
||||
}
|
||||
}
|
||||
if lhsLength == 0 && rhsLength == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
ordered := make([]string, 0, orderedLength)
|
||||
if lhs != nil {
|
||||
lhs.IterateUsing(a, func(key string, _ Value) bool {
|
||||
ordered = append(ordered, key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
if rhs != nil {
|
||||
rhs.IterateUsing(a, func(key string, _ Value) bool {
|
||||
if lhs == nil || !lhs.Has(key) {
|
||||
ordered = append(ordered, key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
sort.Strings(ordered)
|
||||
for _, key := range ordered {
|
||||
var litem, ritem Value
|
||||
if lhs != nil {
|
||||
litem, _ = lhs.GetUsing(a, key)
|
||||
}
|
||||
if rhs != nil {
|
||||
ritem, _ = rhs.GetUsing(a, key)
|
||||
}
|
||||
ok := fn(key, litem, ritem)
|
||||
if litem != nil {
|
||||
a.Free(litem)
|
||||
}
|
||||
if ritem != nil {
|
||||
a.Free(ritem)
|
||||
}
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MapLess compares two maps lexically.
|
||||
func MapLess(lhs, rhs Map) bool {
|
||||
return MapCompare(lhs, rhs) == -1
|
||||
}
|
||||
|
||||
// MapCompare compares two maps lexically.
|
||||
func MapCompare(lhs, rhs Map) int {
|
||||
return MapCompareUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// MapCompareUsing uses the provided allocator and compares two maps lexically.
|
||||
func MapCompareUsing(a Allocator, lhs, rhs Map) int {
|
||||
c := 0
|
||||
var llength, rlength int
|
||||
if lhs != nil {
|
||||
llength = lhs.Length()
|
||||
}
|
||||
if rhs != nil {
|
||||
rlength = rhs.Length()
|
||||
}
|
||||
if llength == 0 && rlength == 0 {
|
||||
return 0
|
||||
}
|
||||
i := 0
|
||||
MapZipUsing(a, lhs, rhs, LexicalKeyOrder, func(key string, lhs, rhs Value) bool {
|
||||
switch {
|
||||
case i == llength:
|
||||
c = -1
|
||||
case i == rlength:
|
||||
c = 1
|
||||
case lhs == nil:
|
||||
c = 1
|
||||
case rhs == nil:
|
||||
c = -1
|
||||
default:
|
||||
c = CompareUsing(a, lhs, rhs)
|
||||
}
|
||||
i++
|
||||
return c == 0
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
// MapEquals returns true if lhs == rhs, false otherwise. This function
|
||||
// acts on generic types and should not be used by callers, but can help
|
||||
// implement Map.Equals.
|
||||
// WARN: This is a naive implementation, calling lhs.Equals(rhs) is typically the most efficient.
|
||||
func MapEquals(lhs, rhs Map) bool {
|
||||
return MapEqualsUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// MapEqualsUsing uses the provided allocator and returns true if lhs == rhs,
|
||||
// false otherwise. This function acts on generic types and should not be used
|
||||
// by callers, but can help implement Map.Equals.
|
||||
// WARN: This is a naive implementation, calling lhs.EqualsUsing(allocator, rhs) is typically the most efficient.
|
||||
func MapEqualsUsing(a Allocator, lhs, rhs Map) bool {
|
||||
if lhs == nil && rhs == nil {
|
||||
return true
|
||||
}
|
||||
if lhs == nil || rhs == nil {
|
||||
return false
|
||||
}
|
||||
if lhs.Length() != rhs.Length() {
|
||||
return false
|
||||
}
|
||||
return MapZipUsing(a, lhs, rhs, Unordered, func(key string, lhs, rhs Value) bool {
|
||||
if lhs == nil || rhs == nil {
|
||||
return false
|
||||
}
|
||||
return EqualsUsing(a, lhs, rhs)
|
||||
})
|
||||
}
|
209
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/mapreflect.go
generated
vendored
Normal file
209
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/mapreflect.go
generated
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type mapReflect struct {
|
||||
valueReflect
|
||||
}
|
||||
|
||||
func (r mapReflect) Length() int {
|
||||
val := r.Value
|
||||
return val.Len()
|
||||
}
|
||||
|
||||
func (r mapReflect) Empty() bool {
|
||||
val := r.Value
|
||||
return val.Len() == 0
|
||||
}
|
||||
|
||||
func (r mapReflect) Get(key string) (Value, bool) {
|
||||
return r.GetUsing(HeapAllocator, key)
|
||||
}
|
||||
|
||||
func (r mapReflect) GetUsing(a Allocator, key string) (Value, bool) {
|
||||
k, v, ok := r.get(key)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return a.allocValueReflect().mustReuse(v, nil, &r.Value, &k), true
|
||||
}
|
||||
|
||||
func (r mapReflect) get(k string) (key, value reflect.Value, ok bool) {
|
||||
mapKey := r.toMapKey(k)
|
||||
val := r.Value.MapIndex(mapKey)
|
||||
return mapKey, val, val.IsValid() && val != reflect.Value{}
|
||||
}
|
||||
|
||||
func (r mapReflect) Has(key string) bool {
|
||||
var val reflect.Value
|
||||
val = r.Value.MapIndex(r.toMapKey(key))
|
||||
if !val.IsValid() {
|
||||
return false
|
||||
}
|
||||
return val != reflect.Value{}
|
||||
}
|
||||
|
||||
func (r mapReflect) Set(key string, val Value) {
|
||||
r.Value.SetMapIndex(r.toMapKey(key), reflect.ValueOf(val.Unstructured()))
|
||||
}
|
||||
|
||||
func (r mapReflect) Delete(key string) {
|
||||
val := r.Value
|
||||
val.SetMapIndex(r.toMapKey(key), reflect.Value{})
|
||||
}
|
||||
|
||||
// TODO: Do we need to support types that implement json.Marshaler and are used as string keys?
|
||||
func (r mapReflect) toMapKey(key string) reflect.Value {
|
||||
val := r.Value
|
||||
return reflect.ValueOf(key).Convert(val.Type().Key())
|
||||
}
|
||||
|
||||
func (r mapReflect) Iterate(fn func(string, Value) bool) bool {
|
||||
return r.IterateUsing(HeapAllocator, fn)
|
||||
}
|
||||
|
||||
func (r mapReflect) IterateUsing(a Allocator, fn func(string, Value) bool) bool {
|
||||
if r.Value.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
v := a.allocValueReflect()
|
||||
defer a.Free(v)
|
||||
return eachMapEntry(r.Value, func(e *TypeReflectCacheEntry, key reflect.Value, value reflect.Value) bool {
|
||||
return fn(key.String(), v.mustReuse(value, e, &r.Value, &key))
|
||||
})
|
||||
}
|
||||
|
||||
func eachMapEntry(val reflect.Value, fn func(*TypeReflectCacheEntry, reflect.Value, reflect.Value) bool) bool {
|
||||
iter := val.MapRange()
|
||||
entry := TypeReflectEntryOf(val.Type().Elem())
|
||||
for iter.Next() {
|
||||
next := iter.Value()
|
||||
if !next.IsValid() {
|
||||
continue
|
||||
}
|
||||
if !fn(entry, iter.Key(), next) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r mapReflect) Unstructured() interface{} {
|
||||
result := make(map[string]interface{}, r.Length())
|
||||
r.Iterate(func(s string, value Value) bool {
|
||||
result[s] = value.Unstructured()
|
||||
return true
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func (r mapReflect) Equals(m Map) bool {
|
||||
return r.EqualsUsing(HeapAllocator, m)
|
||||
}
|
||||
|
||||
func (r mapReflect) EqualsUsing(a Allocator, m Map) bool {
|
||||
lhsLength := r.Length()
|
||||
rhsLength := m.Length()
|
||||
if lhsLength != rhsLength {
|
||||
return false
|
||||
}
|
||||
if lhsLength == 0 {
|
||||
return true
|
||||
}
|
||||
vr := a.allocValueReflect()
|
||||
defer a.Free(vr)
|
||||
entry := TypeReflectEntryOf(r.Value.Type().Elem())
|
||||
return m.Iterate(func(key string, value Value) bool {
|
||||
_, lhsVal, ok := r.get(key)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return EqualsUsing(a, vr.mustReuse(lhsVal, entry, nil, nil), value)
|
||||
})
|
||||
}
|
||||
|
||||
func (r mapReflect) Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return r.ZipUsing(HeapAllocator, other, order, fn)
|
||||
}
|
||||
|
||||
func (r mapReflect) ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
if otherMapReflect, ok := other.(*mapReflect); ok && order == Unordered {
|
||||
return r.unorderedReflectZip(a, otherMapReflect, fn)
|
||||
}
|
||||
return defaultMapZip(a, &r, other, order, fn)
|
||||
}
|
||||
|
||||
// unorderedReflectZip provides an optimized unordered zip for mapReflect types.
|
||||
func (r mapReflect) unorderedReflectZip(a Allocator, other *mapReflect, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
if r.Empty() && (other == nil || other.Empty()) {
|
||||
return true
|
||||
}
|
||||
|
||||
lhs := r.Value
|
||||
lhsEntry := TypeReflectEntryOf(lhs.Type().Elem())
|
||||
|
||||
// map lookup via reflection is expensive enough that it is better to keep track of visited keys
|
||||
visited := map[string]struct{}{}
|
||||
|
||||
vlhs, vrhs := a.allocValueReflect(), a.allocValueReflect()
|
||||
defer a.Free(vlhs)
|
||||
defer a.Free(vrhs)
|
||||
|
||||
if other != nil {
|
||||
rhs := other.Value
|
||||
rhsEntry := TypeReflectEntryOf(rhs.Type().Elem())
|
||||
iter := rhs.MapRange()
|
||||
|
||||
for iter.Next() {
|
||||
key := iter.Key()
|
||||
keyString := key.String()
|
||||
next := iter.Value()
|
||||
if !next.IsValid() {
|
||||
continue
|
||||
}
|
||||
rhsVal := vrhs.mustReuse(next, rhsEntry, &rhs, &key)
|
||||
visited[keyString] = struct{}{}
|
||||
var lhsVal Value
|
||||
if _, v, ok := r.get(keyString); ok {
|
||||
lhsVal = vlhs.mustReuse(v, lhsEntry, &lhs, &key)
|
||||
}
|
||||
if !fn(keyString, lhsVal, rhsVal) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iter := lhs.MapRange()
|
||||
for iter.Next() {
|
||||
key := iter.Key()
|
||||
if _, ok := visited[key.String()]; ok {
|
||||
continue
|
||||
}
|
||||
next := iter.Value()
|
||||
if !next.IsValid() {
|
||||
continue
|
||||
}
|
||||
if !fn(key.String(), vlhs.mustReuse(next, lhsEntry, &lhs, &key), nil) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
190
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/mapunstructured.go
generated
vendored
Normal file
190
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/mapunstructured.go
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
type mapUnstructuredInterface map[interface{}]interface{}
|
||||
|
||||
func (m mapUnstructuredInterface) Set(key string, val Value) {
|
||||
m[key] = val.Unstructured()
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Get(key string) (Value, bool) {
|
||||
return m.GetUsing(HeapAllocator, key)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) GetUsing(a Allocator, key string) (Value, bool) {
|
||||
if v, ok := m[key]; !ok {
|
||||
return nil, false
|
||||
} else {
|
||||
return a.allocValueUnstructured().reuse(v), true
|
||||
}
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Has(key string) bool {
|
||||
_, ok := m[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Delete(key string) {
|
||||
delete(m, key)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Iterate(fn func(key string, value Value) bool) bool {
|
||||
return m.IterateUsing(HeapAllocator, fn)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) IterateUsing(a Allocator, fn func(key string, value Value) bool) bool {
|
||||
if len(m) == 0 {
|
||||
return true
|
||||
}
|
||||
vv := a.allocValueUnstructured()
|
||||
defer a.Free(vv)
|
||||
for k, v := range m {
|
||||
if ks, ok := k.(string); !ok {
|
||||
continue
|
||||
} else {
|
||||
if !fn(ks, vv.reuse(v)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Length() int {
|
||||
return len(m)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Empty() bool {
|
||||
return len(m) == 0
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Equals(other Map) bool {
|
||||
return m.EqualsUsing(HeapAllocator, other)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) EqualsUsing(a Allocator, other Map) bool {
|
||||
lhsLength := m.Length()
|
||||
rhsLength := other.Length()
|
||||
if lhsLength != rhsLength {
|
||||
return false
|
||||
}
|
||||
if lhsLength == 0 {
|
||||
return true
|
||||
}
|
||||
vv := a.allocValueUnstructured()
|
||||
defer a.Free(vv)
|
||||
return other.IterateUsing(a, func(key string, value Value) bool {
|
||||
lhsVal, ok := m[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return EqualsUsing(a, vv.reuse(lhsVal), value)
|
||||
})
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return m.ZipUsing(HeapAllocator, other, order, fn)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return defaultMapZip(a, m, other, order, fn)
|
||||
}
|
||||
|
||||
type mapUnstructuredString map[string]interface{}
|
||||
|
||||
func (m mapUnstructuredString) Set(key string, val Value) {
|
||||
m[key] = val.Unstructured()
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Get(key string) (Value, bool) {
|
||||
return m.GetUsing(HeapAllocator, key)
|
||||
}
|
||||
func (m mapUnstructuredString) GetUsing(a Allocator, key string) (Value, bool) {
|
||||
if v, ok := m[key]; !ok {
|
||||
return nil, false
|
||||
} else {
|
||||
return a.allocValueUnstructured().reuse(v), true
|
||||
}
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Has(key string) bool {
|
||||
_, ok := m[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Delete(key string) {
|
||||
delete(m, key)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Iterate(fn func(key string, value Value) bool) bool {
|
||||
return m.IterateUsing(HeapAllocator, fn)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) IterateUsing(a Allocator, fn func(key string, value Value) bool) bool {
|
||||
if len(m) == 0 {
|
||||
return true
|
||||
}
|
||||
vv := a.allocValueUnstructured()
|
||||
defer a.Free(vv)
|
||||
for k, v := range m {
|
||||
if !fn(k, vv.reuse(v)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Length() int {
|
||||
return len(m)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Equals(other Map) bool {
|
||||
return m.EqualsUsing(HeapAllocator, other)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) EqualsUsing(a Allocator, other Map) bool {
|
||||
lhsLength := m.Length()
|
||||
rhsLength := other.Length()
|
||||
if lhsLength != rhsLength {
|
||||
return false
|
||||
}
|
||||
if lhsLength == 0 {
|
||||
return true
|
||||
}
|
||||
vv := a.allocValueUnstructured()
|
||||
defer a.Free(vv)
|
||||
return other.IterateUsing(a, func(key string, value Value) bool {
|
||||
lhsVal, ok := m[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return EqualsUsing(a, vv.reuse(lhsVal), value)
|
||||
})
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return m.ZipUsing(HeapAllocator, other, order, fn)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return defaultMapZip(a, m, other, order, fn)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Empty() bool {
|
||||
return len(m) == 0
|
||||
}
|
484
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/reflectcache.go
generated
vendored
Normal file
484
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/reflectcache.go
generated
vendored
Normal file
@ -0,0 +1,484 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// UnstructuredConverter defines how a type can be converted directly to unstructured.
|
||||
// Types that implement json.Marshaler may also optionally implement this interface to provide a more
|
||||
// direct and more efficient conversion. All types that choose to implement this interface must still
|
||||
// implement this same conversion via json.Marshaler.
|
||||
type UnstructuredConverter interface {
|
||||
json.Marshaler // require that json.Marshaler is implemented
|
||||
|
||||
// ToUnstructured returns the unstructured representation.
|
||||
ToUnstructured() interface{}
|
||||
}
|
||||
|
||||
// TypeReflectCacheEntry keeps data gathered using reflection about how a type is converted to/from unstructured.
|
||||
type TypeReflectCacheEntry struct {
|
||||
isJsonMarshaler bool
|
||||
ptrIsJsonMarshaler bool
|
||||
isJsonUnmarshaler bool
|
||||
ptrIsJsonUnmarshaler bool
|
||||
isStringConvertable bool
|
||||
ptrIsStringConvertable bool
|
||||
|
||||
structFields map[string]*FieldCacheEntry
|
||||
orderedStructFields []*FieldCacheEntry
|
||||
}
|
||||
|
||||
// FieldCacheEntry keeps data gathered using reflection about how the field of a struct is converted to/from
|
||||
// unstructured.
|
||||
type FieldCacheEntry struct {
|
||||
// JsonName returns the name of the field according to the json tags on the struct field.
|
||||
JsonName string
|
||||
// isOmitEmpty is true if the field has the json 'omitempty' tag.
|
||||
isOmitEmpty bool
|
||||
// fieldPath is a list of field indices (see FieldByIndex) to lookup the value of
|
||||
// a field in a reflect.Value struct. The field indices in the list form a path used
|
||||
// to traverse through intermediary 'inline' fields.
|
||||
fieldPath [][]int
|
||||
|
||||
fieldType reflect.Type
|
||||
TypeEntry *TypeReflectCacheEntry
|
||||
}
|
||||
|
||||
func (f *FieldCacheEntry) CanOmit(fieldVal reflect.Value) bool {
|
||||
return f.isOmitEmpty && (safeIsNil(fieldVal) || isZero(fieldVal))
|
||||
}
|
||||
|
||||
// GetFrom returns the field identified by this FieldCacheEntry from the provided struct.
|
||||
func (f *FieldCacheEntry) GetFrom(structVal reflect.Value) reflect.Value {
|
||||
// field might be nested within 'inline' structs
|
||||
for _, elem := range f.fieldPath {
|
||||
structVal = dereference(structVal).FieldByIndex(elem)
|
||||
}
|
||||
return structVal
|
||||
}
|
||||
|
||||
var marshalerType = reflect.TypeOf(new(json.Marshaler)).Elem()
|
||||
var unmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem()
|
||||
var unstructuredConvertableType = reflect.TypeOf(new(UnstructuredConverter)).Elem()
|
||||
var defaultReflectCache = newReflectCache()
|
||||
|
||||
// TypeReflectEntryOf returns the TypeReflectCacheEntry of the provided reflect.Type.
|
||||
func TypeReflectEntryOf(t reflect.Type) *TypeReflectCacheEntry {
|
||||
cm := defaultReflectCache.get()
|
||||
if record, ok := cm[t]; ok {
|
||||
return record
|
||||
}
|
||||
updates := reflectCacheMap{}
|
||||
result := typeReflectEntryOf(cm, t, updates)
|
||||
if len(updates) > 0 {
|
||||
defaultReflectCache.update(updates)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// TypeReflectEntryOf returns all updates needed to add provided reflect.Type, and the types its fields transitively
|
||||
// depend on, to the cache.
|
||||
func typeReflectEntryOf(cm reflectCacheMap, t reflect.Type, updates reflectCacheMap) *TypeReflectCacheEntry {
|
||||
if record, ok := cm[t]; ok {
|
||||
return record
|
||||
}
|
||||
if record, ok := updates[t]; ok {
|
||||
return record
|
||||
}
|
||||
typeEntry := &TypeReflectCacheEntry{
|
||||
isJsonMarshaler: t.Implements(marshalerType),
|
||||
ptrIsJsonMarshaler: reflect.PtrTo(t).Implements(marshalerType),
|
||||
isJsonUnmarshaler: reflect.PtrTo(t).Implements(unmarshalerType),
|
||||
isStringConvertable: t.Implements(unstructuredConvertableType),
|
||||
ptrIsStringConvertable: reflect.PtrTo(t).Implements(unstructuredConvertableType),
|
||||
}
|
||||
if t.Kind() == reflect.Struct {
|
||||
fieldEntries := map[string]*FieldCacheEntry{}
|
||||
buildStructCacheEntry(t, fieldEntries, nil)
|
||||
typeEntry.structFields = fieldEntries
|
||||
sortedByJsonName := make([]*FieldCacheEntry, len(fieldEntries))
|
||||
i := 0
|
||||
for _, entry := range fieldEntries {
|
||||
sortedByJsonName[i] = entry
|
||||
i++
|
||||
}
|
||||
sort.Slice(sortedByJsonName, func(i, j int) bool {
|
||||
return sortedByJsonName[i].JsonName < sortedByJsonName[j].JsonName
|
||||
})
|
||||
typeEntry.orderedStructFields = sortedByJsonName
|
||||
}
|
||||
|
||||
// cyclic type references are allowed, so we must add the typeEntry to the updates map before resolving
|
||||
// the field.typeEntry references, or creating them if they are not already in the cache
|
||||
updates[t] = typeEntry
|
||||
|
||||
for _, field := range typeEntry.structFields {
|
||||
if field.TypeEntry == nil {
|
||||
field.TypeEntry = typeReflectEntryOf(cm, field.fieldType, updates)
|
||||
}
|
||||
}
|
||||
return typeEntry
|
||||
}
|
||||
|
||||
func buildStructCacheEntry(t reflect.Type, infos map[string]*FieldCacheEntry, fieldPath [][]int) {
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
jsonName, omit, isInline, isOmitempty := lookupJsonTags(field)
|
||||
if omit {
|
||||
continue
|
||||
}
|
||||
if isInline {
|
||||
e := field.Type
|
||||
if field.Type.Kind() == reflect.Ptr {
|
||||
e = field.Type.Elem()
|
||||
}
|
||||
if e.Kind() == reflect.Struct {
|
||||
buildStructCacheEntry(e, infos, append(fieldPath, field.Index))
|
||||
}
|
||||
continue
|
||||
}
|
||||
info := &FieldCacheEntry{JsonName: jsonName, isOmitEmpty: isOmitempty, fieldPath: append(fieldPath, field.Index), fieldType: field.Type}
|
||||
infos[jsonName] = info
|
||||
}
|
||||
}
|
||||
|
||||
// Fields returns a map of JSON field name to FieldCacheEntry for structs, or nil for non-structs.
|
||||
func (e TypeReflectCacheEntry) Fields() map[string]*FieldCacheEntry {
|
||||
return e.structFields
|
||||
}
|
||||
|
||||
// Fields returns a map of JSON field name to FieldCacheEntry for structs, or nil for non-structs.
|
||||
func (e TypeReflectCacheEntry) OrderedFields() []*FieldCacheEntry {
|
||||
return e.orderedStructFields
|
||||
}
|
||||
|
||||
// CanConvertToUnstructured returns true if this TypeReflectCacheEntry can convert values of its type to unstructured.
|
||||
func (e TypeReflectCacheEntry) CanConvertToUnstructured() bool {
|
||||
return e.isJsonMarshaler || e.ptrIsJsonMarshaler || e.isStringConvertable || e.ptrIsStringConvertable
|
||||
}
|
||||
|
||||
// ToUnstructured converts the provided value to unstructured and returns it.
|
||||
func (e TypeReflectCacheEntry) ToUnstructured(sv reflect.Value) (interface{}, error) {
|
||||
// This is based on https://github.com/kubernetes/kubernetes/blob/82c9e5c814eb7acc6cc0a090c057294d0667ad66/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L505
|
||||
// and is intended to replace it.
|
||||
|
||||
// Check if the object is a nil pointer.
|
||||
if sv.Kind() == reflect.Ptr && sv.IsNil() {
|
||||
// We're done - we don't need to store anything.
|
||||
return nil, nil
|
||||
}
|
||||
// Check if the object has a custom string converter and use it if available, since it is much more efficient
|
||||
// than round tripping through json.
|
||||
if converter, ok := e.getUnstructuredConverter(sv); ok {
|
||||
return converter.ToUnstructured(), nil
|
||||
}
|
||||
// Check if the object has a custom JSON marshaller/unmarshaller.
|
||||
if marshaler, ok := e.getJsonMarshaler(sv); ok {
|
||||
data, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch {
|
||||
case len(data) == 0:
|
||||
return nil, fmt.Errorf("error decoding from json: empty value")
|
||||
|
||||
case bytes.Equal(data, nullBytes):
|
||||
// We're done - we don't need to store anything.
|
||||
return nil, nil
|
||||
|
||||
case bytes.Equal(data, trueBytes):
|
||||
return true, nil
|
||||
|
||||
case bytes.Equal(data, falseBytes):
|
||||
return false, nil
|
||||
|
||||
case data[0] == '"':
|
||||
var result string
|
||||
err := unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding string from json: %v", err)
|
||||
}
|
||||
return result, nil
|
||||
|
||||
case data[0] == '{':
|
||||
result := make(map[string]interface{})
|
||||
err := unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding object from json: %v", err)
|
||||
}
|
||||
return result, nil
|
||||
|
||||
case data[0] == '[':
|
||||
result := make([]interface{}, 0)
|
||||
err := unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding array from json: %v", err)
|
||||
}
|
||||
return result, nil
|
||||
|
||||
default:
|
||||
var (
|
||||
resultInt int64
|
||||
resultFloat float64
|
||||
err error
|
||||
)
|
||||
if err = unmarshal(data, &resultInt); err == nil {
|
||||
return resultInt, nil
|
||||
} else if err = unmarshal(data, &resultFloat); err == nil {
|
||||
return resultFloat, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("error decoding number from json: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("provided type cannot be converted: %v", sv.Type())
|
||||
}
|
||||
|
||||
// CanConvertFromUnstructured returns true if this TypeReflectCacheEntry can convert objects of the type from unstructured.
|
||||
func (e TypeReflectCacheEntry) CanConvertFromUnstructured() bool {
|
||||
return e.isJsonUnmarshaler
|
||||
}
|
||||
|
||||
// FromUnstructured converts the provided source value from unstructured into the provided destination value.
|
||||
func (e TypeReflectCacheEntry) FromUnstructured(sv, dv reflect.Value) error {
|
||||
// TODO: this could be made much more efficient using direct conversions like
|
||||
// UnstructuredConverter.ToUnstructured provides.
|
||||
st := dv.Type()
|
||||
data, err := json.Marshal(sv.Interface())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error encoding %s to json: %v", st.String(), err)
|
||||
}
|
||||
if unmarshaler, ok := e.getJsonUnmarshaler(dv); ok {
|
||||
return unmarshaler.UnmarshalJSON(data)
|
||||
}
|
||||
return fmt.Errorf("unable to unmarshal %v into %v", sv.Type(), dv.Type())
|
||||
}
|
||||
|
||||
var (
|
||||
nullBytes = []byte("null")
|
||||
trueBytes = []byte("true")
|
||||
falseBytes = []byte("false")
|
||||
)
|
||||
|
||||
func (e TypeReflectCacheEntry) getJsonMarshaler(v reflect.Value) (json.Marshaler, bool) {
|
||||
if e.isJsonMarshaler {
|
||||
return v.Interface().(json.Marshaler), true
|
||||
}
|
||||
if e.ptrIsJsonMarshaler {
|
||||
// Check pointer receivers if v is not a pointer
|
||||
if v.Kind() != reflect.Ptr && v.CanAddr() {
|
||||
v = v.Addr()
|
||||
return v.Interface().(json.Marshaler), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (e TypeReflectCacheEntry) getJsonUnmarshaler(v reflect.Value) (json.Unmarshaler, bool) {
|
||||
if !e.isJsonUnmarshaler {
|
||||
return nil, false
|
||||
}
|
||||
return v.Addr().Interface().(json.Unmarshaler), true
|
||||
}
|
||||
|
||||
func (e TypeReflectCacheEntry) getUnstructuredConverter(v reflect.Value) (UnstructuredConverter, bool) {
|
||||
if e.isStringConvertable {
|
||||
return v.Interface().(UnstructuredConverter), true
|
||||
}
|
||||
if e.ptrIsStringConvertable {
|
||||
// Check pointer receivers if v is not a pointer
|
||||
if v.CanAddr() {
|
||||
v = v.Addr()
|
||||
return v.Interface().(UnstructuredConverter), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
type typeReflectCache struct {
|
||||
// use an atomic and copy-on-write since there are a fixed (typically very small) number of structs compiled into any
|
||||
// go program using this cache
|
||||
value atomic.Value
|
||||
// mu is held by writers when performing load/modify/store operations on the cache, readers do not need to hold a
|
||||
// read-lock since the atomic value is always read-only
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newReflectCache() *typeReflectCache {
|
||||
cache := &typeReflectCache{}
|
||||
cache.value.Store(make(reflectCacheMap))
|
||||
return cache
|
||||
}
|
||||
|
||||
type reflectCacheMap map[reflect.Type]*TypeReflectCacheEntry
|
||||
|
||||
// get returns the reflectCacheMap.
|
||||
func (c *typeReflectCache) get() reflectCacheMap {
|
||||
return c.value.Load().(reflectCacheMap)
|
||||
}
|
||||
|
||||
// update merges the provided updates into the cache.
|
||||
func (c *typeReflectCache) update(updates reflectCacheMap) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
currentCacheMap := c.value.Load().(reflectCacheMap)
|
||||
|
||||
hasNewEntries := false
|
||||
for t := range updates {
|
||||
if _, ok := currentCacheMap[t]; !ok {
|
||||
hasNewEntries = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasNewEntries {
|
||||
// Bail if the updates have been set while waiting for lock acquisition.
|
||||
// This is safe since setting entries is idempotent.
|
||||
return
|
||||
}
|
||||
|
||||
newCacheMap := make(reflectCacheMap, len(currentCacheMap)+len(updates))
|
||||
for k, v := range currentCacheMap {
|
||||
newCacheMap[k] = v
|
||||
}
|
||||
for t, update := range updates {
|
||||
newCacheMap[t] = update
|
||||
}
|
||||
c.value.Store(newCacheMap)
|
||||
}
|
||||
|
||||
// Below json Unmarshal is fromk8s.io/apimachinery/pkg/util/json
|
||||
// to handle number conversions as expected by Kubernetes
|
||||
|
||||
// limit recursive depth to prevent stack overflow errors
|
||||
const maxDepth = 10000
|
||||
|
||||
// unmarshal unmarshals the given data
|
||||
// If v is a *map[string]interface{}, numbers are converted to int64 or float64
|
||||
func unmarshal(data []byte, v interface{}) error {
|
||||
// Build a decoder from the given data
|
||||
decoder := json.NewDecoder(bytes.NewBuffer(data))
|
||||
// Preserve numbers, rather than casting to float64 automatically
|
||||
decoder.UseNumber()
|
||||
// Run the decode
|
||||
if err := decoder.Decode(v); err != nil {
|
||||
return err
|
||||
}
|
||||
next := decoder.InputOffset()
|
||||
if _, err := decoder.Token(); !errors.Is(err, io.EOF) {
|
||||
tail := bytes.TrimLeft(data[next:], " \t\r\n")
|
||||
return fmt.Errorf("unexpected trailing data at offset %d", len(data)-len(tail))
|
||||
}
|
||||
|
||||
// If the decode succeeds, post-process the object to convert json.Number objects to int64 or float64
|
||||
switch v := v.(type) {
|
||||
case *map[string]interface{}:
|
||||
return convertMapNumbers(*v, 0)
|
||||
|
||||
case *[]interface{}:
|
||||
return convertSliceNumbers(*v, 0)
|
||||
|
||||
case *interface{}:
|
||||
return convertInterfaceNumbers(v, 0)
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func convertInterfaceNumbers(v *interface{}, depth int) error {
|
||||
var err error
|
||||
switch v2 := (*v).(type) {
|
||||
case json.Number:
|
||||
*v, err = convertNumber(v2)
|
||||
case map[string]interface{}:
|
||||
err = convertMapNumbers(v2, depth+1)
|
||||
case []interface{}:
|
||||
err = convertSliceNumbers(v2, depth+1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// convertMapNumbers traverses the map, converting any json.Number values to int64 or float64.
|
||||
// values which are map[string]interface{} or []interface{} are recursively visited
|
||||
func convertMapNumbers(m map[string]interface{}, depth int) error {
|
||||
if depth > maxDepth {
|
||||
return fmt.Errorf("exceeded max depth of %d", maxDepth)
|
||||
}
|
||||
|
||||
var err error
|
||||
for k, v := range m {
|
||||
switch v := v.(type) {
|
||||
case json.Number:
|
||||
m[k], err = convertNumber(v)
|
||||
case map[string]interface{}:
|
||||
err = convertMapNumbers(v, depth+1)
|
||||
case []interface{}:
|
||||
err = convertSliceNumbers(v, depth+1)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertSliceNumbers traverses the slice, converting any json.Number values to int64 or float64.
|
||||
// values which are map[string]interface{} or []interface{} are recursively visited
|
||||
func convertSliceNumbers(s []interface{}, depth int) error {
|
||||
if depth > maxDepth {
|
||||
return fmt.Errorf("exceeded max depth of %d", maxDepth)
|
||||
}
|
||||
|
||||
var err error
|
||||
for i, v := range s {
|
||||
switch v := v.(type) {
|
||||
case json.Number:
|
||||
s[i], err = convertNumber(v)
|
||||
case map[string]interface{}:
|
||||
err = convertMapNumbers(v, depth+1)
|
||||
case []interface{}:
|
||||
err = convertSliceNumbers(v, depth+1)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertNumber converts a json.Number to an int64 or float64, or returns an error
|
||||
func convertNumber(n json.Number) (interface{}, error) {
|
||||
// Attempt to convert to an int64 first
|
||||
if i, err := n.Int64(); err == nil {
|
||||
return i, nil
|
||||
}
|
||||
// Return a float64 (default json.Decode() behavior)
|
||||
// An overflow will return an error
|
||||
return n.Float64()
|
||||
}
|
50
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/scalar.go
generated
vendored
Normal file
50
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/scalar.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
// Compare compares floats. The result will be 0 if lhs==rhs, -1 if f <
|
||||
// rhs, and +1 if f > rhs.
|
||||
func FloatCompare(lhs, rhs float64) int {
|
||||
if lhs > rhs {
|
||||
return 1
|
||||
} else if lhs < rhs {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IntCompare compares integers. The result will be 0 if i==rhs, -1 if i <
|
||||
// rhs, and +1 if i > rhs.
|
||||
func IntCompare(lhs, rhs int64) int {
|
||||
if lhs > rhs {
|
||||
return 1
|
||||
} else if lhs < rhs {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Compare compares booleans. The result will be 0 if b==rhs, -1 if b <
|
||||
// rhs, and +1 if b > rhs.
|
||||
func BoolCompare(lhs, rhs bool) int {
|
||||
if lhs == rhs {
|
||||
return 0
|
||||
} else if !lhs {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
208
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/structreflect.go
generated
vendored
Normal file
208
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/structreflect.go
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type structReflect struct {
|
||||
valueReflect
|
||||
}
|
||||
|
||||
func (r structReflect) Length() int {
|
||||
i := 0
|
||||
eachStructField(r.Value, func(_ *TypeReflectCacheEntry, s string, value reflect.Value) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
return i
|
||||
}
|
||||
|
||||
func (r structReflect) Empty() bool {
|
||||
return eachStructField(r.Value, func(_ *TypeReflectCacheEntry, s string, value reflect.Value) bool {
|
||||
return false // exit early if the struct is non-empty
|
||||
})
|
||||
}
|
||||
|
||||
func (r structReflect) Get(key string) (Value, bool) {
|
||||
return r.GetUsing(HeapAllocator, key)
|
||||
}
|
||||
|
||||
func (r structReflect) GetUsing(a Allocator, key string) (Value, bool) {
|
||||
if val, ok := r.findJsonNameField(key); ok {
|
||||
return a.allocValueReflect().mustReuse(val, nil, nil, nil), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r structReflect) Has(key string) bool {
|
||||
_, ok := r.findJsonNameField(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r structReflect) Set(key string, val Value) {
|
||||
fieldEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[key]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("key %s may not be set on struct %T: field does not exist", key, r.Value.Interface()))
|
||||
}
|
||||
oldVal := fieldEntry.GetFrom(r.Value)
|
||||
newVal := reflect.ValueOf(val.Unstructured())
|
||||
r.update(fieldEntry, key, oldVal, newVal)
|
||||
}
|
||||
|
||||
func (r structReflect) Delete(key string) {
|
||||
fieldEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[key]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("key %s may not be deleted on struct %T: field does not exist", key, r.Value.Interface()))
|
||||
}
|
||||
oldVal := fieldEntry.GetFrom(r.Value)
|
||||
if oldVal.Kind() != reflect.Ptr && !fieldEntry.isOmitEmpty {
|
||||
panic(fmt.Sprintf("key %s may not be deleted on struct: %T: value is neither a pointer nor an omitempty field", key, r.Value.Interface()))
|
||||
}
|
||||
r.update(fieldEntry, key, oldVal, reflect.Zero(oldVal.Type()))
|
||||
}
|
||||
|
||||
func (r structReflect) update(fieldEntry *FieldCacheEntry, key string, oldVal, newVal reflect.Value) {
|
||||
if oldVal.CanSet() {
|
||||
oldVal.Set(newVal)
|
||||
return
|
||||
}
|
||||
|
||||
// map items are not addressable, so if a struct is contained in a map, the only way to modify it is
|
||||
// to write a replacement fieldEntry into the map.
|
||||
if r.ParentMap != nil {
|
||||
if r.ParentMapKey == nil {
|
||||
panic("ParentMapKey must not be nil if ParentMap is not nil")
|
||||
}
|
||||
replacement := reflect.New(r.Value.Type()).Elem()
|
||||
fieldEntry.GetFrom(replacement).Set(newVal)
|
||||
r.ParentMap.SetMapIndex(*r.ParentMapKey, replacement)
|
||||
return
|
||||
}
|
||||
|
||||
// This should never happen since NewValueReflect ensures that the root object reflected on is a pointer and map
|
||||
// item replacement is handled above.
|
||||
panic(fmt.Sprintf("key %s may not be modified on struct: %T: struct is not settable", key, r.Value.Interface()))
|
||||
}
|
||||
|
||||
func (r structReflect) Iterate(fn func(string, Value) bool) bool {
|
||||
return r.IterateUsing(HeapAllocator, fn)
|
||||
}
|
||||
|
||||
func (r structReflect) IterateUsing(a Allocator, fn func(string, Value) bool) bool {
|
||||
vr := a.allocValueReflect()
|
||||
defer a.Free(vr)
|
||||
return eachStructField(r.Value, func(e *TypeReflectCacheEntry, s string, value reflect.Value) bool {
|
||||
return fn(s, vr.mustReuse(value, e, nil, nil))
|
||||
})
|
||||
}
|
||||
|
||||
func eachStructField(structVal reflect.Value, fn func(*TypeReflectCacheEntry, string, reflect.Value) bool) bool {
|
||||
for _, fieldCacheEntry := range TypeReflectEntryOf(structVal.Type()).OrderedFields() {
|
||||
fieldVal := fieldCacheEntry.GetFrom(structVal)
|
||||
if fieldCacheEntry.CanOmit(fieldVal) {
|
||||
// omit it
|
||||
continue
|
||||
}
|
||||
ok := fn(fieldCacheEntry.TypeEntry, fieldCacheEntry.JsonName, fieldVal)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r structReflect) Unstructured() interface{} {
|
||||
// Use number of struct fields as a cheap way to rough estimate map size
|
||||
result := make(map[string]interface{}, r.Value.NumField())
|
||||
r.Iterate(func(s string, value Value) bool {
|
||||
result[s] = value.Unstructured()
|
||||
return true
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func (r structReflect) Equals(m Map) bool {
|
||||
return r.EqualsUsing(HeapAllocator, m)
|
||||
}
|
||||
|
||||
func (r structReflect) EqualsUsing(a Allocator, m Map) bool {
|
||||
// MapEquals uses zip and is fairly efficient for structReflect
|
||||
return MapEqualsUsing(a, &r, m)
|
||||
}
|
||||
|
||||
func (r structReflect) findJsonNameFieldAndNotEmpty(jsonName string) (reflect.Value, bool) {
|
||||
structCacheEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[jsonName]
|
||||
if !ok {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
fieldVal := structCacheEntry.GetFrom(r.Value)
|
||||
return fieldVal, !structCacheEntry.CanOmit(fieldVal)
|
||||
}
|
||||
|
||||
func (r structReflect) findJsonNameField(jsonName string) (val reflect.Value, ok bool) {
|
||||
structCacheEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[jsonName]
|
||||
if !ok {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
fieldVal := structCacheEntry.GetFrom(r.Value)
|
||||
return fieldVal, !structCacheEntry.CanOmit(fieldVal)
|
||||
}
|
||||
|
||||
func (r structReflect) Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return r.ZipUsing(HeapAllocator, other, order, fn)
|
||||
}
|
||||
|
||||
func (r structReflect) ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
if otherStruct, ok := other.(*structReflect); ok && r.Value.Type() == otherStruct.Value.Type() {
|
||||
lhsvr, rhsvr := a.allocValueReflect(), a.allocValueReflect()
|
||||
defer a.Free(lhsvr)
|
||||
defer a.Free(rhsvr)
|
||||
return r.structZip(otherStruct, lhsvr, rhsvr, fn)
|
||||
}
|
||||
return defaultMapZip(a, &r, other, order, fn)
|
||||
}
|
||||
|
||||
// structZip provides an optimized zip for structReflect types. The zip is always lexical key ordered since there is
|
||||
// no additional cost to ordering the zip for structured types.
|
||||
func (r structReflect) structZip(other *structReflect, lhsvr, rhsvr *valueReflect, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
lhsVal := r.Value
|
||||
rhsVal := other.Value
|
||||
|
||||
for _, fieldCacheEntry := range TypeReflectEntryOf(lhsVal.Type()).OrderedFields() {
|
||||
lhsFieldVal := fieldCacheEntry.GetFrom(lhsVal)
|
||||
rhsFieldVal := fieldCacheEntry.GetFrom(rhsVal)
|
||||
lhsOmit := fieldCacheEntry.CanOmit(lhsFieldVal)
|
||||
rhsOmit := fieldCacheEntry.CanOmit(rhsFieldVal)
|
||||
if lhsOmit && rhsOmit {
|
||||
continue
|
||||
}
|
||||
var lhsVal, rhsVal Value
|
||||
if !lhsOmit {
|
||||
lhsVal = lhsvr.mustReuse(lhsFieldVal, fieldCacheEntry.TypeEntry, nil, nil)
|
||||
}
|
||||
if !rhsOmit {
|
||||
rhsVal = rhsvr.mustReuse(rhsFieldVal, fieldCacheEntry.TypeEntry, nil, nil)
|
||||
}
|
||||
if !fn(fieldCacheEntry.JsonName, lhsVal, rhsVal) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
347
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/value.go
generated
vendored
Normal file
347
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/value.go
generated
vendored
Normal file
@ -0,0 +1,347 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
yaml "sigs.k8s.io/yaml/goyaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
readPool = jsoniter.NewIterator(jsoniter.ConfigCompatibleWithStandardLibrary).Pool()
|
||||
writePool = jsoniter.NewStream(jsoniter.ConfigCompatibleWithStandardLibrary, nil, 1024).Pool()
|
||||
)
|
||||
|
||||
// A Value corresponds to an 'atom' in the schema. It should return true
|
||||
// for at least one of the IsXXX methods below, or the value is
|
||||
// considered "invalid"
|
||||
type Value interface {
|
||||
// IsMap returns true if the Value is a Map, false otherwise.
|
||||
IsMap() bool
|
||||
// IsList returns true if the Value is a List, false otherwise.
|
||||
IsList() bool
|
||||
// IsBool returns true if the Value is a bool, false otherwise.
|
||||
IsBool() bool
|
||||
// IsInt returns true if the Value is a int64, false otherwise.
|
||||
IsInt() bool
|
||||
// IsFloat returns true if the Value is a float64, false
|
||||
// otherwise.
|
||||
IsFloat() bool
|
||||
// IsString returns true if the Value is a string, false
|
||||
// otherwise.
|
||||
IsString() bool
|
||||
// IsMap returns true if the Value is null, false otherwise.
|
||||
IsNull() bool
|
||||
|
||||
// AsMap converts the Value into a Map (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsMap() Map
|
||||
// AsMapUsing uses the provided allocator and converts the Value
|
||||
// into a Map (or panic if the type doesn't allow it).
|
||||
AsMapUsing(Allocator) Map
|
||||
// AsList converts the Value into a List (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsList() List
|
||||
// AsListUsing uses the provided allocator and converts the Value
|
||||
// into a List (or panic if the type doesn't allow it).
|
||||
AsListUsing(Allocator) List
|
||||
// AsBool converts the Value into a bool (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsBool() bool
|
||||
// AsInt converts the Value into an int64 (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsInt() int64
|
||||
// AsFloat converts the Value into a float64 (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsFloat() float64
|
||||
// AsString converts the Value into a string (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsString() string
|
||||
|
||||
// Unstructured converts the Value into an Unstructured interface{}.
|
||||
Unstructured() interface{}
|
||||
}
|
||||
|
||||
// FromJSON is a helper function for reading a JSON document.
|
||||
func FromJSON(input []byte) (Value, error) {
|
||||
return FromJSONFast(input)
|
||||
}
|
||||
|
||||
// FromJSONFast is a helper function for reading a JSON document.
|
||||
func FromJSONFast(input []byte) (Value, error) {
|
||||
iter := readPool.BorrowIterator(input)
|
||||
defer readPool.ReturnIterator(iter)
|
||||
return ReadJSONIter(iter)
|
||||
}
|
||||
|
||||
// ToJSON is a helper function for producing a JSon document.
|
||||
func ToJSON(v Value) ([]byte, error) {
|
||||
buf := bytes.Buffer{}
|
||||
stream := writePool.BorrowStream(&buf)
|
||||
defer writePool.ReturnStream(stream)
|
||||
WriteJSONStream(v, stream)
|
||||
b := stream.Buffer()
|
||||
err := stream.Flush()
|
||||
// Help jsoniter manage its buffers--without this, the next
|
||||
// use of the stream is likely to require an allocation. Look
|
||||
// at the jsoniter stream code to understand why. They were probably
|
||||
// optimizing for folks using the buffer directly.
|
||||
stream.SetBuffer(b[:0])
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// ReadJSONIter reads a Value from a JSON iterator.
|
||||
func ReadJSONIter(iter *jsoniter.Iterator) (Value, error) {
|
||||
v := iter.Read()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
return nil, iter.Error
|
||||
}
|
||||
return NewValueInterface(v), nil
|
||||
}
|
||||
|
||||
// WriteJSONStream writes a value into a JSON stream.
|
||||
func WriteJSONStream(v Value, stream *jsoniter.Stream) {
|
||||
stream.WriteVal(v.Unstructured())
|
||||
}
|
||||
|
||||
// ToYAML marshals a value as YAML.
|
||||
func ToYAML(v Value) ([]byte, error) {
|
||||
return yaml.Marshal(v.Unstructured())
|
||||
}
|
||||
|
||||
// Equals returns true iff the two values are equal.
|
||||
func Equals(lhs, rhs Value) bool {
|
||||
return EqualsUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// EqualsUsing uses the provided allocator and returns true iff the two values are equal.
|
||||
func EqualsUsing(a Allocator, lhs, rhs Value) bool {
|
||||
if lhs.IsFloat() || rhs.IsFloat() {
|
||||
var lf float64
|
||||
if lhs.IsFloat() {
|
||||
lf = lhs.AsFloat()
|
||||
} else if lhs.IsInt() {
|
||||
lf = float64(lhs.AsInt())
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
var rf float64
|
||||
if rhs.IsFloat() {
|
||||
rf = rhs.AsFloat()
|
||||
} else if rhs.IsInt() {
|
||||
rf = float64(rhs.AsInt())
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return lf == rf
|
||||
}
|
||||
if lhs.IsInt() {
|
||||
if rhs.IsInt() {
|
||||
return lhs.AsInt() == rhs.AsInt()
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsInt() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsString() {
|
||||
if rhs.IsString() {
|
||||
return lhs.AsString() == rhs.AsString()
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsString() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsBool() {
|
||||
if rhs.IsBool() {
|
||||
return lhs.AsBool() == rhs.AsBool()
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsBool() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsList() {
|
||||
if rhs.IsList() {
|
||||
lhsList := lhs.AsListUsing(a)
|
||||
defer a.Free(lhsList)
|
||||
rhsList := rhs.AsListUsing(a)
|
||||
defer a.Free(rhsList)
|
||||
return lhsList.EqualsUsing(a, rhsList)
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsList() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsMap() {
|
||||
if rhs.IsMap() {
|
||||
lhsList := lhs.AsMapUsing(a)
|
||||
defer a.Free(lhsList)
|
||||
rhsList := rhs.AsMapUsing(a)
|
||||
defer a.Free(rhsList)
|
||||
return lhsList.EqualsUsing(a, rhsList)
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsMap() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsNull() {
|
||||
if rhs.IsNull() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsNull() {
|
||||
return false
|
||||
}
|
||||
// No field is set, on either objects.
|
||||
return true
|
||||
}
|
||||
|
||||
// ToString returns a human-readable representation of the value.
|
||||
func ToString(v Value) string {
|
||||
if v.IsNull() {
|
||||
return "null"
|
||||
}
|
||||
switch {
|
||||
case v.IsFloat():
|
||||
return fmt.Sprintf("%v", v.AsFloat())
|
||||
case v.IsInt():
|
||||
return fmt.Sprintf("%v", v.AsInt())
|
||||
case v.IsString():
|
||||
return fmt.Sprintf("%q", v.AsString())
|
||||
case v.IsBool():
|
||||
return fmt.Sprintf("%v", v.AsBool())
|
||||
case v.IsList():
|
||||
strs := []string{}
|
||||
list := v.AsList()
|
||||
for i := 0; i < list.Length(); i++ {
|
||||
strs = append(strs, ToString(list.At(i)))
|
||||
}
|
||||
return "[" + strings.Join(strs, ",") + "]"
|
||||
case v.IsMap():
|
||||
strs := []string{}
|
||||
v.AsMap().Iterate(func(k string, v Value) bool {
|
||||
strs = append(strs, fmt.Sprintf("%v=%v", k, ToString(v)))
|
||||
return true
|
||||
})
|
||||
return strings.Join(strs, "")
|
||||
}
|
||||
// No field is set, on either objects.
|
||||
return "{{undefined}}"
|
||||
}
|
||||
|
||||
// Less provides a total ordering for Value (so that they can be sorted, even
|
||||
// if they are of different types).
|
||||
func Less(lhs, rhs Value) bool {
|
||||
return Compare(lhs, rhs) == -1
|
||||
}
|
||||
|
||||
// Compare provides a total ordering for Value (so that they can be
|
||||
// sorted, even if they are of different types). The result will be 0 if
|
||||
// v==rhs, -1 if v < rhs, and +1 if v > rhs.
|
||||
func Compare(lhs, rhs Value) int {
|
||||
return CompareUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// CompareUsing uses the provided allocator and provides a total
|
||||
// ordering for Value (so that they can be sorted, even if they
|
||||
// are of different types). The result will be 0 if v==rhs, -1
|
||||
// if v < rhs, and +1 if v > rhs.
|
||||
func CompareUsing(a Allocator, lhs, rhs Value) int {
|
||||
if lhs.IsFloat() {
|
||||
if !rhs.IsFloat() {
|
||||
// Extra: compare floats and ints numerically.
|
||||
if rhs.IsInt() {
|
||||
return FloatCompare(lhs.AsFloat(), float64(rhs.AsInt()))
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return FloatCompare(lhs.AsFloat(), rhs.AsFloat())
|
||||
} else if rhs.IsFloat() {
|
||||
// Extra: compare floats and ints numerically.
|
||||
if lhs.IsInt() {
|
||||
return FloatCompare(float64(lhs.AsInt()), rhs.AsFloat())
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
if lhs.IsInt() {
|
||||
if !rhs.IsInt() {
|
||||
return -1
|
||||
}
|
||||
return IntCompare(lhs.AsInt(), rhs.AsInt())
|
||||
} else if rhs.IsInt() {
|
||||
return 1
|
||||
}
|
||||
|
||||
if lhs.IsString() {
|
||||
if !rhs.IsString() {
|
||||
return -1
|
||||
}
|
||||
return strings.Compare(lhs.AsString(), rhs.AsString())
|
||||
} else if rhs.IsString() {
|
||||
return 1
|
||||
}
|
||||
|
||||
if lhs.IsBool() {
|
||||
if !rhs.IsBool() {
|
||||
return -1
|
||||
}
|
||||
return BoolCompare(lhs.AsBool(), rhs.AsBool())
|
||||
} else if rhs.IsBool() {
|
||||
return 1
|
||||
}
|
||||
|
||||
if lhs.IsList() {
|
||||
if !rhs.IsList() {
|
||||
return -1
|
||||
}
|
||||
lhsList := lhs.AsListUsing(a)
|
||||
defer a.Free(lhsList)
|
||||
rhsList := rhs.AsListUsing(a)
|
||||
defer a.Free(rhsList)
|
||||
return ListCompareUsing(a, lhsList, rhsList)
|
||||
} else if rhs.IsList() {
|
||||
return 1
|
||||
}
|
||||
if lhs.IsMap() {
|
||||
if !rhs.IsMap() {
|
||||
return -1
|
||||
}
|
||||
lhsMap := lhs.AsMapUsing(a)
|
||||
defer a.Free(lhsMap)
|
||||
rhsMap := rhs.AsMapUsing(a)
|
||||
defer a.Free(rhsMap)
|
||||
return MapCompareUsing(a, lhsMap, rhsMap)
|
||||
} else if rhs.IsMap() {
|
||||
return 1
|
||||
}
|
||||
if lhs.IsNull() {
|
||||
if !rhs.IsNull() {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
} else if rhs.IsNull() {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Invalid Value-- nothing is set.
|
||||
return 0
|
||||
}
|
294
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/valuereflect.go
generated
vendored
Normal file
294
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/valuereflect.go
generated
vendored
Normal file
@ -0,0 +1,294 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// NewValueReflect creates a Value backed by an "interface{}" type,
|
||||
// typically an structured object in Kubernetes world that is uses reflection to expose.
|
||||
// The provided "interface{}" value must be a pointer so that the value can be modified via reflection.
|
||||
// The provided "interface{}" may contain structs and types that are converted to Values
|
||||
// by the jsonMarshaler interface.
|
||||
func NewValueReflect(value interface{}) (Value, error) {
|
||||
if value == nil {
|
||||
return NewValueInterface(nil), nil
|
||||
}
|
||||
v := reflect.ValueOf(value)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
// The root value to reflect on must be a pointer so that map.Set() and map.Delete() operations are possible.
|
||||
return nil, fmt.Errorf("value provided to NewValueReflect must be a pointer")
|
||||
}
|
||||
return wrapValueReflect(v, nil, nil)
|
||||
}
|
||||
|
||||
// wrapValueReflect wraps the provide reflect.Value as a value. If parent in the data tree is a map, parentMap
|
||||
// and parentMapKey must be provided so that the returned value may be set and deleted.
|
||||
func wrapValueReflect(value reflect.Value, parentMap, parentMapKey *reflect.Value) (Value, error) {
|
||||
val := HeapAllocator.allocValueReflect()
|
||||
return val.reuse(value, nil, parentMap, parentMapKey)
|
||||
}
|
||||
|
||||
// wrapValueReflect wraps the provide reflect.Value as a value, and panics if there is an error. If parent in the data
|
||||
// tree is a map, parentMap and parentMapKey must be provided so that the returned value may be set and deleted.
|
||||
func mustWrapValueReflect(value reflect.Value, parentMap, parentMapKey *reflect.Value) Value {
|
||||
v, err := wrapValueReflect(value, parentMap, parentMapKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// the value interface doesn't care about the type for value.IsNull, so we can use a constant
|
||||
var nilType = reflect.TypeOf(&struct{}{})
|
||||
|
||||
// reuse replaces the value of the valueReflect. If parent in the data tree is a map, parentMap and parentMapKey
|
||||
// must be provided so that the returned value may be set and deleted.
|
||||
func (r *valueReflect) reuse(value reflect.Value, cacheEntry *TypeReflectCacheEntry, parentMap, parentMapKey *reflect.Value) (Value, error) {
|
||||
if cacheEntry == nil {
|
||||
cacheEntry = TypeReflectEntryOf(value.Type())
|
||||
}
|
||||
if cacheEntry.CanConvertToUnstructured() {
|
||||
u, err := cacheEntry.ToUnstructured(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u == nil {
|
||||
value = reflect.Zero(nilType)
|
||||
} else {
|
||||
value = reflect.ValueOf(u)
|
||||
}
|
||||
}
|
||||
r.Value = dereference(value)
|
||||
r.ParentMap = parentMap
|
||||
r.ParentMapKey = parentMapKey
|
||||
r.kind = kind(r.Value)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// mustReuse replaces the value of the valueReflect and panics if there is an error. If parent in the data tree is a
|
||||
// map, parentMap and parentMapKey must be provided so that the returned value may be set and deleted.
|
||||
func (r *valueReflect) mustReuse(value reflect.Value, cacheEntry *TypeReflectCacheEntry, parentMap, parentMapKey *reflect.Value) Value {
|
||||
v, err := r.reuse(value, cacheEntry, parentMap, parentMapKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func dereference(val reflect.Value) reflect.Value {
|
||||
kind := val.Kind()
|
||||
if (kind == reflect.Interface || kind == reflect.Ptr) && !safeIsNil(val) {
|
||||
return val.Elem()
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
type valueReflect struct {
|
||||
ParentMap *reflect.Value
|
||||
ParentMapKey *reflect.Value
|
||||
Value reflect.Value
|
||||
kind reflectType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsMap() bool {
|
||||
return r.kind == mapType || r.kind == structMapType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsList() bool {
|
||||
return r.kind == listType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsBool() bool {
|
||||
return r.kind == boolType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsInt() bool {
|
||||
return r.kind == intType || r.kind == uintType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsFloat() bool {
|
||||
return r.kind == floatType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsString() bool {
|
||||
return r.kind == stringType || r.kind == byteStringType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsNull() bool {
|
||||
return r.kind == nullType
|
||||
}
|
||||
|
||||
type reflectType = int
|
||||
|
||||
const (
|
||||
mapType = iota
|
||||
structMapType
|
||||
listType
|
||||
intType
|
||||
uintType
|
||||
floatType
|
||||
stringType
|
||||
byteStringType
|
||||
boolType
|
||||
nullType
|
||||
)
|
||||
|
||||
func kind(v reflect.Value) reflectType {
|
||||
typ := v.Type()
|
||||
rk := typ.Kind()
|
||||
switch rk {
|
||||
case reflect.Map:
|
||||
if v.IsNil() {
|
||||
return nullType
|
||||
}
|
||||
return mapType
|
||||
case reflect.Struct:
|
||||
return structMapType
|
||||
case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
|
||||
return intType
|
||||
case reflect.Uint, reflect.Uint32, reflect.Uint16, reflect.Uint8:
|
||||
// Uint64 deliberately excluded, see valueUnstructured.Int.
|
||||
return uintType
|
||||
case reflect.Float64, reflect.Float32:
|
||||
return floatType
|
||||
case reflect.String:
|
||||
return stringType
|
||||
case reflect.Bool:
|
||||
return boolType
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
return nullType
|
||||
}
|
||||
elemKind := typ.Elem().Kind()
|
||||
if elemKind == reflect.Uint8 {
|
||||
return byteStringType
|
||||
}
|
||||
return listType
|
||||
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.UnsafePointer, reflect.Interface:
|
||||
if v.IsNil() {
|
||||
return nullType
|
||||
}
|
||||
panic(fmt.Sprintf("unsupported type: %v", v.Type()))
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported type: %v", v.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO find a cleaner way to avoid panics from reflect.IsNil()
|
||||
func safeIsNil(v reflect.Value) bool {
|
||||
k := v.Kind()
|
||||
switch k {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r valueReflect) AsMap() Map {
|
||||
return r.AsMapUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (r valueReflect) AsMapUsing(a Allocator) Map {
|
||||
switch r.kind {
|
||||
case structMapType:
|
||||
v := a.allocStructReflect()
|
||||
v.valueReflect = r
|
||||
return v
|
||||
case mapType:
|
||||
v := a.allocMapReflect()
|
||||
v.valueReflect = r
|
||||
return v
|
||||
default:
|
||||
panic("value is not a map or struct")
|
||||
}
|
||||
}
|
||||
|
||||
func (r valueReflect) AsList() List {
|
||||
return r.AsListUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (r valueReflect) AsListUsing(a Allocator) List {
|
||||
if r.IsList() {
|
||||
v := a.allocListReflect()
|
||||
v.Value = r.Value
|
||||
return v
|
||||
}
|
||||
panic("value is not a list")
|
||||
}
|
||||
|
||||
func (r valueReflect) AsBool() bool {
|
||||
if r.IsBool() {
|
||||
return r.Value.Bool()
|
||||
}
|
||||
panic("value is not a bool")
|
||||
}
|
||||
|
||||
func (r valueReflect) AsInt() int64 {
|
||||
if r.kind == intType {
|
||||
return r.Value.Int()
|
||||
}
|
||||
if r.kind == uintType {
|
||||
return int64(r.Value.Uint())
|
||||
}
|
||||
|
||||
panic("value is not an int")
|
||||
}
|
||||
|
||||
func (r valueReflect) AsFloat() float64 {
|
||||
if r.IsFloat() {
|
||||
return r.Value.Float()
|
||||
}
|
||||
panic("value is not a float")
|
||||
}
|
||||
|
||||
func (r valueReflect) AsString() string {
|
||||
switch r.kind {
|
||||
case stringType:
|
||||
return r.Value.String()
|
||||
case byteStringType:
|
||||
return base64.StdEncoding.EncodeToString(r.Value.Bytes())
|
||||
}
|
||||
panic("value is not a string")
|
||||
}
|
||||
|
||||
func (r valueReflect) Unstructured() interface{} {
|
||||
val := r.Value
|
||||
switch {
|
||||
case r.IsNull():
|
||||
return nil
|
||||
case val.Kind() == reflect.Struct:
|
||||
return structReflect{r}.Unstructured()
|
||||
case val.Kind() == reflect.Map:
|
||||
return mapReflect{valueReflect: r}.Unstructured()
|
||||
case r.IsList():
|
||||
return listReflect{r.Value}.Unstructured()
|
||||
case r.IsString():
|
||||
return r.AsString()
|
||||
case r.IsInt():
|
||||
return r.AsInt()
|
||||
case r.IsBool():
|
||||
return r.AsBool()
|
||||
case r.IsFloat():
|
||||
return r.AsFloat()
|
||||
default:
|
||||
panic(fmt.Sprintf("value of type %s is not a supported by value reflector", val.Type()))
|
||||
}
|
||||
}
|
178
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/valueunstructured.go
generated
vendored
Normal file
178
e2e/vendor/sigs.k8s.io/structured-merge-diff/v4/value/valueunstructured.go
generated
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NewValueInterface creates a Value backed by an "interface{}" type,
|
||||
// typically an unstructured object in Kubernetes world.
|
||||
// interface{} must be one of: map[string]interface{}, map[interface{}]interface{}, []interface{}, int types, float types,
|
||||
// string or boolean. Nested interface{} must also be one of these types.
|
||||
func NewValueInterface(v interface{}) Value {
|
||||
return Value(HeapAllocator.allocValueUnstructured().reuse(v))
|
||||
}
|
||||
|
||||
type valueUnstructured struct {
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// reuse replaces the value of the valueUnstructured.
|
||||
func (vi *valueUnstructured) reuse(value interface{}) Value {
|
||||
vi.Value = value
|
||||
return vi
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsMap() bool {
|
||||
if _, ok := v.Value.(map[string]interface{}); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := v.Value.(map[interface{}]interface{}); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsMap() Map {
|
||||
return v.AsMapUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsMapUsing(_ Allocator) Map {
|
||||
if v.Value == nil {
|
||||
panic("invalid nil")
|
||||
}
|
||||
switch t := v.Value.(type) {
|
||||
case map[string]interface{}:
|
||||
return mapUnstructuredString(t)
|
||||
case map[interface{}]interface{}:
|
||||
return mapUnstructuredInterface(t)
|
||||
}
|
||||
panic(fmt.Errorf("not a map: %#v", v))
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsList() bool {
|
||||
if v.Value == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := v.Value.([]interface{})
|
||||
return ok
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsList() List {
|
||||
return v.AsListUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsListUsing(_ Allocator) List {
|
||||
return listUnstructured(v.Value.([]interface{}))
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsFloat() bool {
|
||||
if v.Value == nil {
|
||||
return false
|
||||
} else if _, ok := v.Value.(float64); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(float32); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsFloat() float64 {
|
||||
if f, ok := v.Value.(float32); ok {
|
||||
return float64(f)
|
||||
}
|
||||
return v.Value.(float64)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsInt() bool {
|
||||
if v.Value == nil {
|
||||
return false
|
||||
} else if _, ok := v.Value.(int); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(int8); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(int16); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(int32); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(int64); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(uint); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(uint8); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(uint16); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(uint32); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsInt() int64 {
|
||||
if i, ok := v.Value.(int); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(int8); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(int16); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(int32); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(uint); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(uint8); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(uint16); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(uint32); ok {
|
||||
return int64(i)
|
||||
}
|
||||
return v.Value.(int64)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsString() bool {
|
||||
if v.Value == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := v.Value.(string)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsString() string {
|
||||
return v.Value.(string)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsBool() bool {
|
||||
if v.Value == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := v.Value.(bool)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsBool() bool {
|
||||
return v.Value.(bool)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsNull() bool {
|
||||
return v.Value == nil
|
||||
}
|
||||
|
||||
func (v valueUnstructured) Unstructured() interface{} {
|
||||
return v.Value
|
||||
}
|
Reference in New Issue
Block a user