mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 04:06:03 +00:00
Update smd repo to handle CRDs
This commit is contained in:
parent
7e4cc38401
commit
1751fc013f
6
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/path.go
generated
vendored
6
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/path.go
generated
vendored
@ -35,6 +35,12 @@ func (fp Path) String() string {
|
||||
return strings.Join(strs, "")
|
||||
}
|
||||
|
||||
func (fp Path) Copy() Path {
|
||||
new := make(Path, len(fp))
|
||||
copy(new, fp)
|
||||
return new
|
||||
}
|
||||
|
||||
// MakePath constructs a Path. The parts may be PathElements, ints, strings.
|
||||
func MakePath(parts ...interface{}) (Path, error) {
|
||||
var fp Path
|
||||
|
10
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go
generated
vendored
10
vendor/sigs.k8s.io/structured-merge-diff/fieldpath/set.go
generated
vendored
@ -154,6 +154,16 @@ func (s *Set) iteratePrefix(prefix Path, f func(Path)) {
|
||||
s.Children.iteratePrefix(prefix, f)
|
||||
}
|
||||
|
||||
// WithPrefix returns the subset of paths which begin with the given prefix,
|
||||
// with the prefix not included.
|
||||
func (s *Set) WithPrefix(pe PathElement) *Set {
|
||||
subset, ok := s.Children.Get(pe)
|
||||
if !ok {
|
||||
return NewSet()
|
||||
}
|
||||
return subset
|
||||
}
|
||||
|
||||
// setNode is a pair of PathElement / Set, for the purpose of expressing
|
||||
// nested set membership.
|
||||
type setNode struct {
|
||||
|
48
vendor/sigs.k8s.io/structured-merge-diff/merge/update.go
generated
vendored
48
vendor/sigs.k8s.io/structured-merge-diff/merge/update.go
generated
vendored
@ -85,6 +85,15 @@ func (s *Updater) update(oldObject, newObject typed.TypedValue, version fieldpat
|
||||
|
||||
for manager, conflictSet := range conflicts {
|
||||
managers[manager].Set = managers[manager].Set.Difference(conflictSet.Set)
|
||||
if managers[manager].Set.Empty() {
|
||||
delete(managers, manager)
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := managers[workflow]; !ok {
|
||||
managers[workflow] = &fieldpath.VersionedSet{
|
||||
Set: fieldpath.NewSet(),
|
||||
}
|
||||
}
|
||||
|
||||
return managers, nil
|
||||
@ -105,13 +114,11 @@ func (s *Updater) Update(liveObject, newObject typed.TypedValue, version fieldpa
|
||||
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(),
|
||||
}
|
||||
}
|
||||
managers[manager].Set = managers[manager].Set.Union(compare.Modified).Union(compare.Added).Difference(compare.Removed)
|
||||
managers[manager].APIVersion = version
|
||||
if managers[manager].Set.Empty() {
|
||||
delete(managers, manager)
|
||||
}
|
||||
return managers, nil
|
||||
}
|
||||
|
||||
@ -121,22 +128,41 @@ func (s *Updater) Update(liveObject, newObject typed.TypedValue, version fieldpa
|
||||
func (s *Updater) Apply(liveObject, configObject typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string, force bool) (typed.TypedValue, fieldpath.ManagedFields, error) {
|
||||
newObject, err := liveObject.Merge(configObject)
|
||||
if err != nil {
|
||||
return typed.TypedValue{}, fieldpath.ManagedFields{}, fmt.Errorf("failed to merge config: %v", err)
|
||||
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to merge config: %v", err)
|
||||
}
|
||||
managers, err = s.update(liveObject, newObject, version, managers, manager, force)
|
||||
if err != nil {
|
||||
return typed.TypedValue{}, fieldpath.ManagedFields{}, err
|
||||
return nil, fieldpath.ManagedFields{}, err
|
||||
}
|
||||
newObject, err = s.removeDisownedItems(newObject, configObject, managers[manager])
|
||||
if err != nil {
|
||||
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to remove fields: %v", err)
|
||||
}
|
||||
|
||||
// TODO: Remove unconflicting removed fields
|
||||
|
||||
set, err := configObject.ToFieldSet()
|
||||
if err != nil {
|
||||
return typed.TypedValue{}, fieldpath.ManagedFields{}, fmt.Errorf("failed to get field set: %v", err)
|
||||
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to get field set: %v", err)
|
||||
}
|
||||
managers[manager] = &fieldpath.VersionedSet{
|
||||
Set: set,
|
||||
APIVersion: version,
|
||||
}
|
||||
if managers[manager].Set.Empty() {
|
||||
delete(managers, manager)
|
||||
}
|
||||
return newObject, managers, nil
|
||||
}
|
||||
|
||||
func (s *Updater) removeDisownedItems(merged, applied typed.TypedValue, lastSet *fieldpath.VersionedSet) (typed.TypedValue, error) {
|
||||
if lastSet.Set.Empty() {
|
||||
return merged, nil
|
||||
}
|
||||
convertedApplied, err := s.Converter.Convert(applied, lastSet.APIVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert applied config to last applied version: %v", err)
|
||||
}
|
||||
appliedSet, err := convertedApplied.ToFieldSet()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create field set from applied config in last applied version: %v", err)
|
||||
}
|
||||
return merged.RemoveItems(lastSet.Set.Difference(appliedSet)), nil
|
||||
}
|
||||
|
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,11 +5,13 @@ 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(
|
||||
|
2
vendor/sigs.k8s.io/structured-merge-diff/schema/elements.go
generated
vendored
2
vendor/sigs.k8s.io/structured-merge-diff/schema/elements.go
generated
vendored
@ -207,7 +207,7 @@ func (s Schema) FindNamedType(name string) (TypeDef, bool) {
|
||||
//
|
||||
// This allows callers to not care about the difference between a (possibly
|
||||
// inlined) reference and a definition.
|
||||
func (s Schema) Resolve(tr TypeRef) (Atom, bool) {
|
||||
func (s *Schema) Resolve(tr TypeRef) (Atom, bool) {
|
||||
if tr.NamedType != nil {
|
||||
t, ok := s.FindNamedType(*tr.NamedType)
|
||||
if !ok {
|
||||
|
57
vendor/sigs.k8s.io/structured-merge-diff/schema/fromvalue.go
generated
vendored
Normal file
57
vendor/sigs.k8s.io/structured-merge-diff/schema/fromvalue.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
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{}}
|
||||
}
|
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,10 +3,12 @@ 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",
|
||||
"validate.go",
|
||||
],
|
||||
|
178
vendor/sigs.k8s.io/structured-merge-diff/typed/deduced.go
generated
vendored
Normal file
178
vendor/sigs.k8s.io/structured-merge-diff/typed/deduced.go
generated
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
|
||||
}
|
3
vendor/sigs.k8s.io/structured-merge-diff/typed/helpers.go
generated
vendored
3
vendor/sigs.k8s.io/structured-merge-diff/typed/helpers.go
generated
vendored
@ -129,6 +129,9 @@ func (ef errorFormatter) validateScalar(t schema.Scalar, v *value.Value, prefix
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
if v.Null {
|
||||
return nil
|
||||
}
|
||||
switch t {
|
||||
case schema.Numeric:
|
||||
if v.FloatValue == nil && v.IntValue == nil {
|
||||
|
61
vendor/sigs.k8s.io/structured-merge-diff/typed/parser.go
generated
vendored
61
vendor/sigs.k8s.io/structured-merge-diff/typed/parser.go
generated
vendored
@ -68,47 +68,80 @@ 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{
|
||||
func (p *Parser) Type(name string) ParseableType {
|
||||
return &parseableType{
|
||||
parser: p,
|
||||
typename: name,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseableType allows for easy production of typed objects.
|
||||
type ParseableType struct {
|
||||
type ParseableType interface {
|
||||
IsValid() bool
|
||||
FromYAML(YAMLObject) (TypedValue, error)
|
||||
FromUnstructured(interface{}) (TypedValue, error)
|
||||
}
|
||||
|
||||
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 {
|
||||
func (p *parseableType) IsValid() bool {
|
||||
_, ok := p.parser.Schema.Resolve(schema.TypeRef{NamedType: &p.typename})
|
||||
return ok
|
||||
}
|
||||
|
||||
// New returns a new empty object with the current schema and the
|
||||
// type "typename".
|
||||
func (p *ParseableType) New() (TypedValue, error) {
|
||||
return p.FromYAML(YAMLObject("{}"))
|
||||
}
|
||||
|
||||
// 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 TypedValue{}, err
|
||||
return nil, err
|
||||
}
|
||||
return AsTyped(v, &p.parser.Schema, p.typename)
|
||||
}
|
||||
|
||||
// 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 TypedValue{}, err
|
||||
return nil, err
|
||||
}
|
||||
return AsTyped(v, &p.parser.Schema, p.typename)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
119
vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go
generated
vendored
Normal file
119
vendor/sigs.k8s.io/structured-merge-diff/typed/remove.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typed
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
type removingWalker struct {
|
||||
value *value.Value
|
||||
schema *schema.Schema
|
||||
toRemove *fieldpath.Set
|
||||
}
|
||||
|
||||
func removeItemsWithSchema(value *value.Value, toRemove *fieldpath.Set, schema *schema.Schema, typeRef schema.TypeRef) {
|
||||
w := &removingWalker{
|
||||
value: value,
|
||||
schema: schema,
|
||||
toRemove: toRemove,
|
||||
}
|
||||
resolveSchema(schema, typeRef, 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) 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
|
||||
|
||||
// If list is null, empty, or atomic just return
|
||||
if l == nil || len(l.Items) == 0 || t.ElementRelationship == schema.Atomic {
|
||||
return nil
|
||||
}
|
||||
|
||||
newItems := []value.Value{}
|
||||
for i, _ := range l.Items {
|
||||
item := l.Items[i]
|
||||
// Ignore error because we have already validated this list
|
||||
pe, _ := listItemToPathElement(t, i, item)
|
||||
path, _ := fieldpath.MakePath(pe)
|
||||
if w.toRemove.Has(path) {
|
||||
continue
|
||||
}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
removeItemsWithSchema(&l.Items[i], subset, w.schema, t.ElementType)
|
||||
}
|
||||
newItems = append(newItems, l.Items[i])
|
||||
}
|
||||
l.Items = newItems
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *removingWalker) doMap(t schema.Map) ValidationErrors {
|
||||
m := w.value.MapValue
|
||||
|
||||
// If map is null, empty, or atomic just return
|
||||
if m == nil || len(m.Items) == 0 || t.ElementRelationship == schema.Atomic {
|
||||
return nil
|
||||
}
|
||||
|
||||
newMap := &value.Map{}
|
||||
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
|
||||
}
|
||||
if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
|
||||
removeItemsWithSchema(&m.Items[i].Value, subset, w.schema, t.ElementType)
|
||||
}
|
||||
newMap.Set(item.Name, m.Items[i].Value)
|
||||
}
|
||||
w.value.MapValue = newMap
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*removingWalker) doUntyped(_ schema.Untyped) ValidationErrors { return nil }
|
||||
|
||||
func (*removingWalker) errorf(_ string, _ ...interface{}) ValidationErrors { return nil }
|
255
vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go
generated
vendored
255
vendor/sigs.k8s.io/structured-merge-diff/typed/typed.go
generated
vendored
@ -25,44 +25,87 @@ import (
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// TypedValue is a value of some specific type.
|
||||
type TypedValue struct {
|
||||
value value.Value
|
||||
typeRef schema.TypeRef
|
||||
schema *schema.Schema
|
||||
// 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 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{
|
||||
tv := typedValue{
|
||||
value: v,
|
||||
typeRef: schema.TypeRef{NamedType: &typeName},
|
||||
schema: s,
|
||||
}
|
||||
if err := tv.Validate(); err != nil {
|
||||
return TypedValue{}, err
|
||||
return nil, err
|
||||
}
|
||||
return tv, nil
|
||||
}
|
||||
|
||||
// AsValue removes the type from the TypedValue and only keeps the value.
|
||||
func (tv TypedValue) AsValue() *value.Value {
|
||||
// AsTypeUnvalidated is just like AsTyped, but doesn't validate that the type
|
||||
// conforms to the schema, for cases where that has already been checked or
|
||||
// where you're going to call a method that validates as a side-effect (like
|
||||
// ToFieldSet).
|
||||
func AsTypedUnvalidated(v value.Value, s *schema.Schema, typeName string) TypedValue {
|
||||
tv := typedValue{
|
||||
value: v,
|
||||
typeRef: schema.TypeRef{NamedType: &typeName},
|
||||
schema: s,
|
||||
}
|
||||
return tv
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return &tv.value
|
||||
}
|
||||
|
||||
// Validate returns an error with a list of every spec violation.
|
||||
func (tv TypedValue) Validate() error {
|
||||
func (tv typedValue) Validate() error {
|
||||
if errs := tv.walker().validate(); len(errs) != 0 {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToFieldSet creates a set containing every leaf field mentioned in tv, or
|
||||
// validation errors, if any were encountered.
|
||||
func (tv TypedValue) ToFieldSet() (*fieldpath.Set, error) {
|
||||
func (tv typedValue) ToFieldSet() (*fieldpath.Set, error) {
|
||||
s := fieldpath.NewSet()
|
||||
w := tv.walker()
|
||||
w.leafFieldCallback = func(p fieldpath.Path) { s.Insert(p) }
|
||||
@ -72,19 +115,91 @@ func (tv TypedValue) ToFieldSet() (*fieldpath.Set, error) {
|
||||
return s, 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) 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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
c = &Comparison{
|
||||
Removed: fieldpath.NewSet(),
|
||||
Modified: fieldpath.NewSet(),
|
||||
Added: fieldpath.NewSet(),
|
||||
}
|
||||
c.Merged, err = merge(tv, trhs, 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.
|
||||
// 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)
|
||||
} else if w.rhs == nil {
|
||||
c.Removed.Insert(w.path)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// RemoveItems removes each provided list or map item from the value.
|
||||
func (tv typedValue) RemoveItems(items *fieldpath.Set) TypedValue {
|
||||
removeItemsWithSchema(&tv.value, items, tv.schema, tv.typeRef)
|
||||
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")
|
||||
}
|
||||
if !reflect.DeepEqual(lhs.typeRef, 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,
|
||||
}
|
||||
errs := mw.merge()
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
out := typedValue{
|
||||
schema: lhs.schema,
|
||||
typeRef: lhs.typeRef,
|
||||
}
|
||||
if mw.out == nil {
|
||||
out.value = value.Value{Null: true}
|
||||
} else {
|
||||
out.value = *mw.out
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Comparison is the return value of a TypedValue.Compare() operation.
|
||||
@ -125,89 +240,3 @@ func (c *Comparison) String() string {
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// 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, 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.
|
||||
// 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)
|
||||
} else if w.rhs == nil {
|
||||
c.Removed.Insert(w.path)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func merge(lhs, rhs TypedValue, rule, postRule mergeRule) (TypedValue, error) {
|
||||
if lhs.schema != rhs.schema {
|
||||
return TypedValue{}, errorFormatter{}.
|
||||
errorf("expected objects with types from the same schema")
|
||||
}
|
||||
if !reflect.DeepEqual(lhs.typeRef, rhs.typeRef) {
|
||||
return TypedValue{}, 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,
|
||||
}
|
||||
errs := mw.merge()
|
||||
if len(errs) > 0 {
|
||||
return TypedValue{}, errs
|
||||
}
|
||||
|
||||
out := TypedValue{
|
||||
schema: lhs.schema,
|
||||
typeRef: lhs.typeRef,
|
||||
}
|
||||
if mw.out == nil {
|
||||
out.value = value.Value{Null: true}
|
||||
} else {
|
||||
out.value = *mw.out
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AsTypeUnvalidated is just like WithType, but doesn't validate that the type
|
||||
// conforms to the schema, for cases where that has already been checked or
|
||||
// where you're going to call a method that validates as a side-effect (like
|
||||
// ToFieldSet).
|
||||
func AsTypedUnvalidated(v value.Value, s *schema.Schema, typeName string) TypedValue {
|
||||
tv := TypedValue{
|
||||
value: v,
|
||||
typeRef: schema.TypeRef{NamedType: &typeName},
|
||||
schema: s,
|
||||
}
|
||||
return tv
|
||||
}
|
||||
|
2
vendor/sigs.k8s.io/structured-merge-diff/typed/validate.go
generated
vendored
2
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,
|
||||
|
2
vendor/sigs.k8s.io/structured-merge-diff/value/unstructured.go
generated
vendored
2
vendor/sigs.k8s.io/structured-merge-diff/value/unstructured.go
generated
vendored
@ -31,7 +31,7 @@ import (
|
||||
func FromYAML(input []byte) (Value, error) {
|
||||
var decoded interface{}
|
||||
|
||||
if len(input) == 0 || (len(input) == 4 && string(input) == "null") {
|
||||
if len(input) == 4 && string(input) == "null" {
|
||||
// Special case since the yaml package doesn't accurately
|
||||
// preserve this.
|
||||
return Value{Null: true}, nil
|
||||
|
Loading…
Reference in New Issue
Block a user