Merge pull request #85227 from apelisse/update-smd

Update structured-merge-diff to latest version
This commit is contained in:
Kubernetes Prow Robot 2019-11-13 20:02:24 -08:00 committed by GitHub
commit 85bc79d81f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 485 additions and 201 deletions

2
go.mod
View File

@ -559,7 +559,7 @@ replace (
mvdan.cc/lint => mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b mvdan.cc/lint => mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b
mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34 mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34
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-20190817042607-6149e4549fca sigs.k8s.io/structured-merge-diff => sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06
sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.1.0 sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.1.0
sourcegraph.com/sqs/pbtypes => sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 sourcegraph.com/sqs/pbtypes => sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4
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

@ -601,8 +601,8 @@ mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jC
mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY=
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-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
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=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@ -446,7 +446,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-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
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

@ -50,7 +50,7 @@ require (
k8s.io/klog v1.0.0 k8s.io/klog v1.0.0
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06
sigs.k8s.io/yaml v1.1.0 sigs.k8s.io/yaml v1.1.0
) )

View File

@ -357,7 +357,7 @@ k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d h1:1P0iBJsBzxRmR+dIFnM+Iu4aLxnoa
k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/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-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
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

@ -77,7 +77,7 @@ func NewPathElement(s string) (fieldpath.PathElement, error) {
if err != nil { if err != nil {
return fieldpath.PathElement{}, err return fieldpath.PathElement{}, err
} }
fields := []value.Field{} fields := value.FieldList{}
for k, v := range kv { for k, v := range kv {
b, err := json.Marshal(v) b, err := json.Marshal(v)
if err != nil { if err != nil {
@ -94,7 +94,7 @@ func NewPathElement(s string) (fieldpath.PathElement, error) {
}) })
} }
return fieldpath.PathElement{ return fieldpath.PathElement{
Key: &value.Map{Items: fields}, Key: &fields,
}, nil }, nil
default: default:
// Ignore unknown key types // Ignore unknown key types
@ -109,7 +109,7 @@ func PathElementString(pe fieldpath.PathElement) (string, error) {
return Field + Separator + *pe.FieldName, nil return Field + Separator + *pe.FieldName, nil
case pe.Key != nil: case pe.Key != nil:
kv := map[string]json.RawMessage{} kv := map[string]json.RawMessage{}
for _, k := range pe.Key.Items { for _, k := range *pe.Key {
b, err := k.Value.ToJSON() b, err := k.Value.ToJSON()
if err != nil { if err != nil {
return "", err return "", err

View File

@ -397,7 +397,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-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
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

@ -352,6 +352,6 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKf
k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d h1:1P0iBJsBzxRmR+dIFnM+Iu4aLxnoa7lBqozW/0uHbT8= k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d h1:1P0iBJsBzxRmR+dIFnM+Iu4aLxnoa7lBqozW/0uHbT8=
k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/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-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
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

@ -394,7 +394,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-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
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

@ -1929,7 +1929,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-20190817042607-6149e4549fca => sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca # sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 => sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06
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

@ -8,6 +8,7 @@ go_library(
"fromvalue.go", "fromvalue.go",
"managers.go", "managers.go",
"path.go", "path.go",
"pathelementmap.go",
"serialize.go", "serialize.go",
"serialize-pe.go", "serialize-pe.go",
"set.go", "set.go",

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.Map Key *value.FieldList
// 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
@ -62,7 +62,7 @@ func (e PathElement) Less(rhs PathElement) bool {
if rhs.Key == nil { if rhs.Key == nil {
return true return true
} }
return e.Key.Less(rhs.Key) return e.Key.Less(*rhs.Key)
} else if rhs.Key != nil { } else if rhs.Key != nil {
return false return false
} }
@ -101,13 +101,11 @@ func (e PathElement) String() string {
case e.FieldName != nil: case e.FieldName != nil:
return "." + *e.FieldName return "." + *e.FieldName
case e.Key != nil: case e.Key != nil:
strs := make([]string, len(e.Key.Items)) strs := make([]string, len(*e.Key))
for i, k := range e.Key.Items { for i, k := range *e.Key {
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 // Keys are supposed to be sorted.
// in a set structure.
sort.Strings(strs)
return "[" + strings.Join(strs, ",") + "]" return "[" + strings.Join(strs, ",") + "]"
case e.Value != nil: case e.Value != nil:
return fmt.Sprintf("[=%v]", e.Value) return fmt.Sprintf("[=%v]", e.Value)
@ -123,18 +121,19 @@ func (e PathElement) String() string {
// names (type must be string) with values (type must be value.Value). If these // names (type must be string) with values (type must be value.Value). If these
// conditions are not met, KeyByFields will panic--it's intended for static // conditions are not met, KeyByFields will panic--it's intended for static
// construction and shouldn't have user-produced values passed to it. // construction and shouldn't have user-produced values passed to it.
func KeyByFields(nameValues ...interface{}) []value.Field { func KeyByFields(nameValues ...interface{}) *value.FieldList {
if len(nameValues)%2 != 0 { if len(nameValues)%2 != 0 {
panic("must have a value for every name") panic("must have a value for every name")
} }
out := []value.Field{} out := value.FieldList{}
for i := 0; i < len(nameValues)-1; i += 2 { for i := 0; i < len(nameValues)-1; i += 2 {
out = append(out, value.Field{ out = append(out, value.Field{
Name: nameValues[i].(string), Name: nameValues[i].(string),
Value: nameValues[i+1].(value.Value), Value: nameValues[i+1].(value.Value),
}) })
} }
return out out.Sort()
return &out
} }
// PathElementSet is a set of path elements. // PathElementSet is a set of path elements.
@ -143,6 +142,12 @@ type PathElementSet struct {
members sortedPathElements members sortedPathElements
} }
func MakePathElementSet(size int) PathElementSet {
return PathElementSet{
members: make(sortedPathElements, 0, size),
}
}
type sortedPathElements []PathElement type sortedPathElements []PathElement
// Implement the sort interface; this would permit bulk creation, which would // Implement the sort interface; this would permit bulk creation, which would

View File

@ -104,7 +104,7 @@ func GuessBestListPathElement(index int, item value.Value) PathElement {
return PathElement{Index: &index} return PathElement{Index: &index}
} }
var keys []value.Field var keys value.FieldList
for _, name := range AssociativeListCandidateFieldNames { for _, name := range AssociativeListCandidateFieldNames {
f, ok := item.MapValue.Get(name) f, ok := item.MapValue.Get(name)
if !ok { if !ok {
@ -117,7 +117,8 @@ 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: &value.Map{Items: keys}} keys.Sort()
return PathElement{Key: &keys}
} }
return PathElement{Index: &index} return PathElement{Index: &index}
} }

View File

@ -88,11 +88,11 @@ func MakePath(parts ...interface{}) (Path, error) {
fp = append(fp, PathElement{Index: &t}) fp = append(fp, PathElement{Index: &t})
case string: case string:
fp = append(fp, PathElement{FieldName: &t}) fp = append(fp, PathElement{FieldName: &t})
case []value.Field: case *value.FieldList:
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: &value.Map{Items: t}}) fp = append(fp, PathElement{Key: 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

@ -0,0 +1,85 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fieldpath
import (
"sort"
"sigs.k8s.io/structured-merge-diff/value"
)
// PathElementValueMap is a map from PathElement to value.Value.
//
// TODO(apelisse): We have multiple very similar implementation of this
// for PathElementSet and SetNodeMap, so we could probably share the
// code.
type PathElementValueMap struct {
members sortedPathElementValues
}
func MakePathElementValueMap(size int) PathElementValueMap {
return PathElementValueMap{
members: make(sortedPathElementValues, 0, size),
}
}
type pathElementValue struct {
PathElement PathElement
Value value.Value
}
type sortedPathElementValues []pathElementValue
// Implement the sort interface; this would permit bulk creation, which would
// be faster than doing it one at a time via Insert.
func (spev sortedPathElementValues) Len() int { return len(spev) }
func (spev sortedPathElementValues) Less(i, j int) bool {
return spev[i].PathElement.Less(spev[j].PathElement)
}
func (spev sortedPathElementValues) Swap(i, j int) { spev[i], spev[j] = spev[j], spev[i] }
// Insert adds the pathelement and associated value in the map.
func (s *PathElementValueMap) Insert(pe PathElement, v value.Value) {
loc := sort.Search(len(s.members), func(i int) bool {
return !s.members[i].PathElement.Less(pe)
})
if loc == len(s.members) {
s.members = append(s.members, pathElementValue{pe, v})
return
}
if s.members[loc].PathElement.Equals(pe) {
return
}
s.members = append(s.members, pathElementValue{})
copy(s.members[loc+1:], s.members[loc:])
s.members[loc] = pathElementValue{pe, v}
}
// Get retrieves the value associated with the given PathElement from the map.
// (nil, false) is returned if there is no such PathElement.
func (s *PathElementValueMap) Get(pe PathElement) (value.Value, bool) {
loc := sort.Search(len(s.members), func(i int) bool {
return !s.members[i].PathElement.Less(pe)
})
if loc == len(s.members) {
return value.Value{}, false
}
if s.members[loc].PathElement.Equals(pe) {
return s.members[loc].Value, true
}
return value.Value{}, false
}

View File

@ -83,14 +83,18 @@ func DeserializePathElement(s string) (PathElement, error) {
case peKeySepBytes[0]: case peKeySepBytes[0]:
iter := readPool.BorrowIterator(b) iter := readPool.BorrowIterator(b)
defer readPool.ReturnIterator(iter) defer readPool.ReturnIterator(iter)
fields := value.FieldList{}
iter.ReadObjectCB(func(iter *jsoniter.Iterator, key string) bool {
v, err := value.ReadJSONIter(iter) v, err := value.ReadJSONIter(iter)
if err != nil { if err != nil {
return PathElement{}, err iter.Error = err
return false
} }
if v.MapValue == nil { fields = append(fields, value.Field{Name: key, Value: v})
return PathElement{}, fmt.Errorf("expected key value pairs but got %#v", v) return true
} })
return PathElement{Key: v.MapValue}, nil fields.Sort()
return PathElement{Key: &fields}, iter.Error
case peIndexSepBytes[0]: case peIndexSepBytes[0]:
i, err := strconv.Atoi(s[2:]) i, err := strconv.Atoi(s[2:])
if err != nil { if err != nil {
@ -129,8 +133,15 @@ func serializePathElementToWriter(w io.Writer, pe PathElement) error {
if _, err := stream.Write(peKeySepBytes); err != nil { if _, err := stream.Write(peKeySepBytes); err != nil {
return err return err
} }
v := value.Value{MapValue: pe.Key} stream.WriteObjectStart()
v.WriteJSONStream(stream) for i, field := range *pe.Key {
if i > 0 {
stream.WriteMore()
}
stream.WriteObjectField(field.Name)
field.Value.WriteJSONStream(stream)
}
stream.WriteObjectEnd()
case pe.Value != nil: case pe.Value != nil:
if _, err := stream.Write(peValueSepBytes); err != nil { if _, err := stream.Write(peValueSepBytes); err != nil {
return err return err

View File

@ -41,12 +41,12 @@ func (s *Updater) EnableUnionFeature() {
s.enableUnions = true s.enableUnions = true
} }
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, *typed.Comparison, error) {
conflicts := fieldpath.ManagedFields{} conflicts := fieldpath.ManagedFields{}
removed := fieldpath.ManagedFields{} removed := fieldpath.ManagedFields{}
compare, err := oldObject.Compare(newObject) compare, err := oldObject.Compare(newObject)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to compare objects: %v", err) return nil, nil, fmt.Errorf("failed to compare objects: %v", err)
} }
versions := map[fieldpath.APIVersion]*typed.Comparison{ versions := map[fieldpath.APIVersion]*typed.Comparison{
@ -66,7 +66,7 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa
delete(managers, manager) delete(managers, manager)
continue continue
} }
return nil, fmt.Errorf("failed to convert old object: %v", err) return nil, nil, fmt.Errorf("failed to convert old object: %v", err)
} }
versionedNewObject, err := s.Converter.Convert(newObject, managerSet.APIVersion()) versionedNewObject, err := s.Converter.Convert(newObject, managerSet.APIVersion())
if err != nil { if err != nil {
@ -74,11 +74,11 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa
delete(managers, manager) delete(managers, manager)
continue continue
} }
return nil, fmt.Errorf("failed to convert new object: %v", err) return nil, nil, fmt.Errorf("failed to convert new object: %v", err)
} }
compare, err = versionedOldObject.Compare(versionedNewObject) compare, err = versionedOldObject.Compare(versionedNewObject)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to compare objects: %v", err) return nil, nil, fmt.Errorf("failed to compare objects: %v", err)
} }
versions[managerSet.APIVersion()] = compare versions[managerSet.APIVersion()] = compare
} }
@ -94,7 +94,7 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa
} }
if !force && len(conflicts) != 0 { if !force && len(conflicts) != 0 {
return nil, ConflictsFromManagers(conflicts) return nil, nil, ConflictsFromManagers(conflicts)
} }
for manager, conflictSet := range conflicts { for manager, conflictSet := range conflicts {
@ -111,7 +111,7 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa
} }
} }
return managers, nil return managers, compare, nil
} }
// Update is the method you should call once you've merged your final // Update is the method you should call once you've merged your final
@ -128,14 +128,10 @@ func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldp
} }
} }
managers = shallowCopyManagers(managers) managers = shallowCopyManagers(managers)
managers, err = s.update(liveObject, newObject, version, managers, manager, true) managers, compare, err := s.update(liveObject, newObject, version, managers, manager, true)
if err != nil { if err != nil {
return nil, fieldpath.ManagedFields{}, err return nil, fieldpath.ManagedFields{}, err
} }
compare, err := liveObject.Compare(newObject)
if err != nil {
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to compare live and new objects: %v", err)
}
if _, ok := managers[manager]; !ok { if _, ok := managers[manager]; !ok {
managers[manager] = fieldpath.NewVersionedSet(fieldpath.NewSet(), version, false) managers[manager] = fieldpath.NewVersionedSet(fieldpath.NewSet(), version, false)
} }
@ -182,7 +178,7 @@ func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fiel
if err != nil { if err != nil {
return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to prune fields: %v", err) return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to prune fields: %v", err)
} }
managers, err = s.update(liveObject, newObject, version, managers, manager, force) managers, _, err = s.update(liveObject, newObject, version, managers, manager, force)
if err != nil { if err != nil {
return nil, fieldpath.ManagedFields{}, err return nil, fieldpath.ManagedFields{}, err
} }

