Run update-vendor.sh
This commit is contained in:
40
vendor/sigs.k8s.io/structured-merge-diff/merge/update.go
generated
vendored
40
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
|
||||
}
|
||||
|
||||
@@ -33,15 +33,15 @@ type Updater struct {
|
||||
Converter Converter
|
||||
}
|
||||
|
||||
func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, workflow string, force bool) (fieldpath.ManagedFields, error) {
|
||||
func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, workflow string, force bool) (fieldpath.ManagedFields, error) {
|
||||
conflicts := fieldpath.ManagedFields{}
|
||||
removed := fieldpath.ManagedFields{}
|
||||
type Versioned struct {
|
||||
oldObject typed.TypedValue
|
||||
newObject typed.TypedValue
|
||||
oldObject *typed.TypedValue
|
||||
newObject *typed.TypedValue
|
||||
}
|
||||
versions := map[fieldpath.APIVersion]Versioned{
|
||||
version: Versioned{
|
||||
version: {
|
||||
oldObject: oldObject,
|
||||
newObject: newObject,
|
||||
},
|
||||
@@ -119,16 +119,19 @@ 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) {
|
||||
var err error
|
||||
func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string) (*typed.TypedValue, fieldpath.ManagedFields, error) {
|
||||
newObject, err := liveObject.NormalizeUnions(newObject)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
managers = shallowCopyManagers(managers)
|
||||
managers, err = s.update(liveObject, newObject, version, managers, manager, true)
|
||||
if err != nil {
|
||||
return fieldpath.ManagedFields{}, err
|
||||
return nil, 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)
|
||||
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to compare live and new objects: %v", err)
|
||||
}
|
||||
if _, ok := managers[manager]; !ok {
|
||||
managers[manager] = &fieldpath.VersionedSet{
|
||||
@@ -140,18 +143,26 @@ func (s *Updater) Update(liveObject, newObject typed.TypedValue, version fieldpa
|
||||
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)
|
||||
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)
|
||||
}
|
||||
newObject, err = configObject.NormalizeUnionsApply(newObject)
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
lastSet := managers[manager]
|
||||
set, err := configObject.ToFieldSet()
|
||||
if err != nil {
|
||||
@@ -185,7 +196,7 @@ 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) {
|
||||
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
|
||||
}
|
||||
@@ -210,7 +221,7 @@ func (s *Updater) prune(merged typed.TypedValue, managers fieldpath.ManagedField
|
||||
|
||||
// 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 {
|
||||
@@ -249,11 +260,10 @@ 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) {
|
||||
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) {
|
||||
|
2
vendor/sigs.k8s.io/structured-merge-diff/schema/BUILD
generated
vendored
2
vendor/sigs.k8s.io/structured-merge-diff/schema/BUILD
generated
vendored
@@ -5,13 +5,11 @@ go_library(
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"elements.go",
|
||||
"fromvalue.go",
|
||||
"schemaschema.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/schema",
|
||||
importpath = "sigs.k8s.io/structured-merge-diff/schema",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//vendor/sigs.k8s.io/structured-merge-diff/value:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
|
147
vendor/sigs.k8s.io/structured-merge-diff/schema/elements.go
generated
vendored
147
vendor/sigs.k8s.io/structured-merge-diff/schema/elements.go
generated
vendored
@@ -43,13 +43,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,47 +64,105 @@ 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.
|
||||
// Each field has a name and a type.
|
||||
// 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.
|
||||
//
|
||||
// TODO: in the future, we will add one-of groups (sometimes called unions).
|
||||
type Struct struct {
|
||||
// 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.
|
||||
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"`
|
||||
}
|
||||
|
||||
// UnionFields are mapping between the fields that are part of the union and
|
||||
// their discriminated value. The discriminated value has to be set, and
|
||||
// should not conflict with other discriminated value in the list.
|
||||
type UnionField struct {
|
||||
// FieldName is the name of the field that is part of the union. This
|
||||
// is the serialized form of the field.
|
||||
FieldName string `yaml:"fieldName"`
|
||||
// Discriminatorvalue is the value of the discriminator to
|
||||
// select that field. If the union doesn't have a discriminator,
|
||||
// this field is ignored.
|
||||
DiscriminatorValue string `yaml:"discriminatorValue"`
|
||||
}
|
||||
|
||||
// Union, or oneof, means that only one of multiple fields of a structure can be
|
||||
// set at a time. Setting the discriminator helps clearing oher fields:
|
||||
// - If discriminator changed to non-nil, and a new field has been added
|
||||
// that doesn't match, an error is returned,
|
||||
// - If discriminator hasn't changed and two fields or more are set, an
|
||||
// error is returned,
|
||||
// - If discriminator changed to non-nil, all other fields but the
|
||||
// discriminated one will be cleared,
|
||||
// - Otherwise, If only one field is left, update discriminator to that value.
|
||||
type Union struct {
|
||||
// Discriminator, if present, is the name of the field that
|
||||
// discriminates fields in the union. The mapping between the value of
|
||||
// the discriminator and the field is done by using the Fields list
|
||||
// below.
|
||||
Discriminator *string `yaml:"discriminator,omitempty"`
|
||||
|
||||
// DeduceInvalidDiscriminator indicates if the discriminator
|
||||
// should be updated automatically based on the fields set. This
|
||||
// typically defaults to false since we don't want to deduce by
|
||||
// default (the behavior exists to maintain compatibility on
|
||||
// existing types and shouldn't be used for new types).
|
||||
DeduceInvalidDiscriminator bool `yaml:"deduceInvalidDiscriminator,omitempty"`
|
||||
|
||||
// This is the list of fields that belong to this union. All the
|
||||
// fields present in here have to be part of the parent
|
||||
// structure. Discriminator (if oneOf has one), is NOT included in
|
||||
// this list. The value for field is how we map the name of the field
|
||||
// to actual value for discriminator.
|
||||
Fields []UnionField `yaml:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// StructField pairs a field name with a field type.
|
||||
type StructField struct {
|
||||
// Name is the field name.
|
||||
@@ -129,15 +186,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,51 +202,6 @@ 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) {
|
||||
|
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:
|
||||
|
2
vendor/sigs.k8s.io/structured-merge-diff/typed/BUILD
generated
vendored
2
vendor/sigs.k8s.io/structured-merge-diff/typed/BUILD
generated
vendored
@@ -3,13 +3,13 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"deduced.go",
|
||||
"doc.go",
|
||||
"helpers.go",
|
||||
"merge.go",
|
||||
"parser.go",
|
||||
"remove.go",
|
||||
"typed.go",
|
||||
"union.go",
|
||||
"validate.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/sigs.k8s.io/structured-merge-diff/typed",
|
||||
|
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
|
||||
}
|
50
vendor/sigs.k8s.io/structured-merge-diff/typed/helpers.go
generated
vendored
50
vendor/sigs.k8s.io/structured-merge-diff/typed/helpers.go
generated
vendored
@@ -90,31 +90,43 @@ 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
|
||||
|
||||
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)
|
||||
case a.Scalar != nil:
|
||||
return ah.doScalar(*a.Scalar)
|
||||
case a.List != nil:
|
||||
return ah.doList(*a.List)
|
||||
}
|
||||
|
||||
name := "inlined"
|
||||
@@ -164,29 +176,17 @@ 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) {
|
||||
pe := fieldpath.PathElement{}
|
||||
if child.Null {
|
||||
|
148
vendor/sigs.k8s.io/structured-merge-diff/typed/merge.go
generated
vendored
148
vendor/sigs.k8s.io/structured-merge-diff/typed/merge.go
generated
vendored
@@ -17,6 +17,8 @@ limitations under the License.
|
||||
package typed
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
@@ -61,12 +63,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 reflect.DeepEqual(alhs, 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)
|
||||
}
|
||||
@@ -110,61 +126,13 @@ func (w *mergingWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeR
|
||||
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) 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,37 +140,6 @@ 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) {
|
||||
out := &value.List{}
|
||||
|
||||
@@ -300,11 +237,8 @@ func (w *mergingWalker) derefList(prefix string, v *value.Value, dest **value.Li
|
||||
|
||||
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
|
||||
@@ -329,10 +263,22 @@ func (w *mergingWalker) doList(t schema.List) (errs ValidationErrors) {
|
||||
func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs ValidationErrors) {
|
||||
out := &value.Map{}
|
||||
|
||||
fieldTypes := map[string]schema.TypeRef{}
|
||||
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]
|
||||
fieldTypes[f.Name] = f.Type
|
||||
}
|
||||
|
||||
if lhs != nil {
|
||||
for _, litem := range lhs.Items {
|
||||
name := litem.Name
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &name}, t.ElementType)
|
||||
fieldType := t.ElementType
|
||||
if ft, ok := fieldTypes[name]; ok {
|
||||
fieldType = ft
|
||||
}
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &name}, fieldType)
|
||||
w2.lhs = &litem.Value
|
||||
if rhs != nil {
|
||||
if ritem, ok := rhs.Get(litem.Name); ok {
|
||||
@@ -356,7 +302,11 @@ func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs V
|
||||
}
|
||||
|
||||
name := ritem.Name
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &name}, t.ElementType)
|
||||
fieldType := t.ElementType
|
||||
if ft, ok := fieldTypes[name]; ok {
|
||||
fieldType = ft
|
||||
}
|
||||
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &name}, fieldType)
|
||||
w2.rhs = &ritem.Value
|
||||
if newErrs := w2.merge(); len(newErrs) > 0 {
|
||||
errs = append(errs, newErrs...)
|
||||
@@ -374,11 +324,8 @@ func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs V
|
||||
|
||||
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 +342,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_")
|
||||
|
47
vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go
generated
vendored
47
vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go
generated
vendored
@@ -31,7 +31,7 @@ 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
|
||||
@@ -40,29 +40,6 @@ func (w *removingWalker) doLeaf() 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) {
|
||||
l := w.value.ListValue
|
||||
|
||||
@@ -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)
|
||||
@@ -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 }
|
||||
|
168
vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go
generated
vendored
168
vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go
generated
vendored
@@ -25,45 +25,13 @@ import (
|
||||
"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,36 +44,38 @@ 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 {
|
||||
// Validate returns an error with a list of every spec violation.
|
||||
func (tv TypedValue) Validate() error {
|
||||
if errs := tv.walker().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()
|
||||
w.leafFieldCallback = func(p fieldpath.Path) { s.Insert(p) }
|
||||
@@ -116,27 +86,34 @@ 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) {
|
||||
c.Merged, err = merge(&tv, rhs, func(w *mergingWalker) {
|
||||
if w.lhs == nil {
|
||||
c.Added.Insert(w.path)
|
||||
} else if w.rhs == nil {
|
||||
@@ -163,14 +140,71 @@ 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.
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
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")
|
||||
@@ -193,7 +227,7 @@ func merge(lhs, rhs typedValue, rule, postRule mergeRule) (TypedValue, error) {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
out := typedValue{
|
||||
out := &TypedValue{
|
||||
schema: lhs.schema,
|
||||
typeRef: lhs.typeRef,
|
||||
}
|
||||
@@ -212,7 +246,7 @@ func merge(lhs, rhs typedValue, rule, postRule mergeRule) (TypedValue, error) {
|
||||
type Comparison struct {
|
||||
// Merged is the result of merging the two objects, as explained in the
|
||||
// comments on TypedValue.Merge().
|
||||
Merged TypedValue
|
||||
Merged *TypedValue
|
||||
|
||||
// Removed contains any fields removed by rhs (the right-hand-side
|
||||
// object in the comparison).
|
||||
|
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
|
||||
}
|
79
vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go
generated
vendored
79
vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go
generated
vendored
@@ -22,7 +22,7 @@ import (
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
func (tv typedValue) walker() *validatingObjectWalker {
|
||||
func (tv TypedValue) walker() *validatingObjectWalker {
|
||||
return &validatingObjectWalker{
|
||||
value: tv.value,
|
||||
schema: tv.schema,
|
||||
@@ -52,7 +52,7 @@ type validatingObjectWalker struct {
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) validate() ValidationErrors {
|
||||
return resolveSchema(v.schema, v.typeRef, v)
|
||||
return resolveSchema(v.schema, v.typeRef, &v.value, v)
|
||||
}
|
||||
|
||||
// doLeaf should be called on leaves before descending into children, if there
|
||||
@@ -98,51 +98,6 @@ 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{}{}
|
||||
for i, child := range list.Items {
|
||||
@@ -190,21 +145,34 @@ func (v validatingObjectWalker) doList(t schema.List) (errs ValidationErrors) {
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) visitMapItems(t schema.Map, m *value.Map) (errs ValidationErrors) {
|
||||
fieldTypes := map[string]schema.TypeRef{}
|
||||
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]
|
||||
fieldTypes[f.Name] = f.Type
|
||||
}
|
||||
|
||||
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()...)
|
||||
|
||||
v2.doNode()
|
||||
var ok bool
|
||||
if v2.typeRef, ok = fieldTypes[name]; ok {
|
||||
errs = append(errs, v2.validate()...)
|
||||
} else {
|
||||
v2.typeRef = t.ElementType
|
||||
errs = append(errs, v2.validate()...)
|
||||
v2.doNode()
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (v validatingObjectWalker) doMap(t schema.Map) (errs ValidationErrors) {
|
||||
m, err := mapOrStructValue(v.value, "map")
|
||||
m, err := mapValue(v.value)
|
||||
if err != nil {
|
||||
return v.error(err)
|
||||
}
|
||||
@@ -221,12 +189,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
|
||||
}
|
||||
|
12
vendor/sigs.k8s.io/structured-merge-diff/value/value.go
generated
vendored
12
vendor/sigs.k8s.io/structured-merge-diff/value/value.go
generated
vendored
@@ -84,6 +84,18 @@ func (m *Map) Set(key string, value Value) {
|
||||
m.index = nil // Since the append might have reallocated
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// StringValue returns s as a scalar string Value.
|
||||
func StringValue(s string) Value {
|
||||
s2 := String(s)
|
||||
|
Reference in New Issue
Block a user