mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 01:38:10 +00:00
vendor: Update vendor logic
This commit is contained in:
parent
c6ac5cbc87
commit
4ca64b85f0
1540 changed files with 265304 additions and 91616 deletions
201
vendor/sigs.k8s.io/metrics-server/LICENSE
generated
vendored
Normal file
201
vendor/sigs.k8s.io/metrics-server/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.
|
||||
61
vendor/sigs.k8s.io/metrics-server/pkg/api/install.go
generated
vendored
Normal file
61
vendor/sigs.k8s.io/metrics-server/pkg/api/install.go
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// 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 api
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
coreinf "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/metrics/pkg/apis/metrics"
|
||||
"k8s.io/metrics/pkg/apis/metrics/install"
|
||||
"k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
)
|
||||
|
||||
var (
|
||||
// Scheme contains the types needed by the resource metrics API.
|
||||
Scheme = runtime.NewScheme()
|
||||
// Codecs is a codec factory for serving the resource metrics API.
|
||||
Codecs = serializer.NewCodecFactory(Scheme)
|
||||
)
|
||||
|
||||
func init() {
|
||||
install.Install(Scheme)
|
||||
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
|
||||
}
|
||||
|
||||
// Build constructs APIGroupInfo the metrics.k8s.io API group using the given getters.
|
||||
func Build(m MetricsGetter, informers coreinf.Interface) genericapiserver.APIGroupInfo {
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(metrics.GroupName, Scheme, metav1.ParameterCodec, Codecs)
|
||||
|
||||
node := newNodeMetrics(metrics.Resource("nodemetrics"), m, informers.Nodes().Lister())
|
||||
pod := newPodMetrics(metrics.Resource("podmetrics"), m, informers.Pods().Lister())
|
||||
metricsServerResources := map[string]rest.Storage{
|
||||
"nodes": node,
|
||||
"pods": pod,
|
||||
}
|
||||
apiGroupInfo.VersionedResourcesStorageMap[v1beta1.SchemeGroupVersion.Version] = metricsServerResources
|
||||
|
||||
return apiGroupInfo
|
||||
}
|
||||
|
||||
// InstallStorage builds the metrics for the metrics.k8s.io API, and then installs it into the given API metrics-server.
|
||||
func Install(metrics MetricsGetter, informers coreinf.Interface, server *genericapiserver.GenericAPIServer) error {
|
||||
info := Build(metrics, informers)
|
||||
return server.InstallAPIGroup(&info)
|
||||
}
|
||||
63
vendor/sigs.k8s.io/metrics-server/pkg/api/interfaces.go
generated
vendored
Normal file
63
vendor/sigs.k8s.io/metrics-server/pkg/api/interfaces.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// 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 api
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apitypes "k8s.io/apimachinery/pkg/types"
|
||||
metrics "k8s.io/metrics/pkg/apis/metrics"
|
||||
)
|
||||
|
||||
// MetricsGetter is both a PodMetricsGetter and a NodeMetricsGetter
|
||||
type MetricsGetter interface {
|
||||
PodMetricsGetter
|
||||
NodeMetricsGetter
|
||||
}
|
||||
|
||||
// TimeInfo represents the timing information for a metric, which was
|
||||
// potentially calculated over some window of time (e.g. for CPU usage rate).
|
||||
type TimeInfo struct {
|
||||
// NB: we consider the earliest timestamp amongst multiple containers
|
||||
// for the purposes of determining if a metric is tained by a time
|
||||
// period, like pod startup (used by things like the HPA).
|
||||
|
||||
// Timestamp is the time at which the metrics were initially collected.
|
||||
// In the case of a rate metric, it should be the timestamp of the last
|
||||
// data point used in the calculation. If it represents multiple metric
|
||||
// points, it should be the earliest such timestamp from all of the points.
|
||||
Timestamp time.Time
|
||||
|
||||
// Window represents the window used to calculate rate metrics associated
|
||||
// with this timestamp.
|
||||
Window time.Duration
|
||||
}
|
||||
|
||||
// PodMetricsGetter knows how to fetch metrics for the containers in a pod.
|
||||
type PodMetricsGetter interface {
|
||||
// GetContainerMetrics gets the latest metrics for all containers in each listed pod,
|
||||
// returning both the metrics and the associated collection timestamp.
|
||||
// If a pod is missing, the container metrics should be nil for that pod.
|
||||
GetContainerMetrics(pods ...apitypes.NamespacedName) ([]TimeInfo, [][]metrics.ContainerMetrics, error)
|
||||
}
|
||||
|
||||
// NodeMetricsGetter knows how to fetch metrics for a node.
|
||||
type NodeMetricsGetter interface {
|
||||
// GetNodeMetrics gets the latest metrics for the given nodes,
|
||||
// returning both the metrics and the associated collection timestamp.
|
||||
// If a node is missing, the resourcelist should be nil for that node.
|
||||
GetNodeMetrics(nodes ...string) ([]TimeInfo, []corev1.ResourceList, error)
|
||||
}
|
||||
217
vendor/sigs.k8s.io/metrics-server/pkg/api/node.go
generated
vendored
Normal file
217
vendor/sigs.k8s.io/metrics-server/pkg/api/node.go
generated
vendored
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
// 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 api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
v1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/metrics/pkg/apis/metrics"
|
||||
_ "k8s.io/metrics/pkg/apis/metrics/install"
|
||||
)
|
||||
|
||||
type nodeMetrics struct {
|
||||
groupResource schema.GroupResource
|
||||
metrics NodeMetricsGetter
|
||||
nodeLister v1listers.NodeLister
|
||||
}
|
||||
|
||||
var _ rest.KindProvider = &nodeMetrics{}
|
||||
var _ rest.Storage = &nodeMetrics{}
|
||||
var _ rest.Getter = &nodeMetrics{}
|
||||
var _ rest.Lister = &nodeMetrics{}
|
||||
var _ rest.Scoper = &nodeMetrics{}
|
||||
var _ rest.TableConvertor = &nodeMetrics{}
|
||||
|
||||
func newNodeMetrics(groupResource schema.GroupResource, metrics NodeMetricsGetter, nodeLister v1listers.NodeLister) *nodeMetrics {
|
||||
return &nodeMetrics{
|
||||
groupResource: groupResource,
|
||||
metrics: metrics,
|
||||
nodeLister: nodeLister,
|
||||
}
|
||||
}
|
||||
|
||||
// Storage interface
|
||||
func (m *nodeMetrics) New() runtime.Object {
|
||||
return &metrics.NodeMetrics{}
|
||||
}
|
||||
|
||||
// KindProvider interface
|
||||
func (m *nodeMetrics) Kind() string {
|
||||
return "NodeMetrics"
|
||||
}
|
||||
|
||||
// Lister interface
|
||||
func (m *nodeMetrics) NewList() runtime.Object {
|
||||
return &metrics.NodeMetricsList{}
|
||||
}
|
||||
|
||||
// Lister interface
|
||||
func (m *nodeMetrics) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
|
||||
labelSelector := labels.Everything()
|
||||
fieldSelector := fields.Everything()
|
||||
if options != nil && options.LabelSelector != nil {
|
||||
labelSelector = options.LabelSelector
|
||||
}
|
||||
if options != nil && options.FieldSelector != nil {
|
||||
fieldSelector = options.FieldSelector
|
||||
}
|
||||
nodes, err := m.nodeLister.ListWithPredicate(func(node *v1.Node) bool {
|
||||
if labelSelector.Empty() && fieldSelector.Empty() {
|
||||
return true
|
||||
}
|
||||
fieldsSet := generic.AddObjectMetaFieldsSet(make(fields.Set, 2), &node.ObjectMeta, true)
|
||||
return labelSelector.Matches(labels.Set(node.Labels)) && fieldSelector.Matches(fieldsSet)
|
||||
})
|
||||
if err != nil {
|
||||
errMsg := fmt.Errorf("Error while listing nodes for selector %v: %v", labelSelector, err)
|
||||
klog.Error(errMsg)
|
||||
return &metrics.NodeMetricsList{}, errMsg
|
||||
}
|
||||
|
||||
names := make([]string, len(nodes))
|
||||
for i, node := range nodes {
|
||||
names[i] = node.Name
|
||||
}
|
||||
// maintain the same ordering invariant as the Kube API would over nodes
|
||||
sort.Strings(names)
|
||||
|
||||
metricsItems, err := m.getNodeMetrics(names...)
|
||||
if err != nil {
|
||||
errMsg := fmt.Errorf("Error while fetching node metrics for selector %v: %v", labelSelector, err)
|
||||
klog.Error(errMsg)
|
||||
return &metrics.NodeMetricsList{}, errMsg
|
||||
}
|
||||
|
||||
return &metrics.NodeMetricsList{Items: metricsItems}, nil
|
||||
}
|
||||
|
||||
func (m *nodeMetrics) Get(ctx context.Context, name string, opts *metav1.GetOptions) (runtime.Object, error) {
|
||||
nodeMetrics, err := m.getNodeMetrics(name)
|
||||
if err == nil && len(nodeMetrics) == 0 {
|
||||
err = fmt.Errorf("no metrics known for node %q", name)
|
||||
}
|
||||
if err != nil {
|
||||
klog.Errorf("unable to fetch node metrics for node %q: %v", name, err)
|
||||
return nil, errors.NewNotFound(m.groupResource, name)
|
||||
}
|
||||
|
||||
return &nodeMetrics[0], nil
|
||||
}
|
||||
|
||||
func (m *nodeMetrics) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1beta1.Table, error) {
|
||||
var table metav1beta1.Table
|
||||
|
||||
switch t := object.(type) {
|
||||
case *metrics.NodeMetrics:
|
||||
table.ResourceVersion = t.ResourceVersion
|
||||
table.SelfLink = t.SelfLink
|
||||
addNodeMetricsToTable(&table, *t)
|
||||
case *metrics.NodeMetricsList:
|
||||
table.ResourceVersion = t.ResourceVersion
|
||||
table.SelfLink = t.SelfLink
|
||||
table.Continue = t.Continue
|
||||
addNodeMetricsToTable(&table, t.Items...)
|
||||
default:
|
||||
}
|
||||
|
||||
return &table, nil
|
||||
}
|
||||
|
||||
func addNodeMetricsToTable(table *metav1beta1.Table, nodes ...metrics.NodeMetrics) {
|
||||
var names []string
|
||||
for i, node := range nodes {
|
||||
if names == nil {
|
||||
for k := range node.Usage {
|
||||
names = append(names, string(k))
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
table.ColumnDefinitions = []metav1beta1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name", Description: "Name of the resource"},
|
||||
}
|
||||
for _, name := range names {
|
||||
table.ColumnDefinitions = append(table.ColumnDefinitions, metav1beta1.TableColumnDefinition{
|
||||
Name: name,
|
||||
Type: "string",
|
||||
Format: "quantity",
|
||||
})
|
||||
}
|
||||
table.ColumnDefinitions = append(table.ColumnDefinitions, metav1beta1.TableColumnDefinition{
|
||||
Name: "Window",
|
||||
Type: "string",
|
||||
Format: "duration",
|
||||
})
|
||||
}
|
||||
row := make([]interface{}, 0, len(names)+1)
|
||||
row = append(row, node.Name)
|
||||
for _, name := range names {
|
||||
v := node.Usage[v1.ResourceName(name)]
|
||||
row = append(row, v.String())
|
||||
}
|
||||
row = append(row, node.Window.Duration.String())
|
||||
table.Rows = append(table.Rows, metav1beta1.TableRow{
|
||||
Cells: row,
|
||||
Object: runtime.RawExtension{Object: &nodes[i]},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (m *nodeMetrics) getNodeMetrics(names ...string) ([]metrics.NodeMetrics, error) {
|
||||
timestamps, usages, err := m.metrics.GetNodeMetrics(names...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]metrics.NodeMetrics, 0, len(names))
|
||||
|
||||
for i, name := range names {
|
||||
if usages[i] == nil {
|
||||
klog.Errorf("unable to fetch node metrics for node %q: no metrics known for node", name)
|
||||
|
||||
continue
|
||||
}
|
||||
res = append(res, metrics.NodeMetrics{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
CreationTimestamp: metav1.NewTime(time.Now()),
|
||||
},
|
||||
Timestamp: metav1.NewTime(timestamps[i].Timestamp),
|
||||
Window: metav1.Duration{Duration: timestamps[i].Window},
|
||||
Usage: usages[i],
|
||||
})
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (m *nodeMetrics) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
266
vendor/sigs.k8s.io/metrics-server/pkg/api/pod.go
generated
vendored
Normal file
266
vendor/sigs.k8s.io/metrics-server/pkg/api/pod.go
generated
vendored
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
// 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 api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
apitypes "k8s.io/apimachinery/pkg/types"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
v1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/metrics/pkg/apis/metrics"
|
||||
_ "k8s.io/metrics/pkg/apis/metrics/install"
|
||||
)
|
||||
|
||||
type podMetrics struct {
|
||||
groupResource schema.GroupResource
|
||||
metrics PodMetricsGetter
|
||||
podLister v1listers.PodLister
|
||||
}
|
||||
|
||||
var _ rest.KindProvider = &podMetrics{}
|
||||
var _ rest.Storage = &podMetrics{}
|
||||
var _ rest.Getter = &podMetrics{}
|
||||
var _ rest.Lister = &podMetrics{}
|
||||
var _ rest.TableConvertor = &podMetrics{}
|
||||
|
||||
func newPodMetrics(groupResource schema.GroupResource, metrics PodMetricsGetter, podLister v1listers.PodLister) *podMetrics {
|
||||
return &podMetrics{
|
||||
groupResource: groupResource,
|
||||
metrics: metrics,
|
||||
podLister: podLister,
|
||||
}
|
||||
}
|
||||
|
||||
// Storage interface
|
||||
func (m *podMetrics) New() runtime.Object {
|
||||
return &metrics.PodMetrics{}
|
||||
}
|
||||
|
||||
// KindProvider interface
|
||||
func (m *podMetrics) Kind() string {
|
||||
return "PodMetrics"
|
||||
}
|
||||
|
||||
// Lister interface
|
||||
func (m *podMetrics) NewList() runtime.Object {
|
||||
return &metrics.PodMetricsList{}
|
||||
}
|
||||
|
||||
// Lister interface
|
||||
func (m *podMetrics) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
|
||||
labelSelector := labels.Everything()
|
||||
if options != nil && options.LabelSelector != nil {
|
||||
labelSelector = options.LabelSelector
|
||||
}
|
||||
|
||||
namespace := genericapirequest.NamespaceValue(ctx)
|
||||
pods, err := m.podLister.Pods(namespace).List(labelSelector)
|
||||
if err != nil {
|
||||
errMsg := fmt.Errorf("Error while listing pods for selector %v in namespace %q: %v", labelSelector, namespace, err)
|
||||
klog.Error(errMsg)
|
||||
return &metrics.PodMetricsList{}, errMsg
|
||||
}
|
||||
|
||||
// currently the PodLister API does not support filtering using FieldSelectors, we have to filter manually
|
||||
if options != nil && options.FieldSelector != nil {
|
||||
newPods := make([]*v1.Pod, 0, len(pods))
|
||||
fields := make(fields.Set, 2)
|
||||
for _, pod := range pods {
|
||||
for k := range fields {
|
||||
delete(fields, k)
|
||||
}
|
||||
fieldsSet := generic.AddObjectMetaFieldsSet(fields, &pod.ObjectMeta, true)
|
||||
if !options.FieldSelector.Matches(fieldsSet) {
|
||||
continue
|
||||
}
|
||||
newPods = append(newPods, pod)
|
||||
}
|
||||
pods = newPods
|
||||
}
|
||||
|
||||
// maintain the same ordering invariant as the Kube API would over pods
|
||||
sort.Slice(pods, func(i, j int) bool {
|
||||
if pods[i].Namespace != pods[j].Namespace {
|
||||
return pods[i].Namespace < pods[j].Namespace
|
||||
}
|
||||
return pods[i].Name < pods[j].Name
|
||||
})
|
||||
|
||||
metricsItems, err := m.getPodMetrics(pods...)
|
||||
if err != nil {
|
||||
errMsg := fmt.Errorf("Error while fetching pod metrics for selector %v in namespace %q: %v", labelSelector, namespace, err)
|
||||
klog.Error(errMsg)
|
||||
return &metrics.PodMetricsList{}, errMsg
|
||||
}
|
||||
|
||||
return &metrics.PodMetricsList{Items: metricsItems}, nil
|
||||
}
|
||||
|
||||
// Getter interface
|
||||
func (m *podMetrics) Get(ctx context.Context, name string, opts *metav1.GetOptions) (runtime.Object, error) {
|
||||
namespace := genericapirequest.NamespaceValue(ctx)
|
||||
|
||||
pod, err := m.podLister.Pods(namespace).Get(name)
|
||||
if err != nil {
|
||||
errMsg := fmt.Errorf("Error while getting pod %v: %v", name, err)
|
||||
klog.Error(errMsg)
|
||||
if errors.IsNotFound(err) {
|
||||
// return not-found errors directly
|
||||
return &metrics.PodMetrics{}, err
|
||||
}
|
||||
return &metrics.PodMetrics{}, errMsg
|
||||
}
|
||||
if pod == nil {
|
||||
return &metrics.PodMetrics{}, errors.NewNotFound(v1.Resource("pods"), fmt.Sprintf("%v/%v", namespace, name))
|
||||
}
|
||||
|
||||
podMetrics, err := m.getPodMetrics(pod)
|
||||
if err == nil && len(podMetrics) == 0 {
|
||||
err = fmt.Errorf("no metrics known for pod \"%s/%s\"", pod.Namespace, pod.Name)
|
||||
}
|
||||
if err != nil {
|
||||
klog.Errorf("unable to fetch pod metrics for pod %s/%s: %v", pod.Namespace, pod.Name, err)
|
||||
return nil, errors.NewNotFound(m.groupResource, fmt.Sprintf("%v/%v", namespace, name))
|
||||
}
|
||||
return &podMetrics[0], nil
|
||||
}
|
||||
|
||||
func (m *podMetrics) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1beta1.Table, error) {
|
||||
var table metav1beta1.Table
|
||||
|
||||
switch t := object.(type) {
|
||||
case *metrics.PodMetrics:
|
||||
table.ResourceVersion = t.ResourceVersion
|
||||
table.SelfLink = t.SelfLink
|
||||
addPodMetricsToTable(&table, *t)
|
||||
case *metrics.PodMetricsList:
|
||||
table.ResourceVersion = t.ResourceVersion
|
||||
table.SelfLink = t.SelfLink
|
||||
table.Continue = t.Continue
|
||||
addPodMetricsToTable(&table, t.Items...)
|
||||
default:
|
||||
}
|
||||
|
||||
return &table, nil
|
||||
}
|
||||
|
||||
func addPodMetricsToTable(table *metav1beta1.Table, pods ...metrics.PodMetrics) {
|
||||
usage := make(v1.ResourceList, 3)
|
||||
var names []string
|
||||
for i, pod := range pods {
|
||||
for k := range usage {
|
||||
delete(usage, k)
|
||||
}
|
||||
for _, container := range pod.Containers {
|
||||
for k, v := range container.Usage {
|
||||
u := usage[k]
|
||||
u.Add(v)
|
||||
usage[k] = u
|
||||
}
|
||||
}
|
||||
if names == nil {
|
||||
for k := range usage {
|
||||
names = append(names, string(k))
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
table.ColumnDefinitions = []metav1beta1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name", Description: "Name of the resource"},
|
||||
}
|
||||
for _, name := range names {
|
||||
table.ColumnDefinitions = append(table.ColumnDefinitions, metav1beta1.TableColumnDefinition{
|
||||
Name: name,
|
||||
Type: "string",
|
||||
Format: "quantity",
|
||||
})
|
||||
}
|
||||
table.ColumnDefinitions = append(table.ColumnDefinitions, metav1beta1.TableColumnDefinition{
|
||||
Name: "Window",
|
||||
Type: "string",
|
||||
Format: "duration",
|
||||
})
|
||||
}
|
||||
row := make([]interface{}, 0, len(names)+1)
|
||||
row = append(row, pod.Name)
|
||||
for _, name := range names {
|
||||
v := usage[v1.ResourceName(name)]
|
||||
row = append(row, v.String())
|
||||
}
|
||||
row = append(row, pod.Window.Duration.String())
|
||||
table.Rows = append(table.Rows, metav1beta1.TableRow{
|
||||
Cells: row,
|
||||
Object: runtime.RawExtension{Object: &pods[i]},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (m *podMetrics) getPodMetrics(pods ...*v1.Pod) ([]metrics.PodMetrics, error) {
|
||||
namespacedNames := make([]apitypes.NamespacedName, len(pods))
|
||||
for i, pod := range pods {
|
||||
namespacedNames[i] = apitypes.NamespacedName{
|
||||
Name: pod.Name,
|
||||
Namespace: pod.Namespace,
|
||||
}
|
||||
}
|
||||
timestamps, containerMetrics, err := m.metrics.GetContainerMetrics(namespacedNames...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]metrics.PodMetrics, 0, len(pods))
|
||||
|
||||
for i, pod := range pods {
|
||||
if pod.Status.Phase != v1.PodRunning {
|
||||
// ignore pod not in Running phase
|
||||
continue
|
||||
}
|
||||
if containerMetrics[i] == nil {
|
||||
klog.Errorf("unable to fetch pod metrics for pod %s/%s: no metrics known for pod", pod.Namespace, pod.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
res = append(res, metrics.PodMetrics{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: pod.Name,
|
||||
Namespace: pod.Namespace,
|
||||
CreationTimestamp: metav1.NewTime(time.Now()),
|
||||
},
|
||||
Timestamp: metav1.NewTime(timestamps[i].Timestamp),
|
||||
Window: metav1.Duration{Duration: timestamps[i].Window},
|
||||
Containers: containerMetrics[i],
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (m *podMetrics) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
182
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/element.go
generated
vendored
182
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/element.go
generated
vendored
|
|
@ -35,7 +35,7 @@ type PathElement struct {
|
|||
// Key selects the list element which has fields matching those given.
|
||||
// The containing object must be an associative list with map typed
|
||||
// elements.
|
||||
Key []value.Field
|
||||
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
|
||||
|
|
@ -47,19 +47,65 @@ type PathElement struct {
|
|||
Index *int
|
||||
}
|
||||
|
||||
// Less provides an order for path elements.
|
||||
func (e PathElement) Less(rhs PathElement) bool {
|
||||
if e.FieldName != nil {
|
||||
if rhs.FieldName == nil {
|
||||
return true
|
||||
}
|
||||
return *e.FieldName < *rhs.FieldName
|
||||
} else if rhs.FieldName != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if e.Key != nil {
|
||||
if rhs.Key == nil {
|
||||
return true
|
||||
}
|
||||
return e.Key.Less(*rhs.Key)
|
||||
} else if rhs.Key != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if e.Value != nil {
|
||||
if rhs.Value == nil {
|
||||
return true
|
||||
}
|
||||
return e.Value.Less(*rhs.Value)
|
||||
} else if rhs.Value != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if e.Index != nil {
|
||||
if rhs.Index == nil {
|
||||
return true
|
||||
}
|
||||
return *e.Index < *rhs.Index
|
||||
} else if rhs.Index != nil {
|
||||
// Yes, I know the next statement is the same. But this way
|
||||
// the obvious way of extending the function wil be bug-free.
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals returns true if both path elements are equal.
|
||||
func (e PathElement) Equals(rhs PathElement) bool {
|
||||
return !e.Less(rhs) && !rhs.Less(e)
|
||||
}
|
||||
|
||||
// String presents the path element as a human-readable string.
|
||||
func (e PathElement) String() string {
|
||||
switch {
|
||||
case e.FieldName != nil:
|
||||
return "." + *e.FieldName
|
||||
case len(e.Key) > 0:
|
||||
strs := make([]string, len(e.Key))
|
||||
for i, k := range e.Key {
|
||||
case e.Key != nil:
|
||||
strs := make([]string, len(*e.Key))
|
||||
for i, k := range *e.Key {
|
||||
strs[i] = fmt.Sprintf("%v=%v", k.Name, k.Value)
|
||||
}
|
||||
// The order must be canonical, since we use the string value
|
||||
// in a set structure.
|
||||
sort.Strings(strs)
|
||||
// Keys are supposed to be sorted.
|
||||
return "[" + strings.Join(strs, ",") + "]"
|
||||
case e.Value != nil:
|
||||
return fmt.Sprintf("[=%v]", e.Value)
|
||||
|
|
@ -75,79 +121,124 @@ func (e PathElement) String() string {
|
|||
// 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.Field {
|
||||
func KeyByFields(nameValues ...interface{}) *value.FieldList {
|
||||
if len(nameValues)%2 != 0 {
|
||||
panic("must have a value for every name")
|
||||
}
|
||||
out := []value.Field{}
|
||||
out := value.FieldList{}
|
||||
for i := 0; i < len(nameValues)-1; i += 2 {
|
||||
out = append(out, value.Field{
|
||||
Name: nameValues[i].(string),
|
||||
Value: nameValues[i+1].(value.Value),
|
||||
})
|
||||
}
|
||||
return out
|
||||
out.Sort()
|
||||
return &out
|
||||
}
|
||||
|
||||
// PathElementSet is a set of path elements.
|
||||
// TODO: serialize as a list.
|
||||
type PathElementSet struct {
|
||||
// The strange construction is because there's no way to test
|
||||
// PathElements for equality (it can't be used as a key for a map).
|
||||
members map[string]PathElement
|
||||
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) {
|
||||
serialized := pe.String()
|
||||
if s.members == nil {
|
||||
s.members = map[string]PathElement{
|
||||
serialized: pe,
|
||||
}
|
||||
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 _, ok := s.members[serialized]; !ok {
|
||||
s.members[serialized] = pe
|
||||
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{
|
||||
members: map[string]PathElement{},
|
||||
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++
|
||||
}
|
||||
}
|
||||
for k, v := range s.members {
|
||||
out.members[k] = v
|
||||
|
||||
if i < len(s.members) {
|
||||
out.members = append(out.members, s.members[i:]...)
|
||||
}
|
||||
for k, v := range s2.members {
|
||||
out.members[k] = v
|
||||
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{
|
||||
members: map[string]PathElement{},
|
||||
}
|
||||
for k, v := range s.members {
|
||||
if _, ok := s2.members[k]; ok {
|
||||
out.members[k] = v
|
||||
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{
|
||||
members: map[string]PathElement{},
|
||||
}
|
||||
for k, v := range s.members {
|
||||
if _, ok := s2.members[k]; !ok {
|
||||
out.members[k] = v
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -156,11 +247,16 @@ 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 {
|
||||
if s.members == nil {
|
||||
loc := sort.Search(len(s.members), func(i int) bool {
|
||||
return !s.members[i].Less(pe)
|
||||
})
|
||||
if loc == len(s.members) {
|
||||
return false
|
||||
}
|
||||
_, ok := s.members[pe.String()]
|
||||
return ok
|
||||
if s.members[loc].Equals(pe) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals returns true if s and s2 have exactly the same members.
|
||||
|
|
@ -169,14 +265,14 @@ func (s *PathElementSet) Equals(s2 *PathElementSet) bool {
|
|||
return false
|
||||
}
|
||||
for k := range s.members {
|
||||
if _, ok := s2.members[k]; !ok {
|
||||
if !s.members[k].Equals(s2.members[k]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Iterate calls f for each PathElement in the set.
|
||||
// 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)
|
||||
|
|
|
|||
5
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/fromvalue.go
generated
vendored
5
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/fromvalue.go
generated
vendored
|
|
@ -104,7 +104,7 @@ func GuessBestListPathElement(index int, item value.Value) PathElement {
|
|||
return PathElement{Index: &index}
|
||||
}
|
||||
|
||||
var keys []value.Field
|
||||
var keys value.FieldList
|
||||
for _, name := range AssociativeListCandidateFieldNames {
|
||||
f, ok := item.MapValue.Get(name)
|
||||
if !ok {
|
||||
|
|
@ -117,7 +117,8 @@ func GuessBestListPathElement(index int, item value.Value) PathElement {
|
|||
keys = append(keys, *f)
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
return PathElement{Key: keys}
|
||||
keys.Sort()
|
||||
return PathElement{Key: &keys}
|
||||
}
|
||||
return PathElement{Index: &index}
|
||||
}
|
||||
|
|
|
|||
49
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/managers.go
generated
vendored
49
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/managers.go
generated
vendored
|
|
@ -16,16 +16,42 @@ package fieldpath
|
|||
// 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
|
||||
APIVersion APIVersion
|
||||
Applied bool
|
||||
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
|
||||
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
|
||||
|
|
@ -37,7 +63,7 @@ func (lhs ManagedFields) Difference(rhs ManagedFields) ManagedFields {
|
|||
for manager, left := range lhs {
|
||||
right, ok := rhs[manager]
|
||||
if !ok {
|
||||
if !left.Empty() {
|
||||
if !left.Set().Empty() {
|
||||
diff[manager] = left
|
||||
}
|
||||
continue
|
||||
|
|
@ -46,17 +72,14 @@ func (lhs ManagedFields) Difference(rhs ManagedFields) ManagedFields {
|
|||
// 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 {
|
||||
if left.APIVersion() != right.APIVersion() {
|
||||
diff[manager] = right
|
||||
continue
|
||||
}
|
||||
|
||||
newSet := left.Difference(right.Set).Union(right.Difference(left.Set))
|
||||
newSet := left.Set().Difference(right.Set()).Union(right.Set().Difference(left.Set()))
|
||||
if !newSet.Empty() {
|
||||
diff[manager] = &VersionedSet{
|
||||
Set: newSet,
|
||||
APIVersion: right.APIVersion,
|
||||
}
|
||||
diff[manager] = NewVersionedSet(newSet, right.APIVersion(), false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +88,7 @@ func (lhs ManagedFields) Difference(rhs ManagedFields) ManagedFields {
|
|||
// Already done
|
||||
continue
|
||||
}
|
||||
if !set.Empty() {
|
||||
if !set.Set().Empty() {
|
||||
diff[manager] = set
|
||||
}
|
||||
}
|
||||
|
|
|
|||
38
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/path.go
generated
vendored
38
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/path.go
generated
vendored
|
|
@ -35,6 +35,40 @@ func (fp Path) String() string {
|
|||
return strings.Join(strs, "")
|
||||
}
|
||||
|
||||
// Equals returns true if the two paths are equivalent.
|
||||
func (fp Path) Equals(fp2 Path) bool {
|
||||
return !fp.Less(fp2) && !fp2.Less(fp)
|
||||
}
|
||||
|
||||
// Less provides a lexical order for Paths.
|
||||
func (fp Path) Less(rhs Path) bool {
|
||||
i := 0
|
||||
for {
|
||||
if i >= len(fp) && i >= len(rhs) {
|
||||
// Paths are the same length and all items are equal.
|
||||
return false
|
||||
}
|
||||
if i >= len(fp) {
|
||||
// LHS is shorter.
|
||||
return true
|
||||
}
|
||||
if i >= len(rhs) {
|
||||
// RHS is shorter.
|
||||
return false
|
||||
}
|
||||
if fp[i].Less(rhs[i]) {
|
||||
// LHS is less; return
|
||||
return true
|
||||
}
|
||||
if rhs[i].Less(fp[i]) {
|
||||
// RHS is less; return
|
||||
return false
|
||||
}
|
||||
// The items are equal; continue.
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func (fp Path) Copy() Path {
|
||||
new := make(Path, len(fp))
|
||||
copy(new, fp)
|
||||
|
|
@ -54,8 +88,8 @@ func MakePath(parts ...interface{}) (Path, error) {
|
|||
fp = append(fp, PathElement{Index: &t})
|
||||
case string:
|
||||
fp = append(fp, PathElement{FieldName: &t})
|
||||
case []value.Field:
|
||||
if len(t) == 0 {
|
||||
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})
|
||||
|
|
|
|||
85
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/pathelementmap.go
generated
vendored
Normal file
85
vendor/sigs.k8s.io/structured-merge-diff/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/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 value.Value{}, false
|
||||
}
|
||||
if s.members[loc].PathElement.Equals(pe) {
|
||||
return s.members[loc].Value, true
|
||||
}
|
||||
return value.Value{}, false
|
||||
}
|
||||
166
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/serialize-pe.go
generated
vendored
Normal file
166
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/serialize-pe.go
generated
vendored
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
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/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)
|
||||
field.Value.WriteJSONStream(stream)
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
case pe.Value != nil:
|
||||
if _, err := stream.Write(peValueSepBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
pe.Value.WriteJSONStream(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
|
||||
}
|
||||
237
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/serialize.go
generated
vendored
Normal file
237
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/serialize.go
generated
vendored
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
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()
|
||||
}
|
||||
|
||||
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++
|
||||
}
|
||||
|
||||
if includeSelf && !first {
|
||||
preWrite()
|
||||
stream.WriteObjectField(".")
|
||||
stream.WriteEmptyObject()
|
||||
}
|
||||
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
|
||||
}
|
||||
131
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go
generated
vendored
131
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go
generated
vendored
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package fieldpath
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -172,24 +173,33 @@ type setNode struct {
|
|||
|
||||
// SetNodeMap is a map of PathElement to subset.
|
||||
type SetNodeMap struct {
|
||||
members map[string]setNode
|
||||
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 {
|
||||
serialized := pe.String()
|
||||
if s.members == nil {
|
||||
s.members = map[string]setNode{}
|
||||
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 n, ok := s.members[serialized]; ok {
|
||||
return n.set
|
||||
if s.members[loc].pathElement.Equals(pe) {
|
||||
return s.members[loc].set
|
||||
}
|
||||
ss := &Set{}
|
||||
s.members[serialized] = setNode{
|
||||
pathElement: pe,
|
||||
set: ss,
|
||||
}
|
||||
return ss
|
||||
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.
|
||||
|
|
@ -213,12 +223,14 @@ func (s *SetNodeMap) Empty() bool {
|
|||
|
||||
// Get returns (the associated set, true) or (nil, false) if there is none.
|
||||
func (s *SetNodeMap) Get(pe PathElement) (*Set, bool) {
|
||||
if s.members == nil {
|
||||
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
|
||||
}
|
||||
serialized := pe.String()
|
||||
if n, ok := s.members[serialized]; ok {
|
||||
return n.set, true
|
||||
if s.members[loc].pathElement.Equals(pe) {
|
||||
return s.members[loc].set, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
|
@ -229,12 +241,11 @@ func (s *SetNodeMap) Equals(s2 *SetNodeMap) bool {
|
|||
if len(s.members) != len(s2.members) {
|
||||
return false
|
||||
}
|
||||
for k, v := range s.members {
|
||||
v2, ok := s2.members[k]
|
||||
if !ok {
|
||||
for i := range s.members {
|
||||
if !s.members[i].pathElement.Equals(s2.members[i].pathElement) {
|
||||
return false
|
||||
}
|
||||
if !v.set.Equals(v2.set) {
|
||||
if !s.members[i].set.Equals(s2.members[i].set) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -244,21 +255,28 @@ func (s *SetNodeMap) Equals(s2 *SetNodeMap) bool {
|
|||
// Union returns a SetNodeMap with members that appear in either s or s2.
|
||||
func (s *SetNodeMap) Union(s2 *SetNodeMap) *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
for k, sn := range s.members {
|
||||
pe := sn.pathElement
|
||||
if sn2, ok := s2.members[k]; ok {
|
||||
*out.Descend(pe) = *sn.set.Union(sn2.set)
|
||||
|
||||
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 {
|
||||
*out.Descend(pe) = *sn.set
|
||||
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++
|
||||
}
|
||||
}
|
||||
for k, sn2 := range s2.members {
|
||||
pe := sn2.pathElement
|
||||
if _, ok := s.members[k]; ok {
|
||||
// already handled
|
||||
continue
|
||||
}
|
||||
*out.Descend(pe) = *sn2.set
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -266,13 +284,20 @@ func (s *SetNodeMap) Union(s2 *SetNodeMap) *SetNodeMap {
|
|||
// Intersection returns a SetNodeMap with members that appear in both s and s2.
|
||||
func (s *SetNodeMap) Intersection(s2 *SetNodeMap) *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
for k, sn := range s.members {
|
||||
pe := sn.pathElement
|
||||
if sn2, ok := s2.members[k]; ok {
|
||||
i := *sn.set.Intersection(sn2.set)
|
||||
if !i.Empty() {
|
||||
*out.Descend(pe) = i
|
||||
|
||||
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
|
||||
|
|
@ -281,18 +306,30 @@ func (s *SetNodeMap) Intersection(s2 *SetNodeMap) *SetNodeMap {
|
|||
// Difference returns a SetNodeMap with members that appear in s but not in s2.
|
||||
func (s *SetNodeMap) Difference(s2 *Set) *SetNodeMap {
|
||||
out := &SetNodeMap{}
|
||||
for k, sn := range s.members {
|
||||
pe := sn.pathElement
|
||||
if sn2, ok := s2.Children.members[k]; ok {
|
||||
diff := *sn.set.Difference(sn2.set)
|
||||
// We aren't permitted to add nodes with no elements.
|
||||
if !diff.Empty() {
|
||||
*out.Descend(pe) = diff
|
||||
}
|
||||
|
||||
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 {
|
||||
*out.Descend(pe) = *sn.set
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
23
vendor/sigs.k8s.io/structured-merge-diff/merge/conflict.go
generated
vendored
23
vendor/sigs.k8s.io/structured-merge-diff/merge/conflict.go
generated
vendored
|
|
@ -40,6 +40,14 @@ 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
|
||||
|
||||
|
|
@ -74,12 +82,25 @@ func (conflicts Conflicts) Error() string {
|
|||
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.Iterate(func(p fieldpath.Path) {
|
||||
set.Set().Iterate(func(p fieldpath.Path) {
|
||||
conflicts = append(conflicts, Conflict{
|
||||
Manager: manager,
|
||||
Path: p,
|
||||
|
|
|
|||
155
vendor/sigs.k8s.io/structured-merge-diff/merge/update.go
generated
vendored
155
vendor/sigs.k8s.io/structured-merge-diff/merge/update.go
generated
vendored
|
|
@ -23,7 +23,7 @@ import (
|
|||
// 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)
|
||||
Convert(object *typed.TypedValue, version fieldpath.APIVersion) (*typed.TypedValue, error)
|
||||
IsMissingVersionError(error) bool
|
||||
}
|
||||
|
||||
|
|
@ -31,87 +31,87 @@ type Converter interface {
|
|||
// merge the object on Apply.
|
||||
type Updater struct {
|
||||
Converter Converter
|
||||
|
||||
enableUnions bool
|
||||
}
|
||||
|
||||
func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, workflow string, force bool) (fieldpath.ManagedFields, error) {
|
||||
// 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{}
|
||||
type Versioned struct {
|
||||
oldObject typed.TypedValue
|
||||
newObject typed.TypedValue
|
||||
compare, err := oldObject.Compare(newObject)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to compare objects: %v", err)
|
||||
}
|
||||
versions := map[fieldpath.APIVersion]Versioned{
|
||||
version: Versioned{
|
||||
oldObject: oldObject,
|
||||
newObject: newObject,
|
||||
},
|
||||
|
||||
versions := map[fieldpath.APIVersion]*typed.Comparison{
|
||||
version: compare,
|
||||
}
|
||||
|
||||
for manager, managerSet := range managers {
|
||||
if manager == workflow {
|
||||
continue
|
||||
}
|
||||
versioned, ok := versions[managerSet.APIVersion]
|
||||
compare, ok := versions[managerSet.APIVersion()]
|
||||
if !ok {
|
||||
var err error
|
||||
versioned.oldObject, err = s.Converter.Convert(oldObject, managerSet.APIVersion)
|
||||
versionedOldObject, err := s.Converter.Convert(oldObject, managerSet.APIVersion())
|
||||
if err != nil {
|
||||
if s.Converter.IsMissingVersionError(err) {
|
||||
delete(managers, manager)
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("failed to convert old object: %v", err)
|
||||
return nil, nil, fmt.Errorf("failed to convert old object: %v", err)
|
||||
}
|
||||
versioned.newObject, err = s.Converter.Convert(newObject, managerSet.APIVersion)
|
||||
versionedNewObject, err := s.Converter.Convert(newObject, managerSet.APIVersion())
|
||||
if err != nil {
|
||||
if s.Converter.IsMissingVersionError(err) {
|
||||
delete(managers, manager)
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("failed to convert new object: %v", err)
|
||||
return nil, nil, fmt.Errorf("failed to convert new object: %v", err)
|
||||
}
|
||||
versions[managerSet.APIVersion] = versioned
|
||||
}
|
||||
compare, err := versioned.oldObject.Compare(versioned.newObject)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to compare objects: %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.Intersection(compare.Modified.Union(compare.Added))
|
||||
conflictSet := managerSet.Set().Intersection(compare.Modified.Union(compare.Added))
|
||||
if !conflictSet.Empty() {
|
||||
conflicts[manager] = &fieldpath.VersionedSet{
|
||||
Set: conflictSet,
|
||||
APIVersion: managerSet.APIVersion,
|
||||
}
|
||||
conflicts[manager] = fieldpath.NewVersionedSet(conflictSet, managerSet.APIVersion(), false)
|
||||
}
|
||||
|
||||
if !compare.Removed.Empty() {
|
||||
removed[manager] = &fieldpath.VersionedSet{
|
||||
Set: compare.Removed,
|
||||
APIVersion: managerSet.APIVersion,
|
||||
}
|
||||
removed[manager] = fieldpath.NewVersionedSet(compare.Removed, managerSet.APIVersion(), false)
|
||||
}
|
||||
}
|
||||
|
||||
if !force && len(conflicts) != 0 {
|
||||
return nil, ConflictsFromManagers(conflicts)
|
||||
return nil, nil, ConflictsFromManagers(conflicts)
|
||||
}
|
||||
|
||||
for manager, conflictSet := range conflicts {
|
||||
managers[manager].Set = managers[manager].Set.Difference(conflictSet.Set)
|
||||
managers[manager] = fieldpath.NewVersionedSet(managers[manager].Set().Difference(conflictSet.Set()), managers[manager].APIVersion(), managers[manager].Applied())
|
||||
}
|
||||
|
||||
for manager, removedSet := range removed {
|
||||
managers[manager].Set = managers[manager].Set.Difference(removedSet.Set)
|
||||
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() {
|
||||
if managers[manager].Set().Empty() {
|
||||
delete(managers, manager)
|
||||
}
|
||||
}
|
||||
|
||||
return managers, nil
|
||||
return managers, compare, nil
|
||||
}
|
||||
|
||||
// Update is the method you should call once you've merged your final
|
||||
|
|
@ -119,54 +119,66 @@ func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpat
|
|||
// 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) (fieldpath.ManagedFields, error) {
|
||||
func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string) (*typed.TypedValue, fieldpath.ManagedFields, error) {
|
||||
var err error
|
||||
managers = shallowCopyManagers(managers)
|
||||
managers, err = s.update(liveObject, newObject, version, managers, manager, true)
|
||||
if err != nil {
|
||||
return fieldpath.ManagedFields{}, err
|
||||
}
|
||||
compare, err := liveObject.Compare(newObject)
|
||||
if err != nil {
|
||||
return fieldpath.ManagedFields{}, fmt.Errorf("failed to compare live and new objects: %v", err)
|
||||
}
|
||||
if _, ok := managers[manager]; !ok {
|
||||
managers[manager] = &fieldpath.VersionedSet{
|
||||
Set: fieldpath.NewSet(),
|
||||
if s.enableUnions {
|
||||
newObject, err = liveObject.NormalizeUnions(newObject)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
}
|
||||
managers[manager].Set = managers[manager].Set.Union(compare.Modified).Union(compare.Added).Difference(compare.Removed)
|
||||
managers[manager].APIVersion = version
|
||||
if managers[manager].Set.Empty() {
|
||||
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 managers, nil
|
||||
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) {
|
||||
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.VersionedSet{
|
||||
Set: set,
|
||||
APIVersion: version,
|
||||
Applied: true,
|
||||
}
|
||||
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)
|
||||
managers, _, err = s.update(liveObject, newObject, version, managers, manager, force)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
|
|
@ -185,18 +197,18 @@ func shallowCopyManagers(managers fieldpath.ManagedFields) fieldpath.ManagedFiel
|
|||
// * 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() {
|
||||
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)
|
||||
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 := 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)
|
||||
|
|
@ -205,20 +217,20 @@ func (s *Updater) prune(merged typed.TypedValue, managers fieldpath.ManagedField
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed add back dangling items: %v", err)
|
||||
}
|
||||
return s.Converter.Convert(pruned, managers[applyingManager].APIVersion)
|
||||
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) {
|
||||
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()
|
||||
if managerSet.Applied() {
|
||||
if _, ok := managedAtVersion[managerSet.APIVersion()]; !ok {
|
||||
managedAtVersion[managerSet.APIVersion()] = fieldpath.NewSet()
|
||||
}
|
||||
managedAtVersion[managerSet.APIVersion] = managedAtVersion[managerSet.APIVersion].Union(managerSet.Set)
|
||||
managedAtVersion[managerSet.APIVersion()] = managedAtVersion[managerSet.APIVersion()].Union(managerSet.Set())
|
||||
}
|
||||
}
|
||||
for version, managed := range managedAtVersion {
|
||||
|
|
@ -249,12 +261,11 @@ func (s *Updater) addBackOwnedItems(merged, pruned typed.TypedValue, managedFiel
|
|||
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)
|
||||
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
|
||||
|
|
@ -269,5 +280,5 @@ func (s *Updater) addBackDanglingItems(merged, pruned typed.TypedValue, lastSet
|
|||
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
|
||||
return merged.RemoveItems(mergedSet.Difference(prunedSet).Intersection(lastSet.Set())), nil
|
||||
}
|
||||
|
|
|
|||
186
vendor/sigs.k8s.io/structured-merge-diff/schema/elements.go
generated
vendored
186
vendor/sigs.k8s.io/structured-merge-diff/schema/elements.go
generated
vendored
|
|
@ -16,9 +16,17 @@ 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.
|
||||
|
|
@ -43,13 +51,12 @@ type TypeRef struct {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
// Exactly one of the below must be set.
|
||||
*Scalar `yaml:"scalar,omitempty"`
|
||||
*Struct `yaml:"struct,omitempty"`
|
||||
*List `yaml:"list,omitempty"`
|
||||
*Map `yaml:"map,omitempty"`
|
||||
*Untyped `yaml:"untyped,omitempty"`
|
||||
*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
|
||||
|
|
@ -65,45 +72,122 @@ const (
|
|||
)
|
||||
|
||||
// ElementRelationship is an enum of the different possible relationships
|
||||
// between the elements of container types (maps, lists, structs, untyped).
|
||||
// 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, structs, untyped) behave
|
||||
// as scalars / leaf fields (which is the default for untyped data).
|
||||
// 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 and structs).
|
||||
// relationship (default behavior for maps).
|
||||
Separable = ElementRelationship("separable")
|
||||
)
|
||||
|
||||
// Struct represents a type which is composed of a number of different fields.
|
||||
// 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.
|
||||
//
|
||||
// TODO: in the future, we will add one-of groups (sometimes called unions).
|
||||
type Struct struct {
|
||||
// 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"`
|
||||
|
||||
// TODO: Implement unions, either this way or by inlining.
|
||||
// Unions are groupings of fields with special rules. They may refer to
|
||||
// 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.
|
||||
// Unions []Union `yaml:"unions,omitempty"`
|
||||
// One can have multiple unions in the same struct, but the fields can't
|
||||
// overlap between unions.
|
||||
Unions []Union `yaml:"unions,omitempty"`
|
||||
|
||||
// ElementRelationship states the relationship between the struct's items.
|
||||
// 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 structs is `separable`; it's permitted to
|
||||
// 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.
|
||||
|
|
@ -129,15 +213,14 @@ type List struct {
|
|||
// * `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 struct, the list is treated as a map.
|
||||
// - The list element must not be a map or a list itself.
|
||||
// - 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
|
||||
// struct, then Keys must have non-zero length, and it lists the fields
|
||||
// of the element's struct type which are to be used as the keys of the
|
||||
// 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.
|
||||
|
|
@ -146,60 +229,17 @@ type List struct {
|
|||
Keys []string `yaml:"keys,omitempty"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Although serialized the same, maps are different from structs in that each
|
||||
// map item must have the same type.
|
||||
//
|
||||
// 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).
|
||||
type Map struct {
|
||||
// ElementType is the type of the list's elements.
|
||||
ElementType TypeRef `yaml:"elementType,omitempty"`
|
||||
|
||||
// ElementRelationship states the relationship between the map's items.
|
||||
// * `separable` 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.
|
||||
// TODO: find a simple example.
|
||||
// The default behavior for maps is `separable`; it's permitted to
|
||||
// leave this unset to get the default behavior.
|
||||
ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
|
||||
}
|
||||
|
||||
// Untyped represents types that allow arbitrary content. (Think: plugin
|
||||
// objects.)
|
||||
type Untyped struct {
|
||||
// ElementRelationship states the relationship between the items, if
|
||||
// container-typed data happens to be present here.
|
||||
// * `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.
|
||||
// TODO: support "guess" (guesses at associative list keys)
|
||||
// TODO: support "lookup" (calls a lookup function to figure out the
|
||||
// schema based on the data)
|
||||
// The default behavior for untyped data is `atomic`; it's permitted to
|
||||
// leave this unset to get the default behavior.
|
||||
ElementRelationship ElementRelationship `yaml:"elementRelationship,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) {
|
||||
for _, t := range s.Types {
|
||||
if t.Name == name {
|
||||
return t, true
|
||||
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
|
||||
}
|
||||
}
|
||||
return TypeDef{}, false
|
||||
})
|
||||
t, ok := s.m[name]
|
||||
return t, ok
|
||||
}
|
||||
|
||||
// Resolve is a convenience function which returns the atom referenced, whether
|
||||
|
|
|
|||
166
vendor/sigs.k8s.io/structured-merge-diff/schema/equals.go
generated
vendored
Normal file
166
vendor/sigs.k8s.io/structured-merge-diff/schema/equals.go
generated
vendored
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
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 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.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.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.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.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.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.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.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.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
|
||||
}
|
||||
57
vendor/sigs.k8s.io/structured-merge-diff/schema/fromvalue.go
generated
vendored
57
vendor/sigs.k8s.io/structured-merge-diff/schema/fromvalue.go
generated
vendored
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// TypeRefFromValue creates an inlined type from a value v
|
||||
func TypeRefFromValue(v value.Value) TypeRef {
|
||||
atom := atomFor(v)
|
||||
return TypeRef{
|
||||
Inlined: atom,
|
||||
}
|
||||
}
|
||||
|
||||
func atomFor(v value.Value) Atom {
|
||||
switch {
|
||||
// Untyped cases (handled at the bottom of this function)
|
||||
case v.Null:
|
||||
case v.ListValue != nil:
|
||||
case v.FloatValue != nil:
|
||||
case v.IntValue != nil:
|
||||
case v.StringValue != nil:
|
||||
case v.BooleanValue != nil:
|
||||
// Recursive case
|
||||
case v.MapValue != nil:
|
||||
s := Struct{}
|
||||
for i := range v.MapValue.Items {
|
||||
child := v.MapValue.Items[i]
|
||||
field := StructField{
|
||||
Name: child.Name,
|
||||
Type: TypeRef{
|
||||
Inlined: atomFor(child.Value),
|
||||
},
|
||||
}
|
||||
s.Fields = append(s.Fields, field)
|
||||
}
|
||||
return Atom{Struct: &s}
|
||||
}
|
||||
|
||||
return Atom{Untyped: &Untyped{}}
|
||||
}
|
||||
78
vendor/sigs.k8s.io/structured-merge-diff/schema/schemaschema.go
generated
vendored
78
vendor/sigs.k8s.io/structured-merge-diff/schema/schemaschema.go
generated
vendored
|
|
@ -20,7 +20,7 @@ package schema
|
|||
// It will validate itself. It can be unmarshalled into a Schema type.
|
||||
var SchemaSchemaYAML = `types:
|
||||
- name: schema
|
||||
struct:
|
||||
map:
|
||||
fields:
|
||||
- name: types
|
||||
type:
|
||||
|
|
@ -31,7 +31,7 @@ var SchemaSchemaYAML = `types:
|
|||
keys:
|
||||
- name
|
||||
- name: typeDef
|
||||
struct:
|
||||
map:
|
||||
fields:
|
||||
- name: name
|
||||
type:
|
||||
|
|
@ -39,20 +39,17 @@ var SchemaSchemaYAML = `types:
|
|||
- name: scalar
|
||||
type:
|
||||
scalar: string
|
||||
- name: struct
|
||||
type:
|
||||
namedType: struct
|
||||
- name: list
|
||||
type:
|
||||
namedType: list
|
||||
- name: map
|
||||
type:
|
||||
namedType: map
|
||||
- name: list
|
||||
type:
|
||||
namedType: list
|
||||
- name: untyped
|
||||
type:
|
||||
namedType: untyped
|
||||
- name: typeRef
|
||||
struct:
|
||||
map:
|
||||
fields:
|
||||
- name: namedType
|
||||
type:
|
||||
|
|
@ -60,22 +57,19 @@ var SchemaSchemaYAML = `types:
|
|||
- name: scalar
|
||||
type:
|
||||
scalar: string
|
||||
- name: struct
|
||||
type:
|
||||
namedType: struct
|
||||
- name: list
|
||||
type:
|
||||
namedType: list
|
||||
- name: map
|
||||
type:
|
||||
namedType: map
|
||||
- name: list
|
||||
type:
|
||||
namedType: list
|
||||
- name: untyped
|
||||
type:
|
||||
namedType: untyped
|
||||
- name: scalar
|
||||
scalar: string
|
||||
- name: struct
|
||||
struct:
|
||||
- name: map
|
||||
map:
|
||||
fields:
|
||||
- name: fields
|
||||
type:
|
||||
|
|
@ -84,11 +78,46 @@ var SchemaSchemaYAML = `types:
|
|||
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
|
||||
struct:
|
||||
map:
|
||||
fields:
|
||||
- name: name
|
||||
type:
|
||||
|
|
@ -97,7 +126,7 @@ var SchemaSchemaYAML = `types:
|
|||
type:
|
||||
namedType: typeRef
|
||||
- name: list
|
||||
struct:
|
||||
map:
|
||||
fields:
|
||||
- name: elementType
|
||||
type:
|
||||
|
|
@ -110,17 +139,8 @@ var SchemaSchemaYAML = `types:
|
|||
list:
|
||||
elementType:
|
||||
scalar: string
|
||||
- name: map
|
||||
struct:
|
||||
fields:
|
||||
- name: elementType
|
||||
type:
|
||||
namedType: typeRef
|
||||
- name: elementRelationship
|
||||
type:
|
||||
scalar: string
|
||||
- name: untyped
|
||||
struct:
|
||||
map:
|
||||
fields:
|
||||
- name: elementRelationship
|
||||
type:
|
||||
|
|
|
|||
178
vendor/sigs.k8s.io/structured-merge-diff/typed/deduced.go
generated
vendored
178
vendor/sigs.k8s.io/structured-merge-diff/typed/deduced.go
generated
vendored
|
|
@ -1,178 +0,0 @@
|
|||
/*
|
||||
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 (
|
||||
"reflect"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// deducedTypedValue holds a value and guesses what it is and what to
|
||||
// do with it.
|
||||
type deducedTypedValue struct {
|
||||
value value.Value
|
||||
}
|
||||
|
||||
// AsTypedDeduced is going to generate it's own type definition based on
|
||||
// the content of the object. This is useful for CRDs that don't have a
|
||||
// validation field.
|
||||
func AsTypedDeduced(v value.Value) TypedValue {
|
||||
return deducedTypedValue{value: v}
|
||||
}
|
||||
|
||||
func (dv deducedTypedValue) AsValue() *value.Value {
|
||||
return &dv.value
|
||||
}
|
||||
|
||||
func (deducedTypedValue) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dv deducedTypedValue) ToFieldSet() (*fieldpath.Set, error) {
|
||||
set := fieldpath.NewSet()
|
||||
fieldsetDeduced(dv.value, fieldpath.Path{}, set)
|
||||
return set, nil
|
||||
}
|
||||
|
||||
func fieldsetDeduced(v value.Value, path fieldpath.Path, set *fieldpath.Set) {
|
||||
if v.MapValue == nil {
|
||||
set.Insert(path)
|
||||
return
|
||||
}
|
||||
|
||||
// We have a map.
|
||||
// copy the existing path, append each item, and recursively call.
|
||||
for i := range v.MapValue.Items {
|
||||
child := v.MapValue.Items[i]
|
||||
np := path.Copy()
|
||||
np = append(np, fieldpath.PathElement{FieldName: &child.Name})
|
||||
fieldsetDeduced(child.Value, np, set)
|
||||
}
|
||||
}
|
||||
|
||||
func (dv deducedTypedValue) Merge(pso TypedValue) (TypedValue, error) {
|
||||
tpso, ok := pso.(deducedTypedValue)
|
||||
if !ok {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("can't merge deducedTypedValue with %T", tpso)
|
||||
}
|
||||
return AsTypedDeduced(mergeDeduced(dv.value, tpso.value)), nil
|
||||
}
|
||||
|
||||
func mergeDeduced(lhs, rhs value.Value) value.Value {
|
||||
// If both sides are maps, merge them, otherwise return right
|
||||
// side.
|
||||
if rhs.MapValue == nil || lhs.MapValue == nil {
|
||||
return rhs
|
||||
}
|
||||
|
||||
v := value.Value{MapValue: &value.Map{}}
|
||||
for i := range lhs.MapValue.Items {
|
||||
child := lhs.MapValue.Items[i]
|
||||
v.MapValue.Set(child.Name, child.Value)
|
||||
}
|
||||
for i := range rhs.MapValue.Items {
|
||||
child := rhs.MapValue.Items[i]
|
||||
if sub, ok := v.MapValue.Get(child.Name); ok {
|
||||
new := mergeDeduced(sub.Value, child.Value)
|
||||
v.MapValue.Set(child.Name, new)
|
||||
} else {
|
||||
v.MapValue.Set(child.Name, child.Value)
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (dv deducedTypedValue) Compare(rhs TypedValue) (c *Comparison, err error) {
|
||||
trhs, ok := rhs.(deducedTypedValue)
|
||||
if !ok {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("can't merge deducedTypedValue with %T", rhs)
|
||||
}
|
||||
|
||||
c = &Comparison{
|
||||
Removed: fieldpath.NewSet(),
|
||||
Modified: fieldpath.NewSet(),
|
||||
Added: fieldpath.NewSet(),
|
||||
}
|
||||
|
||||
added(dv.value, trhs.value, fieldpath.Path{}, c.Added)
|
||||
added(trhs.value, dv.value, fieldpath.Path{}, c.Removed)
|
||||
modified(dv.value, trhs.value, fieldpath.Path{}, c.Modified)
|
||||
|
||||
merge, err := dv.Merge(rhs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Merged = merge
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func added(lhs, rhs value.Value, path fieldpath.Path, set *fieldpath.Set) {
|
||||
if lhs.MapValue == nil && rhs.MapValue == nil {
|
||||
// Both non-maps, nothing added, do nothing.
|
||||
} else if lhs.MapValue == nil && rhs.MapValue != nil {
|
||||
// From leaf to map, add leaf fields of map.
|
||||
fieldsetDeduced(rhs, path, set)
|
||||
} else if lhs.MapValue != nil && rhs.MapValue == nil {
|
||||
// Went from map to field, add field.
|
||||
set.Insert(path)
|
||||
} else {
|
||||
// Both are maps.
|
||||
for i := range rhs.MapValue.Items {
|
||||
child := rhs.MapValue.Items[i]
|
||||
np := path.Copy()
|
||||
np = append(np, fieldpath.PathElement{FieldName: &child.Name})
|
||||
|
||||
if v, ok := lhs.MapValue.Get(child.Name); ok {
|
||||
added(v.Value, child.Value, np, set)
|
||||
} else {
|
||||
fieldsetDeduced(child.Value, np, set)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func modified(lhs, rhs value.Value, path fieldpath.Path, set *fieldpath.Set) {
|
||||
if lhs.MapValue == nil && rhs.MapValue == nil {
|
||||
if !reflect.DeepEqual(lhs, rhs) {
|
||||
set.Insert(path)
|
||||
}
|
||||
} else if lhs.MapValue != nil && rhs.MapValue != nil {
|
||||
// Both are maps.
|
||||
for i := range rhs.MapValue.Items {
|
||||
child := rhs.MapValue.Items[i]
|
||||
|
||||
v, ok := lhs.MapValue.Get(child.Name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
np := path.Copy()
|
||||
np = append(np, fieldpath.PathElement{FieldName: &child.Name})
|
||||
modified(v.Value, child.Value, np, set)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveItems does nothing because all lists in a deducedTypedValue are considered atomic,
|
||||
// and there are no maps because it is indistinguishable from a struct.
|
||||
func (dv deducedTypedValue) RemoveItems(_ *fieldpath.Set) TypedValue {
|
||||
return dv
|
||||
}
|
||||
84
vendor/sigs.k8s.io/structured-merge-diff/typed/helpers.go
generated
vendored
84
vendor/sigs.k8s.io/structured-merge-diff/typed/helpers.go
generated
vendored
|
|
@ -67,6 +67,14 @@ func (ef *errorFormatter) descend(pe fieldpath.PathElement) {
|
|||
ef.path = append(ef.path, pe)
|
||||
}
|
||||
|
||||
// parent returns the parent, for the purpose of buffer reuse. It's an error to
|
||||
// call this if there is no parent.
|
||||
func (ef *errorFormatter) parent() errorFormatter {
|
||||
return errorFormatter{
|
||||
path: ef.path[:len(ef.path)-1],
|
||||
}
|
||||
}
|
||||
|
||||
func (ef errorFormatter) errorf(format string, args ...interface{}) ValidationErrors {
|
||||
return ValidationErrors{{
|
||||
Path: append(fieldpath.Path{}, ef.path...),
|
||||
|
|
@ -89,32 +97,44 @@ func (ef errorFormatter) prefixError(prefix string, err error) ValidationErrors
|
|||
}
|
||||
|
||||
type atomHandler interface {
|
||||
doScalar(schema.Scalar) ValidationErrors
|
||||
doStruct(schema.Struct) ValidationErrors
|
||||
doList(schema.List) ValidationErrors
|
||||
doMap(schema.Map) ValidationErrors
|
||||
doUntyped(schema.Untyped) ValidationErrors
|
||||
doScalar(*schema.Scalar) ValidationErrors
|
||||
doList(*schema.List) ValidationErrors
|
||||
doMap(*schema.Map) ValidationErrors
|
||||
|
||||
errorf(msg string, args ...interface{}) ValidationErrors
|
||||
}
|
||||
|
||||
func resolveSchema(s *schema.Schema, tr schema.TypeRef, ah atomHandler) ValidationErrors {
|
||||
func resolveSchema(s *schema.Schema, tr schema.TypeRef, v *value.Value, ah atomHandler) ValidationErrors {
|
||||
a, ok := s.Resolve(tr)
|
||||
if !ok {
|
||||
return ah.errorf("schema error: no type found matching: %v", *tr.NamedType)
|
||||
}
|
||||
|
||||
a = deduceAtom(a, v)
|
||||
return handleAtom(a, tr, ah)
|
||||
}
|
||||
|
||||
func deduceAtom(a schema.Atom, v *value.Value) schema.Atom {
|
||||
switch {
|
||||
case v == nil:
|
||||
case v.FloatValue != nil, v.IntValue != nil, v.StringValue != nil, v.BooleanValue != nil:
|
||||
return schema.Atom{Scalar: a.Scalar}
|
||||
case v.ListValue != nil:
|
||||
return schema.Atom{List: a.List}
|
||||
case v.MapValue != nil:
|
||||
return schema.Atom{Map: a.Map}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func handleAtom(a schema.Atom, tr schema.TypeRef, ah atomHandler) ValidationErrors {
|
||||
switch {
|
||||
case a.Scalar != nil:
|
||||
return ah.doScalar(*a.Scalar)
|
||||
case a.Struct != nil:
|
||||
return ah.doStruct(*a.Struct)
|
||||
case a.List != nil:
|
||||
return ah.doList(*a.List)
|
||||
case a.Map != nil:
|
||||
return ah.doMap(*a.Map)
|
||||
case a.Untyped != nil:
|
||||
return ah.doUntyped(*a.Untyped)
|
||||
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"
|
||||
|
|
@ -125,14 +145,14 @@ func resolveSchema(s *schema.Schema, tr schema.TypeRef, ah atomHandler) Validati
|
|||
return ah.errorf("schema error: invalid atom: %v", name)
|
||||
}
|
||||
|
||||
func (ef errorFormatter) validateScalar(t schema.Scalar, v *value.Value, prefix string) (errs ValidationErrors) {
|
||||
func (ef errorFormatter) validateScalar(t *schema.Scalar, v *value.Value, prefix string) (errs ValidationErrors) {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
if v.Null {
|
||||
return nil
|
||||
}
|
||||
switch t {
|
||||
switch *t {
|
||||
case schema.Numeric:
|
||||
if v.FloatValue == nil && v.IntValue == nil {
|
||||
// TODO: should the schema separate int and float?
|
||||
|
|
@ -164,30 +184,18 @@ func listValue(val value.Value) (*value.List, error) {
|
|||
}
|
||||
|
||||
// Returns the map, or an error. Reminder: nil is a valid map and might be returned.
|
||||
func mapOrStructValue(val value.Value, typeName string) (*value.Map, error) {
|
||||
func mapValue(val value.Value) (*value.Map, error) {
|
||||
switch {
|
||||
case val.Null:
|
||||
return nil, nil
|
||||
case val.MapValue != nil:
|
||||
return val.MapValue, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("expected %v, got %v", typeName, val)
|
||||
return nil, fmt.Errorf("expected map, got %v", val)
|
||||
}
|
||||
}
|
||||
|
||||
func (ef errorFormatter) rejectExtraStructFields(m *value.Map, allowedNames map[string]struct{}, prefix string) (errs ValidationErrors) {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
for _, f := range m.Items {
|
||||
if _, allowed := allowedNames[f.Name]; !allowed {
|
||||
errs = append(errs, ef.errorf("%vfield %v is not mentioned in the schema", prefix, f.Name)...)
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func keyedAssociativeListItemToPathElement(list schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
|
||||
func keyedAssociativeListItemToPathElement(list *schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
|
||||
pe := fieldpath.PathElement{}
|
||||
if child.Null {
|
||||
// For now, the keys are required which means that null entries
|
||||
|
|
@ -197,6 +205,7 @@ func keyedAssociativeListItemToPathElement(list schema.List, index int, child va
|
|||
if child.MapValue == nil {
|
||||
return pe, errors.New("associative list with keys may not have non-map elements")
|
||||
}
|
||||
keyMap := value.FieldList{}
|
||||
for _, fieldName := range list.Keys {
|
||||
var fieldValue value.Value
|
||||
field, ok := child.MapValue.Get(fieldName)
|
||||
|
|
@ -206,15 +215,14 @@ func keyedAssociativeListItemToPathElement(list schema.List, index int, child va
|
|||
// Treat keys as required.
|
||||
return pe, fmt.Errorf("associative list with keys has an element that omits key field %q", fieldName)
|
||||
}
|
||||
pe.Key = append(pe.Key, value.Field{
|
||||
Name: fieldName,
|
||||
Value: fieldValue,
|
||||
})
|
||||
keyMap = append(keyMap, value.Field{Name: fieldName, Value: fieldValue})
|
||||
}
|
||||
keyMap.Sort()
|
||||
pe.Key = &keyMap
|
||||
return pe, nil
|
||||
}
|
||||
|
||||
func setItemToPathElement(list schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
|
||||
func setItemToPathElement(list *schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
|
||||
pe := fieldpath.PathElement{}
|
||||
switch {
|
||||
case child.MapValue != nil:
|
||||
|
|
@ -234,7 +242,7 @@ func setItemToPathElement(list schema.List, index int, child value.Value) (field
|
|||
}
|
||||
}
|
||||
|
||||
func listItemToPathElement(list schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
|
||||
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)
|
||||
|
|
|
|||
228
vendor/sigs.k8s.io/structured-merge-diff/typed/merge.go
generated
vendored
228
vendor/sigs.k8s.io/structured-merge-diff/typed/merge.go
generated
vendored
|
|
@ -41,6 +41,9 @@ type mergingWalker struct {
|
|||
|
||||
// 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
|
||||
|
|
@ -61,12 +64,26 @@ var (
|
|||
)
|
||||
|
||||
// merge sets w.out.
|
||||
func (w *mergingWalker) merge() ValidationErrors {
|
||||
func (w *mergingWalker) merge() (errs ValidationErrors) {
|
||||
if w.lhs == nil && w.rhs == nil {
|
||||
// check this condidition here instead of everywhere below.
|
||||
return w.errorf("at least one of lhs and rhs must be provided")
|
||||
}
|
||||
errs := resolveSchema(w.schema, w.typeRef, w)
|
||||
a, ok := w.schema.Resolve(w.typeRef)
|
||||
if !ok {
|
||||
return w.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)
|
||||
}
|
||||
|
|
@ -87,7 +104,7 @@ func (w *mergingWalker) doLeaf() {
|
|||
w.rule(w)
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doScalar(t schema.Scalar) (errs ValidationErrors) {
|
||||
func (w *mergingWalker) doScalar(t *schema.Scalar) (errs ValidationErrors) {
|
||||
errs = append(errs, w.validateScalar(t, w.lhs, "lhs: ")...)
|
||||
errs = append(errs, w.validateScalar(t, w.rhs, "rhs: ")...)
|
||||
if len(errs) > 0 {
|
||||
|
|
@ -101,70 +118,39 @@ func (w *mergingWalker) doScalar(t schema.Scalar) (errs ValidationErrors) {
|
|||
}
|
||||
|
||||
func (w *mergingWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef) *mergingWalker {
|
||||
w2 := *w
|
||||
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.errorFormatter.descend(pe)
|
||||
w2.lhs = nil
|
||||
w2.rhs = nil
|
||||
w2.out = nil
|
||||
return &w2
|
||||
return w2
|
||||
}
|
||||
|
||||
func (w *mergingWalker) visitStructFields(t schema.Struct, lhs, rhs *value.Map) (errs ValidationErrors) {
|
||||
out := &value.Map{}
|
||||
|
||||
valOrNil := func(m *value.Map, name string) *value.Value {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
val, ok := m.Get(name)
|
||||
if ok {
|
||||
return &val.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
allowedNames := map[string]struct{}{}
|
||||
for i := range t.Fields {
|
||||
// I don't want to use the loop variable since a reference
|
||||
// might outlive the loop iteration (in an error message).
|
||||
f := t.Fields[i]
|
||||
allowedNames[f.Name] = struct{}{}
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &f.Name}, f.Type)
|
||||
w2.lhs = valOrNil(lhs, f.Name)
|
||||
w2.rhs = valOrNil(rhs, f.Name)
|
||||
if w2.lhs == nil && w2.rhs == nil {
|
||||
// All fields are optional
|
||||
continue
|
||||
}
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
} else if w2.out != nil {
|
||||
out.Set(f.Name, *w2.out)
|
||||
}
|
||||
}
|
||||
|
||||
// All fields may be optional, but unknown fields are not allowed.
|
||||
errs = append(errs, w.rejectExtraStructFields(lhs, allowedNames, "lhs: ")...)
|
||||
errs = append(errs, w.rejectExtraStructFields(rhs, allowedNames, "rhs: ")...)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
if len(out.Items) > 0 {
|
||||
w.out = &value.Value{MapValue: out}
|
||||
}
|
||||
|
||||
return errs
|
||||
func (w *mergingWalker) finishDescent(w2 *mergingWalker) {
|
||||
// if the descent caused a realloc, ensure that we reuse the buffer
|
||||
// for the next sibling.
|
||||
w.errorFormatter = w2.errorFormatter.parent()
|
||||
*w.spareWalkers = append(*w.spareWalkers, w2)
|
||||
}
|
||||
|
||||
func (w *mergingWalker) derefMapOrStruct(prefix, typeName string, v *value.Value, dest **value.Map) (errs ValidationErrors) {
|
||||
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 := mapOrStructValue(*v, typeName)
|
||||
m, err := mapValue(*v)
|
||||
if err != nil {
|
||||
return w.prefixError(prefix, err)
|
||||
}
|
||||
|
|
@ -172,38 +158,7 @@ func (w *mergingWalker) derefMapOrStruct(prefix, typeName string, v *value.Value
|
|||
return nil
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doStruct(t schema.Struct) (errs ValidationErrors) {
|
||||
var lhs, rhs *value.Map
|
||||
errs = append(errs, w.derefMapOrStruct("lhs: ", "struct", w.lhs, &lhs)...)
|
||||
errs = append(errs, w.derefMapOrStruct("rhs: ", "struct", w.rhs, &rhs)...)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
// If both lhs and rhs are empty/null, treat it as a
|
||||
// leaf: this helps preserve the empty/null
|
||||
// distinction.
|
||||
emptyPromoteToLeaf := (lhs == nil || len(lhs.Items) == 0) &&
|
||||
(rhs == nil || len(rhs.Items) == 0)
|
||||
|
||||
if t.ElementRelationship == schema.Atomic || emptyPromoteToLeaf {
|
||||
w.doLeaf()
|
||||
return nil
|
||||
}
|
||||
|
||||
if lhs == nil && rhs == nil {
|
||||
// nil is a valid map!
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = w.visitStructFields(t, lhs, rhs)
|
||||
|
||||
// TODO: Check unions.
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (errs ValidationErrors) {
|
||||
func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs *value.List) (errs ValidationErrors) {
|
||||
out := &value.List{}
|
||||
|
||||
// TODO: ordering is totally wrong.
|
||||
|
|
@ -213,8 +168,9 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
|
|||
rhsOrder := []fieldpath.PathElement{}
|
||||
|
||||
// First, collect all RHS children.
|
||||
observedRHS := map[string]value.Value{}
|
||||
var observedRHS fieldpath.PathElementValueMap
|
||||
if rhs != nil {
|
||||
observedRHS = fieldpath.MakePathElementValueMap(len(rhs.Items))
|
||||
for i, child := range rhs.Items {
|
||||
pe, err := listItemToPathElement(t, i, child)
|
||||
if err != nil {
|
||||
|
|
@ -224,18 +180,18 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
|
|||
// this element.
|
||||
continue
|
||||
}
|
||||
keyStr := pe.String()
|
||||
if _, found := observedRHS[keyStr]; found {
|
||||
errs = append(errs, w.errorf("rhs: duplicate entries for key %v", keyStr)...)
|
||||
if _, ok := observedRHS.Get(pe); ok {
|
||||
errs = append(errs, w.errorf("rhs: duplicate entries for key %v", pe.String())...)
|
||||
}
|
||||
observedRHS[keyStr] = child
|
||||
observedRHS.Insert(pe, child)
|
||||
rhsOrder = append(rhsOrder, pe)
|
||||
}
|
||||
}
|
||||
|
||||
// Then merge with LHS children.
|
||||
observedLHS := map[string]struct{}{}
|
||||
var observedLHS fieldpath.PathElementSet
|
||||
if lhs != nil {
|
||||
observedLHS = fieldpath.MakePathElementSet(len(lhs.Items))
|
||||
for i, child := range lhs.Items {
|
||||
pe, err := listItemToPathElement(t, i, child)
|
||||
if err != nil {
|
||||
|
|
@ -245,15 +201,14 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
|
|||
// this element.
|
||||
continue
|
||||
}
|
||||
keyStr := pe.String()
|
||||
if _, found := observedLHS[keyStr]; found {
|
||||
errs = append(errs, w.errorf("lhs: duplicate entries for key %v", keyStr)...)
|
||||
if observedLHS.Has(pe) {
|
||||
errs = append(errs, w.errorf("lhs: duplicate entries for key %v", pe.String())...)
|
||||
continue
|
||||
}
|
||||
observedLHS[keyStr] = struct{}{}
|
||||
observedLHS.Insert(pe)
|
||||
w2 := w.prepareDescent(pe, t.ElementType)
|
||||
w2.lhs = &child
|
||||
if rchild, ok := observedRHS[keyStr]; ok {
|
||||
if rchild, ok := observedRHS.Get(pe); ok {
|
||||
w2.rhs = &rchild
|
||||
}
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
|
|
@ -261,21 +216,23 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
|
|||
} else if w2.out != nil {
|
||||
out.Items = append(out.Items, *w2.out)
|
||||
}
|
||||
// Keep track of children that have been handled
|
||||
delete(observedRHS, keyStr)
|
||||
w.finishDescent(w2)
|
||||
}
|
||||
}
|
||||
|
||||
for _, rhsToCheck := range rhsOrder {
|
||||
if unmergedChild, ok := observedRHS[rhsToCheck.String()]; ok {
|
||||
w2 := w.prepareDescent(rhsToCheck, t.ElementType)
|
||||
w2.rhs = &unmergedChild
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
} else if w2.out != nil {
|
||||
out.Items = append(out.Items, *w2.out)
|
||||
}
|
||||
for _, pe := range rhsOrder {
|
||||
if observedLHS.Has(pe) {
|
||||
continue
|
||||
}
|
||||
value, _ := observedRHS.Get(pe)
|
||||
w2 := w.prepareDescent(pe, t.ElementType)
|
||||
w2.rhs = &value
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
} else if w2.out != nil {
|
||||
out.Items = append(out.Items, *w2.out)
|
||||
}
|
||||
w.finishDescent(w2)
|
||||
}
|
||||
|
||||
if len(out.Items) > 0 {
|
||||
|
|
@ -298,13 +255,10 @@ func (w *mergingWalker) derefList(prefix string, v *value.Value, dest **value.Li
|
|||
return nil
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doList(t schema.List) (errs ValidationErrors) {
|
||||
func (w *mergingWalker) doList(t *schema.List) (errs ValidationErrors) {
|
||||
var lhs, rhs *value.List
|
||||
errs = append(errs, w.derefList("lhs: ", w.lhs, &lhs)...)
|
||||
errs = append(errs, w.derefList("rhs: ", w.rhs, &rhs)...)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
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
|
||||
|
|
@ -326,13 +280,17 @@ func (w *mergingWalker) doList(t schema.List) (errs ValidationErrors) {
|
|||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs ValidationErrors) {
|
||||
func (w *mergingWalker) visitMapItems(t *schema.Map, lhs, rhs *value.Map) (errs ValidationErrors) {
|
||||
out := &value.Map{}
|
||||
|
||||
if lhs != nil {
|
||||
for _, litem := range lhs.Items {
|
||||
name := litem.Name
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &name}, t.ElementType)
|
||||
for i := range lhs.Items {
|
||||
litem := &lhs.Items[i]
|
||||
fieldType := t.ElementType
|
||||
if sf, ok := t.FindField(litem.Name); ok {
|
||||
fieldType = sf.Type
|
||||
}
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &litem.Name}, fieldType)
|
||||
w2.lhs = &litem.Value
|
||||
if rhs != nil {
|
||||
if ritem, ok := rhs.Get(litem.Name); ok {
|
||||
|
|
@ -342,27 +300,33 @@ func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs V
|
|||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
} else if w2.out != nil {
|
||||
out.Set(name, *w2.out)
|
||||
out.Items = append(out.Items, value.Field{litem.Name, *w2.out})
|
||||
}
|
||||
w.finishDescent(w2)
|
||||
}
|
||||
}
|
||||
|
||||
if rhs != nil {
|
||||
for _, ritem := range rhs.Items {
|
||||
for j := range rhs.Items {
|
||||
ritem := &rhs.Items[j]
|
||||
if lhs != nil {
|
||||
if _, ok := lhs.Get(ritem.Name); ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
name := ritem.Name
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &name}, t.ElementType)
|
||||
fieldType := t.ElementType
|
||||
if sf, ok := t.FindField(ritem.Name); ok {
|
||||
fieldType = sf.Type
|
||||
}
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &ritem.Name}, fieldType)
|
||||
w2.rhs = &ritem.Value
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
} else if w2.out != nil {
|
||||
out.Set(name, *w2.out)
|
||||
out.Items = append(out.Items, value.Field{ritem.Name, *w2.out})
|
||||
}
|
||||
w.finishDescent(w2)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -372,13 +336,10 @@ func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs V
|
|||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doMap(t schema.Map) (errs ValidationErrors) {
|
||||
func (w *mergingWalker) doMap(t *schema.Map) (errs ValidationErrors) {
|
||||
var lhs, rhs *value.Map
|
||||
errs = append(errs, w.derefMapOrStruct("lhs: ", "map", w.lhs, &lhs)...)
|
||||
errs = append(errs, w.derefMapOrStruct("rhs: ", "map", w.rhs, &rhs)...)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
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
|
||||
|
|
@ -395,16 +356,7 @@ func (w *mergingWalker) doMap(t schema.Map) (errs ValidationErrors) {
|
|||
return nil
|
||||
}
|
||||
|
||||
errs = w.visitMapItems(t, lhs, rhs)
|
||||
errs = append(errs, w.visitMapItems(t, lhs, rhs)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (w *mergingWalker) doUntyped(t schema.Untyped) (errs ValidationErrors) {
|
||||
if t.ElementRelationship == "" || t.ElementRelationship == schema.Atomic {
|
||||
// Untyped sections allow anything, and are considered leaf
|
||||
// fields.
|
||||
w.doLeaf()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
92
vendor/sigs.k8s.io/structured-merge-diff/typed/parser.go
generated
vendored
92
vendor/sigs.k8s.io/structured-merge-diff/typed/parser.go
generated
vendored
|
|
@ -33,9 +33,9 @@ type Parser struct {
|
|||
}
|
||||
|
||||
// create builds an unvalidated parser.
|
||||
func create(schema YAMLObject) (*Parser, error) {
|
||||
func create(s YAMLObject) (*Parser, error) {
|
||||
p := Parser{}
|
||||
err := yaml.Unmarshal([]byte(schema), &p.Schema)
|
||||
err := yaml.Unmarshal([]byte(s), &p.Schema)
|
||||
return &p, err
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +55,11 @@ func NewParser(schema YAMLObject) (*Parser, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to validate schema: %v", err)
|
||||
}
|
||||
return create(schema)
|
||||
p, err := create(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// TypeNames returns a list of types this parser understands.
|
||||
|
|
@ -69,79 +73,65 @@ func (p *Parser) TypeNames() (names []string) {
|
|||
// 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{
|
||||
parser: p,
|
||||
typename: name,
|
||||
return ParseableType{
|
||||
Schema: &p.Schema,
|
||||
TypeRef: schema.TypeRef{NamedType: &name},
|
||||
}
|
||||
}
|
||||
|
||||
// ParseableType allows for easy production of typed objects.
|
||||
type ParseableType interface {
|
||||
IsValid() bool
|
||||
FromYAML(YAMLObject) (TypedValue, error)
|
||||
FromUnstructured(interface{}) (TypedValue, error)
|
||||
type ParseableType struct {
|
||||
TypeRef schema.TypeRef
|
||||
Schema *schema.Schema
|
||||
}
|
||||
|
||||
type parseableType struct {
|
||||
parser *Parser
|
||||
typename string
|
||||
}
|
||||
|
||||
var _ ParseableType = &parseableType{}
|
||||
|
||||
// IsValid return true if p's schema and typename are valid.
|
||||
func (p *parseableType) IsValid() bool {
|
||||
_, ok := p.parser.Schema.Resolve(schema.TypeRef{NamedType: &p.typename})
|
||||
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) {
|
||||
func (p ParseableType) FromYAML(object YAMLObject) (*TypedValue, error) {
|
||||
v, err := value.FromYAML([]byte(object))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AsTyped(v, &p.parser.Schema, p.typename)
|
||||
return AsTyped(v, p.Schema, p.TypeRef)
|
||||
}
|
||||
|
||||
// FromUnstructured converts a go interface to a TypedValue. It will return an
|
||||
// error if the resulting object fails schema validation.
|
||||
func (p *parseableType) FromUnstructured(in interface{}) (TypedValue, error) {
|
||||
func (p ParseableType) FromUnstructured(in interface{}) (*TypedValue, error) {
|
||||
v, err := value.FromUnstructured(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AsTyped(v, &p.parser.Schema, p.typename)
|
||||
return AsTyped(v, p.Schema, p.TypeRef)
|
||||
}
|
||||
|
||||
// DeducedParseableType is a ParseableType that deduces the type from
|
||||
// the content of the object.
|
||||
type DeducedParseableType struct{}
|
||||
|
||||
var _ ParseableType = DeducedParseableType{}
|
||||
|
||||
// IsValid always returns true for a DeducedParseableType.
|
||||
func (p DeducedParseableType) IsValid() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// FromYAML parses a yaml string into an object and deduces the type for
|
||||
// that object.
|
||||
func (p DeducedParseableType) FromYAML(object YAMLObject) (TypedValue, error) {
|
||||
v, err := value.FromYAML([]byte(object))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AsTypedDeduced(v), nil
|
||||
}
|
||||
|
||||
// FromUnstructured converts a go interface to a TypedValue. It will return an
|
||||
// error if the input object uses un-handled types.
|
||||
func (p DeducedParseableType) FromUnstructured(in interface{}) (TypedValue, error) {
|
||||
v, err := value.FromUnstructured(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return AsTypedDeduced(v), nil
|
||||
}
|
||||
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_")
|
||||
|
|
|
|||
53
vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go
generated
vendored
53
vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go
generated
vendored
|
|
@ -31,39 +31,16 @@ func removeItemsWithSchema(value *value.Value, toRemove *fieldpath.Set, schema *
|
|||
schema: schema,
|
||||
toRemove: toRemove,
|
||||
}
|
||||
resolveSchema(schema, typeRef, w)
|
||||
resolveSchema(schema, typeRef, value, w)
|
||||
}
|
||||
|
||||
// doLeaf should be called on leaves before descending into children, if there
|
||||
// will be a descent. It modifies w.inLeaf.
|
||||
func (w *removingWalker) doLeaf() ValidationErrors { return nil }
|
||||
|
||||
func (w *removingWalker) doScalar(t schema.Scalar) ValidationErrors { return nil }
|
||||
func (w *removingWalker) doScalar(t *schema.Scalar) ValidationErrors { return nil }
|
||||
|
||||
func (w *removingWalker) doStruct(t schema.Struct) ValidationErrors {
|
||||
s := w.value.MapValue
|
||||
|
||||
// If struct is null, empty, or atomic just return
|
||||
if s == nil || len(s.Items) == 0 || t.ElementRelationship == schema.Atomic {
|
||||
return nil
|
||||
}
|
||||
|
||||
fieldTypes := map[string]schema.TypeRef{}
|
||||
for _, structField := range t.Fields {
|
||||
fieldTypes[structField.Name] = structField.Type
|
||||
}
|
||||
|
||||
for i, _ := range s.Items {
|
||||
item := s.Items[i]
|
||||
pe := fieldpath.PathElement{FieldName: &item.Name}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
removeItemsWithSchema(&s.Items[i].Value, subset, w.schema, fieldTypes[item.Name])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *removingWalker) doList(t schema.List) (errs ValidationErrors) {
|
||||
func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) {
|
||||
l := w.value.ListValue
|
||||
|
||||
// If list is null, empty, or atomic just return
|
||||
|
|
@ -72,7 +49,7 @@ func (w *removingWalker) doList(t schema.List) (errs ValidationErrors) {
|
|||
}
|
||||
|
||||
newItems := []value.Value{}
|
||||
for i, _ := range l.Items {
|
||||
for i := range l.Items {
|
||||
item := l.Items[i]
|
||||
// Ignore error because we have already validated this list
|
||||
pe, _ := listItemToPathElement(t, i, item)
|
||||
|
|
@ -93,7 +70,7 @@ func (w *removingWalker) doList(t schema.List) (errs ValidationErrors) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (w *removingWalker) doMap(t schema.Map) ValidationErrors {
|
||||
func (w *removingWalker) doMap(t *schema.Map) ValidationErrors {
|
||||
m := w.value.MapValue
|
||||
|
||||
// If map is null, empty, or atomic just return
|
||||
|
|
@ -101,16 +78,26 @@ func (w *removingWalker) doMap(t schema.Map) ValidationErrors {
|
|||
return nil
|
||||
}
|
||||
|
||||
fieldTypes := map[string]schema.TypeRef{}
|
||||
for _, structField := range t.Fields {
|
||||
fieldTypes[structField.Name] = structField.Type
|
||||
}
|
||||
|
||||
newMap := &value.Map{}
|
||||
for i, _ := range m.Items {
|
||||
for i := range m.Items {
|
||||
item := m.Items[i]
|
||||
pe := fieldpath.PathElement{FieldName: &item.Name}
|
||||
path, _ := fieldpath.MakePath(pe)
|
||||
if w.toRemove.Has(path) {
|
||||
continue
|
||||
fieldType := t.ElementType
|
||||
if ft, ok := fieldTypes[item.Name]; ok {
|
||||
fieldType = ft
|
||||
} else {
|
||||
if w.toRemove.Has(path) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
removeItemsWithSchema(&m.Items[i].Value, subset, w.schema, t.ElementType)
|
||||
removeItemsWithSchema(&m.Items[i].Value, subset, w.schema, fieldType)
|
||||
}
|
||||
newMap.Set(item.Name, m.Items[i].Value)
|
||||
}
|
||||
|
|
@ -122,6 +109,4 @@ func (w *removingWalker) doMap(t schema.Map) ValidationErrors {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (*removingWalker) doUntyped(_ schema.Untyped) ValidationErrors { return nil }
|
||||
|
||||
func (*removingWalker) errorf(_ string, _ ...interface{}) ValidationErrors { return nil }
|
||||
|
|
|
|||
233
vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go
generated
vendored
233
vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go
generated
vendored
|
|
@ -18,52 +18,21 @@ package typed
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// TypedValue is a value with an associated type.
|
||||
type TypedValue interface {
|
||||
// AsValue removes the type from the TypedValue and only keeps the value.
|
||||
AsValue() *value.Value
|
||||
// Validate returns an error with a list of every spec violation.
|
||||
Validate() error
|
||||
// ToFieldSet creates a set containing every leaf field and item mentioned, or
|
||||
// validation errors, if any were encountered.
|
||||
ToFieldSet() (*fieldpath.Set, error)
|
||||
// Merge returns the result of merging tv and pso ("partially specified
|
||||
// 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.
|
||||
Merge(pso TypedValue) (TypedValue, error)
|
||||
// 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.
|
||||
Compare(rhs TypedValue) (c *Comparison, err error)
|
||||
// RemoveItems removes each provided list or map item from the value.
|
||||
RemoveItems(items *fieldpath.Set) TypedValue
|
||||
}
|
||||
|
||||
// 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, typeName string) (TypedValue, error) {
|
||||
tv := typedValue{
|
||||
func AsTyped(v value.Value, s *schema.Schema, typeRef schema.TypeRef) (*TypedValue, error) {
|
||||
tv := &TypedValue{
|
||||
value: v,
|
||||
typeRef: schema.TypeRef{NamedType: &typeName},
|
||||
typeRef: typeRef,
|
||||
schema: s,
|
||||
}
|
||||
if err := tv.Validate(); err != nil {
|
||||
|
|
@ -76,38 +45,43 @@ func AsTyped(v value.Value, s *schema.Schema, typeName string) (TypedValue, erro
|
|||
// 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, typeName string) TypedValue {
|
||||
tv := typedValue{
|
||||
func AsTypedUnvalidated(v value.Value, s *schema.Schema, typeRef schema.TypeRef) *TypedValue {
|
||||
tv := &TypedValue{
|
||||
value: v,
|
||||
typeRef: schema.TypeRef{NamedType: &typeName},
|
||||
typeRef: typeRef,
|
||||
schema: s,
|
||||
}
|
||||
return tv
|
||||
}
|
||||
|
||||
// typedValue is a value of some specific type.
|
||||
type typedValue struct {
|
||||
// TypedValue is a value of some specific type.
|
||||
type TypedValue struct {
|
||||
value value.Value
|
||||
typeRef schema.TypeRef
|
||||
schema *schema.Schema
|
||||
}
|
||||
|
||||
var _ TypedValue = typedValue{}
|
||||
|
||||
func (tv typedValue) AsValue() *value.Value {
|
||||
// AsValue removes the type from the TypedValue and only keeps the value.
|
||||
func (tv TypedValue) AsValue() *value.Value {
|
||||
return &tv.value
|
||||
}
|
||||
|
||||
func (tv typedValue) Validate() error {
|
||||
if errs := tv.walker().validate(); len(errs) != 0 {
|
||||
// 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(); len(errs) != 0 {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tv typedValue) ToFieldSet() (*fieldpath.Set, error) {
|
||||
// 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) {
|
||||
s := fieldpath.NewSet()
|
||||
w := tv.walker()
|
||||
defer w.finished()
|
||||
w.leafFieldCallback = func(p fieldpath.Path) { s.Insert(p) }
|
||||
w.nodeFieldCallback = func(p fieldpath.Path) { s.Insert(p) }
|
||||
if errs := w.validate(); len(errs) != 0 {
|
||||
|
|
@ -116,38 +90,43 @@ func (tv typedValue) ToFieldSet() (*fieldpath.Set, error) {
|
|||
return s, nil
|
||||
}
|
||||
|
||||
func (tv typedValue) Merge(pso TypedValue) (TypedValue, error) {
|
||||
tpso, ok := pso.(typedValue)
|
||||
if !ok {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("can't merge typedValue with %T", pso)
|
||||
}
|
||||
return merge(tv, tpso, ruleKeepRHS, 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)
|
||||
}
|
||||
|
||||
func (tv typedValue) Compare(rhs TypedValue) (c *Comparison, err error) {
|
||||
trhs, ok := rhs.(typedValue)
|
||||
if !ok {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("can't compare typedValue with %T", rhs)
|
||||
}
|
||||
// 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(),
|
||||
}
|
||||
c.Merged, err = merge(tv, trhs, func(w *mergingWalker) {
|
||||
_, 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 !reflect.DeepEqual(w.rhs, w.lhs) {
|
||||
// TODO: reflect.DeepEqual is not sufficient for this.
|
||||
} else if !w.rhs.Equals(*w.lhs) {
|
||||
// TODO: Equality is not sufficient for this.
|
||||
// Need to implement equality check on the value type.
|
||||
c.Modified.Insert(w.path)
|
||||
}
|
||||
|
||||
ruleKeepRHS(w)
|
||||
}, func(w *mergingWalker) {
|
||||
if w.lhs == nil {
|
||||
c.Added.Insert(w.path)
|
||||
|
|
@ -163,37 +142,115 @@ func (tv typedValue) Compare(rhs TypedValue) (c *Comparison, err error) {
|
|||
}
|
||||
|
||||
// RemoveItems removes each provided list or map item from the value.
|
||||
func (tv typedValue) RemoveItems(items *fieldpath.Set) TypedValue {
|
||||
copied := tv
|
||||
copied.value, _ = value.FromUnstructured(tv.value.ToUnstructured(true))
|
||||
removeItemsWithSchema(&copied.value, items, copied.schema, copied.typeRef)
|
||||
return copied
|
||||
func (tv TypedValue) RemoveItems(items *fieldpath.Set) *TypedValue {
|
||||
tv.value, _ = value.FromUnstructured(tv.value.ToUnstructured(true))
|
||||
removeItemsWithSchema(&tv.value, items, tv.schema, tv.typeRef)
|
||||
return &tv
|
||||
}
|
||||
|
||||
func merge(lhs, rhs typedValue, rule, postRule mergeRule) (TypedValue, error) {
|
||||
// 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
|
||||
w.out = &v
|
||||
}
|
||||
if err := normalizeUnions(w); err != nil {
|
||||
errs = append(errs, w.error(err)...)
|
||||
}
|
||||
}
|
||||
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
|
||||
w.out = &v
|
||||
}
|
||||
if err := normalizeUnionsApply(w); err != nil {
|
||||
errs = append(errs, w.error(err)...)
|
||||
}
|
||||
}
|
||||
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.Value{Null: true}
|
||||
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, errorFormatter{}.
|
||||
errorf("expected objects with types from the same schema")
|
||||
}
|
||||
if !reflect.DeepEqual(lhs.typeRef, rhs.typeRef) {
|
||||
if !lhs.typeRef.Equals(rhs.typeRef) {
|
||||
return nil, errorFormatter{}.
|
||||
errorf("expected objects of the same type, but got %v and %v", lhs.typeRef, rhs.typeRef)
|
||||
}
|
||||
|
||||
mw := mergingWalker{
|
||||
lhs: &lhs.value,
|
||||
rhs: &rhs.value,
|
||||
schema: lhs.schema,
|
||||
typeRef: lhs.typeRef,
|
||||
rule: rule,
|
||||
postItemHook: postRule,
|
||||
}
|
||||
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()
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
out := typedValue{
|
||||
out := &TypedValue{
|
||||
schema: lhs.schema,
|
||||
typeRef: lhs.typeRef,
|
||||
}
|
||||
|
|
@ -210,10 +267,6 @@ func merge(lhs, rhs typedValue, rule, postRule mergeRule) (TypedValue, error) {
|
|||
// 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 {
|
||||
// Merged is the result of merging the two objects, as explained in the
|
||||
// comments on TypedValue.Merge().
|
||||
Merged TypedValue
|
||||
|
||||
// Removed contains any fields removed by rhs (the right-hand-side
|
||||
// object in the comparison).
|
||||
Removed *fieldpath.Set
|
||||
|
|
@ -231,15 +284,15 @@ func (c *Comparison) IsSame() bool {
|
|||
|
||||
// String returns a human readable version of the comparison.
|
||||
func (c *Comparison) String() string {
|
||||
str := fmt.Sprintf("- Merged Object:\n%v\n", c.Merged.AsValue())
|
||||
bld := strings.Builder{}
|
||||
if !c.Modified.Empty() {
|
||||
str += fmt.Sprintf("- Modified Fields:\n%v\n", c.Modified)
|
||||
bld.WriteString(fmt.Sprintf("- Modified Fields:\n%v\n", c.Modified))
|
||||
}
|
||||
if !c.Added.Empty() {
|
||||
str += fmt.Sprintf("- Added Fields:\n%v\n", c.Added)
|
||||
bld.WriteString(fmt.Sprintf("- Added Fields:\n%v\n", c.Added))
|
||||
}
|
||||
if !c.Removed.Empty() {
|
||||
str += fmt.Sprintf("- Removed Fields:\n%v\n", c.Removed)
|
||||
bld.WriteString(fmt.Sprintf("- Removed Fields:\n%v\n", c.Removed))
|
||||
}
|
||||
return str
|
||||
return bld.String()
|
||||
}
|
||||
|
|
|
|||
273
vendor/sigs.k8s.io/structured-merge-diff/typed/union.go
generated
vendored
Normal file
273
vendor/sigs.k8s.io/structured-merge-diff/typed/union.go
generated
vendored
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
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/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/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
|
||||
}
|
||||
|
||||
old := &value.Map{}
|
||||
if w.lhs != nil {
|
||||
old = w.lhs.MapValue
|
||||
}
|
||||
for _, union := range atom.Map.Unions {
|
||||
if err := newUnion(&union).Normalize(old, w.rhs.MapValue, w.out.MapValue); 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
|
||||
}
|
||||
|
||||
old := &value.Map{}
|
||||
if w.lhs != nil {
|
||||
old = w.lhs.MapValue
|
||||
}
|
||||
for _, union := range atom.Map.Unions {
|
||||
if err := newUnion(&union).NormalizeApply(old, w.rhs.MapValue, w.out.MapValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
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.StringValue(string(v)))
|
||||
}
|
||||
|
||||
func (d *discriminator) Get(m *value.Map) discriminated {
|
||||
if d == nil || m == nil {
|
||||
return ""
|
||||
}
|
||||
f, ok := m.Get(d.name)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
if f.Value.StringValue == nil {
|
||||
return ""
|
||||
}
|
||||
return discriminated(*f.Value.StringValue)
|
||||
}
|
||||
|
||||
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.Value.Null {
|
||||
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)
|
||||
}
|
||||
|
||||
// Update 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
|
||||
}
|
||||
162
vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go
generated
vendored
162
vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go
generated
vendored
|
|
@ -17,17 +17,33 @@ limitations under the License.
|
|||
package typed
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
func (tv typedValue) walker() *validatingObjectWalker {
|
||||
return &validatingObjectWalker{
|
||||
value: tv.value,
|
||||
schema: tv.schema,
|
||||
typeRef: tv.typeRef,
|
||||
}
|
||||
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.value = value.Value{}
|
||||
v.schema = nil
|
||||
v.typeRef = schema.TypeRef{}
|
||||
v.leafFieldCallback = nil
|
||||
v.nodeFieldCallback = nil
|
||||
v.inLeaf = false
|
||||
vPool.Put(v)
|
||||
}
|
||||
|
||||
type validatingObjectWalker struct {
|
||||
|
|
@ -49,10 +65,37 @@ type validatingObjectWalker struct {
|
|||
|
||||
// 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 *[]*validatingObjectWalker
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) validate() ValidationErrors {
|
||||
return resolveSchema(v.schema, v.typeRef, v)
|
||||
func (v *validatingObjectWalker) prepareDescent(pe fieldpath.PathElement, 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
|
||||
v2.errorFormatter.descend(pe)
|
||||
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.errorFormatter = v2.errorFormatter.parent()
|
||||
*v.spareWalkers = append(*v.spareWalkers, v2)
|
||||
}
|
||||
|
||||
func (v *validatingObjectWalker) validate() ValidationErrors {
|
||||
return resolveSchema(v.schema, v.typeRef, &v.value, v)
|
||||
}
|
||||
|
||||
// doLeaf should be called on leaves before descending into children, if there
|
||||
|
|
@ -87,7 +130,7 @@ func (v *validatingObjectWalker) doNode() {
|
|||
}
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doScalar(t schema.Scalar) ValidationErrors {
|
||||
func (v *validatingObjectWalker) doScalar(t *schema.Scalar) ValidationErrors {
|
||||
if errs := v.validateScalar(t, &v.value, ""); len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
|
@ -98,53 +141,8 @@ func (v validatingObjectWalker) doScalar(t schema.Scalar) ValidationErrors {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) visitStructFields(t schema.Struct, m *value.Map) (errs ValidationErrors) {
|
||||
allowedNames := map[string]struct{}{}
|
||||
for i := range t.Fields {
|
||||
// I don't want to use the loop variable since a reference
|
||||
// might outlive the loop iteration (in an error message).
|
||||
f := t.Fields[i]
|
||||
allowedNames[f.Name] = struct{}{}
|
||||
child, ok := m.Get(f.Name)
|
||||
if !ok {
|
||||
// All fields are optional
|
||||
continue
|
||||
}
|
||||
v2 := v
|
||||
v2.errorFormatter.descend(fieldpath.PathElement{FieldName: &f.Name})
|
||||
v2.value = child.Value
|
||||
v2.typeRef = f.Type
|
||||
errs = append(errs, v2.validate()...)
|
||||
}
|
||||
|
||||
// All fields may be optional, but unknown fields are not allowed.
|
||||
return append(errs, v.rejectExtraStructFields(m, allowedNames, "")...)
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doStruct(t schema.Struct) (errs ValidationErrors) {
|
||||
m, err := mapOrStructValue(v.value, "struct")
|
||||
if err != nil {
|
||||
return v.error(err)
|
||||
}
|
||||
|
||||
if t.ElementRelationship == schema.Atomic {
|
||||
v.doLeaf()
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
// nil is a valid map!
|
||||
return nil
|
||||
}
|
||||
|
||||
errs = v.visitStructFields(t, m)
|
||||
|
||||
// TODO: Check unions.
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) visitListItems(t schema.List, list *value.List) (errs ValidationErrors) {
|
||||
observedKeys := map[string]struct{}{}
|
||||
func (v *validatingObjectWalker) visitListItems(t *schema.List, list *value.List) (errs ValidationErrors) {
|
||||
observedKeys := fieldpath.MakePathElementSet(len(list.Items))
|
||||
for i, child := range list.Items {
|
||||
pe, err := listItemToPathElement(t, i, child)
|
||||
if err != nil {
|
||||
|
|
@ -154,23 +152,21 @@ func (v validatingObjectWalker) visitListItems(t schema.List, list *value.List)
|
|||
// this element.
|
||||
continue
|
||||
}
|
||||
keyStr := pe.String()
|
||||
if _, found := observedKeys[keyStr]; found {
|
||||
errs = append(errs, v.errorf("duplicate entries for key %v", keyStr)...)
|
||||
if observedKeys.Has(pe) {
|
||||
errs = append(errs, v.errorf("duplicate entries for key %v", pe.String())...)
|
||||
}
|
||||
observedKeys[keyStr] = struct{}{}
|
||||
v2 := v
|
||||
v2.errorFormatter.descend(pe)
|
||||
observedKeys.Insert(pe)
|
||||
v2 := v.prepareDescent(pe, t.ElementType)
|
||||
v2.value = child
|
||||
v2.typeRef = t.ElementType
|
||||
errs = append(errs, v2.validate()...)
|
||||
|
||||
v2.doNode()
|
||||
v.finishDescent(v2)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doList(t schema.List) (errs ValidationErrors) {
|
||||
func (v *validatingObjectWalker) doList(t *schema.List) (errs ValidationErrors) {
|
||||
list, err := listValue(v.value)
|
||||
if err != nil {
|
||||
return v.error(err)
|
||||
|
|
@ -189,22 +185,29 @@ func (v validatingObjectWalker) doList(t schema.List) (errs ValidationErrors) {
|
|||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) visitMapItems(t schema.Map, m *value.Map) (errs ValidationErrors) {
|
||||
for _, item := range m.Items {
|
||||
v2 := v
|
||||
name := item.Name
|
||||
v2.errorFormatter.descend(fieldpath.PathElement{FieldName: &name})
|
||||
v2.value = item.Value
|
||||
v2.typeRef = t.ElementType
|
||||
errs = append(errs, v2.validate()...)
|
||||
func (v *validatingObjectWalker) visitMapItems(t *schema.Map, m *value.Map) (errs ValidationErrors) {
|
||||
for i := range m.Items {
|
||||
item := &m.Items[i]
|
||||
pe := fieldpath.PathElement{FieldName: &item.Name}
|
||||
|
||||
v2.doNode()
|
||||
if sf, ok := t.FindField(item.Name); ok {
|
||||
v2 := v.prepareDescent(pe, sf.Type)
|
||||
v2.value = item.Value
|
||||
errs = append(errs, v2.validate()...)
|
||||
v.finishDescent(v2)
|
||||
} else {
|
||||
v2 := v.prepareDescent(pe, t.ElementType)
|
||||
v2.value = item.Value
|
||||
errs = append(errs, v2.validate()...)
|
||||
v2.doNode()
|
||||
v.finishDescent(v2)
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doMap(t schema.Map) (errs ValidationErrors) {
|
||||
m, err := mapOrStructValue(v.value, "map")
|
||||
func (v *validatingObjectWalker) doMap(t *schema.Map) (errs ValidationErrors) {
|
||||
m, err := mapValue(v.value)
|
||||
if err != nil {
|
||||
return v.error(err)
|
||||
}
|
||||
|
|
@ -221,12 +224,3 @@ func (v validatingObjectWalker) doMap(t schema.Map) (errs ValidationErrors) {
|
|||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doUntyped(t schema.Untyped) (errs ValidationErrors) {
|
||||
if t.ElementRelationship == "" || t.ElementRelationship == schema.Atomic {
|
||||
// Untyped sections allow anything, and are considered leaf
|
||||
// fields.
|
||||
v.doLeaf()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
149
vendor/sigs.k8s.io/structured-merge-diff/value/fastjson.go
generated
vendored
Normal file
149
vendor/sigs.k8s.io/structured-merge-diff/value/fastjson.go
generated
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
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"
|
||||
"fmt"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
var (
|
||||
readPool = jsoniter.NewIterator(jsoniter.ConfigCompatibleWithStandardLibrary).Pool()
|
||||
writePool = jsoniter.NewStream(jsoniter.ConfigCompatibleWithStandardLibrary, nil, 1024).Pool()
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
func ReadJSONIter(iter *jsoniter.Iterator) (Value, error) {
|
||||
next := iter.WhatIsNext()
|
||||
switch next {
|
||||
case jsoniter.InvalidValue:
|
||||
iter.ReportError("reading an object", "got invalid token")
|
||||
return Value{}, iter.Error
|
||||
case jsoniter.StringValue:
|
||||
str := String(iter.ReadString())
|
||||
return Value{StringValue: &str}, nil
|
||||
case jsoniter.NumberValue:
|
||||
number := iter.ReadNumber()
|
||||
isFloat := false
|
||||
for _, c := range number {
|
||||
if c == 'e' || c == 'E' || c == '.' {
|
||||
isFloat = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isFloat {
|
||||
f, err := number.Float64()
|
||||
if err != nil {
|
||||
iter.ReportError("parsing as float", err.Error())
|
||||
return Value{}, err
|
||||
}
|
||||
return Value{FloatValue: (*Float)(&f)}, nil
|
||||
}
|
||||
i, err := number.Int64()
|
||||
if err != nil {
|
||||
iter.ReportError("parsing as float", err.Error())
|
||||
return Value{}, err
|
||||
}
|
||||
return Value{IntValue: (*Int)(&i)}, nil
|
||||
case jsoniter.NilValue:
|
||||
iter.ReadNil()
|
||||
return Value{Null: true}, nil
|
||||
case jsoniter.BoolValue:
|
||||
b := Boolean(iter.ReadBool())
|
||||
return Value{BooleanValue: &b}, nil
|
||||
case jsoniter.ArrayValue:
|
||||
list := &List{}
|
||||
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
|
||||
v, err := ReadJSONIter(iter)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
return false
|
||||
}
|
||||
list.Items = append(list.Items, v)
|
||||
return true
|
||||
})
|
||||
return Value{ListValue: list}, iter.Error
|
||||
case jsoniter.ObjectValue:
|
||||
m := &Map{}
|
||||
iter.ReadObjectCB(func(iter *jsoniter.Iterator, key string) bool {
|
||||
v, err := ReadJSONIter(iter)
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
return false
|
||||
}
|
||||
m.Items = append(m.Items, Field{Name: key, Value: v})
|
||||
return true
|
||||
})
|
||||
return Value{MapValue: m}, iter.Error
|
||||
default:
|
||||
return Value{}, fmt.Errorf("unexpected object type %v", next)
|
||||
}
|
||||
}
|
||||
|
||||
// ToJSONFast is a helper function for producing a JSon document.
|
||||
func (v *Value) ToJSONFast() ([]byte, error) {
|
||||
buf := bytes.Buffer{}
|
||||
stream := writePool.BorrowStream(&buf)
|
||||
defer writePool.ReturnStream(stream)
|
||||
v.WriteJSONStream(stream)
|
||||
err := stream.Flush()
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
func (v *Value) WriteJSONStream(stream *jsoniter.Stream) {
|
||||
switch {
|
||||
case v.Null:
|
||||
stream.WriteNil()
|
||||
case v.FloatValue != nil:
|
||||
stream.WriteFloat64(float64(*v.FloatValue))
|
||||
case v.IntValue != nil:
|
||||
stream.WriteInt64(int64(*v.IntValue))
|
||||
case v.BooleanValue != nil:
|
||||
stream.WriteBool(bool(*v.BooleanValue))
|
||||
case v.StringValue != nil:
|
||||
stream.WriteString(string(*v.StringValue))
|
||||
case v.ListValue != nil:
|
||||
stream.WriteArrayStart()
|
||||
for i := range v.ListValue.Items {
|
||||
if i > 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
v.ListValue.Items[i].WriteJSONStream(stream)
|
||||
}
|
||||
stream.WriteArrayEnd()
|
||||
case v.MapValue != nil:
|
||||
stream.WriteObjectStart()
|
||||
for i := range v.MapValue.Items {
|
||||
if i > 0 {
|
||||
stream.WriteMore()
|
||||
}
|
||||
stream.WriteObjectField(v.MapValue.Items[i].Name)
|
||||
v.MapValue.Items[i].Value.WriteJSONStream(stream)
|
||||
}
|
||||
stream.WriteObjectEnd()
|
||||
default:
|
||||
stream.Write([]byte("invalid_value"))
|
||||
}
|
||||
}
|
||||
411
vendor/sigs.k8s.io/structured-merge-diff/value/value.go
generated
vendored
411
vendor/sigs.k8s.io/structured-merge-diff/value/value.go
generated
vendored
|
|
@ -18,6 +18,7 @@ package value
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -33,22 +34,296 @@ type Value struct {
|
|||
Null bool // represents an explicit `"foo" = null`
|
||||
}
|
||||
|
||||
// Equals returns true iff the two values are equal.
|
||||
func (v Value) Equals(rhs Value) bool {
|
||||
if v.FloatValue != nil || rhs.FloatValue != nil {
|
||||
var lf float64
|
||||
if v.FloatValue != nil {
|
||||
lf = float64(*v.FloatValue)
|
||||
} else if v.IntValue != nil {
|
||||
lf = float64(*v.IntValue)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
var rf float64
|
||||
if rhs.FloatValue != nil {
|
||||
rf = float64(*rhs.FloatValue)
|
||||
} else if rhs.IntValue != nil {
|
||||
rf = float64(*rhs.IntValue)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return lf == rf
|
||||
}
|
||||
if v.IntValue != nil {
|
||||
if rhs.IntValue != nil {
|
||||
return *v.IntValue == *rhs.IntValue
|
||||
}
|
||||
return false
|
||||
}
|
||||
if v.StringValue != nil {
|
||||
if rhs.StringValue != nil {
|
||||
return *v.StringValue == *rhs.StringValue
|
||||
}
|
||||
return false
|
||||
}
|
||||
if v.BooleanValue != nil {
|
||||
if rhs.BooleanValue != nil {
|
||||
return *v.BooleanValue == *rhs.BooleanValue
|
||||
}
|
||||
return false
|
||||
}
|
||||
if v.ListValue != nil {
|
||||
if rhs.ListValue != nil {
|
||||
return v.ListValue.Equals(rhs.ListValue)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if v.MapValue != nil {
|
||||
if rhs.MapValue != nil {
|
||||
return v.MapValue.Equals(rhs.MapValue)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if v.Null {
|
||||
if rhs.Null {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
// No field is set, on either objects.
|
||||
return true
|
||||
}
|
||||
|
||||
// Less provides a total ordering for Value (so that they can be sorted, even
|
||||
// if they are of different types).
|
||||
func (v Value) Less(rhs Value) bool {
|
||||
return v.Compare(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 (v Value) Compare(rhs Value) int {
|
||||
if v.FloatValue != nil {
|
||||
if rhs.FloatValue == nil {
|
||||
// Extra: compare floats and ints numerically.
|
||||
if rhs.IntValue != nil {
|
||||
return v.FloatValue.Compare(Float(*rhs.IntValue))
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return v.FloatValue.Compare(*rhs.FloatValue)
|
||||
} else if rhs.FloatValue != nil {
|
||||
// Extra: compare floats and ints numerically.
|
||||
if v.IntValue != nil {
|
||||
return Float(*v.IntValue).Compare(*rhs.FloatValue)
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
if v.IntValue != nil {
|
||||
if rhs.IntValue == nil {
|
||||
return -1
|
||||
}
|
||||
return v.IntValue.Compare(*rhs.IntValue)
|
||||
} else if rhs.IntValue != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
if v.StringValue != nil {
|
||||
if rhs.StringValue == nil {
|
||||
return -1
|
||||
}
|
||||
return strings.Compare(string(*v.StringValue), string(*rhs.StringValue))
|
||||
} else if rhs.StringValue != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
if v.BooleanValue != nil {
|
||||
if rhs.BooleanValue == nil {
|
||||
return -1
|
||||
}
|
||||
return v.BooleanValue.Compare(*rhs.BooleanValue)
|
||||
} else if rhs.BooleanValue != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
if v.ListValue != nil {
|
||||
if rhs.ListValue == nil {
|
||||
return -1
|
||||
}
|
||||
return v.ListValue.Compare(rhs.ListValue)
|
||||
} else if rhs.ListValue != nil {
|
||||
return 1
|
||||
}
|
||||
if v.MapValue != nil {
|
||||
if rhs.MapValue == nil {
|
||||
return -1
|
||||
}
|
||||
return v.MapValue.Compare(rhs.MapValue)
|
||||
} else if rhs.MapValue != nil {
|
||||
return 1
|
||||
}
|
||||
if v.Null {
|
||||
if !rhs.Null {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
} else if rhs.Null {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Invalid Value-- nothing is set.
|
||||
return 0
|
||||
}
|
||||
|
||||
type Int int64
|
||||
type Float float64
|
||||
type String string
|
||||
type Boolean bool
|
||||
|
||||
// Compare compares integers. The result will be 0 if i==rhs, -1 if i <
|
||||
// rhs, and +1 if i > rhs.
|
||||
func (i Int) Compare(rhs Int) int {
|
||||
if i > rhs {
|
||||
return 1
|
||||
} else if i < rhs {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Compare compares floats. The result will be 0 if f==rhs, -1 if f <
|
||||
// rhs, and +1 if f > rhs.
|
||||
func (f Float) Compare(rhs Float) int {
|
||||
if f > rhs {
|
||||
return 1
|
||||
} else if f < 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 (b Boolean) Compare(rhs Boolean) int {
|
||||
if b == rhs {
|
||||
return 0
|
||||
} else if b == false {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// Field is an individual key-value pair.
|
||||
type Field struct {
|
||||
Name string
|
||||
Value Value
|
||||
}
|
||||
|
||||
// FieldList is a list of key-value pairs. Each field is expected to
|
||||
// have a different name.
|
||||
type FieldList []Field
|
||||
|
||||
// Sort sorts the field list by Name.
|
||||
func (f FieldList) Sort() {
|
||||
if len(f) < 2 {
|
||||
return
|
||||
}
|
||||
if len(f) == 2 {
|
||||
if f[1].Name < f[0].Name {
|
||||
f[0], f[1] = f[1], f[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
sort.SliceStable(f, func(i, j int) bool {
|
||||
return f[i].Name < f[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
// Less compares two lists lexically.
|
||||
func (f FieldList) Less(rhs FieldList) bool {
|
||||
return f.Compare(rhs) == -1
|
||||
}
|
||||
|
||||
// Less 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 := f[i].Value.Compare(rhs[i].Value); c != 0 {
|
||||
return c
|
||||
}
|
||||
// The items are equal; continue.
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// List is a list of items.
|
||||
type List struct {
|
||||
Items []Value
|
||||
}
|
||||
|
||||
// Equals compares two lists lexically.
|
||||
func (l *List) Equals(rhs *List) bool {
|
||||
if len(l.Items) != len(rhs.Items) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, lv := range l.Items {
|
||||
if !lv.Equals(rhs.Items[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Less compares two lists lexically.
|
||||
func (l *List) Less(rhs *List) bool {
|
||||
return l.Compare(rhs) == -1
|
||||
}
|
||||
|
||||
// Compare compares two lists lexically. The result will be 0 if l==rhs, -1
|
||||
// if l < rhs, and +1 if l > rhs.
|
||||
func (l *List) Compare(rhs *List) int {
|
||||
i := 0
|
||||
for {
|
||||
if i >= len(l.Items) && i >= len(rhs.Items) {
|
||||
// Lists are the same length and all items are equal.
|
||||
return 0
|
||||
}
|
||||
if i >= len(l.Items) {
|
||||
// LHS is shorter.
|
||||
return -1
|
||||
}
|
||||
if i >= len(rhs.Items) {
|
||||
// RHS is shorter.
|
||||
return 1
|
||||
}
|
||||
if c := l.Items[i].Compare(rhs.Items[i]); c != 0 {
|
||||
return c
|
||||
}
|
||||
// The items are equal; continue.
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// Map is a map of key-value pairs. It represents both structs and maps. We use
|
||||
// a list and a go-language map to preserve order.
|
||||
//
|
||||
|
|
@ -58,20 +333,129 @@ type Map struct {
|
|||
|
||||
// may be nil; lazily constructed.
|
||||
// TODO: Direct modifications to Items above will cause serious problems.
|
||||
index map[string]*Field
|
||||
index map[string]int
|
||||
// may be empty; lazily constructed.
|
||||
// TODO: Direct modifications to Items above will cause serious problems.
|
||||
order []int
|
||||
}
|
||||
|
||||
func (m *Map) computeOrder() []int {
|
||||
if len(m.order) != len(m.Items) {
|
||||
m.order = make([]int, len(m.Items))
|
||||
for i := range m.order {
|
||||
m.order[i] = i
|
||||
}
|
||||
sort.SliceStable(m.order, func(i, j int) bool {
|
||||
return m.Items[m.order[i]].Name < m.Items[m.order[j]].Name
|
||||
})
|
||||
}
|
||||
return m.order
|
||||
}
|
||||
|
||||
// Equals compares two maps lexically.
|
||||
func (m *Map) Equals(rhs *Map) bool {
|
||||
if len(m.Items) != len(rhs.Items) {
|
||||
return false
|
||||
}
|
||||
for _, lfield := range m.Items {
|
||||
rfield, ok := rhs.Get(lfield.Name)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !lfield.Value.Equals(rfield.Value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Less compares two maps lexically.
|
||||
func (m *Map) Less(rhs *Map) bool {
|
||||
return m.Compare(rhs) == -1
|
||||
}
|
||||
|
||||
// Compare compares two maps lexically.
|
||||
func (m *Map) Compare(rhs *Map) int {
|
||||
var noAllocL, noAllocR [2]int
|
||||
var morder, rorder []int
|
||||
|
||||
// For very short maps (<2 elements) this permits us to avoid
|
||||
// allocating the order array. We could make this accomodate larger
|
||||
// maps, but 2 items should be enough to cover most path element
|
||||
// comparisons, and at some point there will be diminishing returns.
|
||||
// This has a large effect on the path element deserialization test,
|
||||
// because everything is sorted / compared, but only once.
|
||||
switch len(m.Items) {
|
||||
case 0:
|
||||
morder = noAllocL[0:0]
|
||||
case 1:
|
||||
morder = noAllocL[0:1]
|
||||
case 2:
|
||||
morder = noAllocL[0:2]
|
||||
if m.Items[0].Name > m.Items[1].Name {
|
||||
morder[0] = 1
|
||||
} else {
|
||||
morder[1] = 1
|
||||
}
|
||||
default:
|
||||
morder = m.computeOrder()
|
||||
}
|
||||
|
||||
switch len(rhs.Items) {
|
||||
case 0:
|
||||
rorder = noAllocR[0:0]
|
||||
case 1:
|
||||
rorder = noAllocR[0:1]
|
||||
case 2:
|
||||
rorder = noAllocR[0:2]
|
||||
if rhs.Items[0].Name > rhs.Items[1].Name {
|
||||
rorder[0] = 1
|
||||
} else {
|
||||
rorder[1] = 1
|
||||
}
|
||||
default:
|
||||
rorder = rhs.computeOrder()
|
||||
}
|
||||
|
||||
i := 0
|
||||
for {
|
||||
if i >= len(morder) && i >= len(rorder) {
|
||||
// Maps are the same length and all items are equal.
|
||||
return 0
|
||||
}
|
||||
if i >= len(morder) {
|
||||
// LHS is shorter.
|
||||
return -1
|
||||
}
|
||||
if i >= len(rorder) {
|
||||
// RHS is shorter.
|
||||
return 1
|
||||
}
|
||||
fa, fb := &m.Items[morder[i]], &rhs.Items[rorder[i]]
|
||||
if c := strings.Compare(fa.Name, fb.Name); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := fa.Value.Compare(fb.Value); c != 0 {
|
||||
return c
|
||||
}
|
||||
// The items are equal; continue.
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the (Field, true) or (nil, false) if it is not present
|
||||
func (m *Map) Get(key string) (*Field, bool) {
|
||||
if m.index == nil {
|
||||
m.index = map[string]*Field{}
|
||||
m.index = map[string]int{}
|
||||
for i := range m.Items {
|
||||
f := &m.Items[i]
|
||||
m.index[f.Name] = f
|
||||
m.index[m.Items[i].Name] = i
|
||||
}
|
||||
}
|
||||
f, ok := m.index[key]
|
||||
return f, ok
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return &m.Items[f], true
|
||||
}
|
||||
|
||||
// Set inserts or updates the given item.
|
||||
|
|
@ -81,7 +465,22 @@ func (m *Map) Set(key string, value Value) {
|
|||
return
|
||||
}
|
||||
m.Items = append(m.Items, Field{Name: key, Value: value})
|
||||
m.index = nil // Since the append might have reallocated
|
||||
i := len(m.Items) - 1
|
||||
m.index[key] = i
|
||||
m.order = nil
|
||||
}
|
||||
|
||||
// Delete removes the key from the set.
|
||||
func (m *Map) Delete(key string) {
|
||||
items := []Field{}
|
||||
for i := range m.Items {
|
||||
if m.Items[i].Name != key {
|
||||
items = append(items, m.Items[i])
|
||||
}
|
||||
}
|
||||
m.Items = items
|
||||
m.index = nil // Since the list has changed
|
||||
m.order = nil
|
||||
}
|
||||
|
||||
// StringValue returns s as a scalar string Value.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue