This commit is contained in:
Daniel Smith 2019-07-19 15:34:30 -07:00
parent f55efb8dae
commit 3c81a9a1c9
16 changed files with 441 additions and 104 deletions

2
go.mod
View File

@ -467,7 +467,7 @@ replace (
modernc.org/strutil => modernc.org/strutil v1.0.0 modernc.org/strutil => modernc.org/strutil v1.0.0
modernc.org/xc => modernc.org/xc v1.0.0 modernc.org/xc => modernc.org/xc v1.0.0
sigs.k8s.io/kustomize => sigs.k8s.io/kustomize v2.0.3+incompatible sigs.k8s.io/kustomize => sigs.k8s.io/kustomize v2.0.3+incompatible
sigs.k8s.io/structured-merge-diff => sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 sigs.k8s.io/structured-merge-diff => sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3
sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.1.0 sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.1.0
vbom.ml/util => vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc vbom.ml/util => vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc
) )

4
go.sum
View File

@ -484,8 +484,8 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 h1:9df9ZMJ21XLKSCuwZyI+qfbnQl/hDmrM7wjBK85iv8g= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 h1:yCY9zAYErawYwXdOYmwEBzcGCr/6eIUujYZE2DIQve8=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc h1:MksmcCZQWAQJCTA5T0jgI/0sJ51AVm4Z41MrmfczEoc= vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc h1:MksmcCZQWAQJCTA5T0jgI/0sJ51AVm4Z41MrmfczEoc=

View File

@ -310,7 +310,7 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 h1:9df9ZMJ21XLKSCuwZyI+qfbnQl/hDmrM7wjBK85iv8g= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 h1:yCY9zAYErawYwXdOYmwEBzcGCr/6eIUujYZE2DIQve8=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@ -59,7 +59,7 @@ require (
k8s.io/klog v0.3.1 k8s.io/klog v0.3.1
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058 k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3
sigs.k8s.io/yaml v1.1.0 sigs.k8s.io/yaml v1.1.0
) )

View File

@ -229,7 +229,7 @@ k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMI
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 h1:9df9ZMJ21XLKSCuwZyI+qfbnQl/hDmrM7wjBK85iv8g= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 h1:yCY9zAYErawYwXdOYmwEBzcGCr/6eIUujYZE2DIQve8=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@ -266,7 +266,7 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 h1:9df9ZMJ21XLKSCuwZyI+qfbnQl/hDmrM7wjBK85iv8g= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 h1:yCY9zAYErawYwXdOYmwEBzcGCr/6eIUujYZE2DIQve8=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@ -209,6 +209,6 @@ k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4y
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMISFn01els+2ZAcw= k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMISFn01els+2ZAcw=
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@ -263,7 +263,7 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 h1:9df9ZMJ21XLKSCuwZyI+qfbnQl/hDmrM7wjBK85iv8g= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 h1:yCY9zAYErawYwXdOYmwEBzcGCr/6eIUujYZE2DIQve8=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

2
vendor/modules.txt vendored
View File

@ -1752,7 +1752,7 @@ sigs.k8s.io/kustomize/pkg/transformers
sigs.k8s.io/kustomize/pkg/transformers/config sigs.k8s.io/kustomize/pkg/transformers/config
sigs.k8s.io/kustomize/pkg/transformers/config/defaultconfig sigs.k8s.io/kustomize/pkg/transformers/config/defaultconfig
sigs.k8s.io/kustomize/pkg/types sigs.k8s.io/kustomize/pkg/types
# sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 => sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 # sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 => sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3
sigs.k8s.io/structured-merge-diff/fieldpath sigs.k8s.io/structured-merge-diff/fieldpath
sigs.k8s.io/structured-merge-diff/merge sigs.k8s.io/structured-merge-diff/merge
sigs.k8s.io/structured-merge-diff/schema sigs.k8s.io/structured-merge-diff/schema

View File

