mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-10-23 16:08:16 +00:00
Update Structured Merge Diff to V3
This commit is contained in:
committed by
Jennifer Buckley
parent
c9b4cf3d25
commit
b33fbc84d9
201
vendor/sigs.k8s.io/structured-merge-diff/v3/LICENSE
generated
vendored
Normal file
201
vendor/sigs.k8s.io/structured-merge-diff/v3/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.
|
37
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/BUILD
generated
vendored
Normal file
37
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/BUILD
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"element.go",
|
||||
"fromvalue.go",
|
||||
"managers.go",
|
||||
"path.go",
|
||||
"pathelementmap.go",
|
||||
"serialize.go",
|
||||
"serialize-pe.go",
|
||||
"set.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath",
|
||||
importpath = "sigs.k8s.io/structured-merge-diff/v3/fieldpath",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/json-iterator/go:go_default_library",
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/v3/value:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
21
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/doc.go
generated
vendored
Normal file
21
vendor/sigs.k8s.io/structured-merge-diff/v3/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
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/element.go
generated
vendored
Normal file
317
vendor/sigs.k8s.io/structured-merge-diff/v3/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/v3/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)
|
||||
}
|
||||
}
|
124
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/fromvalue.go
generated
vendored
Normal file
124
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/fromvalue.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
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/v3/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,
|
||||
do: func(p Path) { s.Insert(p) },
|
||||
}
|
||||
|
||||
w.walk()
|
||||
return s
|
||||
}
|
||||
|
||||
type objectWalker struct {
|
||||
path Path
|
||||
value value.Value
|
||||
|
||||
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.
|
||||
list := w.value.AsList()
|
||||
for i := 0; i < list.Length(); i++ {
|
||||
w2 := *w
|
||||
w2.path = append(w.path, GuessBestListPathElement(i, list.At(i)))
|
||||
w2.value = list.At(i)
|
||||
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.
|
||||
|
||||
w.value.AsMap().Iterate(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 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}
|
||||
}
|
||||
|
||||
var keys value.FieldList
|
||||
for _, name := range AssociativeListCandidateFieldNames {
|
||||
f, ok := item.AsMap().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}
|
||||
}
|
113
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/managers.go
generated
vendored
Normal file
113
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/managers.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
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
|
||||
|
||||
// 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
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/path.go
generated
vendored
Normal file
118
vendor/sigs.k8s.io/structured-merge-diff/v3/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/v3/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
|
||||
}
|
85
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/pathelementmap.go
generated
vendored
Normal file
85
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/pathelementmap.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
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/v3/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 {
|
||||
members sortedPathElementValues
|
||||
}
|
||||
|
||||
func MakePathElementValueMap(size int) PathElementValueMap {
|
||||
return PathElementValueMap{
|
||||
members: make(sortedPathElementValues, 0, size),
|
||||
}
|
||||
}
|
||||
|
||||
type pathElementValue struct {
|
||||
PathElement PathElement
|
||||
Value value.Value
|
||||
}
|
||||
|
||||
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.
|
||||
func (s *PathElementValueMap) Insert(pe PathElement, v value.Value) {
|
||||
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) {
|
||||
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 *PathElementValueMap) Get(pe PathElement) (value.Value, 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
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/serialize-pe.go
generated
vendored
Normal file
168
vendor/sigs.k8s.io/structured-merge-diff/v3/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/v3/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
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/serialize.go
generated
vendored
Normal file
238
vendor/sigs.k8s.io/structured-merge-diff/v3/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.emitContents_v1(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) emitContents_v1(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 mpe.Less(cpe) {
|
||||
preWrite()
|
||||
if err := serializePathElementToWriter(r.reset(), mpe); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectField(r.unsafeString())
|
||||
stream.WriteEmptyObject()
|
||||
mi++
|
||||
} else if cpe.Less(mpe) {
|
||||
preWrite()
|
||||
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
|
||||
return err
|
||||
}
|
||||
stream.WriteObjectField(r.unsafeString())
|
||||
stream.WriteObjectStart()
|
||||
if err := s.Children.members[ci].set.emitContents_v1(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.emitContents_v1(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.emitContents_v1(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, _ := readIter_v1(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 readIter_v1(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 := readIter_v1(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
|
||||
}
|
348
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/set.go
generated
vendored
Normal file
348
vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath/set.go
generated
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
30
vendor/sigs.k8s.io/structured-merge-diff/v3/merge/BUILD
generated
vendored
Normal file
30
vendor/sigs.k8s.io/structured-merge-diff/v3/merge/BUILD
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"conflict.go",
|
||||
"update.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/v3/merge",
|
||||
importpath = "sigs.k8s.io/structured-merge-diff/v3/merge",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath:go_default_library",
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/v3/typed:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
112
vendor/sigs.k8s.io/structured-merge-diff/v3/merge/conflict.go
generated
vendored
Normal file
112
vendor/sigs.k8s.io/structured-merge-diff/v3/merge/conflict.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
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/v3/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
|
||||
}
|
||||
|
||||
// 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,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return conflicts
|
||||
}
|
285
vendor/sigs.k8s.io/structured-merge-diff/v3/merge/update.go
generated
vendored
Normal file
285
vendor/sigs.k8s.io/structured-merge-diff/v3/merge/update.go
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
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/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/typed"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Updater is the object used to compute updated FieldSets and also
|
||||
// merge the object on Apply.
|
||||
type Updater struct {
|
||||
Converter Converter
|
||||
|
||||
enableUnions bool
|
||||
}
|
||||
|
||||
// EnableUnionFeature turns on union handling. It is disabled by default until the
|
||||
// feature is complete.
|
||||
func (s *Updater) EnableUnionFeature() {
|
||||
s.enableUnions = true
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
versions := map[fieldpath.APIVersion]*typed.Comparison{
|
||||
version: compare,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
versions[managerSet.APIVersion()] = compare
|
||||
}
|
||||
|
||||
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
|
||||
if s.enableUnions {
|
||||
newObject, err = liveObject.NormalizeUnions(newObject)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
}
|
||||
managers = shallowCopyManagers(managers)
|
||||
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)
|
||||
}
|
||||
managers[manager] = fieldpath.NewVersionedSet(
|
||||
managers[manager].Set().Union(compare.Modified).Union(compare.Added).Difference(compare.Removed),
|
||||
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) {
|
||||
managers = shallowCopyManagers(managers)
|
||||
var err error
|
||||
if s.enableUnions {
|
||||
configObject, err = configObject.NormalizeUnionsApply(configObject)
|
||||
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)
|
||||
}
|
||||
if s.enableUnions {
|
||||
newObject, err = configObject.NormalizeUnionsApply(newObject)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
}
|
||||
lastSet := managers[manager]
|
||||
set, err := configObject.ToFieldSet()
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to get field set: %v", err)
|
||||
}
|
||||
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
|
||||
}
|
||||
return newObject, managers, nil
|
||||
}
|
||||
|
||||
func shallowCopyManagers(managers fieldpath.ManagedFields) fieldpath.ManagedFields {
|
||||
newManagers := fieldpath.ManagedFields{}
|
||||
for manager, set := range managers {
|
||||
newManagers[manager] = set
|
||||
}
|
||||
return newManagers
|
||||
}
|
||||
|
||||
// prune will remove a list or map item, iff:
|
||||
// * applyingManager applied it last time
|
||||
// * applyingManager didn't apply it this time
|
||||
// * no other applier claims to manage it
|
||||
func (s *Updater) prune(merged *typed.TypedValue, managers fieldpath.ManagedFields, applyingManager string, lastSet fieldpath.VersionedSet) (*typed.TypedValue, error) {
|
||||
if lastSet == nil || lastSet.Set().Empty() {
|
||||
return merged, nil
|
||||
}
|
||||
convertedMerged, err := s.Converter.Convert(merged, lastSet.APIVersion())
|
||||
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)
|
||||
}
|
||||
|
||||
pruned := convertedMerged.RemoveItems(lastSet.Set())
|
||||
pruned, err = s.addBackOwnedItems(convertedMerged, pruned, 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 list and map items that were removed by prune,
|
||||
// but other appliers (or the current applier's new config) claim to own.
|
||||
func (s *Updater) addBackOwnedItems(merged, pruned *typed.TypedValue, managedFields fieldpath.ManagedFields, applyingManager string) (*typed.TypedValue, error) {
|
||||
var err error
|
||||
managedAtVersion := map[fieldpath.APIVersion]*fieldpath.Set{}
|
||||
for _, managerSet := range managedFields {
|
||||
if managerSet.Applied() {
|
||||
if _, ok := managedAtVersion[managerSet.APIVersion()]; !ok {
|
||||
managedAtVersion[managerSet.APIVersion()] = fieldpath.NewSet()
|
||||
}
|
||||
managedAtVersion[managerSet.APIVersion()] = managedAtVersion[managerSet.APIVersion()].Union(managerSet.Set())
|
||||
}
|
||||
}
|
||||
for version, managed := range managedAtVersion {
|
||||
merged, err = s.Converter.Convert(merged, version)
|
||||
if err != nil {
|
||||
if s.Converter.IsMissingVersionError(err) {
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("failed to convert merged object at version %v: %v", version, err)
|
||||
}
|
||||
pruned, err = s.Converter.Convert(pruned, version)
|
||||
if err != nil {
|
||||
if s.Converter.IsMissingVersionError(err) {
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("failed to convert pruned object at version %v: %v", version, err)
|
||||
}
|
||||
mergedSet, err := merged.ToFieldSet()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create field set from merged object at version %v: %v", version, err)
|
||||
}
|
||||
prunedSet, err := pruned.ToFieldSet()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create field set from pruned object at version %v: %v", version, err)
|
||||
}
|
||||
pruned = merged.RemoveItems(mergedSet.Difference(prunedSet.Union(managed)))
|
||||
}
|
||||
return pruned, nil
|
||||
}
|
||||
|
||||
// addBackDanglingItems makes sure that the only items removed by prune are items that were
|
||||
// previously owned by the currently applying manager. This will add back unowned items and items
|
||||
// which are owned by Updaters that shouldn't be removed.
|
||||
func (s *Updater) addBackDanglingItems(merged, pruned *typed.TypedValue, lastSet fieldpath.VersionedSet) (*typed.TypedValue, error) {
|
||||
convertedPruned, err := s.Converter.Convert(pruned, lastSet.APIVersion())
|
||||
if err != nil {
|
||||
if s.Converter.IsMissingVersionError(err) {
|
||||
return merged, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to convert pruned object to last applied version: %v", err)
|
||||
}
|
||||
prunedSet, err := convertedPruned.ToFieldSet()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create field set from pruned object in last applied version: %v", err)
|
||||
}
|
||||
mergedSet, err := merged.ToFieldSet()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create field set from merged object in last applied version: %v", err)
|
||||
}
|
||||
return merged.RemoveItems(mergedSet.Difference(prunedSet).Intersection(lastSet.Set())), nil
|
||||
}
|
28
vendor/sigs.k8s.io/structured-merge-diff/v3/schema/BUILD
generated
vendored
Normal file
28
vendor/sigs.k8s.io/structured-merge-diff/v3/schema/BUILD
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"elements.go",
|
||||
"equals.go",
|
||||
"schemaschema.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/v3/schema",
|
||||
importpath = "sigs.k8s.io/structured-merge-diff/v3/schema",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
28
vendor/sigs.k8s.io/structured-merge-diff/v3/schema/doc.go
generated
vendored
Normal file
28
vendor/sigs.k8s.io/structured-merge-diff/v3/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
|
259
vendor/sigs.k8s.io/structured-merge-diff/v3/schema/elements.go
generated
vendored
Normal file
259
vendor/sigs.k8s.io/structured-merge-diff/v3/schema/elements.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 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
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// TODO: split numeric into float/int? Something even more fine-grained?
|
||||
type Scalar string
|
||||
|
||||
const (
|
||||
Numeric = Scalar("numeric")
|
||||
String = Scalar("string")
|
||||
Boolean = Scalar("boolean")
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Resolve is a convenience function which returns the atom referenced, whether
|
||||
// it is inline or named. Returns (Atom{}, false) if the type can't be resolved.
|
||||
//
|
||||
// This allows callers to not care about the difference between a (possibly
|
||||
// inlined) reference and a definition.
|
||||
func (s *Schema) Resolve(tr TypeRef) (Atom, bool) {
|
||||
if tr.NamedType != nil {
|
||||
t, ok := s.FindNamedType(*tr.NamedType)
|
||||
if !ok {
|
||||
return Atom{}, false
|
||||
}
|
||||
return t.Atom, true
|
||||
}
|
||||
return tr.Inlined, true
|
||||
}
|
194
vendor/sigs.k8s.io/structured-merge-diff/v3/schema/equals.go
generated
vendored
Normal file
194
vendor/sigs.k8s.io/structured-merge-diff/v3/schema/equals.go
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
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
|
||||
|
||||
// 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
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
148
vendor/sigs.k8s.io/structured-merge-diff/v3/schema/schemaschema.go
generated
vendored
Normal file
148
vendor/sigs.k8s.io/structured-merge-diff/v3/schema/schemaschema.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
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: 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: bool
|
||||
- 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: list
|
||||
map:
|
||||
fields:
|
||||
- name: elementType
|
||||
type:
|
||||
namedType: typeRef
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
- name: keys
|
||||
type:
|
||||
list:
|
||||
elementType:
|
||||
scalar: string
|
||||
- name: untyped
|
||||
map:
|
||||
fields:
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
`
|
39
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/BUILD
generated
vendored
Normal file
39
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/BUILD
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"helpers.go",
|
||||
"merge.go",
|
||||
"parser.go",
|
||||
"remove.go",
|
||||
"tofieldset.go",
|
||||
"typed.go",
|
||||
"union.go",
|
||||
"validate.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/v3/typed",
|
||||
importpath = "sigs.k8s.io/structured-merge-diff/v3/typed",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/v3/fieldpath:go_default_library",
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/v3/schema:go_default_library",
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/v3/value:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
18
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/doc.go
generated
vendored
Normal file
18
vendor/sigs.k8s.io/structured-merge-diff/v3/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
|
238
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/helpers.go
generated
vendored
Normal file
238
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
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/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/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 {
|
||||
return errorf("schema error: no type found matching: %v", *tr.NamedType)
|
||||
}
|
||||
|
||||
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(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.AsList(), nil
|
||||
}
|
||||
|
||||
// Returns the map, or an error. Reminder: nil is a valid map and might be returned.
|
||||
func mapValue(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.AsMap(), nil
|
||||
}
|
||||
|
||||
func keyedAssociativeListItemToPathElement(list *schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
|
||||
pe := fieldpath.PathElement{}
|
||||
if child.IsNull() {
|
||||
// For now, the keys are required which means that 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{}
|
||||
for _, fieldName := range list.Keys {
|
||||
if val, ok := child.AsMap().Get(fieldName); ok {
|
||||
keyMap = append(keyMap, value.Field{Name: fieldName, Value: val})
|
||||
} else {
|
||||
return pe, fmt.Errorf("associative list with keys has an element that omits key field %q", fieldName)
|
||||
}
|
||||
}
|
||||
keyMap.Sort()
|
||||
pe.Key = &keyMap
|
||||
return pe, nil
|
||||
}
|
||||
|
||||
func setItemToPathElement(list *schema.List, index int, 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(list *schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
|
||||
if list.ElementRelationship == schema.Associative {
|
||||
if len(list.Keys) > 0 {
|
||||
return keyedAssociativeListItemToPathElement(list, index, child)
|
||||
}
|
||||
|
||||
// If there's no keys, then we must be a set of primitives.
|
||||
return setItemToPathElement(list, index, child)
|
||||
}
|
||||
|
||||
// Use the index as a key for atomic lists.
|
||||
return fieldpath.PathElement{Index: &index}, nil
|
||||
}
|
369
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/merge.go
generated
vendored
Normal file
369
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/merge.go
generated
vendored
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
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 (
|
||||
"math"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/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
|
||||
}
|
||||
|
||||
// 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)
|
||||
if 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) (errs ValidationErrors) {
|
||||
errs = append(errs, validateScalar(t, w.lhs, "lhs: ")...)
|
||||
errs = append(errs, validateScalar(t, w.rhs, "rhs: ")...)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
// 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, dest *value.Map) (errs ValidationErrors) {
|
||||
// taking dest as input so that it can be called as a one-liner with
|
||||
// append.
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
m, err := mapValue(v)
|
||||
if err != nil {
|
||||
return errorf("%v: %v", prefix, err)
|
||||
}
|
||||
*dest = m
|
||||
return 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()
|
||||
}
|
||||
out := make([]interface{}, 0, int(math.Max(float64(rLen), float64(lLen))))
|
||||
|
||||
// TODO: ordering is totally wrong.
|
||||
// TODO: might as well make the map order work the same way.
|
||||
|
||||
// This is a cheap hack to at least make the output order stable.
|
||||
rhsOrder := make([]fieldpath.PathElement, 0, rLen)
|
||||
|
||||
// First, collect all RHS children.
|
||||
observedRHS := fieldpath.MakePathElementValueMap(rLen)
|
||||
if rhs != nil {
|
||||
for i := 0; i < rhs.Length(); i++ {
|
||||
child := rhs.At(i)
|
||||
pe, err := listItemToPathElement(t, i, child)
|
||||
if err != nil {
|
||||
errs = append(errs, errorf("rhs: element %v: %v", i, err.Error())...)
|
||||
// If we can't construct the path element, we can't
|
||||
// even report errors deeper in the schema, so bail on
|
||||
// this element.
|
||||
continue
|
||||
}
|
||||
if _, ok := observedRHS.Get(pe); ok {
|
||||
errs = append(errs, errorf("rhs: duplicate entries for key %v", pe.String())...)
|
||||
}
|
||||
observedRHS.Insert(pe, child)
|
||||
rhsOrder = append(rhsOrder, pe)
|
||||
}
|
||||
}
|
||||
|
||||
// Then merge with LHS children.
|
||||
observedLHS := fieldpath.MakePathElementSet(lLen)
|
||||
if lhs != nil {
|
||||
for i := 0; i < lhs.Length(); i++ {
|
||||
child := lhs.At(i)
|
||||
pe, err := listItemToPathElement(t, i, child)
|
||||
if err != nil {
|
||||
errs = append(errs, errorf("lhs: element %v: %v", i, err.Error())...)
|
||||
// If we can't construct the path element, we can't
|
||||
// even report errors deeper in the schema, so bail on
|
||||
// this element.
|
||||
continue
|
||||
}
|
||||
if observedLHS.Has(pe) {
|
||||
errs = append(errs, errorf("lhs: duplicate entries for key %v", pe.String())...)
|
||||
continue
|
||||
}
|
||||
observedLHS.Insert(pe)
|
||||
w2 := w.prepareDescent(pe, t.ElementType)
|
||||
w2.lhs = value.Value(child)
|
||||
if rchild, ok := observedRHS.Get(pe); ok {
|
||||
w2.rhs = rchild
|
||||
}
|
||||
errs = append(errs, w2.merge(pe.String)...)
|
||||
if w2.out != nil {
|
||||
out = append(out, *w2.out)
|
||||
}
|
||||
w.finishDescent(w2)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pe := range rhsOrder {
|
||||
if observedLHS.Has(pe) {
|
||||
continue
|
||||
}
|
||||
value, _ := observedRHS.Get(pe)
|
||||
w2 := w.prepareDescent(pe, t.ElementType)
|
||||
w2.rhs = value
|
||||
errs = append(errs, w2.merge(pe.String)...)
|
||||
if w2.out != nil {
|
||||
out = append(out, *w2.out)
|
||||
}
|
||||
w.finishDescent(w2)
|
||||
}
|
||||
|
||||
if len(out) > 0 {
|
||||
i := interface{}(out)
|
||||
w.out = &i
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) derefList(prefix string, v value.Value, dest *value.List) (errs ValidationErrors) {
|
||||
// taking dest as input so that it can be called as a one-liner with
|
||||
// append.
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
l, err := listValue(v)
|
||||
if err != nil {
|
||||
return errorf("%v: %v", prefix, err)
|
||||
}
|
||||
*dest = l
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doList(t *schema.List) (errs ValidationErrors) {
|
||||
var lhs, rhs value.List
|
||||
w.derefList("lhs: ", w.lhs, &lhs)
|
||||
w.derefList("rhs: ", w.rhs, &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{}{}
|
||||
|
||||
if lhs != nil {
|
||||
lhs.Iterate(func(key string, val value.Value) bool {
|
||||
var rval value.Value
|
||||
if rhs != nil {
|
||||
if item, ok := rhs.Get(key); ok {
|
||||
rval = item
|
||||
defer rval.Recycle()
|
||||
}
|
||||
}
|
||||
errs = append(errs, w.visitMapItem(t, out, key, val, rval)...)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
if rhs != nil {
|
||||
rhs.Iterate(func(key string, val value.Value) bool {
|
||||
if lhs != nil {
|
||||
if lhs.Has(key) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
errs = append(errs, w.visitMapItem(t, out, key, nil, val)...)
|
||||
return true
|
||||
})
|
||||
}
|
||||
if len(out) > 0 {
|
||||
i := interface{}(out)
|
||||
w.out = &i
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doMap(t *schema.Map) (errs ValidationErrors) {
|
||||
var lhs, rhs value.Map
|
||||
w.derefMap("lhs: ", w.lhs, &lhs)
|
||||
w.derefMap("rhs: ", w.rhs, &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 = append(errs, w.visitMapItems(t, lhs, rhs)...)
|
||||
|
||||
return errs
|
||||
}
|
151
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/parser.go
generated
vendored
Normal file
151
vendor/sigs.k8s.io/structured-merge-diff/v3/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"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/value"
|
||||
)
|
||||
|
||||
// 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) (*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)
|
||||
}
|
||||
|
||||
// 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{}) (*TypedValue, error) {
|
||||
return AsTyped(value.NewValueInterface(in), p.Schema, p.TypeRef)
|
||||
}
|
||||
|
||||
// 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{}) (*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)
|
||||
}
|
||||
|
||||
// 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_")
|
107
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/remove.go
generated
vendored
Normal file
107
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/remove.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
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/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/value"
|
||||
)
|
||||
|
||||
type removingWalker struct {
|
||||
value value.Value
|
||||
out interface{}
|
||||
schema *schema.Schema
|
||||
toRemove *fieldpath.Set
|
||||
}
|
||||
|
||||
func removeItemsWithSchema(val value.Value, toRemove *fieldpath.Set, schema *schema.Schema, typeRef schema.TypeRef) value.Value {
|
||||
w := &removingWalker{
|
||||
value: val,
|
||||
schema: schema,
|
||||
toRemove: toRemove,
|
||||
}
|
||||
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) {
|
||||
l := w.value.AsList()
|
||||
|
||||
// If list is null, empty, or atomic just return
|
||||
if l == nil || l.Length() == 0 || t.ElementRelationship == schema.Atomic {
|
||||
return nil
|
||||
}
|
||||
|
||||
var newItems []interface{}
|
||||
for i := 0; i < l.Length(); i++ {
|
||||
item := l.At(i)
|
||||
// Ignore error because we have already validated this list
|
||||
pe, _ := listItemToPathElement(t, i, item)
|
||||
path, _ := fieldpath.MakePath(pe)
|
||||
if w.toRemove.Has(path) {
|
||||
continue
|
||||
}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
item = removeItemsWithSchema(item, subset, w.schema, t.ElementType)
|
||||
}
|
||||
newItems = append(newItems, item.Unstructured())
|
||||
}
|
||||
if len(newItems) > 0 {
|
||||
w.out = newItems
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *removingWalker) doMap(t *schema.Map) ValidationErrors {
|
||||
m := w.value.AsMap()
|
||||
|
||||
// If map is null, empty, or atomic just return
|
||||
if m == nil || m.Length() == 0 || t.ElementRelationship == schema.Atomic {
|
||||
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
|
||||
} else {
|
||||
if w.toRemove.Has(path) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
val = removeItemsWithSchema(val, subset, w.schema, fieldType)
|
||||
}
|
||||
newMap[k] = val.Unstructured()
|
||||
return true
|
||||
})
|
||||
if len(newMap) > 0 {
|
||||
w.out = newMap
|
||||
}
|
||||
return nil
|
||||
}
|
160
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/tofieldset.go
generated
vendored
Normal file
160
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/tofieldset.go
generated
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
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/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/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{}
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
for i := 0; i < list.Length(); i++ {
|
||||
child := list.At(i)
|
||||
pe, _ := listItemToPathElement(t, i, child)
|
||||
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.value)
|
||||
|
||||
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 _, 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.value)
|
||||
|
||||
if t.ElementRelationship == schema.Atomic {
|
||||
v.set.Insert(v.path)
|
||||
return nil
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = v.visitMapItems(t, m)
|
||||
|
||||
return errs
|
||||
}
|
290
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/typed.go
generated
vendored
Normal file
290
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/typed.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"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/value"
|
||||
)
|
||||
|
||||
// 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) (*TypedValue, error) {
|
||||
tv := &TypedValue{
|
||||
value: v,
|
||||
typeRef: typeRef,
|
||||
schema: s,
|
||||
}
|
||||
if err := tv.Validate(); 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).
|
||||
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
|
||||
}
|
||||
|
||||
// AsValue removes the type from the TypedValue and only keeps the value.
|
||||
func (tv TypedValue) AsValue() value.Value {
|
||||
return tv.value
|
||||
}
|
||||
|
||||
// Validate returns an error with a list of every spec violation.
|
||||
func (tv TypedValue) Validate() error {
|
||||
w := tv.walker()
|
||||
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:
|
||||
// * like tv, if pso doesn't change anything in the container
|
||||
// * 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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
c = &Comparison{
|
||||
Removed: fieldpath.NewSet(),
|
||||
Modified: fieldpath.NewSet(),
|
||||
Added: fieldpath.NewSet(),
|
||||
}
|
||||
_, err = merge(&tv, rhs, func(w *mergingWalker) {
|
||||
if w.lhs == nil {
|
||||
c.Added.Insert(w.path)
|
||||
} else if w.rhs == nil {
|
||||
c.Removed.Insert(w.path)
|
||||
} else if !value.Equals(w.rhs, w.lhs) {
|
||||
// TODO: Equality is not sufficient for this.
|
||||
// Need to implement equality check on the value type.
|
||||
c.Modified.Insert(w.path)
|
||||
}
|
||||
}, func(w *mergingWalker) {
|
||||
if w.lhs == nil {
|
||||
c.Added.Insert(w.path)
|
||||
} else if w.rhs == nil {
|
||||
c.Removed.Insert(w.path)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, 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)
|
||||
return &tv
|
||||
}
|
||||
|
||||
// NormalizeUnions takes the new object and normalizes the union:
|
||||
// - 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.
|
||||
//
|
||||
// Please note: union behavior isn't finalized yet and this is still experimental.
|
||||
func (tv TypedValue) NormalizeUnions(new *TypedValue) (*TypedValue, error) {
|
||||
var errs ValidationErrors
|
||||
var normalizeFn = func(w *mergingWalker) {
|
||||
if w.rhs != nil {
|
||||
v := w.rhs.Unstructured()
|
||||
w.out = &v
|
||||
}
|
||||
if err := normalizeUnions(w); err != nil {
|
||||
errs = append(errs, errorf(err.Error())...)
|
||||
}
|
||||
}
|
||||
out, mergeErrs := merge(&tv, new, func(w *mergingWalker) {}, normalizeFn)
|
||||
if mergeErrs != nil {
|
||||
errs = append(errs, mergeErrs.(ValidationErrors)...)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// NormalizeUnionsApply specifically normalize unions on apply. It
|
||||
// validates that the applied union is correct (there should be no
|
||||
// ambiguity there), and clear the fields according to the sent intent.
|
||||
//
|
||||
// Please note: union behavior isn't finalized yet and this is still experimental.
|
||||
func (tv TypedValue) NormalizeUnionsApply(new *TypedValue) (*TypedValue, error) {
|
||||
var errs ValidationErrors
|
||||
var normalizeFn = func(w *mergingWalker) {
|
||||
if w.rhs != nil {
|
||||
v := w.rhs.Unstructured()
|
||||
w.out = &v
|
||||
}
|
||||
if err := normalizeUnionsApply(w); err != nil {
|
||||
errs = append(errs, errorf(err.Error())...)
|
||||
}
|
||||
}
|
||||
out, mergeErrs := merge(&tv, new, func(w *mergingWalker) {}, normalizeFn)
|
||||
if mergeErrs != nil {
|
||||
errs = append(errs, mergeErrs.(ValidationErrors)...)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
276
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/union.go
generated
vendored
Normal file
276
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/union.go
generated
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v3/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/value"
|
||||
)
|
||||
|
||||
func normalizeUnions(w *mergingWalker) error {
|
||||
atom, found := w.schema.Resolve(w.typeRef)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("Unable to resolve schema in normalize union: %v/%v", w.schema, w.typeRef))
|
||||
}
|
||||
// Unions can only be in structures, and the struct must not have been removed
|
||||
if atom.Map == nil || w.out == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var old value.Map
|
||||
if w.lhs != nil && !w.lhs.IsNull() {
|
||||
old = w.lhs.AsMap()
|
||||
}
|
||||
for _, union := range atom.Map.Unions {
|
||||
if err := newUnion(&union).Normalize(old, w.rhs.AsMap(), value.NewValueInterface(*w.out).AsMap()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeUnionsApply(w *mergingWalker) error {
|
||||
atom, found := w.schema.Resolve(w.typeRef)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("Unable to resolve schema in normalize union: %v/%v", w.schema, w.typeRef))
|
||||
}
|
||||
// Unions can only be in structures, and the struct must not have been removed
|
||||
if atom.Map == nil || w.out == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var old value.Map
|
||||
if w.lhs != nil && !w.lhs.IsNull() {
|
||||
old = w.lhs.AsMap()
|
||||
}
|
||||
|
||||
for _, union := range atom.Map.Unions {
|
||||
out := value.NewValueInterface(*w.out)
|
||||
if err := newUnion(&union).NormalizeApply(old, w.rhs.AsMap(), out.AsMap()); err != nil {
|
||||
return err
|
||||
}
|
||||
*w.out = out.Unstructured()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type discriminated string
|
||||
type field string
|
||||
|
||||
type discriminatedNames struct {
|
||||
f2d map[field]discriminated
|
||||
d2f map[discriminated]field
|
||||
}
|
||||
|
||||
func newDiscriminatedName(f2d map[field]discriminated) discriminatedNames {
|
||||
d2f := map[discriminated]field{}
|
||||
for key, value := range f2d {
|
||||
d2f[value] = key
|
||||
}
|
||||
return discriminatedNames{
|
||||
f2d: f2d,
|
||||
d2f: d2f,
|
||||
}
|
||||
}
|
||||
|
||||
func (dn discriminatedNames) toField(d discriminated) field {
|
||||
if f, ok := dn.d2f[d]; ok {
|
||||
return f
|
||||
}
|
||||
return field(d)
|
||||
}
|
||||
|
||||
func (dn discriminatedNames) toDiscriminated(f field) discriminated {
|
||||
if d, ok := dn.f2d[f]; ok {
|
||||
return d
|
||||
}
|
||||
return discriminated(f)
|
||||
}
|
||||
|
||||
type discriminator struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (d *discriminator) Set(m value.Map, v discriminated) {
|
||||
if d == nil {
|
||||
return
|
||||
}
|
||||
m.Set(d.name, value.NewValueInterface(string(v)))
|
||||
}
|
||||
|
||||
func (d *discriminator) Get(m value.Map) discriminated {
|
||||
if d == nil || m == nil {
|
||||
return ""
|
||||
}
|
||||
val, ok := m.Get(d.name)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
if !val.IsString() {
|
||||
return ""
|
||||
}
|
||||
return discriminated(val.AsString())
|
||||
}
|
||||
|
||||
type fieldsSet map[field]struct{}
|
||||
|
||||
// newFieldsSet returns a map of the fields that are part of the union and are set
|
||||
// in the given map.
|
||||
func newFieldsSet(m value.Map, fields []field) fieldsSet {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
set := fieldsSet{}
|
||||
for _, f := range fields {
|
||||
if subField, ok := m.Get(string(f)); ok && !subField.IsNull() {
|
||||
set.Add(f)
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func (fs fieldsSet) Add(f field) {
|
||||
if fs == nil {
|
||||
fs = map[field]struct{}{}
|
||||
}
|
||||
fs[f] = struct{}{}
|
||||
}
|
||||
|
||||
func (fs fieldsSet) One() *field {
|
||||
for f := range fs {
|
||||
return &f
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs fieldsSet) Has(f field) bool {
|
||||
_, ok := fs[f]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (fs fieldsSet) List() []field {
|
||||
fields := []field{}
|
||||
for f := range fs {
|
||||
fields = append(fields, f)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
func (fs fieldsSet) Difference(o fieldsSet) fieldsSet {
|
||||
n := fieldsSet{}
|
||||
for f := range fs {
|
||||
if !o.Has(f) {
|
||||
n.Add(f)
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (fs fieldsSet) String() string {
|
||||
s := []string{}
|
||||
for k := range fs {
|
||||
s = append(s, string(k))
|
||||
}
|
||||
return strings.Join(s, ", ")
|
||||
}
|
||||
|
||||
type union struct {
|
||||
deduceInvalidDiscriminator bool
|
||||
d *discriminator
|
||||
dn discriminatedNames
|
||||
f []field
|
||||
}
|
||||
|
||||
func newUnion(su *schema.Union) *union {
|
||||
u := &union{}
|
||||
if su.Discriminator != nil {
|
||||
u.d = &discriminator{name: *su.Discriminator}
|
||||
}
|
||||
f2d := map[field]discriminated{}
|
||||
for _, f := range su.Fields {
|
||||
u.f = append(u.f, field(f.FieldName))
|
||||
f2d[field(f.FieldName)] = discriminated(f.DiscriminatorValue)
|
||||
}
|
||||
u.dn = newDiscriminatedName(f2d)
|
||||
u.deduceInvalidDiscriminator = su.DeduceInvalidDiscriminator
|
||||
return u
|
||||
}
|
||||
|
||||
// clear removes all the fields in map that are part of the union, but
|
||||
// the one we decided to keep.
|
||||
func (u *union) clear(m value.Map, f field) {
|
||||
for _, fieldName := range u.f {
|
||||
if field(fieldName) != f {
|
||||
m.Delete(string(fieldName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *union) Normalize(old, new, out value.Map) error {
|
||||
os := newFieldsSet(old, u.f)
|
||||
ns := newFieldsSet(new, u.f)
|
||||
diff := ns.Difference(os)
|
||||
|
||||
if u.d.Get(old) != u.d.Get(new) && u.d.Get(new) != "" {
|
||||
if len(diff) == 1 && u.d.Get(new) != u.dn.toDiscriminated(*diff.One()) {
|
||||
return fmt.Errorf("discriminator (%v) and field changed (%v) don't match", u.d.Get(new), diff.One())
|
||||
}
|
||||
if len(diff) > 1 {
|
||||
return fmt.Errorf("multiple new fields added: %v", diff)
|
||||
}
|
||||
u.clear(out, u.dn.toField(u.d.Get(new)))
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(ns) > 1 {
|
||||
return fmt.Errorf("multiple fields set without discriminator change: %v", ns)
|
||||
}
|
||||
|
||||
// Set discriminiator if it needs to be deduced.
|
||||
if u.deduceInvalidDiscriminator && len(ns) == 1 {
|
||||
u.d.Set(out, u.dn.toDiscriminated(*ns.One()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *union) NormalizeApply(applied, merged, out value.Map) error {
|
||||
as := newFieldsSet(applied, u.f)
|
||||
if len(as) > 1 {
|
||||
return fmt.Errorf("more than one field of union applied: %v", as)
|
||||
}
|
||||
if len(as) == 0 {
|
||||
// None is set, just leave.
|
||||
return nil
|
||||
}
|
||||
// We have exactly one, discriminiator must match if set
|
||||
if u.d.Get(applied) != "" && u.d.Get(applied) != u.dn.toDiscriminated(*as.One()) {
|
||||
return fmt.Errorf("applied discriminator (%v) doesn't match applied field (%v)", u.d.Get(applied), *as.One())
|
||||
}
|
||||
|
||||
// Update discriminiator if needed
|
||||
if u.deduceInvalidDiscriminator {
|
||||
u.d.Set(out, u.dn.toDiscriminated(*as.One()))
|
||||
}
|
||||
// Clear others fields.
|
||||
u.clear(out, *as.One())
|
||||
|
||||
return nil
|
||||
}
|
190
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/validate.go
generated
vendored
Normal file
190
vendor/sigs.k8s.io/structured-merge-diff/v3/typed/validate.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/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/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
|
||||
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
|
||||
|
||||
// Allocate only as many walkers as needed for the depth by storing them here.
|
||||
spareWalkers *[]*validatingObjectWalker
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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.At(i)
|
||||
var pe fieldpath.PathElement
|
||||
if t.ElementRelationship != schema.Associative {
|
||||
pe.Index = &i
|
||||
} else {
|
||||
var err error
|
||||
pe, err = listItemToPathElement(t, i, child)
|
||||
if err != nil {
|
||||
errs = append(errs, errorf("element %v: %v", i, err.Error())...)
|
||||
// If we can't construct the path element, we can't
|
||||
// even report errors deeper in the schema, so bail on
|
||||
// this element.
|
||||
return
|
||||
}
|
||||
if observedKeys.Has(pe) {
|
||||
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.value)
|
||||
if err != nil {
|
||||
return errorf(err.Error())
|
||||
}
|
||||
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = v.visitListItems(t, list)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) 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
|
||||
} 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.value)
|
||||
if err != nil {
|
||||
return errorf(err.Error())
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = v.visitMapItems(t, m)
|
||||
|
||||
return errs
|
||||
}
|
42
vendor/sigs.k8s.io/structured-merge-diff/v3/value/BUILD
generated
vendored
Normal file
42
vendor/sigs.k8s.io/structured-merge-diff/v3/value/BUILD
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"fields.go",
|
||||
"jsontagutil.go",
|
||||
"list.go",
|
||||
"listreflect.go",
|
||||
"listunstructured.go",
|
||||
"map.go",
|
||||
"mapreflect.go",
|
||||
"mapunstructured.go",
|
||||
"scalar.go",
|
||||
"structreflect.go",
|
||||
"value.go",
|
||||
"valuereflect.go",
|
||||
"valueunstructured.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/v3/value",
|
||||
importpath = "sigs.k8s.io/structured-merge-diff/v3/value",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/json-iterator/go:go_default_library",
|
||||
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
21
vendor/sigs.k8s.io/structured-merge-diff/v3/value/doc.go
generated
vendored
Normal file
21
vendor/sigs.k8s.io/structured-merge-diff/v3/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
vendor/sigs.k8s.io/structured-merge-diff/v3/value/fields.go
generated
vendored
Normal file
97
vendor/sigs.k8s.io/structured-merge-diff/v3/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 expectUpdated 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
vendor/sigs.k8s.io/structured-merge-diff/v3/value/jsontagutil.go
generated
vendored
Normal file
91
vendor/sigs.k8s.io/structured-merge-diff/v3/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
|
||||
}
|
80
vendor/sigs.k8s.io/structured-merge-diff/v3/value/list.go
generated
vendored
Normal file
80
vendor/sigs.k8s.io/structured-merge-diff/v3/value/list.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
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
|
||||
}
|
||||
|
||||
// ListEquals compares two lists lexically.
|
||||
func ListEquals(lhs, rhs List) bool {
|
||||
if lhs.Length() != rhs.Length() {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < lhs.Length(); i++ {
|
||||
lv := lhs.At(i)
|
||||
rv := rhs.At(i)
|
||||
if !Equals(lv, rv) {
|
||||
lv.Recycle()
|
||||
rv.Recycle()
|
||||
return false
|
||||
}
|
||||
lv.Recycle()
|
||||
rv.Recycle()
|
||||
}
|
||||
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 {
|
||||
i := 0
|
||||
for {
|
||||
if i >= lhs.Length() && i >= rhs.Length() {
|
||||
// Lists are the same length and all items are equal.
|
||||
return 0
|
||||
}
|
||||
if i >= lhs.Length() {
|
||||
// LHS is shorter.
|
||||
return -1
|
||||
}
|
||||
if i >= rhs.Length() {
|
||||
// RHS is shorter.
|
||||
return 1
|
||||
}
|
||||
lv := lhs.At(i)
|
||||
rv := rhs.At(i)
|
||||
if c := Compare(lv, rv); c != 0 {
|
||||
return c
|
||||
}
|
||||
lv.Recycle()
|
||||
rv.Recycle()
|
||||
// The items are equal; continue.
|
||||
i++
|
||||
}
|
||||
}
|
42
vendor/sigs.k8s.io/structured-merge-diff/v3/value/listreflect.go
generated
vendored
Normal file
42
vendor/sigs.k8s.io/structured-merge-diff/v3/value/listreflect.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
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))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
27
vendor/sigs.k8s.io/structured-merge-diff/v3/value/listunstructured.go
generated
vendored
Normal file
27
vendor/sigs.k8s.io/structured-merge-diff/v3/value/listunstructured.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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])
|
||||
}
|
113
vendor/sigs.k8s.io/structured-merge-diff/v3/value/map.go
generated
vendored
Normal file
113
vendor/sigs.k8s.io/structured-merge-diff/v3/value/map.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// 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)
|
||||
// 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
|
||||
// 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
|
||||
// Length returns the number of items in the map.
|
||||
Length() int
|
||||
}
|
||||
|
||||
// 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 {
|
||||
lorder := make([]string, 0, lhs.Length())
|
||||
lhs.Iterate(func(key string, _ Value) bool {
|
||||
lorder = append(lorder, key)
|
||||
return true
|
||||
})
|
||||
sort.Strings(lorder)
|
||||
rorder := make([]string, 0, rhs.Length())
|
||||
rhs.Iterate(func(key string, _ Value) bool {
|
||||
rorder = append(rorder, key)
|
||||
return true
|
||||
})
|
||||
sort.Strings(rorder)
|
||||
|
||||
i := 0
|
||||
for {
|
||||
if i >= len(lorder) && i >= len(rorder) {
|
||||
// Maps are the same length and all items are equal.
|
||||
return 0
|
||||
}
|
||||
if i >= len(lorder) {
|
||||
// LHS is shorter.
|
||||
return -1
|
||||
}
|
||||
if i >= len(rorder) {
|
||||
// RHS is shorter.
|
||||
return 1
|
||||
}
|
||||
if c := strings.Compare(lorder[i], rorder[i]); c != 0 {
|
||||
return c
|
||||
}
|
||||
litem, _ := lhs.Get(lorder[i])
|
||||
ritem, _ := rhs.Get(rorder[i])
|
||||
if c := Compare(litem, ritem); c != 0 {
|
||||
return c
|
||||
}
|
||||
litem.Recycle()
|
||||
ritem.Recycle()
|
||||
// The items are equal; continue.
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
func MapEquals(lhs, rhs Map) bool {
|
||||
if lhs.Length() != rhs.Length() {
|
||||
return false
|
||||
}
|
||||
return lhs.Iterate(func(k string, v Value) bool {
|
||||
vo, ok := rhs.Get(k)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !Equals(v, vo) {
|
||||
vo.Recycle()
|
||||
return false
|
||||
}
|
||||
vo.Recycle()
|
||||
return true
|
||||
})
|
||||
}
|
107
vendor/sigs.k8s.io/structured-merge-diff/v3/value/mapreflect.go
generated
vendored
Normal file
107
vendor/sigs.k8s.io/structured-merge-diff/v3/value/mapreflect.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
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) Get(key string) (Value, bool) {
|
||||
mapKey := r.toMapKey(key)
|
||||
val := r.Value.MapIndex(mapKey)
|
||||
if !val.IsValid() {
|
||||
return nil, false
|
||||
}
|
||||
return mustWrapValueReflectMapItem(&r.Value, &mapKey, val), 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 eachMapEntry(r.Value, func(s string, value reflect.Value) bool {
|
||||
mapVal := mustWrapValueReflect(value)
|
||||
defer mapVal.Recycle()
|
||||
return fn(s, mapVal)
|
||||
})
|
||||
}
|
||||
|
||||
func eachMapEntry(val reflect.Value, fn func(string, reflect.Value) bool) bool {
|
||||
iter := val.MapRange()
|
||||
for iter.Next() {
|
||||
next := iter.Value()
|
||||
if !next.IsValid() {
|
||||
continue
|
||||
}
|
||||
if !fn(iter.Key().String(), 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 {
|
||||
if r.Length() != m.Length() {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: Optimize to avoid Iterate looping here by using r.Value.MapRange or similar if it improves performance.
|
||||
return m.Iterate(func(key string, value Value) bool {
|
||||
lhsVal, ok := r.Get(key)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return Equals(lhsVal, value)
|
||||
})
|
||||
}
|
145
vendor/sigs.k8s.io/structured-merge-diff/v3/value/mapunstructured.go
generated
vendored
Normal file
145
vendor/sigs.k8s.io/structured-merge-diff/v3/value/mapunstructured.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
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) {
|
||||
if v, ok := m[key]; !ok {
|
||||
return nil, false
|
||||
} else {
|
||||
return NewValueInterface(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 {
|
||||
for k, v := range m {
|
||||
if ks, ok := k.(string); !ok {
|
||||
continue
|
||||
} else {
|
||||
vv := NewValueInterface(v)
|
||||
if !fn(ks, vv) {
|
||||
vv.Recycle()
|
||||
return false
|
||||
}
|
||||
vv.Recycle()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Length() int {
|
||||
return len(m)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Equals(other Map) bool {
|
||||
if m.Length() != other.Length() {
|
||||
return false
|
||||
}
|
||||
for k, v := range m {
|
||||
ks, ok := k.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
vo, ok := other.Get(ks)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
vv := NewValueInterface(v)
|
||||
if !Equals(vv, vo) {
|
||||
vv.Recycle()
|
||||
vo.Recycle()
|
||||
return false
|
||||
}
|
||||
vo.Recycle()
|
||||
vv.Recycle()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
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) {
|
||||
if v, ok := m[key]; !ok {
|
||||
return nil, false
|
||||
} else {
|
||||
return NewValueInterface(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 {
|
||||
for k, v := range m {
|
||||
vv := NewValueInterface(v)
|
||||
if !fn(k, vv) {
|
||||
vv.Recycle()
|
||||
return false
|
||||
}
|
||||
vv.Recycle()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Length() int {
|
||||
return len(m)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Equals(other Map) bool {
|
||||
if m.Length() != other.Length() {
|
||||
return false
|
||||
}
|
||||
for k, v := range m {
|
||||
vo, ok := other.Get(k)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
vv := NewValueInterface(v)
|
||||
if !Equals(vv, vo) {
|
||||
vo.Recycle()
|
||||
vv.Recycle()
|
||||
return false
|
||||
}
|
||||
vo.Recycle()
|
||||
vv.Recycle()
|
||||
}
|
||||
return true
|
||||
}
|
50
vendor/sigs.k8s.io/structured-merge-diff/v3/value/scalar.go
generated
vendored
Normal file
50
vendor/sigs.k8s.io/structured-merge-diff/v3/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 == false {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
269
vendor/sigs.k8s.io/structured-merge-diff/v3/value/structreflect.go
generated
vendored
Normal file
269
vendor/sigs.k8s.io/structured-merge-diff/v3/value/structreflect.go
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
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"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// reflectStructCache keeps track of json tag related data for structs and fields to speed up reflection.
|
||||
// TODO: This overlaps in functionality with the fieldCache in
|
||||
// https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L57 but
|
||||
// is more efficient at lookup by json field name. The logic should be consolidated. Only one copy of the cache needs
|
||||
// to be kept for each running process.
|
||||
var (
|
||||
reflectStructCache = newStructCache()
|
||||
)
|
||||
|
||||
type structCache 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
|
||||
}
|
||||
|
||||
type structCacheMap map[reflect.Type]structCacheEntry
|
||||
|
||||
// structCacheEntry contains information about each struct field, keyed by json field name, that is expensive to
|
||||
// compute using reflection.
|
||||
type structCacheEntry map[string]*fieldCacheEntry
|
||||
|
||||
// Get returns true and fieldCacheEntry for the given type if the type is in the cache. Otherwise Get returns false.
|
||||
func (c *structCache) Get(t reflect.Type) (map[string]*fieldCacheEntry, bool) {
|
||||
entry, ok := c.value.Load().(structCacheMap)[t]
|
||||
return entry, ok
|
||||
}
|
||||
|
||||
// Set sets the fieldCacheEntry for the given type via a copy-on-write update to the struct cache.
|
||||
func (c *structCache) Set(t reflect.Type, m map[string]*fieldCacheEntry) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
currentCacheMap := c.value.Load().(structCacheMap)
|
||||
|
||||
if _, ok := currentCacheMap[t]; ok {
|
||||
// Bail if the entry has been set while waiting for lock acquisition.
|
||||
// This is safe since setting entries is idempotent.
|
||||
return
|
||||
}
|
||||
|
||||
newCacheMap := make(structCacheMap, len(currentCacheMap)+1)
|
||||
for k, v := range currentCacheMap {
|
||||
newCacheMap[k] = v
|
||||
}
|
||||
newCacheMap[t] = m
|
||||
c.value.Store(newCacheMap)
|
||||
}
|
||||
|
||||
func newStructCache() *structCache {
|
||||
cache := &structCache{}
|
||||
cache.value.Store(make(structCacheMap))
|
||||
return cache
|
||||
}
|
||||
|
||||
type fieldCacheEntry struct {
|
||||
// isOmitEmpty is true if the field has the json 'omitempty' tag.
|
||||
isOmitEmpty bool
|
||||
// fieldPath is the field indices (see FieldByIndex) to lookup the value of
|
||||
// a field in a reflect.Value struct. A path of field indices is used
|
||||
// to support traversing to a field field in struct fields that have the 'inline'
|
||||
// json tag.
|
||||
fieldPath [][]int
|
||||
}
|
||||
|
||||
func (f *fieldCacheEntry) getFieldFromStruct(structVal reflect.Value) reflect.Value {
|
||||
// field might be field within 'inline' structs
|
||||
for _, elem := range f.fieldPath {
|
||||
structVal = structVal.FieldByIndex(elem)
|
||||
}
|
||||
return structVal
|
||||
}
|
||||
|
||||
func getStructCacheEntry(t reflect.Type) structCacheEntry {
|
||||
if hints, ok := reflectStructCache.Get(t); ok {
|
||||
return hints
|
||||
}
|
||||
|
||||
hints := map[string]*fieldCacheEntry{}
|
||||
buildStructCacheEntry(t, hints, nil)
|
||||
|
||||
reflectStructCache.Set(t, hints)
|
||||
return hints
|
||||
}
|
||||
|
||||
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 {
|
||||
buildStructCacheEntry(field.Type, infos, append(fieldPath, field.Index))
|
||||
continue
|
||||
}
|
||||
info := &fieldCacheEntry{isOmitEmpty: isOmitempty, fieldPath: append(fieldPath, field.Index)}
|
||||
infos[jsonName] = info
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
type structReflect struct {
|
||||
valueReflect
|
||||
}
|
||||
|
||||
func (r structReflect) Length() int {
|
||||
i := 0
|
||||
eachStructField(r.Value, func(s string, value reflect.Value) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
return i
|
||||
}
|
||||
|
||||
func (r structReflect) Get(key string) (Value, bool) {
|
||||
if val, ok, _ := r.findJsonNameField(key); ok {
|
||||
return mustWrapValueReflect(val), 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 := getStructCacheEntry(r.Value.Type())[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.getFieldFromStruct(r.Value)
|
||||
newVal := reflect.ValueOf(val.Unstructured())
|
||||
r.update(fieldEntry, key, oldVal, newVal)
|
||||
}
|
||||
|
||||
func (r structReflect) Delete(key string) {
|
||||
fieldEntry, ok := getStructCacheEntry(r.Value.Type())[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.getFieldFromStruct(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.getFieldFromStruct(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 eachStructField(r.Value, func(s string, value reflect.Value) bool {
|
||||
v := mustWrapValueReflect(value)
|
||||
defer v.Recycle()
|
||||
return fn(s, v)
|
||||
})
|
||||
}
|
||||
|
||||
func eachStructField(structVal reflect.Value, fn func(string, reflect.Value) bool) bool {
|
||||
for jsonName, fieldCacheEntry := range getStructCacheEntry(structVal.Type()) {
|
||||
fieldVal := fieldCacheEntry.getFieldFromStruct(structVal)
|
||||
if fieldCacheEntry.isOmitEmpty && (safeIsNil(fieldVal) || isZero(fieldVal)) {
|
||||
// omit it
|
||||
continue
|
||||
}
|
||||
ok := fn(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 {
|
||||
if rhsStruct, ok := m.(structReflect); ok {
|
||||
return reflect.DeepEqual(r.Value.Interface(), rhsStruct.Value.Interface())
|
||||
}
|
||||
if r.Length() != m.Length() {
|
||||
return false
|
||||
}
|
||||
structCacheEntry := getStructCacheEntry(r.Value.Type())
|
||||
|
||||
return m.Iterate(func(s string, value Value) bool {
|
||||
fieldCacheEntry, ok := structCacheEntry[s]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
lhsVal := fieldCacheEntry.getFieldFromStruct(r.Value)
|
||||
return Equals(mustWrapValueReflect(lhsVal), value)
|
||||
})
|
||||
}
|
||||
|
||||
func (r structReflect) findJsonNameFieldAndNotEmpty(jsonName string) (reflect.Value, bool) {
|
||||
structCacheEntry, ok := getStructCacheEntry(r.Value.Type())[jsonName]
|
||||
if !ok {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
fieldVal := structCacheEntry.getFieldFromStruct(r.Value)
|
||||
omit := structCacheEntry.isOmitEmpty && (safeIsNil(fieldVal) || isZero(fieldVal))
|
||||
return fieldVal, !omit
|
||||
}
|
||||
|
||||
func (r structReflect) findJsonNameField(jsonName string) (val reflect.Value, ok bool, omitEmpty bool) {
|
||||
structCacheEntry, ok := getStructCacheEntry(r.Value.Type())[jsonName]
|
||||
if !ok {
|
||||
return reflect.Value{}, false, false
|
||||
}
|
||||
fieldVal := structCacheEntry.getFieldFromStruct(r.Value)
|
||||
return fieldVal, true, structCacheEntry.isOmitEmpty
|
||||
}
|
315
vendor/sigs.k8s.io/structured-merge-diff/v3/value/value.go
generated
vendored
Normal file
315
vendor/sigs.k8s.io/structured-merge-diff/v3/value/value.go
generated
vendored
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
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"
|
||||
"gopkg.in/yaml.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
|
||||
// AsList converts the Value into a List (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsList() 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
|
||||
|
||||
// Recycle returns a value of this type that is no longer needed. The
|
||||
// value shouldn't be used after this call.
|
||||
Recycle()
|
||||
|
||||
// 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 {
|
||||
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() {
|
||||
return ListEquals(lhs.AsList(), rhs.AsList())
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsList() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsMap() {
|
||||
if rhs.IsMap() {
|
||||
return lhs.AsMap().Equals(rhs.AsMap())
|
||||
}
|
||||
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{}
|
||||
for i := 0; i < v.AsList().Length(); i++ {
|
||||
strs = append(strs, ToString(v.AsList().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 {
|
||||
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
|
||||
}
|
||||
return ListCompare(lhs.AsList(), rhs.AsList())
|
||||
} else if rhs.IsList() {
|
||||
return 1
|
||||
}
|
||||
if lhs.IsMap() {
|
||||
if !rhs.IsMap() {
|
||||
return -1
|
||||
}
|
||||
return MapCompare(lhs.AsMap(), rhs.AsMap())
|
||||
} 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
|
||||
}
|
319
vendor/sigs.k8s.io/structured-merge-diff/v3/value/valuereflect.go
generated
vendored
Normal file
319
vendor/sigs.k8s.io/structured-merge-diff/v3/value/valuereflect.go
generated
vendored
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
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 (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var reflectPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &valueReflect{}
|
||||
},
|
||||
}
|
||||
|
||||
// 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(nil, nil, v)
|
||||
}
|
||||
|
||||
func wrapValueReflect(parentMap, parentMapKey *reflect.Value, value reflect.Value) (Value, error) {
|
||||
// TODO: conversion of json.Marshaller interface types is expensive. This can be mostly optimized away by
|
||||
// introducing conversion functions that do not require going through JSON and using those here.
|
||||
if marshaler, ok := getMarshaler(value); ok {
|
||||
return toUnstructured(marshaler, value)
|
||||
}
|
||||
value = dereference(value)
|
||||
val := reflectPool.Get().(*valueReflect)
|
||||
val.Value = value
|
||||
val.ParentMap = parentMap
|
||||
val.ParentMapKey = parentMapKey
|
||||
return Value(val), nil
|
||||
}
|
||||
|
||||
func mustWrapValueReflect(value reflect.Value) Value {
|
||||
v, err := wrapValueReflect(nil, nil, value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func mustWrapValueReflectMapItem(parentMap, parentMapKey *reflect.Value, value reflect.Value) Value {
|
||||
v, err := wrapValueReflect(parentMap, parentMapKey, value)
|
||||
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
|
||||
}
|
||||
|
||||
func (r valueReflect) IsMap() bool {
|
||||
return r.isKind(reflect.Map, reflect.Struct)
|
||||
}
|
||||
|
||||
func (r valueReflect) IsList() bool {
|
||||
typ := r.Value.Type()
|
||||
return typ.Kind() == reflect.Slice && !(typ.Elem().Kind() == reflect.Uint8)
|
||||
}
|
||||
|
||||
func (r valueReflect) IsBool() bool {
|
||||
return r.isKind(reflect.Bool)
|
||||
}
|
||||
|
||||
func (r valueReflect) IsInt() bool {
|
||||
// Uint64 deliberately excluded, see valueUnstructured.Int.
|
||||
return r.isKind(reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Uint, reflect.Uint32, reflect.Uint16, reflect.Uint8)
|
||||
}
|
||||
|
||||
func (r valueReflect) IsFloat() bool {
|
||||
return r.isKind(reflect.Float64, reflect.Float32)
|
||||
}
|
||||
|
||||
func (r valueReflect) IsString() bool {
|
||||
kind := r.Value.Kind()
|
||||
if kind == reflect.String {
|
||||
return true
|
||||
}
|
||||
if kind == reflect.Slice && r.Value.Type().Elem().Kind() == reflect.Uint8 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r valueReflect) IsNull() bool {
|
||||
return safeIsNil(r.Value)
|
||||
}
|
||||
|
||||
func (r valueReflect) isKind(kinds ...reflect.Kind) bool {
|
||||
kind := r.Value.Kind()
|
||||
for _, k := range kinds {
|
||||
if kind == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 {
|
||||
val := r.Value
|
||||
switch val.Kind() {
|
||||
case reflect.Struct:
|
||||
return structReflect{r}
|
||||
case reflect.Map:
|
||||
return mapReflect{r}
|
||||
default:
|
||||
panic("value is not a map or struct")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *valueReflect) Recycle() {
|
||||
reflectPool.Put(r)
|
||||
}
|
||||
|
||||
func (r valueReflect) AsList() List {
|
||||
if r.IsList() {
|
||||
return listReflect{r.Value}
|
||||
}
|
||||
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.isKind(reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8) {
|
||||
return r.Value.Int()
|
||||
}
|
||||
if r.isKind(reflect.Uint, reflect.Uint32, reflect.Uint16, reflect.Uint8) {
|
||||
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 {
|
||||
kind := r.Value.Kind()
|
||||
if kind == reflect.String {
|
||||
return r.Value.String()
|
||||
}
|
||||
if kind == reflect.Slice && r.Value.Type().Elem().Kind() == reflect.Uint8 {
|
||||
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{r}.Unstructured()
|
||||
case r.IsList():
|
||||
return listReflect{Value: 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()))
|
||||
}
|
||||
}
|
||||
|
||||
// The below getMarshaler and toUnstructured functions are based on
|
||||
// https://github.com/kubernetes/kubernetes/blob/40df9f82d0572a123f5ad13f48312978a2ff5877/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L509
|
||||
// and should somehow be consolidated with it
|
||||
|
||||
var marshalerType = reflect.TypeOf(new(json.Marshaler)).Elem()
|
||||
|
||||
func getMarshaler(v reflect.Value) (json.Marshaler, bool) {
|
||||
// Check value receivers if v is not a pointer and pointer receivers if v is a pointer
|
||||
if v.Type().Implements(marshalerType) {
|
||||
return v.Interface().(json.Marshaler), true
|
||||
}
|
||||
// Check pointer receivers if v is not a pointer
|
||||
if v.Kind() != reflect.Ptr && v.CanAddr() {
|
||||
v = v.Addr()
|
||||
if v.Type().Implements(marshalerType) {
|
||||
return v.Interface().(json.Marshaler), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
var (
|
||||
nullBytes = []byte("null")
|
||||
trueBytes = []byte("true")
|
||||
falseBytes = []byte("false")
|
||||
)
|
||||
|
||||
func toUnstructured(marshaler json.Marshaler, sv reflect.Value) (Value, error) {
|
||||
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 NewValueInterface(nil), nil
|
||||
|
||||
case bytes.Equal(data, trueBytes):
|
||||
return NewValueInterface(true), nil
|
||||
|
||||
case bytes.Equal(data, falseBytes):
|
||||
return NewValueInterface(false), nil
|
||||
|
||||
case data[0] == '"':
|
||||
var result string
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding string from json: %v", err)
|
||||
}
|
||||
return NewValueInterface(result), nil
|
||||
|
||||
case data[0] == '{':
|
||||
result := make(map[string]interface{})
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding object from json: %v", err)
|
||||
}
|
||||
return NewValueInterface(result), nil
|
||||
|
||||
case data[0] == '[':
|
||||
result := make([]interface{}, 0)
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding array from json: %v", err)
|
||||
}
|
||||
return NewValueInterface(result), nil
|
||||
|
||||
default:
|
||||
var (
|
||||
resultInt int64
|
||||
resultFloat float64
|
||||
err error
|
||||
)
|
||||
if err = json.Unmarshal(data, &resultInt); err == nil {
|
||||
return NewValueInterface(resultInt), nil
|
||||
}
|
||||
if err = json.Unmarshal(data, &resultFloat); err == nil {
|
||||
return NewValueInterface(resultFloat), nil
|
||||
}
|
||||
return nil, fmt.Errorf("error decoding number from json: %v", err)
|
||||
}
|
||||
}
|
177
vendor/sigs.k8s.io/structured-merge-diff/v3/value/valueunstructured.go
generated
vendored
Normal file
177
vendor/sigs.k8s.io/structured-merge-diff/v3/value/valueunstructured.go
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
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"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var viPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &valueUnstructured{}
|
||||
},
|
||||
}
|
||||
|
||||
// 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 {
|
||||
vi := viPool.Get().(*valueUnstructured)
|
||||
vi.Value = v
|
||||
return Value(vi)
|
||||
}
|
||||
|
||||
type valueUnstructured struct {
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 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) Recycle() {
|
||||
viPool.Put(v)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) Unstructured() interface{} {
|
||||
return v.Value
|
||||
}
|
Reference in New Issue
Block a user