View File

@ -16,9 +16,17 @@ limitations under the License.
package schema package schema
import "sync"
// Schema is a list of named types. // Schema is a list of named types.
//
// Schema types are indexed in a map before the first search so this type
// should be considered immutable.
type Schema struct { type Schema struct {
Types []TypeDef `yaml:"types,omitempty"` Types []TypeDef `yaml:"types,omitempty"`
once sync.Once
m map[string]TypeDef
} }
// A TypeSpecifier references a particular type in a schema. // A TypeSpecifier references a particular type in a schema.
@ -92,6 +100,9 @@ const (
// //
// Maps may also represent a type which is composed of a number of different fields. // Maps may also represent a type which is composed of a number of different fields.
// Each field has a name and a type. // Each field has a name and a type.
//
// Fields are indexed in a map before the first search so this type
// should be considered immutable.
type Map struct { type Map struct {
// Each struct field appears exactly once in this list. The order in // Each struct field appears exactly once in this list. The order in
// this list defines the canonical field ordering. // this list defines the canonical field ordering.
@ -117,6 +128,22 @@ type Map struct {
// The default behavior for maps 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. // leave this unset to get the default behavior.
ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"` ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
once sync.Once
m map[string]StructField
}
// FindField is a convenience function that returns the referenced StructField,
// if it exists, or (nil, false) if it doesn't.
func (m *Map) FindField(name string) (StructField, bool) {
m.once.Do(func() {
m.m = make(map[string]StructField, len(m.Fields))
for _, field := range m.Fields {
m.m[field.Name] = field
}
})
sf, ok := m.m[name]
return sf, ok
} }
// UnionFields are mapping between the fields that are part of the union and // UnionFields are mapping between the fields that are part of the union and
@ -204,13 +231,15 @@ type List struct {
// FindNamedType is a convenience function that returns the referenced TypeDef, // FindNamedType is a convenience function that returns the referenced TypeDef,
// if it exists, or (nil, false) if it doesn't. // if it exists, or (nil, false) if it doesn't.
func (s Schema) FindNamedType(name string) (TypeDef, bool) { func (s *Schema) FindNamedType(name string) (TypeDef, bool) {
s.once.Do(func() {
s.m = make(map[string]TypeDef, len(s.Types))
for _, t := range s.Types { for _, t := range s.Types {
if t.Name == name { s.m[t.Name] = t
return t, true
} }
} })
return TypeDef{}, false t, ok := s.m[name]
return t, ok
} }
// Resolve is a convenience function which returns the atom referenced, whether // Resolve is a convenience function which returns the atom referenced, whether

View File

@ -97,9 +97,9 @@ func (ef errorFormatter) prefixError(prefix string, err error) ValidationErrors
} }
type atomHandler interface { type atomHandler interface {
doScalar(schema.Scalar) ValidationErrors doScalar(*schema.Scalar) ValidationErrors
doList(schema.List) ValidationErrors doList(*schema.List) ValidationErrors
doMap(schema.Map) ValidationErrors doMap(*schema.Map) ValidationErrors
errorf(msg string, args ...interface{}) ValidationErrors errorf(msg string, args ...interface{}) ValidationErrors
} }
@ -130,11 +130,11 @@ func deduceAtom(a schema.Atom, v *value.Value) schema.Atom {
func handleAtom(a schema.Atom, tr schema.TypeRef, ah atomHandler) ValidationErrors { func handleAtom(a schema.Atom, tr schema.TypeRef, ah atomHandler) ValidationErrors {
switch { switch {
case a.Map != nil: case a.Map != nil:
return ah.doMap(*a.Map) return ah.doMap(a.Map)
case a.Scalar != nil: case a.Scalar != nil:
return ah.doScalar(*a.Scalar) return ah.doScalar(a.Scalar)
case a.List != nil: case a.List != nil:
return ah.doList(*a.List) return ah.doList(a.List)
} }
name := "inlined" name := "inlined"
@ -145,14 +145,14 @@ func handleAtom(a schema.Atom, tr schema.TypeRef, ah atomHandler) ValidationErro
return ah.errorf("schema error: invalid atom: %v", name) return ah.errorf("schema error: invalid atom: %v", name)
} }
func (ef errorFormatter) validateScalar(t schema.Scalar, v *value.Value, prefix string) (errs ValidationErrors) { func (ef errorFormatter) validateScalar(t *schema.Scalar, v *value.Value, prefix string) (errs ValidationErrors) {
if v == nil { if v == nil {
return nil return nil
} }
if v.Null { if v.Null {
return nil return nil
} }
switch t { switch *t {
case schema.Numeric: case schema.Numeric:
if v.FloatValue == nil && v.IntValue == nil { if v.FloatValue == nil && v.IntValue == nil {
// TODO: should the schema separate int and float? // TODO: should the schema separate int and float?
@ -195,7 +195,7 @@ func mapValue(val value.Value) (*value.Map, error) {
} }
} }
func keyedAssociativeListItemToPathElement(list schema.List, index int, child value.Value) (fieldpath.PathElement, error) { func keyedAssociativeListItemToPathElement(list *schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
pe := fieldpath.PathElement{} pe := fieldpath.PathElement{}
if child.Null { if child.Null {
// For now, the keys are required which means that null entries // For now, the keys are required which means that null entries
@ -205,7 +205,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{} keyMap := value.FieldList{}
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)
@ -215,13 +215,14 @@ 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)
} }
keyMap.Set(fieldName, fieldValue) keyMap = append(keyMap, value.Field{Name: fieldName, Value: fieldValue})
} }
pe.Key = keyMap keyMap.Sort()
pe.Key = &keyMap
return pe, nil return pe, nil
} }
func setItemToPathElement(list schema.List, index int, child value.Value) (fieldpath.PathElement, error) { func setItemToPathElement(list *schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
pe := fieldpath.PathElement{} pe := fieldpath.PathElement{}
switch { switch {
case child.MapValue != nil: case child.MapValue != nil:
@ -241,7 +242,7 @@ func setItemToPathElement(list schema.List, index int, child value.Value) (field
} }
} }
func listItemToPathElement(list schema.List, index int, child value.Value) (fieldpath.PathElement, error) { func listItemToPathElement(list *schema.List, index int, child value.Value) (fieldpath.PathElement, error) {
if list.ElementRelationship == schema.Associative { if list.ElementRelationship == schema.Associative {
if len(list.Keys) > 0 { if len(list.Keys) > 0 {
return keyedAssociativeListItemToPathElement(list, index, child) return keyedAssociativeListItemToPathElement(list, index, child)

View File

@ -104,7 +104,7 @@ func (w *mergingWalker) doLeaf() {
w.rule(w) w.rule(w)
} }
func (w *mergingWalker) doScalar(t schema.Scalar) (errs ValidationErrors) { func (w *mergingWalker) doScalar(t *schema.Scalar) (errs ValidationErrors) {
errs = append(errs, w.validateScalar(t, w.lhs, "lhs: ")...) errs = append(errs, w.validateScalar(t, w.lhs, "lhs: ")...)
errs = append(errs, w.validateScalar(t, w.rhs, "rhs: ")...) errs = append(errs, w.validateScalar(t, w.rhs, "rhs: ")...)
if len(errs) > 0 { if len(errs) > 0 {
@ -158,7 +158,7 @@ func (w *mergingWalker) derefMap(prefix string, v *value.Value, dest **value.Map
return nil return nil
} }
func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (errs ValidationErrors) { func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs *value.List) (errs ValidationErrors) {
out := &value.List{} out := &value.List{}
// TODO: ordering is totally wrong. // TODO: ordering is totally wrong.
@ -168,8 +168,9 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
rhsOrder := []fieldpath.PathElement{} rhsOrder := []fieldpath.PathElement{}
// First, collect all RHS children. // First, collect all RHS children.
observedRHS := map[string]value.Value{} var observedRHS fieldpath.PathElementValueMap
if rhs != nil { if rhs != nil {
observedRHS = fieldpath.MakePathElementValueMap(len(rhs.Items))
for i, child := range rhs.Items { for i, child := range rhs.Items {
pe, err := listItemToPathElement(t, i, child) pe, err := listItemToPathElement(t, i, child)
if err != nil { if err != nil {
@ -179,18 +180,18 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
// this element. // this element.
continue continue
} }
keyStr := pe.String() if _, ok := observedRHS.Get(pe); ok {
if _, found := observedRHS[keyStr]; found { errs = append(errs, w.errorf("rhs: duplicate entries for key %v", pe.String())...)
errs = append(errs, w.errorf("rhs: duplicate entries for key %v", keyStr)...)
} }
observedRHS[keyStr] = child observedRHS.Insert(pe, child)
rhsOrder = append(rhsOrder, pe) rhsOrder = append(rhsOrder, pe)
} }
} }
// Then merge with LHS children. // Then merge with LHS children.
observedLHS := map[string]struct{}{} var observedLHS fieldpath.PathElementSet
if lhs != nil { if lhs != nil {
observedLHS = fieldpath.MakePathElementSet(len(lhs.Items))
for i, child := range lhs.Items { for i, child := range lhs.Items {
pe, err := listItemToPathElement(t, i, child) pe, err := listItemToPathElement(t, i, child)
if err != nil { if err != nil {
@ -200,15 +201,14 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
// this element. // this element.
continue continue
} }
keyStr := pe.String() if observedLHS.Has(pe) {
if _, found := observedLHS[keyStr]; found { errs = append(errs, w.errorf("lhs: duplicate entries for key %v", pe.String())...)
errs = append(errs, w.errorf("lhs: duplicate entries for key %v", keyStr)...)
continue continue
} }
observedLHS[keyStr] = struct{}{} observedLHS.Insert(pe)
w2 := w.prepareDescent(pe, t.ElementType) w2 := w.prepareDescent(pe, t.ElementType)
w2.lhs = &child w2.lhs = &child
if rchild, ok := observedRHS[keyStr]; ok { if rchild, ok := observedRHS.Get(pe); ok {
w2.rhs = &rchild w2.rhs = &rchild
} }
if newErrs := w2.merge(); len(newErrs) > 0 { if newErrs := w2.merge(); len(newErrs) > 0 {
@ -217,15 +217,16 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
out.Items = append(out.Items, *w2.out) out.Items = append(out.Items, *w2.out)
} }
w.finishDescent(w2) w.finishDescent(w2)
// Keep track of children that have been handled
delete(observedRHS, keyStr)
} }
} }
for _, rhsToCheck := range rhsOrder { for _, pe := range rhsOrder {
if unmergedChild, ok := observedRHS[rhsToCheck.String()]; ok { if observedLHS.Has(pe) {
w2 := w.prepareDescent(rhsToCheck, t.ElementType) continue
w2.rhs = &unmergedChild }
value, _ := observedRHS.Get(pe)
w2 := w.prepareDescent(pe, t.ElementType)
w2.rhs = &value
if newErrs := w2.merge(); len(newErrs) > 0 { if newErrs := w2.merge(); len(newErrs) > 0 {
errs = append(errs, newErrs...) errs = append(errs, newErrs...)
} else if w2.out != nil { } else if w2.out != nil {
@ -233,7 +234,6 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
} }
w.finishDescent(w2) w.finishDescent(w2)
} }
}
if len(out.Items) > 0 { if len(out.Items) > 0 {
w.out = &value.Value{ListValue: out} w.out = &value.Value{ListValue: out}
@ -255,7 +255,7 @@ func (w *mergingWalker) derefList(prefix string, v *value.Value, dest **value.Li
return nil return nil
} }
func (w *mergingWalker) doList(t schema.List) (errs ValidationErrors) { func (w *mergingWalker) doList(t *schema.List) (errs ValidationErrors) {
var lhs, rhs *value.List var lhs, rhs *value.List
w.derefList("lhs: ", w.lhs, &lhs) w.derefList("lhs: ", w.lhs, &lhs)
w.derefList("rhs: ", w.rhs, &rhs) w.derefList("rhs: ", w.rhs, &rhs)
@ -280,23 +280,15 @@ func (w *mergingWalker) doList(t schema.List) (errs ValidationErrors) {
return errs return errs
} }
func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs ValidationErrors) { func (w *mergingWalker) visitMapItems(t *schema.Map, lhs, rhs *value.Map) (errs ValidationErrors) {
out := &value.Map{} 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 { if lhs != nil {
for i := range lhs.Items { for i := range lhs.Items {
litem := &lhs.Items[i] litem := &lhs.Items[i]
fieldType := t.ElementType fieldType := t.ElementType
if ft, ok := fieldTypes[litem.Name]; ok { if sf, ok := t.FindField(litem.Name); ok {
fieldType = ft fieldType = sf.Type
} }
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &litem.Name}, fieldType) w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &litem.Name}, fieldType)
w2.lhs = &litem.Value w2.lhs = &litem.Value
@ -324,8 +316,8 @@ func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs V
} }
fieldType := t.ElementType fieldType := t.ElementType
if ft, ok := fieldTypes[ritem.Name]; ok { if sf, ok := t.FindField(ritem.Name); ok {
fieldType = ft fieldType = sf.Type
} }
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &ritem.Name}, fieldType) w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &ritem.Name}, fieldType)
w2.rhs = &ritem.Value w2.rhs = &ritem.Value
@ -344,7 +336,7 @@ func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs V
return errs return errs
} }
func (w *mergingWalker) doMap(t schema.Map) (errs ValidationErrors) { func (w *mergingWalker) doMap(t *schema.Map) (errs ValidationErrors) {
var lhs, rhs *value.Map var lhs, rhs *value.Map
w.derefMap("lhs: ", w.lhs, &lhs) w.derefMap("lhs: ", w.lhs, &lhs)
w.derefMap("rhs: ", w.rhs, &rhs) w.derefMap("rhs: ", w.rhs, &rhs)

View File

@ -38,9 +38,9 @@ func removeItemsWithSchema(value *value.Value, toRemove *fieldpath.Set, schema *
// will be a descent. It modifies w.inLeaf. // will be a descent. It modifies w.inLeaf.
func (w *removingWalker) doLeaf() ValidationErrors { return nil } func (w *removingWalker) doLeaf() ValidationErrors { return nil }
func (w *removingWalker) doScalar(t schema.Scalar) ValidationErrors { return nil } func (w *removingWalker) doScalar(t *schema.Scalar) ValidationErrors { return nil }
func (w *removingWalker) doList(t schema.List) (errs ValidationErrors) { func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) {
l := w.value.ListValue l := w.value.ListValue
// If list is null, empty, or atomic just return // If list is null, empty, or atomic just return
@ -70,7 +70,7 @@ func (w *removingWalker) doList(t schema.List) (errs ValidationErrors) {
return nil return nil
} }
func (w *removingWalker) doMap(t schema.Map) ValidationErrors { func (w *removingWalker) doMap(t *schema.Map) ValidationErrors {
m := w.value.MapValue m := w.value.MapValue
// If map is null, empty, or atomic just return // If map is null, empty, or atomic just return

View File

@ -18,6 +18,7 @@ package typed
import ( import (
"fmt" "fmt"
"strings"
"sync" "sync"
"sigs.k8s.io/structured-merge-diff/fieldpath" "sigs.k8s.io/structured-merge-diff/fieldpath"
@ -116,7 +117,7 @@ func (tv TypedValue) Compare(rhs *TypedValue) (c *Comparison, err error) {
Modified: fieldpath.NewSet(), Modified: fieldpath.NewSet(),
Added: fieldpath.NewSet(), Added: fieldpath.NewSet(),
} }
c.Merged, err = merge(&tv, rhs, func(w *mergingWalker) { _, err = merge(&tv, rhs, func(w *mergingWalker) {
if w.lhs == nil { if w.lhs == nil {
c.Added.Insert(w.path) c.Added.Insert(w.path)
} else if w.rhs == nil { } else if w.rhs == nil {
@ -126,8 +127,6 @@ func (tv TypedValue) Compare(rhs *TypedValue) (c *Comparison, err error) {
// Need to implement equality check on the value type. // Need to implement equality check on the value type.
c.Modified.Insert(w.path) c.Modified.Insert(w.path)
} }
ruleKeepRHS(w)
}, func(w *mergingWalker) { }, func(w *mergingWalker) {
if w.lhs == nil { if w.lhs == nil {
c.Added.Insert(w.path) c.Added.Insert(w.path)
@ -268,10 +267,6 @@ func merge(lhs, rhs *TypedValue, rule, postRule mergeRule) (*TypedValue, error)
// No field will appear in more than one of the three fieldsets. If all of the // No field will appear in more than one of the three fieldsets. If all of the
// fieldsets are empty, then the objects must have been equal. // fieldsets are empty, then the objects must have been equal.
type Comparison struct { type Comparison struct {
// Merged is the result of merging the two objects, as explained in the
// comments on TypedValue.Merge().
Merged *TypedValue
// Removed contains any fields removed by rhs (the right-hand-side // Removed contains any fields removed by rhs (the right-hand-side
// object in the comparison). // object in the comparison).
Removed *fieldpath.Set Removed *fieldpath.Set
@ -289,15 +284,15 @@ func (c *Comparison) IsSame() bool {
// String returns a human readable version of the comparison. // String returns a human readable version of the comparison.
func (c *Comparison) String() string { func (c *Comparison) String() string {
str := fmt.Sprintf("- Merged Object:\n%v\n", c.Merged.AsValue()) bld := strings.Builder{}
if !c.Modified.Empty() { if !c.Modified.Empty() {
str += fmt.Sprintf("- Modified Fields:\n%v\n", c.Modified) bld.WriteString(fmt.Sprintf("- Modified Fields:\n%v\n", c.Modified))
} }
if !c.Added.Empty() { if !c.Added.Empty() {
str += fmt.Sprintf("- Added Fields:\n%v\n", c.Added) bld.WriteString(fmt.Sprintf("- Added Fields:\n%v\n", c.Added))
} }
if !c.Removed.Empty() { if !c.Removed.Empty() {
str += fmt.Sprintf("- Removed Fields:\n%v\n", c.Removed) bld.WriteString(fmt.Sprintf("- Removed Fields:\n%v\n", c.Removed))
} }
return str return bld.String()
} }

View File

@ -130,7 +130,7 @@ func (v *validatingObjectWalker) doNode() {
} }
} }
func (v *validatingObjectWalker) doScalar(t schema.Scalar) ValidationErrors { func (v *validatingObjectWalker) doScalar(t *schema.Scalar) ValidationErrors {
if errs := v.validateScalar(t, &v.value, ""); len(errs) > 0 { if errs := v.validateScalar(t, &v.value, ""); len(errs) > 0 {
return errs return errs
} }
@ -141,8 +141,8 @@ func (v *validatingObjectWalker) doScalar(t schema.Scalar) ValidationErrors {
return nil return nil
} }
func (v *validatingObjectWalker) visitListItems(t schema.List, list *value.List) (errs ValidationErrors) { func (v *validatingObjectWalker) visitListItems(t *schema.List, list *value.List) (errs ValidationErrors) {
observedKeys := map[string]struct{}{} observedKeys := fieldpath.MakePathElementSet(len(list.Items))
for i, child := range list.Items { for i, child := range list.Items {
pe, err := listItemToPathElement(t, i, child) pe, err := listItemToPathElement(t, i, child)
if err != nil { if err != nil {
@ -152,11 +152,10 @@ func (v *validatingObjectWalker) visitListItems(t schema.List, list *value.List)
// this element. // this element.
continue continue
} }
keyStr := pe.String() if observedKeys.Has(pe) {
if _, found := observedKeys[keyStr]; found { errs = append(errs, v.errorf("duplicate entries for key %v", pe.String())...)
errs = append(errs, v.errorf("duplicate entries for key %v", keyStr)...)
} }
observedKeys[keyStr] = struct{}{} observedKeys.Insert(pe)
v2 := v.prepareDescent(pe, t.ElementType) v2 := v.prepareDescent(pe, t.ElementType)
v2.value = child v2.value = child
errs = append(errs, v2.validate()...) errs = append(errs, v2.validate()...)
@ -167,7 +166,7 @@ func (v *validatingObjectWalker) visitListItems(t schema.List, list *value.List)
return errs return errs
} }
func (v *validatingObjectWalker) doList(t schema.List) (errs ValidationErrors) { func (v *validatingObjectWalker) doList(t *schema.List) (errs ValidationErrors) {
list, err := listValue(v.value) list, err := listValue(v.value)
if err != nil { if err != nil {
return v.error(err) return v.error(err)
@ -186,21 +185,13 @@ func (v *validatingObjectWalker) doList(t schema.List) (errs ValidationErrors) {
return errs return errs
} }
func (v *validatingObjectWalker) visitMapItems(t schema.Map, m *value.Map) (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 i := range m.Items { for i := range m.Items {
item := &m.Items[i] item := &m.Items[i]
pe := fieldpath.PathElement{FieldName: &item.Name} pe := fieldpath.PathElement{FieldName: &item.Name}
if tr, ok := fieldTypes[item.Name]; ok { if sf, ok := t.FindField(item.Name); ok {
v2 := v.prepareDescent(pe, tr) v2 := v.prepareDescent(pe, sf.Type)
v2.value = item.Value v2.value = item.Value
errs = append(errs, v2.validate()...) errs = append(errs, v2.validate()...)
v.finishDescent(v2) v.finishDescent(v2)
@ -215,7 +206,7 @@ func (v *validatingObjectWalker) visitMapItems(t schema.Map, m *value.Map) (errs
return errs return errs
} }
func (v *validatingObjectWalker) doMap(t schema.Map) (errs ValidationErrors) { func (v *validatingObjectWalker) doMap(t *schema.Map) (errs ValidationErrors) {
m, err := mapValue(v.value) m, err := mapValue(v.value)
if err != nil { if err != nil {
return v.error(err) return v.error(err)

View File

@ -36,86 +36,146 @@ type Value struct {
// Equals returns true iff the two values are equal. // Equals returns true iff the two values are equal.
func (v Value) Equals(rhs Value) bool { func (v Value) Equals(rhs Value) bool {
return !v.Less(rhs) && !rhs.Less(v) if v.FloatValue != nil || rhs.FloatValue != nil {
var lf float64
if v.FloatValue != nil {
lf = float64(*v.FloatValue)
} else if v.IntValue != nil {
lf = float64(*v.IntValue)
} else {
return false
}
var rf float64
if rhs.FloatValue != nil {
rf = float64(*rhs.FloatValue)
} else if rhs.IntValue != nil {
rf = float64(*rhs.IntValue)
} else {
return false
}
return lf == rf
}
if v.IntValue != nil {
if rhs.IntValue != nil {
return *v.IntValue == *rhs.IntValue
}
return false
}
if v.StringValue != nil {
if rhs.StringValue != nil {
return *v.StringValue == *rhs.StringValue
}
return false
}
if v.BooleanValue != nil {
if rhs.BooleanValue != nil {
return *v.BooleanValue == *rhs.BooleanValue
}
return false
}
if v.ListValue != nil {
if rhs.ListValue != nil {
return v.ListValue.Equals(rhs.ListValue)
}
return false
}
if v.MapValue != nil {
if rhs.MapValue != nil {
return v.MapValue.Equals(rhs.MapValue)
}
return false
}
if v.Null {
if rhs.Null {
return true
}
return false
}
// No field is set, on either objects.
return true
} }
// Less provides a total ordering for Value (so that they can be sorted, even // Less provides a total ordering for Value (so that they can be sorted, even
// if they are of different types). // if they are of different types).
func (v Value) Less(rhs Value) bool { func (v Value) Less(rhs Value) bool {
return v.Compare(rhs) == -1
}
// Compare provides a total ordering for Value (so that they can be
// sorted, even if they are of different types). The result will be 0 if
// v==rhs, -1 if v < rhs, and +1 if v > rhs.
func (v Value) Compare(rhs Value) int {
if v.FloatValue != nil { if v.FloatValue != nil {
if rhs.FloatValue == nil { if rhs.FloatValue == nil {
// Extra: compare floats and ints numerically. // Extra: compare floats and ints numerically.
if rhs.IntValue != nil { if rhs.IntValue != nil {
return float64(*v.FloatValue) < float64(*rhs.IntValue) return v.FloatValue.Compare(Float(*rhs.IntValue))
} }
return true return -1
} }
return *v.FloatValue < *rhs.FloatValue return v.FloatValue.Compare(*rhs.FloatValue)
} else if rhs.FloatValue != nil { } else if rhs.FloatValue != nil {
// Extra: compare floats and ints numerically. // Extra: compare floats and ints numerically.
if v.IntValue != nil { if v.IntValue != nil {
return float64(*v.IntValue) < float64(*rhs.FloatValue) return Float(*v.IntValue).Compare(*rhs.FloatValue)
} }
return false return 1
} }
if v.IntValue != nil { if v.IntValue != nil {
if rhs.IntValue == nil { if rhs.IntValue == nil {
return true return -1
} }
return *v.IntValue < *rhs.IntValue return v.IntValue.Compare(*rhs.IntValue)
} else if rhs.IntValue != nil { } else if rhs.IntValue != nil {
return false return 1
} }
if v.StringValue != nil { if v.StringValue != nil {
if rhs.StringValue == nil { if rhs.StringValue == nil {
return true return -1
} }
return *v.StringValue < *rhs.StringValue return strings.Compare(string(*v.StringValue), string(*rhs.StringValue))
} else if rhs.StringValue != nil { } else if rhs.StringValue != nil {
return false return 1
} }
if v.BooleanValue != nil { if v.BooleanValue != nil {
if rhs.BooleanValue == nil { if rhs.BooleanValue == nil {
return true return -1
} }
if *v.BooleanValue == *rhs.BooleanValue { return v.BooleanValue.Compare(*rhs.BooleanValue)
return false
}
return *v.BooleanValue == false
} else if rhs.BooleanValue != nil { } else if rhs.BooleanValue != nil {
return false return 1
} }
if v.ListValue != nil { if v.ListValue != nil {
if rhs.ListValue == nil { if rhs.ListValue == nil {
return true return -1
} }
return v.ListValue.Less(rhs.ListValue) return v.ListValue.Compare(rhs.ListValue)
} else if rhs.ListValue != nil { } else if rhs.ListValue != nil {
return false return 1
} }
if v.MapValue != nil { if v.MapValue != nil {
if rhs.MapValue == nil { if rhs.MapValue == nil {
return true return -1
} }
return v.MapValue.Less(rhs.MapValue) return v.MapValue.Compare(rhs.MapValue)
} else if rhs.MapValue != nil { } else if rhs.MapValue != nil {
return false return 1
} }
if v.Null { if v.Null {
if !rhs.Null { if !rhs.Null {
return true return -1
} }
return false return 0
} else if rhs.Null { } else if rhs.Null {
return false return 1
} }
// Invalid Value-- nothing is set. // Invalid Value-- nothing is set.
return false return 0
} }
type Int int64 type Int int64
@ -123,40 +183,141 @@ type Float float64
type String string type String string
type Boolean bool type Boolean bool
// Compare compares integers. The result will be 0 if i==rhs, -1 if i <
// rhs, and +1 if i > rhs.
func (i Int) Compare(rhs Int) int {
if i > rhs {
return 1
} else if i < rhs {
return -1
}
return 0
}
// Compare compares floats. The result will be 0 if f==rhs, -1 if f <
// rhs, and +1 if f > rhs.
func (f Float) Compare(rhs Float) int {
if f > rhs {
return 1
} else if f < rhs {
return -1
}
return 0
}
// Compare compares booleans. The result will be 0 if b==rhs, -1 if b <
// rhs, and +1 if b > rhs.
func (b Boolean) Compare(rhs Boolean) int {
if b == rhs {
return 0
} else if b == false {
return -1
}
return 1
}
// Field is an individual key-value pair. // Field is an individual key-value pair.
type Field struct { type Field struct {
Name string Name string
Value Value Value Value
} }
// FieldList is a list of key-value pairs. Each field is expected to
// have a different name.
type FieldList []Field
// Sort sorts the field list by Name.
func (f FieldList) Sort() {
if len(f) < 2 {
return
}
if len(f) == 2 {
if f[1].Name < f[0].Name {
f[0], f[1] = f[1], f[0]
}
return
}
sort.SliceStable(f, func(i, j int) bool {
return f[i].Name < f[j].Name
})
}
// Less compares two lists lexically.
func (f FieldList) Less(rhs FieldList) bool {
return f.Compare(rhs) == -1
}
// Less compares two lists lexically. The result will be 0 if f==rhs, -1
// if f < rhs, and +1 if f > rhs.
func (f FieldList) Compare(rhs FieldList) int {
i := 0
for {
if i >= len(f) && i >= len(rhs) {
// Maps are the same length and all items are equal.
return 0
}
if i >= len(f) {
// F is shorter.
return -1
}
if i >= len(rhs) {
// RHS is shorter.
return 1
}
if c := strings.Compare(f[i].Name, rhs[i].Name); c != 0 {
return c
}
if c := f[i].Value.Compare(rhs[i].Value); c != 0 {
return c
}
// The items are equal; continue.
i++
}
}
// List is a list of items. // List is a list of items.
type List struct { type List struct {
Items []Value Items []Value
} }
// Equals compares two lists lexically.
func (l *List) Equals(rhs *List) bool {
if len(l.Items) != len(rhs.Items) {
return false
}
for i, lv := range l.Items {
if !lv.Equals(rhs.Items[i]) {
return false
}
}
return true
}
// Less compares two lists lexically. // Less compares two lists lexically.
func (l *List) Less(rhs *List) bool { func (l *List) Less(rhs *List) bool {
return l.Compare(rhs) == -1
}
// Compare compares two lists lexically. The result will be 0 if l==rhs, -1
// if l < rhs, and +1 if l > rhs.
func (l *List) Compare(rhs *List) int {
i := 0 i := 0
for { for {
if i >= len(l.Items) && i >= len(rhs.Items) { if i >= len(l.Items) && i >= len(rhs.Items) {
// Lists are the same length and all items are equal. // Lists are the same length and all items are equal.
return false return 0
} }
if i >= len(l.Items) { if i >= len(l.Items) {
// LHS is shorter. // LHS is shorter.
return true return -1
} }
if i >= len(rhs.Items) { if i >= len(rhs.Items) {
// RHS is shorter. // RHS is shorter.
return false return 1
} }
if l.Items[i].Less(rhs.Items[i]) { if c := l.Items[i].Compare(rhs.Items[i]); c != 0 {
// LHS is less; return return c
return true
}
if rhs.Items[i].Less(l.Items[i]) {
// RHS is less; return
return false
} }
// The items are equal; continue. // The items are equal; continue.
i++ i++
@ -191,8 +352,30 @@ func (m *Map) computeOrder() []int {
return m.order return m.order
} }
// Equals compares two maps lexically.
func (m *Map) Equals(rhs *Map) bool {
if len(m.Items) != len(rhs.Items) {
return false
}
for _, lfield := range m.Items {
rfield, ok := rhs.Get(lfield.Name)
if !ok {
return false
}
if !lfield.Value.Equals(rfield.Value) {
return false
}
}
return true
}
// Less compares two maps lexically. // Less compares two maps lexically.
func (m *Map) Less(rhs *Map) bool { func (m *Map) Less(rhs *Map) bool {
return m.Compare(rhs) == -1
}
// Compare compares two maps lexically.
func (m *Map) Compare(rhs *Map) int {
var noAllocL, noAllocR [2]int var noAllocL, noAllocR [2]int
var morder, rorder []int var morder, rorder []int
@ -238,28 +421,22 @@ func (m *Map) Less(rhs *Map) bool {
for { for {
if i >= len(morder) && i >= len(rorder) { if i >= len(morder) && i >= len(rorder) {
// Maps are the same length and all items are equal. // Maps are the same length and all items are equal.
return false return 0
} }
if i >= len(morder) { if i >= len(morder) {
// LHS is shorter. // LHS is shorter.
return true return -1
} }
if i >= len(rorder) { if i >= len(rorder) {
// RHS is shorter. // RHS is shorter.
return false return 1
} }
fa, fb := &m.Items[morder[i]], &rhs.Items[rorder[i]] fa, fb := &m.Items[morder[i]], &rhs.Items[rorder[i]]
if fa.Name != fb.Name { if c := strings.Compare(fa.Name, fb.Name); c != 0 {
// the map having the field name that sorts lexically less is "less" return c
return fa.Name < fb.Name
} }
if fa.Value.Less(fb.Value) { if c := fa.Value.Compare(fb.Value); c != 0 {
// LHS is less; return return c
return true
}
if fb.Value.Less(fa.Value) {
// RHS is less; return
return false
} }
// The items are equal; continue. // The items are equal; continue.
i++ i++