@ -35,7 +35,7 @@ type PathElement struct {
// Key selects the list element which has fields matching those given. // Key selects the list element which has fields matching those given.
// The containing object must be an associative list with map typed // The containing object must be an associative list with map typed
// elements. // elements.
Key []value.Field Key *value.Map
// Value selects the list element with the given value. The containing // Value selects the list element with the given value. The containing
// object must be an associative list with a primitive typed element // object must be an associative list with a primitive typed element
@ -47,14 +47,62 @@ type PathElement struct {
Index *int Index *int
} }
// Less provides an order for path elements.
func (e PathElement) Less(rhs PathElement) bool {
if e.FieldName != nil {
if rhs.FieldName == nil {
return true
}
return *e.FieldName < *rhs.FieldName
} else if rhs.FieldName != nil {
return false
}
if e.Key != nil {
if rhs.Key == nil {
return true
}
return e.Key.Less(rhs.Key)
} else if rhs.Key != nil {
return false
}
if e.Value != nil {
if rhs.Value == nil {
return true
}
return e.Value.Less(*rhs.Value)
} else if rhs.Value != nil {
return false
}
if e.Index != nil {
if rhs.Index == nil {
return true
}
return *e.Index < *rhs.Index
} else if rhs.Index != nil {
// Yes, I know the next statement is the same. But this way
// the obvious way of extending the function wil be bug-free.
return false
}
return false
}
// Equals returns true if both path elements are equal.
func (e PathElement) Equals(rhs PathElement) bool {
return !e.Less(rhs) && !rhs.Less(e)
}
// String presents the path element as a human-readable string. // String presents the path element as a human-readable string.
func (e PathElement) String() string { func (e PathElement) String() string {
switch { switch {
case e.FieldName != nil: case e.FieldName != nil:
return "." + *e.FieldName return "." + *e.FieldName
case len(e.Key) > 0: case e.Key != nil:
strs := make([]string, len(e.Key)) strs := make([]string, len(e.Key.Items))
for i, k := range e.Key { for i, k := range e.Key.Items {
strs[i] = fmt.Sprintf("%v=%v", k.Name, k.Value) strs[i] = fmt.Sprintf("%v=%v", k.Name, k.Value)
} }
// The order must be canonical, since we use the string value // The order must be canonical, since we use the string value
@ -92,62 +140,100 @@ func KeyByFields(nameValues ...interface{}) []value.Field {
// PathElementSet is a set of path elements. // PathElementSet is a set of path elements.
// TODO: serialize as a list. // TODO: serialize as a list.
type PathElementSet struct { type PathElementSet struct {
// The strange construction is because there's no way to test members sortedPathElements
// PathElements for equality (it can't be used as a key for a map).
members map[string]PathElement
} }
type sortedPathElements []PathElement
// Implement the sort interface; this would permit bulk creation, which would
// be faster than doing it one at a time via Insert.
func (spe sortedPathElements) Len() int { return len(spe) }
func (spe sortedPathElements) Less(i, j int) bool { return spe[i].Less(spe[j]) }
func (spe sortedPathElements) Swap(i, j int) { spe[i], spe[j] = spe[j], spe[i] }
// Insert adds pe to the set. // Insert adds pe to the set.
func (s *PathElementSet) Insert(pe PathElement) { func (s *PathElementSet) Insert(pe PathElement) {
serialized := pe.String() loc := sort.Search(len(s.members), func(i int) bool {
if s.members == nil { return !s.members[i].Less(pe)
s.members = map[string]PathElement{ })
serialized: pe, if loc == len(s.members) {
} s.members = append(s.members, pe)
return return
} }
if _, ok := s.members[serialized]; !ok { if s.members[loc].Equals(pe) {
s.members[serialized] = pe return
} }
s.members = append(s.members, PathElement{})
copy(s.members[loc+1:], s.members[loc:])
s.members[loc] = pe
} }
// Union returns a set containing elements that appear in either s or s2. // Union returns a set containing elements that appear in either s or s2.
func (s *PathElementSet) Union(s2 *PathElementSet) *PathElementSet { func (s *PathElementSet) Union(s2 *PathElementSet) *PathElementSet {
out := &PathElementSet{ out := &PathElementSet{}
members: map[string]PathElement{},
i, j := 0, 0
for i < len(s.members) && j < len(s2.members) {
if s.members[i].Less(s2.members[j]) {
out.members = append(out.members, s.members[i])
i++
} else {
out.members = append(out.members, s2.members[j])
if !s2.members[j].Less(s.members[i]) {
i++
} }
for k, v := range s.members { j++
out.members[k] = v
} }
for k, v := range s2.members { }
out.members[k] = v
if i < len(s.members) {
out.members = append(out.members, s.members[i:]...)
}
if j < len(s2.members) {
out.members = append(out.members, s2.members[j:]...)
} }
return out return out
} }
// Intersection returns a set containing elements which appear in both s and s2. // Intersection returns a set containing elements which appear in both s and s2.
func (s *PathElementSet) Intersection(s2 *PathElementSet) *PathElementSet { func (s *PathElementSet) Intersection(s2 *PathElementSet) *PathElementSet {
out := &PathElementSet{ out := &PathElementSet{}
members: map[string]PathElement{},
i, j := 0, 0
for i < len(s.members) && j < len(s2.members) {
if s.members[i].Less(s2.members[j]) {
i++
} else {
if !s2.members[j].Less(s.members[i]) {
out.members = append(out.members, s.members[i])
i++
} }
for k, v := range s.members { j++
if _, ok := s2.members[k]; ok {
out.members[k] = v
} }
} }
return out return out
} }
// Difference returns a set containing elements which appear in s but not in s2. // Difference returns a set containing elements which appear in s but not in s2.
func (s *PathElementSet) Difference(s2 *PathElementSet) *PathElementSet { func (s *PathElementSet) Difference(s2 *PathElementSet) *PathElementSet {
out := &PathElementSet{ out := &PathElementSet{}
members: map[string]PathElement{},
i, j := 0, 0
for i < len(s.members) && j < len(s2.members) {
if s.members[i].Less(s2.members[j]) {
out.members = append(out.members, s.members[i])
i++
} else {
if !s2.members[j].Less(s.members[i]) {
i++
} }
for k, v := range s.members { j++
if _, ok := s2.members[k]; !ok {
out.members[k] = v
} }
} }
if i < len(s.members) {
out.members = append(out.members, s.members[i:]...)
}
return out return out
} }
@ -156,11 +242,16 @@ func (s *PathElementSet) Size() int { return len(s.members) }
// Has returns true if pe is a member of the set. // Has returns true if pe is a member of the set.
func (s *PathElementSet) Has(pe PathElement) bool { func (s *PathElementSet) Has(pe PathElement) bool {
if s.members == nil { loc := sort.Search(len(s.members), func(i int) bool {
return !s.members[i].Less(pe)
})
if loc == len(s.members) {
return false return false
} }
_, ok := s.members[pe.String()] if s.members[loc].Equals(pe) {
return ok return true
}
return false
} }
// Equals returns true if s and s2 have exactly the same members. // Equals returns true if s and s2 have exactly the same members.
@ -169,14 +260,14 @@ func (s *PathElementSet) Equals(s2 *PathElementSet) bool {
return false return false
} }
for k := range s.members { for k := range s.members {
if _, ok := s2.members[k]; !ok { if !s.members[k].Equals(s2.members[k]) {
return false return false
} }
} }
return true return true
} }
// Iterate calls f for each PathElement in the set. // Iterate calls f for each PathElement in the set. The order is deterministic.
func (s *PathElementSet) Iterate(f func(PathElement)) { func (s *PathElementSet) Iterate(f func(PathElement)) {
for _, pe := range s.members { for _, pe := range s.members {
f(pe) f(pe)

View File

@ -117,7 +117,7 @@ func GuessBestListPathElement(index int, item value.Value) PathElement {
keys = append(keys, *f) keys = append(keys, *f)
} }
if len(keys) > 0 { if len(keys) > 0 {
return PathElement{Key: keys} return PathElement{Key: &value.Map{Items: keys}}
} }
return PathElement{Index: &index} return PathElement{Index: &index}
} }

View File

@ -35,6 +35,40 @@ func (fp Path) String() string {
return strings.Join(strs, "") return strings.Join(strs, "")
} }
// Equals returns true if the two paths are equivalent.
func (fp Path) Equals(fp2 Path) bool {
return !fp.Less(fp2) && !fp2.Less(fp)
}
// Less provides a lexical order for Paths.
func (fp Path) Less(rhs Path) bool {
i := 0
for {
if i >= len(fp) && i >= len(rhs) {
// Paths are the same length and all items are equal.
return false
}
if i >= len(fp) {
// LHS is shorter.
return true
}
if i >= len(rhs) {
// RHS is shorter.
return false
}
if fp[i].Less(rhs[i]) {
// LHS is less; return
return true
}
if rhs[i].Less(fp[i]) {
// RHS is less; return
return false
}
// The items are equal; continue.
i++
}
}
func (fp Path) Copy() Path { func (fp Path) Copy() Path {
new := make(Path, len(fp)) new := make(Path, len(fp))
copy(new, fp) copy(new, fp)
@ -58,7 +92,7 @@ func MakePath(parts ...interface{}) (Path, error) {
if len(t) == 0 { if len(t) == 0 {
return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)") return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)")
} }
fp = append(fp, PathElement{Key: t}) fp = append(fp, PathElement{Key: &value.Map{Items: t}})
case value.Value: case value.Value:
// TODO: understand schema and verify that this is a set type // TODO: understand schema and verify that this is a set type
// TODO: make a copy of t // TODO: make a copy of t

View File

@ -17,6 +17,7 @@ limitations under the License.
package fieldpath package fieldpath
import ( import (
"sort"
"strings" "strings"
) )
@ -172,24 +173,25 @@ type setNode struct {
// SetNodeMap is a map of PathElement to subset. // SetNodeMap is a map of PathElement to subset.
type SetNodeMap struct { type SetNodeMap struct {
members map[string]setNode members []setNode
} }
// Descend adds pe to the set if necessary, returning the associated subset. // Descend adds pe to the set if necessary, returning the associated subset.
func (s *SetNodeMap) Descend(pe PathElement) *Set { func (s *SetNodeMap) Descend(pe PathElement) *Set {
serialized := pe.String() loc := sort.Search(len(s.members), func(i int) bool {
if s.members == nil { return !s.members[i].pathElement.Less(pe)
s.members = map[string]setNode{} })
if loc == len(s.members) {
s.members = append(s.members, setNode{pathElement: pe, set: &Set{}})
return s.members[loc].set
} }
if n, ok := s.members[serialized]; ok { if s.members[loc].pathElement.Equals(pe) {
return n.set return s.members[loc].set
} }
ss := &Set{} s.members = append(s.members, setNode{})
s.members[serialized] = setNode{ copy(s.members[loc+1:], s.members[loc:])
pathElement: pe, s.members[loc] = setNode{pathElement: pe, set: &Set{}}
set: ss, return s.members[loc].set
}
return ss
} }
// Size returns the sum of the number of members of all subsets. // Size returns the sum of the number of members of all subsets.
@ -213,12 +215,14 @@ func (s *SetNodeMap) Empty() bool {
// Get returns (the associated set, true) or (nil, false) if there is none. // Get returns (the associated set, true) or (nil, false) if there is none.
func (s *SetNodeMap) Get(pe PathElement) (*Set, bool) { func (s *SetNodeMap) Get(pe PathElement) (*Set, bool) {
if s.members == nil { loc := sort.Search(len(s.members), func(i int) bool {
return !s.members[i].pathElement.Less(pe)
})
if loc == len(s.members) {
return nil, false return nil, false
} }
serialized := pe.String() if s.members[loc].pathElement.Equals(pe) {
if n, ok := s.members[serialized]; ok { return s.members[loc].set, true
return n.set, true
} }
return nil, false return nil, false
} }
@ -229,12 +233,11 @@ func (s *SetNodeMap) Equals(s2 *SetNodeMap) bool {
if len(s.members) != len(s2.members) { if len(s.members) != len(s2.members) {
return false return false
} }
for k, v := range s.members { for i := range s.members {
v2, ok := s2.members[k] if !s.members[i].pathElement.Equals(s2.members[i].pathElement) {
if !ok {
return false return false
} }
if !v.set.Equals(v2.set) { if !s.members[i].set.Equals(s2.members[i].set) {
return false return false
} }
} }
@ -244,21 +247,28 @@ func (s *SetNodeMap) Equals(s2 *SetNodeMap) bool {
// Union returns a SetNodeMap with members that appear in either s or s2. // Union returns a SetNodeMap with members that appear in either s or s2.
func (s *SetNodeMap) Union(s2 *SetNodeMap) *SetNodeMap { func (s *SetNodeMap) Union(s2 *SetNodeMap) *SetNodeMap {
out := &SetNodeMap{} out := &SetNodeMap{}
for k, sn := range s.members {
pe := sn.pathElement i, j := 0, 0
if sn2, ok := s2.members[k]; ok { for i < len(s.members) && j < len(s2.members) {
*out.Descend(pe) = *sn.set.Union(sn2.set) if s.members[i].pathElement.Less(s2.members[j].pathElement) {
out.members = append(out.members, s.members[i])
i++
} else { } else {
*out.Descend(pe) = *sn.set if !s2.members[j].pathElement.Less(s.members[i].pathElement) {
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: s.members[i].set.Union(s2.members[j].set)})
i++
} else {
out.members = append(out.members, s2.members[j])
}
j++
} }
} }
for k, sn2 := range s2.members {
pe := sn2.pathElement if i < len(s.members) {
if _, ok := s.members[k]; ok { out.members = append(out.members, s.members[i:]...)
// already handled
continue
} }
*out.Descend(pe) = *sn2.set if j < len(s2.members) {
out.members = append(out.members, s2.members[j:]...)
} }
return out return out
} }
@ -266,13 +276,20 @@ func (s *SetNodeMap) Union(s2 *SetNodeMap) *SetNodeMap {
// Intersection returns a SetNodeMap with members that appear in both s and s2. // Intersection returns a SetNodeMap with members that appear in both s and s2.
func (s *SetNodeMap) Intersection(s2 *SetNodeMap) *SetNodeMap { func (s *SetNodeMap) Intersection(s2 *SetNodeMap) *SetNodeMap {
out := &SetNodeMap{} out := &SetNodeMap{}
for k, sn := range s.members {
pe := sn.pathElement i, j := 0, 0
if sn2, ok := s2.members[k]; ok { for i < len(s.members) && j < len(s2.members) {
i := *sn.set.Intersection(sn2.set) if s.members[i].pathElement.Less(s2.members[j].pathElement) {
if !i.Empty() { i++
*out.Descend(pe) = i } else {
if !s2.members[j].pathElement.Less(s.members[i].pathElement) {
res := s.members[i].set.Intersection(s2.members[j].set)
if !res.Empty() {
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: res})
} }
i++
}
j++
} }
} }
return out return out
@ -281,17 +298,29 @@ func (s *SetNodeMap) Intersection(s2 *SetNodeMap) *SetNodeMap {
// Difference returns a SetNodeMap with members that appear in s but not in s2. // Difference returns a SetNodeMap with members that appear in s but not in s2.
func (s *SetNodeMap) Difference(s2 *Set) *SetNodeMap { func (s *SetNodeMap) Difference(s2 *Set) *SetNodeMap {
out := &SetNodeMap{} out := &SetNodeMap{}
for k, sn := range s.members {
pe := sn.pathElement i, j := 0, 0
if sn2, ok := s2.Children.members[k]; ok { for i < len(s.members) && j < len(s2.Children.members) {
diff := *sn.set.Difference(sn2.set) if s.members[i].pathElement.Less(s2.Children.members[j].pathElement) {
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: s.members[i].set})
i++
} else {
if !s2.Children.members[j].pathElement.Less(s.members[i].pathElement) {
diff := s.members[i].set.Difference(s2.Children.members[j].set)
// We aren't permitted to add nodes with no elements. // We aren't permitted to add nodes with no elements.
if !diff.Empty() { if !diff.Empty() {
*out.Descend(pe) = diff out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: diff})
} }
} else {
*out.Descend(pe) = *sn.set i++
} }
j++
}
}
if i < len(s.members) {
out.members = append(out.members, s.members[i:]...)
} }
return out return out
} }

View File

@ -40,6 +40,14 @@ func (c Conflict) Error() string {
return fmt.Sprintf("conflict with %q: %v", c.Manager, c.Path) return fmt.Sprintf("conflict with %q: %v", c.Manager, c.Path)
} }
// Equals returns true if c == c2
func (c Conflict) Equals(c2 Conflict) bool {
if c.Manager != c2.Manager {
return false
}
return c.Path.Equals(c2.Path)
}
// Conflicts accumulates multiple conflicts and aggregates them by managers. // Conflicts accumulates multiple conflicts and aggregates them by managers.
type Conflicts []Conflict type Conflicts []Conflict
@ -74,6 +82,19 @@ func (conflicts Conflicts) Error() string {
return strings.Join(messages, "\n") return strings.Join(messages, "\n")
} }
// Equals returns true if the lists of conflicts are the same.
func (c Conflicts) Equals(c2 Conflicts) bool {
if len(c) != len(c2) {
return false
}
for i := range c {
if !c[i].Equals(c2[i]) {
return false
}
}
return true
}
// ConflictsFromManagers creates a list of conflicts given Managers sets. // ConflictsFromManagers creates a list of conflicts given Managers sets.
func ConflictsFromManagers(sets fieldpath.ManagedFields) Conflicts { func ConflictsFromManagers(sets fieldpath.ManagedFields) Conflicts {
conflicts := []Conflict{} conflicts := []Conflict{}

View File

@ -197,6 +197,7 @@ func keyedAssociativeListItemToPathElement(list schema.List, index int, child va
if child.MapValue == nil { if child.MapValue == nil {
return pe, errors.New("associative list with keys may not have non-map elements") return pe, errors.New("associative list with keys may not have non-map elements")
} }
keyMap := &value.Map{}
for _, fieldName := range list.Keys { for _, fieldName := range list.Keys {
var fieldValue value.Value var fieldValue value.Value
field, ok := child.MapValue.Get(fieldName) field, ok := child.MapValue.Get(fieldName)
@ -206,11 +207,9 @@ func keyedAssociativeListItemToPathElement(list schema.List, index int, child va
// Treat keys as required. // Treat keys as required.
return pe, fmt.Errorf("associative list with keys has an element that omits key field %q", fieldName) return pe, fmt.Errorf("associative list with keys has an element that omits key field %q", fieldName)
} }
pe.Key = append(pe.Key, value.Field{ keyMap.Set(fieldName, fieldValue)
Name: fieldName,
Value: fieldValue,
})
} }
pe.Key = keyMap
return pe, nil return pe, nil
} }

View File

@ -18,6 +18,7 @@ package value
import ( import (
"fmt" "fmt"
"sort"
"strings" "strings"
) )
@ -33,6 +34,85 @@ type Value struct {
Null bool // represents an explicit `"foo" = null` Null bool // represents an explicit `"foo" = null`
} }
// Less provides a total ordering for Value (so that they can be sorted, even
// if they are of different types).
func (v Value) Less(rhs Value) bool {
if v.FloatValue != nil {
if rhs.FloatValue == nil {
// Extra: compare floats and ints numerically.
if rhs.IntValue != nil {
return float64(*v.FloatValue) < float64(*rhs.IntValue)
}
return true
}
return *v.FloatValue < *rhs.FloatValue
} else if rhs.FloatValue != nil {
// Extra: compare floats and ints numerically.
if v.IntValue != nil {
return float64(*v.IntValue) < float64(*rhs.FloatValue)
}
return false
}
if v.IntValue != nil {
if rhs.IntValue == nil {
return true
}
return *v.IntValue < *rhs.IntValue
} else if rhs.IntValue != nil {
return false
}
if v.StringValue != nil {
if rhs.StringValue == nil {
return true
}
return *v.StringValue < *rhs.StringValue
} else if rhs.StringValue != nil {
return false
}
if v.BooleanValue != nil {
if rhs.BooleanValue == nil {
return true
}
if *v.BooleanValue == *rhs.BooleanValue {
return false
}
return *v.BooleanValue == false
} else if rhs.BooleanValue != nil {
return false
}
if v.ListValue != nil {
if rhs.ListValue == nil {
return true
}
return v.ListValue.Less(rhs.ListValue)
} else if rhs.ListValue != nil {
return false
}
if v.MapValue != nil {
if rhs.MapValue == nil {
return true
}
return v.MapValue.Less(rhs.MapValue)
} else if rhs.MapValue != nil {
return false
}
if v.Null {
if !rhs.Null {
return true
}
return false
} else if rhs.Null {
return false
}
// Invalid Value-- nothing is set.
return false
}
type Int int64 type Int int64
type Float float64 type Float float64
type String string type String string
@ -49,6 +129,35 @@ type List struct {
Items []Value Items []Value
} }
// Less compares two lists lexically.
func (l *List) Less(rhs *List) bool {
i := 0
for {
if i >= len(l.Items) && i >= len(rhs.Items) {
// Lists are the same length and all items are equal.
return false
}
if i >= len(l.Items) {
// LHS is shorter.
return true
}
if i >= len(rhs.Items) {
// RHS is shorter.
return false
}
if l.Items[i].Less(rhs.Items[i]) {
// LHS is less; return
return true
}
if rhs.Items[i].Less(l.Items[i]) {
// RHS is less; return
return false
}
// The items are equal; continue.
i++
}
}
// Map is a map of key-value pairs. It represents both structs and maps. We use // Map is a map of key-value pairs. It represents both structs and maps. We use
// a list and a go-language map to preserve order. // a list and a go-language map to preserve order.
// //
@ -59,6 +168,58 @@ type Map struct {
// may be nil; lazily constructed. // may be nil; lazily constructed.
// TODO: Direct modifications to Items above will cause serious problems. // TODO: Direct modifications to Items above will cause serious problems.
index map[string]*Field index map[string]*Field
// may be empty; lazily constructed.
// TODO: Direct modifications to Items above will cause serious problems.
order []int
}
func (m *Map) computeOrder() {
if len(m.order) != len(m.Items) {
m.order = make([]int, len(m.Items))
for i := range m.order {
m.order[i] = i
}
sort.SliceStable(m.order, func(i, j int) bool {
return m.Items[m.order[i]].Name < m.Items[m.order[j]].Name
})
}
}
// Less compares two maps lexically.
func (m *Map) Less(rhs *Map) bool {
m.computeOrder()
rhs.computeOrder()
i := 0
for {
if i >= len(m.order) && i >= len(rhs.order) {
// Maps are the same length and all items are equal.
return false
}
if i >= len(m.order) {
// LHS is shorter.
return true
}
if i >= len(rhs.order) {
// RHS is shorter.
return false
}
fa, fb := &m.Items[m.order[i]], &rhs.Items[rhs.order[i]]
if fa.Name != fb.Name {
// the map having the field name that sorts lexically less is "less"
return fa.Name < fb.Name
}
if fa.Value.Less(fb.Value) {
// LHS is less; return
return true
}
if fb.Value.Less(fa.Value) {
// RHS is less; return
return false
}
// The items are equal; continue.
i++
}
} }
// Get returns the (Field, true) or (nil, false) if it is not present // Get returns the (Field, true) or (nil, false) if it is not present
@ -82,6 +243,7 @@ func (m *Map) Set(key string, value Value) {
} }
m.Items = append(m.Items, Field{Name: key, Value: value}) m.Items = append(m.Items, Field{Name: key, Value: value})
m.index = nil // Since the append might have reallocated m.index = nil // Since the append might have reallocated
m.order = nil
} }
// Delete removes the key from the set. // Delete removes the key from the set.
@ -94,6 +256,7 @@ func (m *Map) Delete(key string) {
} }
m.Items = items m.Items = items
m.index = nil // Since the list has changed m.index = nil // Since the list has changed
m.order = nil
} }
// StringValue returns s as a scalar string Value. // StringValue returns s as a scalar string Value.