diff --git a/Dockerfile b/Dockerfile index d2574d91..6e21c846 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.6 +FROM alpine:3.9 RUN apk add -U ca-certificates \ && update-ca-certificates \ diff --git a/Gopkg.lock b/Gopkg.lock index ffe92615..6d19baa4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,253 +2,578 @@ [[projects]] + digest = "1:8e47871087b94913898333f37af26732faaab30cdb41571136cf7aec9921dae7" name = "github.com/PuerkitoBio/purell" packages = ["."] + pruneopts = "" revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:331a419049c2be691e5ba1d24342fc77c7e767a80c666a18fd8a9f7b82419c1c" name = "github.com/PuerkitoBio/urlesc" packages = ["."] + pruneopts = "" revision = "de5bf2ad457846296e2031421a34e2568e304e35" [[projects]] + digest = "1:8985af323317240990e7f0922ae895d1b563ef230b4c0bfaab7c1d709bfdb5fc" name = "github.com/aws/aws-sdk-go" - packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/ec2query","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/xml/xmlutil","service/ec2","service/ec2/ec2iface","service/sts"] + packages = [ + "aws", + "aws/awserr", + "aws/awsutil", + "aws/client", + "aws/client/metadata", + "aws/corehandlers", + "aws/credentials", + "aws/credentials/ec2rolecreds", + "aws/credentials/endpointcreds", + "aws/credentials/stscreds", + "aws/defaults", + "aws/ec2metadata", + "aws/endpoints", + "aws/request", + "aws/session", + "aws/signer/v4", + "internal/shareddefaults", + "private/protocol", + "private/protocol/ec2query", + "private/protocol/query", + "private/protocol/query/queryutil", + "private/protocol/rest", + "private/protocol/xml/xmlutil", + "service/ec2", + "service/ec2/ec2iface", + "service/sts", + ] + pruneopts = "" revision = "fd9b7491525896e01db35c1e20a5bd94bf11491c" version = "v1.12.48" [[projects]] + digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] + digest = "1:f5d7ff39c266d97b0e854e96ac2845f2b55ccc42f92a6ea5ec8c39adb79ef64f" name = "github.com/emicklei/go-restful" - packages = [".","log"] + packages = [ + ".", + "log", + ] + pruneopts = "" revision = "5741799b275a3c4a5a9623a993576d7545cf7b5c" version = "v2.4.0" [[projects]] + digest = "1:cad2dd7061b8dcb4e0014d89e8070f185fb70ac9ba26acf27ff42b9c3eb0ff9b" name = "github.com/emicklei/go-restful-swagger12" packages = ["."] + pruneopts = "" revision = "dcef7f55730566d41eae5db10e7d6981829720f6" version = "1.0.1" [[projects]] + digest = "1:b13707423743d41665fd23f0c36b2f37bb49c30e94adb813319c44188a51ba22" name = "github.com/ghodss/yaml" packages = ["."] + pruneopts = "" revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" version = "v1.0.0" [[projects]] + digest = "1:a00483fe4106b86fb1187a92b5cf6915c85f294ed4c129ccbe7cb1f1a06abd46" name = "github.com/go-ini/ini" packages = ["."] + pruneopts = "" revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a" version = "v1.32.0" [[projects]] branch = "master" + digest = "1:1287439f7765209116509fffff2b8f853845e4b35572b41a1aadda42cbcffcc2" name = "github.com/go-openapi/jsonpointer" packages = ["."] + pruneopts = "" revision = "779f45308c19820f1a69e9a4cd965f496e0da10f" [[projects]] branch = "master" + digest = "1:07ac8ac445f68b0bc063d11845d479fb7e09c906ead7a8c4165b59777df09d74" name = "github.com/go-openapi/jsonreference" packages = ["."] + pruneopts = "" revision = "36d33bfe519efae5632669801b180bf1a245da3b" [[projects]] branch = "master" + digest = "1:a1accd4ba5834691a303679d73e0cc0d7890e9983688e0530622ea38b2dd6aed" name = "github.com/go-openapi/spec" packages = ["."] + pruneopts = "" revision = "01738944bdee0f26bf66420c5b17d54cfdf55341" [[projects]] branch = "master" + digest = "1:a63976aa16b4247876a24d24514ffdce23021f08a605c7a9309b605b38094b1c" name = "github.com/go-openapi/swag" packages = ["."] + pruneopts = "" revision = "cf0bdb963811675a4d7e74901cefc7411a1df939" [[projects]] + digest = "1:70a80170917a15e1ff02faab5f9e716e945e0676e86599ba144d38f96e30c3bf" name = "github.com/gogo/protobuf" - packages = ["proto","sortkeys"] + packages = [ + "proto", + "sortkeys", + ] + pruneopts = "" revision = "342cbe0a04158f6dcb03ca0079991a51a4248c02" version = "v0.5" [[projects]] branch = "master" + digest = "1:107b233e45174dbab5b1324201d092ea9448e58243ab9f039e4c0f332e121e3a" name = "github.com/golang/glog" packages = ["."] + pruneopts = "" revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" [[projects]] branch = "master" + digest = "1:3b760d3b93f994df8eb1d9ebfad17d3e9e37edcb7f7efaa15b427c0d7a64f4e4" name = "github.com/golang/protobuf" - packages = ["proto","ptypes","ptypes/any","ptypes/duration","ptypes/timestamp"] + packages = [ + "proto", + "ptypes", + "ptypes/any", + "ptypes/duration", + "ptypes/timestamp", + ] + pruneopts = "" revision = "1e59b77b52bf8e4b449a57e6f79f21226d571845" [[projects]] branch = "master" + digest = "1:fb7c7c9c84fb96b4f0fb0acaf33098a0ae5b1361b6d2da59a38e5b9f92b862fb" name = "github.com/google/btree" packages = ["."] + pruneopts = "" revision = "316fb6d3f031ae8f4d457c6c5186b9e3ded70435" [[projects]] branch = "master" + digest = "1:754f77e9c839b24778a4b64422236d38515301d2baeb63113aa3edc42e6af692" name = "github.com/google/gofuzz" packages = ["."] + pruneopts = "" revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" [[projects]] + digest = "1:2a131706ff80636629ab6373f2944569b8252ecc018cda8040931b05d32e3c16" name = "github.com/googleapis/gnostic" - packages = ["OpenAPIv2","compiler","extensions"] + packages = [ + "OpenAPIv2", + "compiler", + "extensions", + ] + pruneopts = "" revision = "ee43cbb60db7bd22502942cccbc39059117352ab" version = "v0.1.0" [[projects]] branch = "master" + digest = "1:32691a653da0dd17135f37c441abcf965f34898fc99e052fd152587bf7786bd7" name = "github.com/gregjones/httpcache" - packages = [".","diskcache"] + packages = [ + ".", + "diskcache", + ] + pruneopts = "" revision = "2bcd89a1743fd4b373f7370ce8ddc14dfbd18229" [[projects]] branch = "master" + digest = "1:43987212a2f16bfacc1a286e9118f212d60c136ed53c6c9477c18921db53140b" name = "github.com/hashicorp/golang-lru" - packages = [".","simplelru"] + packages = [ + ".", + "simplelru", + ] + pruneopts = "" revision = "0a025b7e63adc15a622f29b0b2c4c3848243bbf6" [[projects]] branch = "master" + digest = "1:f81c8d7354cc0c6340f2f7a48724ee6c2b3db3e918ecd441c985b4d2d97dd3e7" name = "github.com/howeyc/gopass" packages = ["."] + pruneopts = "" revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8" [[projects]] + digest = "1:012684836b98fe30c53f8536c01325c52622f420fa27c1fb3ca8a1471c469606" name = "github.com/imdario/mergo" packages = ["."] + pruneopts = "" revision = "7fe0c75c13abdee74b09fcacef5ea1c6bba6a874" version = "0.2.4" [[projects]] + digest = "1:6f49eae0c1e5dab1dafafee34b207aeb7a42303105960944828c2079b92fc88e" name = "github.com/jmespath/go-jmespath" packages = ["."] + pruneopts = "" revision = "0b12d6b5" [[projects]] + digest = "1:416fb7f9304f3cae8e9951aa7dc7b9d5b3e341cc1dc987efd6ca1ed867e0d4f9" name = "github.com/json-iterator/go" packages = ["."] + pruneopts = "" revision = "f7279a603edee96fe7764d3de9c6ff8cf9970994" version = "1.0.4" [[projects]] branch = "master" + digest = "1:d26c006cbf54ca5c040463d5678013169e2bd91fb62c2f960ae283a1723d4278" name = "github.com/juju/ratelimit" packages = ["."] + pruneopts = "" revision = "59fac5042749a5afb9af70e813da1dd5474f0167" [[projects]] branch = "master" + digest = "1:ccc20cacf54eb16464dad02efa1c14fa7c0b9e124639b0d2a51dcc87b0154e4c" name = "github.com/mailru/easyjson" - packages = ["buffer","jlexer","jwriter"] + packages = [ + "buffer", + "jlexer", + "jwriter", + ] + pruneopts = "" revision = "32fa128f234d041f196a9f3e0fea5ac9772c08e1" [[projects]] branch = "master" + digest = "1:58050e2bc9621cc6b68c1da3e4a0d1c40ad1f89062b9855c26521fd42a97a106" + name = "github.com/mattbaird/jsonpatch" + packages = ["."] + pruneopts = "" + revision = "81af80346b1a01caae0cbc27fd3c1ba5b11e189f" + +[[projects]] + branch = "master" + digest = "1:c24598ffeadd2762552269271b3b1510df2d83ee6696c1e543a0ff653af494bc" name = "github.com/petar/GoLLRB" packages = ["llrb"] + pruneopts = "" revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" [[projects]] + digest = "1:b46305723171710475f2dd37547edd57b67b9de9f2a6267cafdd98331fd6897f" name = "github.com/peterbourgon/diskv" packages = ["."] + pruneopts = "" revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" version = "v2.0.1" [[projects]] + digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] + digest = "1:261bc565833ef4f02121450d74eb88d5ae4bd74bfe5d0e862cddb8550ec35000" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "" revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66" version = "v1.0.0" [[projects]] + digest = "1:3926a4ec9a4ff1a072458451aa2d9b98acd059a45b38f7335d31e06c3d6a0159" name = "github.com/stretchr/testify" packages = ["assert"] + pruneopts = "" revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0" version = "v1.1.4" [[projects]] branch = "master" + digest = "1:c3415eeb330bf30a2d8181e516ec79804c198f3d171ab9c9364f29dbe76c05d9" name = "golang.org/x/crypto" packages = ["ssh/terminal"] + pruneopts = "" revision = "94eea52f7b742c7cbe0b03b22f0c4c8631ece122" [[projects]] branch = "master" + digest = "1:a9afbcb2b5dacde3889b77124be6abe68477a09c6da3df224cc74f5e180454e6" name = "golang.org/x/net" - packages = ["context","http2","http2/hpack","idna","lex/httplex"] + packages = [ + "context", + "http2", + "http2/hpack", + "idna", + "lex/httplex", + ] + pruneopts = "" revision = "d866cfc389cec985d6fda2859936a575a55a3ab6" [[projects]] branch = "master" + digest = "1:84bded9d2b66bb5f8ce578ec660d1f1b8aa5cff27088d08eb9bc1395476f7489" name = "golang.org/x/sys" - packages = ["unix","windows"] + packages = [ + "unix", + "windows", + ] + pruneopts = "" revision = "571f7bbbe08da2a8955aed9d4db316e78630e9a3" [[projects]] branch = "master" + digest = "1:9faa0b9d4e79423b3c44d92023e94faefd21aab6ca23dd590f2df8ab06ceab1e" name = "golang.org/x/text" - packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable","width"] + packages = [ + "collate", + "collate/build", + "internal/colltab", + "internal/gen", + "internal/tag", + "internal/triegen", + "internal/ucd", + "language", + "secure/bidirule", + "transform", + "unicode/bidi", + "unicode/cldr", + "unicode/norm", + "unicode/rangetable", + "width", + ] + pruneopts = "" revision = "d5a9226ed7dd70cade6ccae9d37517fe14dd9fee" [[projects]] + digest = "1:e5d1fb981765b6f7513f793a3fcaac7158408cca77f75f7311ac82cc88e9c445" name = "gopkg.in/inf.v0" packages = ["."] + pruneopts = "" revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4" version = "v0.9.0" [[projects]] branch = "v2" + digest = "1:f769ed60e075e4221612c2f4162fccc9d3795ef358fa463425e3b3d7a5debb27" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "" revision = "287cf08546ab5e7e37d55a84f7ed3fd1db036de5" [[projects]] branch = "release-1.8" + digest = "1:0c6dafb9708c45b706ce11fa317b177ae423771dc5596ec4d7476fda490acbe5" name = "k8s.io/api" - packages = ["admissionregistration/v1alpha1","apps/v1beta1","apps/v1beta2","authentication/v1","authentication/v1beta1","authorization/v1","authorization/v1beta1","autoscaling/v1","autoscaling/v2beta1","batch/v1","batch/v1beta1","batch/v2alpha1","certificates/v1beta1","core/v1","extensions/v1beta1","networking/v1","policy/v1beta1","rbac/v1","rbac/v1alpha1","rbac/v1beta1","scheduling/v1alpha1","settings/v1alpha1","storage/v1","storage/v1beta1"] + packages = [ + "admissionregistration/v1alpha1", + "apps/v1beta1", + "apps/v1beta2", + "authentication/v1", + "authentication/v1beta1", + "authorization/v1", + "authorization/v1beta1", + "autoscaling/v1", + "autoscaling/v2beta1", + "batch/v1", + "batch/v1beta1", + "batch/v2alpha1", + "certificates/v1beta1", + "core/v1", + "extensions/v1beta1", + "networking/v1", + "policy/v1beta1", + "rbac/v1", + "rbac/v1alpha1", + "rbac/v1beta1", + "scheduling/v1alpha1", + "settings/v1alpha1", + "storage/v1", + "storage/v1beta1", + ] + pruneopts = "" revision = "9b9dca205a15b6ce9ef10091f05d60a13fdcf418" [[projects]] branch = "release-1.8" + digest = "1:97b53d3e390537e91ccea3ddd34e73f6d91afeff8f40272630fdd6e6c63d43c1" name = "k8s.io/apimachinery" - packages = ["pkg/api/equality","pkg/api/errors","pkg/api/meta","pkg/api/resource","pkg/apis/meta/internalversion","pkg/apis/meta/v1","pkg/apis/meta/v1/unstructured","pkg/apis/meta/v1alpha1","pkg/conversion","pkg/conversion/queryparams","pkg/conversion/unstructured","pkg/fields","pkg/labels","pkg/runtime","pkg/runtime/schema","pkg/runtime/serializer","pkg/runtime/serializer/json","pkg/runtime/serializer/protobuf","pkg/runtime/serializer/recognizer","pkg/runtime/serializer/streaming","pkg/runtime/serializer/versioning","pkg/selection","pkg/types","pkg/util/cache","pkg/util/clock","pkg/util/diff","pkg/util/errors","pkg/util/framer","pkg/util/intstr","pkg/util/json","pkg/util/net","pkg/util/runtime","pkg/util/sets","pkg/util/validation","pkg/util/validation/field","pkg/util/wait","pkg/util/yaml","pkg/version","pkg/watch","third_party/forked/golang/reflect"] + packages = [ + "pkg/api/equality", + "pkg/api/errors", + "pkg/api/meta", + "pkg/api/resource", + "pkg/apis/meta/internalversion", + "pkg/apis/meta/v1", + "pkg/apis/meta/v1/unstructured", + "pkg/apis/meta/v1alpha1", + "pkg/conversion", + "pkg/conversion/queryparams", + "pkg/conversion/unstructured", + "pkg/fields", + "pkg/labels", + "pkg/runtime", + "pkg/runtime/schema", + "pkg/runtime/serializer", + "pkg/runtime/serializer/json", + "pkg/runtime/serializer/protobuf", + "pkg/runtime/serializer/recognizer", + "pkg/runtime/serializer/streaming", + "pkg/runtime/serializer/versioning", + "pkg/selection", + "pkg/types", + "pkg/util/cache", + "pkg/util/clock", + "pkg/util/diff", + "pkg/util/errors", + "pkg/util/framer", + "pkg/util/intstr", + "pkg/util/json", + "pkg/util/net", + "pkg/util/runtime", + "pkg/util/sets", + "pkg/util/validation", + "pkg/util/validation/field", + "pkg/util/wait", + "pkg/util/yaml", + "pkg/version", + "pkg/watch", + "third_party/forked/golang/reflect", + ] + pruneopts = "" revision = "5134afd2c0c91158afac0d8a28bd2177185a3bcc" [[projects]] + digest = "1:0bc370120916738af3e577692dbceac7bb2e46f7a149d18d1b5623523da994d6" name = "k8s.io/client-go" - packages = ["discovery","discovery/fake","kubernetes","kubernetes/fake","kubernetes/scheme","kubernetes/typed/admissionregistration/v1alpha1","kubernetes/typed/admissionregistration/v1alpha1/fake","kubernetes/typed/apps/v1beta1","kubernetes/typed/apps/v1beta1/fake","kubernetes/typed/apps/v1beta2","kubernetes/typed/apps/v1beta2/fake","kubernetes/typed/authentication/v1","kubernetes/typed/authentication/v1/fake","kubernetes/typed/authentication/v1beta1","kubernetes/typed/authentication/v1beta1/fake","kubernetes/typed/authorization/v1","kubernetes/typed/authorization/v1/fake","kubernetes/typed/authorization/v1beta1","kubernetes/typed/authorization/v1beta1/fake","kubernetes/typed/autoscaling/v1","kubernetes/typed/autoscaling/v1/fake","kubernetes/typed/autoscaling/v2beta1","kubernetes/typed/autoscaling/v2beta1/fake","kubernetes/typed/batch/v1","kubernetes/typed/batch/v1/fake","kubernetes/typed/batch/v1beta1","kubernetes/typed/batch/v1beta1/fake","kubernetes/typed/batch/v2alpha1","kubernetes/typed/batch/v2alpha1/fake","kubernetes/typed/certificates/v1beta1","kubernetes/typed/certificates/v1beta1/fake","kubernetes/typed/core/v1","kubernetes/typed/core/v1/fake","kubernetes/typed/extensions/v1beta1","kubernetes/typed/extensions/v1beta1/fake","kubernetes/typed/networking/v1","kubernetes/typed/networking/v1/fake","kubernetes/typed/policy/v1beta1","kubernetes/typed/policy/v1beta1/fake","kubernetes/typed/rbac/v1","kubernetes/typed/rbac/v1/fake","kubernetes/typed/rbac/v1alpha1","kubernetes/typed/rbac/v1alpha1/fake","kubernetes/typed/rbac/v1beta1","kubernetes/typed/rbac/v1beta1/fake","kubernetes/typed/scheduling/v1alpha1","kubernetes/typed/scheduling/v1alpha1/fake","kubernetes/typed/settings/v1alpha1","kubernetes/typed/settings/v1alpha1/fake","kubernetes/typed/storage/v1","kubernetes/typed/storage/v1/fake","kubernetes/typed/storage/v1beta1","kubernetes/typed/storage/v1beta1/fake","pkg/version","rest","rest/watch","testing","tools/auth","tools/cache","tools/clientcmd","tools/clientcmd/api","tools/clientcmd/api/latest","tools/clientcmd/api/v1","tools/metrics","tools/pager","tools/reference","transport","util/cert","util/flowcontrol","util/homedir","util/integer"] + packages = [ + "discovery", + "discovery/fake", + "kubernetes", + "kubernetes/fake", + "kubernetes/scheme", + "kubernetes/typed/admissionregistration/v1alpha1", + "kubernetes/typed/admissionregistration/v1alpha1/fake", + "kubernetes/typed/apps/v1beta1", + "kubernetes/typed/apps/v1beta1/fake", + "kubernetes/typed/apps/v1beta2", + "kubernetes/typed/apps/v1beta2/fake", + "kubernetes/typed/authentication/v1", + "kubernetes/typed/authentication/v1/fake", + "kubernetes/typed/authentication/v1beta1", + "kubernetes/typed/authentication/v1beta1/fake", + "kubernetes/typed/authorization/v1", + "kubernetes/typed/authorization/v1/fake", + "kubernetes/typed/authorization/v1beta1", + "kubernetes/typed/authorization/v1beta1/fake", + "kubernetes/typed/autoscaling/v1", + "kubernetes/typed/autoscaling/v1/fake", + "kubernetes/typed/autoscaling/v2beta1", + "kubernetes/typed/autoscaling/v2beta1/fake", + "kubernetes/typed/batch/v1", + "kubernetes/typed/batch/v1/fake", + "kubernetes/typed/batch/v1beta1", + "kubernetes/typed/batch/v1beta1/fake", + "kubernetes/typed/batch/v2alpha1", + "kubernetes/typed/batch/v2alpha1/fake", + "kubernetes/typed/certificates/v1beta1", + "kubernetes/typed/certificates/v1beta1/fake", + "kubernetes/typed/core/v1", + "kubernetes/typed/core/v1/fake", + "kubernetes/typed/extensions/v1beta1", + "kubernetes/typed/extensions/v1beta1/fake", + "kubernetes/typed/networking/v1", + "kubernetes/typed/networking/v1/fake", + "kubernetes/typed/policy/v1beta1", + "kubernetes/typed/policy/v1beta1/fake", + "kubernetes/typed/rbac/v1", + "kubernetes/typed/rbac/v1/fake", + "kubernetes/typed/rbac/v1alpha1", + "kubernetes/typed/rbac/v1alpha1/fake", + "kubernetes/typed/rbac/v1beta1", + "kubernetes/typed/rbac/v1beta1/fake", + "kubernetes/typed/scheduling/v1alpha1", + "kubernetes/typed/scheduling/v1alpha1/fake", + "kubernetes/typed/settings/v1alpha1", + "kubernetes/typed/settings/v1alpha1/fake", + "kubernetes/typed/storage/v1", + "kubernetes/typed/storage/v1/fake", + "kubernetes/typed/storage/v1beta1", + "kubernetes/typed/storage/v1beta1/fake", + "pkg/version", + "rest", + "rest/watch", + "testing", + "tools/auth", + "tools/cache", + "tools/clientcmd", + "tools/clientcmd/api", + "tools/clientcmd/api/latest", + "tools/clientcmd/api/v1", + "tools/metrics", + "tools/pager", + "tools/reference", + "transport", + "util/cert", + "util/flowcontrol", + "util/homedir", + "util/integer", + ] + pruneopts = "" revision = "2ae454230481a7cb5544325e12ad7658ecccd19b" version = "v5.0.1" [[projects]] branch = "master" + digest = "1:bb821ebb564f88528bc1ead633e642e0b2a38027b3d188e3109aed9c2d74f8d4" name = "k8s.io/kube-openapi" packages = ["pkg/common"] + pruneopts = "" revision = "b16ebc07f5cad97831f961e4b5a9cc1caed33b7e" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "a4c5e284c435f00bf02e23e1fb5d5915b447fdb8b37e0446c210b6c5bf5a411f" + input-imports = [ + "github.com/aws/aws-sdk-go/aws", + "github.com/aws/aws-sdk-go/aws/session", + "github.com/aws/aws-sdk-go/service/ec2", + "github.com/aws/aws-sdk-go/service/ec2/ec2iface", + "github.com/golang/glog", + "github.com/mattbaird/jsonpatch", + "github.com/stretchr/testify/assert", + "k8s.io/api/core/v1", + "k8s.io/apimachinery/pkg/apis/meta/v1", + "k8s.io/apimachinery/pkg/fields", + "k8s.io/apimachinery/pkg/runtime", + "k8s.io/apimachinery/pkg/types", + "k8s.io/apimachinery/pkg/util/wait", + "k8s.io/client-go/kubernetes", + "k8s.io/client-go/kubernetes/fake", + "k8s.io/client-go/rest", + "k8s.io/client-go/tools/cache", + "k8s.io/client-go/tools/clientcmd", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 1480ed24..ca385970 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -42,4 +42,8 @@ [[constraint]] name = "k8s.io/client-go" - version = "v5.0.1" \ No newline at end of file + version = "v5.0.1" + +[[constraint]] + name = "github.com/mattbaird/jsonpatch" + branch = "master" diff --git a/cmd/k8s-ec2-srcdst/main.go b/cmd/k8s-ec2-srcdst/main.go index 63d3aba8..103048df 100644 --- a/cmd/k8s-ec2-srcdst/main.go +++ b/cmd/k8s-ec2-srcdst/main.go @@ -18,10 +18,19 @@ import ( func main() { kubeconfig := flag.String("kubeconfig", "", "Path to a kubeconfig file") version := flag.Bool("version", false, "Prints current k8s-ec2-srcdst version") + patchnode := flag.Bool("patchnode", false, "By default k8s-ec2-srcdst updates the node opbject, this flag turns on patching instead. This can be useful for when AWS api rate limiting is an issue.") flag.Set("logtostderr", "true") flag.Parse() + var my_opts = common.K8sEc2SrcdstOpts{ + Patchnode: *patchnode, + } + + if *patchnode { + glog.V(4).Info("Patch mode activated") + } + if *version { fmt.Println(srcdst.Version) os.Exit(0) @@ -45,5 +54,5 @@ func main() { awsConfig := &aws.Config{} ec2Client := ec2.New(awsSession, awsConfig) - controller.NewSrcDstController(client, ec2Client).Controller.Run(wait.NeverStop) + controller.NewSrcDstController(client, ec2Client, &my_opts).Controller.Run(wait.NeverStop) } diff --git a/pkg/common/util.go b/pkg/common/util.go index 5565d481..6374bc69 100644 --- a/pkg/common/util.go +++ b/pkg/common/util.go @@ -7,6 +7,10 @@ import ( "k8s.io/client-go/tools/clientcmd" ) +type K8sEc2SrcdstOpts struct { + Patchnode bool +} + // GetClientConfig gets the credentials necessary to connect to the Kubernetes // cluster either through the specified kubeconfig or to get the necessary info // from the running pod within the cluster diff --git a/pkg/controller/srcdst_controller.go b/pkg/controller/srcdst_controller.go index 941a9e73..ffb54ddd 100644 --- a/pkg/controller/srcdst_controller.go +++ b/pkg/controller/srcdst_controller.go @@ -1,12 +1,14 @@ package controller import ( + "encoding/json" "fmt" "net/url" "strings" "time" "github.com/golang/glog" + "github.com/mattbaird/jsonpatch" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -15,6 +17,7 @@ import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" ) @@ -30,12 +33,14 @@ const ( ) // NewSrcDstController creates a new Kubernetes controller using client-go's Informer -func NewSrcDstController(client kubernetes.Interface, ec2Client *ec2.EC2) *Controller { +func NewSrcDstController(client kubernetes.Interface, ec2Client *ec2.EC2, my_opts *common.K8sEc2SrcdstOpts) *Controller { c := &Controller{ client: client, ec2Client: ec2Client, } + glog.V(5).Info("Creating New Informer") + nodeListWatcher := cache.NewListWatchFromClient( client.Core().RESTClient(), "nodes", @@ -48,8 +53,9 @@ func NewSrcDstController(client kubernetes.Interface, ec2Client *ec2.EC2) *Contr 60*time.Second, // Callback Functions to trigger on add/update/delete cache.ResourceEventHandlerFuncs{ - AddFunc: c.handler, - UpdateFunc: func(old, new interface{}) { c.handler(new) }, + //AddFunc: c.handler, + AddFunc: func(new interface{}) { c.handler(new, my_opts) }, + UpdateFunc: func(old, new interface{}) { c.handler(new, my_opts) }, }, ) @@ -58,18 +64,18 @@ func NewSrcDstController(client kubernetes.Interface, ec2Client *ec2.EC2) *Contr return c } -func (c *Controller) handler(obj interface{}) { +func (c *Controller) handler(obj interface{}, my_opts *common.K8sEc2SrcdstOpts) { // this handler makes sure that all nodes within a cluster has its src/dst check disabled in EC2 node, ok := obj.(*v1.Node) if !ok { glog.Errorf("Expected Node but handler received: %+v", obj) return } - glog.V(4).Infof("Received update of node: %s", node.Name) - c.disableSrcDstIfEnabled(node) + glog.V(6).Infof("Received update of node: %s", node.Name) + c.disableSrcDstIfEnabled(node, my_opts) } -func (c *Controller) disableSrcDstIfEnabled(node *v1.Node) { +func (c *Controller) disableSrcDstIfEnabled(node *v1.Node, my_opts *common.K8sEc2SrcdstOpts) { srcDstCheckEnabled := true if node.Annotations != nil { if _, ok := node.Annotations[SrcDstCheckDisabledAnnotation]; ok { @@ -77,47 +83,102 @@ func (c *Controller) disableSrcDstIfEnabled(node *v1.Node) { } } + glog.V(6).Infof("Performing disableSrcDstIfEnabled run for node %s", node.Name) + if srcDstCheckEnabled { + + glog.V(5).Infof("srcDstCheckEnabled true for node %s", node.Name) + // src dst check disabled annotation does not exist // call AWS ec2 api to disable - instanceID, err := GetInstanceIDFromProviderID(node.Spec.ProviderID) - if err != nil { - glog.Errorf("Fail to retrieve Instance ID from Provider ID: %v", node.Spec.ProviderID) - return - } - err = c.disableSrcDstCheck(*instanceID) + glog.V(5).Infof("Calling AWS to disable Node SrcDstCheck for node %s", node.Name) + err, instanceID := c.disableSrcDstCheck(node) if err != nil { glog.Errorf("Fail to disable src dst check for EC2 instance: %v; %v", *instanceID, err) return } + // We should not modify the cache object directly, so we make a copy first nodeCopy, err := common.CopyObjToNode(node) if err != nil { glog.Errorf("Failed to make copy of node: %v", err) return } + glog.Infof("Marking node %s with SrcDstCheckDisabledAnnotation", node.Name) nodeCopy.Annotations[SrcDstCheckDisabledAnnotation] = "true" - if _, err := c.client.Core().Nodes().Update(nodeCopy); err != nil { - glog.Errorf("Failed to set %s annotation: %v", SrcDstCheckDisabledAnnotation, err) + if my_opts.Patchnode { + glog.V(5).Infof("Patching node %s", nodeCopy.Name) + // Thanks to https://github.com/tamalsaha/patch-demo/blob/master/main.go#L113 for this stanza + // Prep JSON for the newly changed node object + json_nodeCopy, err := json.Marshal(nodeCopy) + if err != nil { + glog.Errorf("Failed to marshal nodeCopy into JSON, %v", err) + } + // Prep JSON for a copy of the old node object + nodeCopyOrig, err := common.CopyObjToNode(node) + if err != nil { + glog.Errorf("Failed to make an original copy of node: %v", err) + return + } + json_nodeCopyOrig, err := json.Marshal(nodeCopyOrig) + if err != nil { + glog.Errorf("Failed to marshal nodeCopyOrig into JSON, %v", err) + } + // Prepare the JSON patch + patch, err := jsonpatch.CreatePatch(json_nodeCopyOrig, json_nodeCopy) + if err != nil { + glog.Errorf("Failed to CreatePatch between JSONs, %v", err) + } + // Fix indenting + json_patch, err := json.MarshalIndent(patch, "", " ") + if err != nil { + glog.Errorf("Failed to MarshalIndent: %v", err) + } + glog.V(5).Info(string(json_patch)) + // https://github.com/kubernetes/client-go/blob/master/kubernetes/typed/core/v1/node.go#L170 + if _, err := c.client.Core().Nodes().Patch(nodeCopy.Name, types.JSONPatchType, json_patch); err != nil { + glog.Errorf("Failed to patch %s annotation: %v", SrcDstCheckDisabledAnnotation, err) + // Sleep for a second and give it one + // more try. The sleep also stops us + // slamming AWS if api-server is + // containtly refusing to allow the + // patches. + time.Sleep(1 * time.Second) + if _, err := c.client.Core().Nodes().Patch(nodeCopy.Name, types.JSONPatchType, json_patch); err != nil { + glog.Errorf("Failed to patch %s annotation on second attempt: %v", SrcDstCheckDisabledAnnotation, err) + } + } else { + glog.V(5).Infof("Node %s has been patched", nodeCopy.Name) + } + } else { + glog.V(5).Infof("Performing UPDATE for node %s", node.Name) + if _, err := c.client.Core().Nodes().Update(nodeCopy); err != nil { + glog.Errorf("Failed to set %s annotation: %v", SrcDstCheckDisabledAnnotation, err) + } } } else { - glog.V(4).Infof("Skipping node %s because it already has the SrcDstCheckDisabledAnnotation", node.Name) - + glog.V(6).Infof("Skipping node %s because it already has the SrcDstCheckDisabledAnnotation", node.Name) } } -func (c *Controller) disableSrcDstCheck(instanceID string) error { - _, err := c.ec2Client.ModifyInstanceAttribute( +func (c *Controller) disableSrcDstCheck(node *v1.Node) (error, *string) { + + instanceID, err := GetInstanceIDFromProviderID(node.Spec.ProviderID) + if err != nil { + glog.Errorf("Fail to retrieve Instance ID from Provider ID: %v", node.Spec.ProviderID) + return err, instanceID + } + + _, err = c.ec2Client.ModifyInstanceAttribute( &ec2.ModifyInstanceAttributeInput{ - InstanceId: aws.String(instanceID), + InstanceId: aws.String(*instanceID), SourceDestCheck: &ec2.AttributeBooleanValue{ Value: aws.Bool(false), }, }, ) - - return err + return err, instanceID } // GetInstanceIDFromProviderID will only retrieve the InstanceID from AWS diff --git a/pkg/controller/srcdst_controller_test.go b/pkg/controller/srcdst_controller_test.go index 0876229c..e71b8d99 100644 --- a/pkg/controller/srcdst_controller_test.go +++ b/pkg/controller/srcdst_controller_test.go @@ -5,6 +5,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/ottoyiu/k8s-ec2-srcdst/pkg/common" "github.com/stretchr/testify/assert" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -48,6 +49,10 @@ func TestDisableSrcDstIfEnabled(t *testing.T) { ec2Client := NewMockEC2Client() kubeClient := fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{*node0, *node1}}) + var my_opts = common.K8sEc2SrcdstOpts{ + Patchnode: false, + } + c := &Controller{ ec2Client: ec2Client, client: kubeClient, @@ -55,7 +60,7 @@ func TestDisableSrcDstIfEnabled(t *testing.T) { for _, tt := range tests { calledCount := ec2Client.CalledCounter - c.disableSrcDstIfEnabled(tt.node) + c.disableSrcDstIfEnabled(tt.node, &my_opts) called := (ec2Client.CalledCounter - calledCount) > 0 assert.Equal( t, diff --git a/vendor/github.com/mattbaird/jsonpatch/.gitignore b/vendor/github.com/mattbaird/jsonpatch/.gitignore new file mode 100644 index 00000000..daf913b1 --- /dev/null +++ b/vendor/github.com/mattbaird/jsonpatch/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/mattbaird/jsonpatch/LICENSE b/vendor/github.com/mattbaird/jsonpatch/LICENSE new file mode 100644 index 00000000..8f71f43f --- /dev/null +++ b/vendor/github.com/mattbaird/jsonpatch/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/github.com/mattbaird/jsonpatch/README.md b/vendor/github.com/mattbaird/jsonpatch/README.md new file mode 100644 index 00000000..91c03b3f --- /dev/null +++ b/vendor/github.com/mattbaird/jsonpatch/README.md @@ -0,0 +1,46 @@ +# jsonpatch +As per http://jsonpatch.com/ JSON Patch is specified in RFC 6902 from the IETF. + +JSON Patch allows you to generate JSON that describes changes you want to make to a document, so you don't have to send the whole doc. JSON Patch format is supported by HTTP PATCH method, allowing for standards based partial updates via REST APIs. + +```bash +go get github.com/mattbaird/jsonpatch +``` + +I tried some of the other "jsonpatch" go implementations, but none of them could diff two json documents and +generate format like jsonpatch.com specifies. Here's an example of the patch format: + +```json +[ + { "op": "replace", "path": "/baz", "value": "boo" }, + { "op": "add", "path": "/hello", "value": ["world"] }, + { "op": "remove", "path": "/foo"} +] + +``` +The API is super simple +#example +```go +package main + +import ( + "fmt" + "github.com/mattbaird/jsonpatch" +) + +var simpleA = `{"a":100, "b":200, "c":"hello"}` +var simpleB = `{"a":100, "b":200, "c":"goodbye"}` + +func main() { + patch, e := jsonpatch.CreatePatch([]byte(simpleA), []byte(simpleA)) + if e != nil { + fmt.Printf("Error creating JSON patch:%v", e) + return + } + for _, operation := range patch { + fmt.Printf("%s\n", operation.Json()) + } +} +``` + +This code needs more tests, as it's a highly recursive, type-fiddly monster. It's not a lot of code, but it has to deal with a lot of complexity. diff --git a/vendor/github.com/mattbaird/jsonpatch/jsonpatch.go b/vendor/github.com/mattbaird/jsonpatch/jsonpatch.go new file mode 100644 index 00000000..295f260f --- /dev/null +++ b/vendor/github.com/mattbaird/jsonpatch/jsonpatch.go @@ -0,0 +1,257 @@ +package jsonpatch + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +var errBadJSONDoc = fmt.Errorf("Invalid JSON Document") + +type JsonPatchOperation struct { + Operation string `json:"op"` + Path string `json:"path"` + Value interface{} `json:"value,omitempty"` +} + +func (j *JsonPatchOperation) Json() string { + b, _ := json.Marshal(j) + return string(b) +} + +func (j *JsonPatchOperation) MarshalJSON() ([]byte, error) { + var b bytes.Buffer + b.WriteString("{") + b.WriteString(fmt.Sprintf(`"op":"%s"`, j.Operation)) + b.WriteString(fmt.Sprintf(`,"path":"%s"`, j.Path)) + // Consider omitting Value for non-nullable operations. + if j.Value != nil || j.Operation == "replace" || j.Operation == "add" { + v, err := json.Marshal(j.Value) + if err != nil { + return nil, err + } + b.WriteString(`,"value":`) + b.Write(v) + } + b.WriteString("}") + return b.Bytes(), nil +} + +type ByPath []JsonPatchOperation + +func (a ByPath) Len() int { return len(a) } +func (a ByPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByPath) Less(i, j int) bool { return a[i].Path < a[j].Path } + +func NewPatch(operation, path string, value interface{}) JsonPatchOperation { + return JsonPatchOperation{Operation: operation, Path: path, Value: value} +} + +// CreatePatch creates a patch as specified in http://jsonpatch.com/ +// +// 'a' is original, 'b' is the modified document. Both are to be given as json encoded content. +// The function will return an array of JsonPatchOperations +// +// An error will be returned if any of the two documents are invalid. +func CreatePatch(a, b []byte) ([]JsonPatchOperation, error) { + aI := map[string]interface{}{} + bI := map[string]interface{}{} + err := json.Unmarshal(a, &aI) + if err != nil { + return nil, errBadJSONDoc + } + err = json.Unmarshal(b, &bI) + if err != nil { + return nil, errBadJSONDoc + } + return diff(aI, bI, "", []JsonPatchOperation{}) +} + +// Returns true if the values matches (must be json types) +// The types of the values must match, otherwise it will always return false +// If two map[string]interface{} are given, all elements must match. +func matchesValue(av, bv interface{}) bool { + if reflect.TypeOf(av) != reflect.TypeOf(bv) { + return false + } + switch at := av.(type) { + case string: + bt := bv.(string) + if bt == at { + return true + } + case float64: + bt := bv.(float64) + if bt == at { + return true + } + case bool: + bt := bv.(bool) + if bt == at { + return true + } + case map[string]interface{}: + bt := bv.(map[string]interface{}) + for key := range at { + if !matchesValue(at[key], bt[key]) { + return false + } + } + for key := range bt { + if !matchesValue(at[key], bt[key]) { + return false + } + } + return true + case []interface{}: + bt := bv.([]interface{}) + if len(bt) != len(at) { + return false + } + for key := range at { + if !matchesValue(at[key], bt[key]) { + return false + } + } + for key := range bt { + if !matchesValue(at[key], bt[key]) { + return false + } + } + return true + } + return false +} + +// From http://tools.ietf.org/html/rfc6901#section-4 : +// +// Evaluation of each reference token begins by decoding any escaped +// character sequence. This is performed by first transforming any +// occurrence of the sequence '~1' to '/', and then transforming any +// occurrence of the sequence '~0' to '~'. +// TODO decode support: +// var rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~") + +var rfc6901Encoder = strings.NewReplacer("~", "~0", "/", "~1") + +func makePath(path string, newPart interface{}) string { + key := rfc6901Encoder.Replace(fmt.Sprintf("%v", newPart)) + if path == "" { + return "/" + key + } + if strings.HasSuffix(path, "/") { + return path + key + } + return path + "/" + key +} + +// diff returns the (recursive) difference between a and b as an array of JsonPatchOperations. +func diff(a, b map[string]interface{}, path string, patch []JsonPatchOperation) ([]JsonPatchOperation, error) { + for key, bv := range b { + p := makePath(path, key) + av, ok := a[key] + // value was added + if !ok { + patch = append(patch, NewPatch("add", p, bv)) + continue + } + // If types have changed, replace completely + if reflect.TypeOf(av) != reflect.TypeOf(bv) { + patch = append(patch, NewPatch("replace", p, bv)) + continue + } + // Types are the same, compare values + var err error + patch, err = handleValues(av, bv, p, patch) + if err != nil { + return nil, err + } + } + // Now add all deleted values as nil + for key := range a { + _, found := b[key] + if !found { + p := makePath(path, key) + + patch = append(patch, NewPatch("remove", p, nil)) + } + } + return patch, nil +} + +func handleValues(av, bv interface{}, p string, patch []JsonPatchOperation) ([]JsonPatchOperation, error) { + var err error + switch at := av.(type) { + case map[string]interface{}: + bt := bv.(map[string]interface{}) + patch, err = diff(at, bt, p, patch) + if err != nil { + return nil, err + } + case string, float64, bool: + if !matchesValue(av, bv) { + patch = append(patch, NewPatch("replace", p, bv)) + } + case []interface{}: + bt, ok := bv.([]interface{}) + if !ok { + // array replaced by non-array + patch = append(patch, NewPatch("replace", p, bv)) + } else if len(at) != len(bt) { + // arrays are not the same length + patch = append(patch, compareArray(at, bt, p)...) + + } else { + for i := range bt { + patch, err = handleValues(at[i], bt[i], makePath(p, i), patch) + if err != nil { + return nil, err + } + } + } + case nil: + switch bv.(type) { + case nil: + // Both nil, fine. + default: + patch = append(patch, NewPatch("add", p, bv)) + } + default: + panic(fmt.Sprintf("Unknown type:%T ", av)) + } + return patch, nil +} + +func compareArray(av, bv []interface{}, p string) []JsonPatchOperation { + retval := []JsonPatchOperation{} + // var err error + for i, v := range av { + found := false + for _, v2 := range bv { + if reflect.DeepEqual(v, v2) { + found = true + break + } + } + if !found { + retval = append(retval, NewPatch("remove", makePath(p, i), nil)) + } + } + + for i, v := range bv { + found := false + for _, v2 := range av { + if reflect.DeepEqual(v, v2) { + found = true + break + } + } + if !found { + retval = append(retval, NewPatch("add", makePath(p, i), v)) + } + } + + return retval +} diff --git a/vendor/github.com/mattbaird/jsonpatch/jsonpatch_complex_test.go b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_complex_test.go new file mode 100644 index 00000000..2a98871f --- /dev/null +++ b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_complex_test.go @@ -0,0 +1,88 @@ +package jsonpatch + +import ( + "github.com/stretchr/testify/assert" + "sort" + "testing" +) + +var complexBase = `{"a":100, "b":[{"c1":"hello", "d1":"foo"},{"c2":"hello2", "d2":"foo2"} ], "e":{"f":200, "g":"h", "i":"j"}}` +var complexA = `{"a":100, "b":[{"c1":"goodbye", "d1":"foo"},{"c2":"hello2", "d2":"foo2"} ], "e":{"f":200, "g":"h", "i":"j"}}` +var complexB = `{"a":100, "b":[{"c1":"hello", "d1":"foo"},{"c2":"hello2", "d2":"foo2"} ], "e":{"f":100, "g":"h", "i":"j"}}` +var complexC = `{"a":100, "b":[{"c1":"hello", "d1":"foo"},{"c2":"hello2", "d2":"foo2"} ], "e":{"f":200, "g":"h", "i":"j"}, "k":[{"l":"m"}, {"l":"o"}]}` +var complexD = `{"a":100, "b":[{"c1":"hello", "d1":"foo"},{"c2":"hello2", "d2":"foo2"}, {"c3":"hello3", "d3":"foo3"} ], "e":{"f":200, "g":"h", "i":"j"}}` +var complexE = `{"a":100, "b":[{"c1":"hello", "d1":"foo"},{"c2":"hello2", "d2":"foo2"} ], "e":{"f":200, "g":"h", "i":"j"}}` + +func TestComplexSame(t *testing.T) { + patch, e := CreatePatch([]byte(complexBase), []byte(complexBase)) + assert.NoError(t, e) + assert.Equal(t, 0, len(patch), "they should be equal") +} +func TestComplexOneStringReplaceInArray(t *testing.T) { + patch, e := CreatePatch([]byte(complexBase), []byte(complexA)) + assert.NoError(t, e) + assert.Equal(t, 1, len(patch), "they should be equal") + change := patch[0] + assert.Equal(t, "replace", change.Operation, "they should be equal") + assert.Equal(t, "/b/0/c1", change.Path, "they should be equal") + assert.Equal(t, "goodbye", change.Value, "they should be equal") +} + +func TestComplexOneIntReplace(t *testing.T) { + patch, e := CreatePatch([]byte(complexBase), []byte(complexB)) + assert.NoError(t, e) + assert.Equal(t, 1, len(patch), "they should be equal") + change := patch[0] + assert.Equal(t, "replace", change.Operation, "they should be equal") + assert.Equal(t, "/e/f", change.Path, "they should be equal") + var expected float64 = 100 + assert.Equal(t, expected, change.Value, "they should be equal") +} + +func TestComplexOneAdd(t *testing.T) { + patch, e := CreatePatch([]byte(complexBase), []byte(complexC)) + assert.NoError(t, e) + assert.Equal(t, 1, len(patch), "they should be equal") + change := patch[0] + assert.Equal(t, "add", change.Operation, "they should be equal") + assert.Equal(t, "/k", change.Path, "they should be equal") + a := make(map[string]interface{}) + b := make(map[string]interface{}) + a["l"] = "m" + b["l"] = "o" + expected := []interface{}{a, b} + assert.Equal(t, expected, change.Value, "they should be equal") +} + +func TestComplexOneAddToArray(t *testing.T) { + patch, e := CreatePatch([]byte(complexBase), []byte(complexC)) + assert.NoError(t, e) + assert.Equal(t, 1, len(patch), "they should be equal") + change := patch[0] + assert.Equal(t, "add", change.Operation, "they should be equal") + assert.Equal(t, "/k", change.Path, "they should be equal") + a := make(map[string]interface{}) + b := make(map[string]interface{}) + a["l"] = "m" + b["l"] = "o" + expected := []interface{}{a, b} + assert.Equal(t, expected, change.Value, "they should be equal") +} + +func TestComplexVsEmpty(t *testing.T) { + patch, e := CreatePatch([]byte(complexBase), []byte(empty)) + assert.NoError(t, e) + assert.Equal(t, 3, len(patch), "they should be equal") + sort.Sort(ByPath(patch)) + change := patch[0] + assert.Equal(t, "remove", change.Operation, "they should be equal") + assert.Equal(t, "/a", change.Path, "they should be equal") + + change = patch[1] + assert.Equal(t, "remove", change.Operation, "they should be equal") + assert.Equal(t, "/b", change.Path, "they should be equal") + + change = patch[2] + assert.Equal(t, "remove", change.Operation, "they should be equal") + assert.Equal(t, "/e", change.Path, "they should be equal") +} diff --git a/vendor/github.com/mattbaird/jsonpatch/jsonpatch_geojson_test.go b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_geojson_test.go new file mode 100644 index 00000000..6a92da03 --- /dev/null +++ b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_geojson_test.go @@ -0,0 +1,48 @@ +package jsonpatch + +import ( + "github.com/stretchr/testify/assert" + "sort" + "testing" +) + +var point = `{"type":"Point", "coordinates":[0.0, 1.0]}` +var lineString = `{"type":"LineString", "coordinates":[[0.0, 1.0], [2.0, 3.0]]}` + +func TestPointLineStringReplace(t *testing.T) { + patch, e := CreatePatch([]byte(point), []byte(lineString)) + assert.NoError(t, e) + assert.Equal(t, len(patch), 3, "they should be equal") + sort.Sort(ByPath(patch)) + change := patch[0] + assert.Equal(t, change.Operation, "replace", "they should be equal") + assert.Equal(t, change.Path, "/coordinates/0", "they should be equal") + assert.Equal(t, change.Value, []interface{}{0.0, 1.0}, "they should be equal") + change = patch[1] + assert.Equal(t, change.Operation, "replace", "they should be equal") + assert.Equal(t, change.Path, "/coordinates/1", "they should be equal") + assert.Equal(t, change.Value, []interface{}{2.0, 3.0}, "they should be equal") + change = patch[2] + assert.Equal(t, change.Operation, "replace", "they should be equal") + assert.Equal(t, change.Path, "/type", "they should be equal") + assert.Equal(t, change.Value, "LineString", "they should be equal") +} + +func TestLineStringPointReplace(t *testing.T) { + patch, e := CreatePatch([]byte(lineString), []byte(point)) + assert.NoError(t, e) + assert.Equal(t, len(patch), 3, "they should be equal") + sort.Sort(ByPath(patch)) + change := patch[0] + assert.Equal(t, change.Operation, "replace", "they should be equal") + assert.Equal(t, change.Path, "/coordinates/0", "they should be equal") + assert.Equal(t, change.Value, 0.0, "they should be equal") + change = patch[1] + assert.Equal(t, change.Operation, "replace", "they should be equal") + assert.Equal(t, change.Path, "/coordinates/1", "they should be equal") + assert.Equal(t, change.Value, 1.0, "they should be equal") + change = patch[2] + assert.Equal(t, change.Operation, "replace", "they should be equal") + assert.Equal(t, change.Path, "/type", "they should be equal") + assert.Equal(t, change.Value, "Point", "they should be equal") +} diff --git a/vendor/github.com/mattbaird/jsonpatch/jsonpatch_hypercomplex_test.go b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_hypercomplex_test.go new file mode 100644 index 00000000..f34423b4 --- /dev/null +++ b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_hypercomplex_test.go @@ -0,0 +1,181 @@ +package jsonpatch + +import ( + "github.com/stretchr/testify/assert" + "sort" + "testing" +) + +var hyperComplexBase = ` +{ + "goods": [ + { + "id": "0001", + "type": "donut", + "name": "Cake", + "ppu": 0.55, + "batters": + { + "batter": + [ + { "id": "1001", "type": "Regular" }, + { "id": "1002", "type": "Chocolate" }, + { "id": "1003", "type": "Blueberry" }, + { "id": "1004", "type": "Devil's Food" } + ] + }, + "topping": + [ + { "id": "5001", "type": "None" }, + { "id": "5002", "type": "Glazed" }, + { "id": "5005", "type": "Sugar" }, + { "id": "5007", "type": "Powdered Sugar" }, + { "id": "5006", "type": "Chocolate with Sprinkles" }, + { "id": "5003", "type": "Chocolate" }, + { "id": "5004", "type": "Maple" } + ] + }, + { + "id": "0002", + "type": "donut", + "name": "Raised", + "ppu": 0.55, + "batters": + { + "batter": + [ + { "id": "1001", "type": "Regular" } + ] + }, + "topping": + [ + { "id": "5001", "type": "None" }, + { "id": "5002", "type": "Glazed" }, + { "id": "5005", "type": "Sugar" }, + { "id": "5003", "type": "Chocolate" }, + { "id": "5004", "type": "Maple" } + ] + }, + { + "id": "0003", + "type": "donut", + "name": "Old Fashioned", + "ppu": 0.55, + "batters": + { + "batter": + [ + { "id": "1001", "type": "Regular" }, + { "id": "1002", "type": "Chocolate" } + ] + }, + "topping": + [ + { "id": "5001", "type": "None" }, + { "id": "5002", "type": "Glazed" }, + { "id": "5003", "type": "Chocolate" }, + { "id": "5004", "type": "Maple" } + ] + } +] +}` + +var hyperComplexA = ` +{ + "goods": [ + { + "id": "0001", + "type": "donut", + "name": "Cake", + "ppu": 0.55, + "batters": + { + "batter": + [ + { "id": "1001", "type": "Regular" }, + { "id": "1002", "type": "Chocolate" }, + { "id": "1003", "type": "Strawberry" }, + { "id": "1004", "type": "Devil's Food" } + ] + }, + "topping": + [ + { "id": "5001", "type": "None" }, + { "id": "5002", "type": "Glazed" }, + { "id": "5005", "type": "Sugar" }, + { "id": "5007", "type": "Powdered Sugar" }, + { "id": "5006", "type": "Chocolate with Sprinkles" }, + { "id": "5003", "type": "Chocolate" }, + { "id": "5004", "type": "Maple" } + ] + }, + { + "id": "0002", + "type": "donut", + "name": "Raised", + "ppu": 0.55, + "batters": + { + "batter": + [ + { "id": "1001", "type": "Regular" } + ] + }, + "topping": + [ + { "id": "5001", "type": "None" }, + { "id": "5002", "type": "Glazed" }, + { "id": "5005", "type": "Sugar" }, + { "id": "5003", "type": "Chocolate" }, + { "id": "5004", "type": "Maple" } + ] + }, + { + "id": "0003", + "type": "donut", + "name": "Old Fashioned", + "ppu": 0.55, + "batters": + { + "batter": + [ + { "id": "1001", "type": "Regular" }, + { "id": "1002", "type": "Chocolate" }, + { "id": "1003", "type": "Vanilla" } + ] + }, + "topping": + [ + { "id": "5001", "type": "None" }, + { "id": "5002", "type": "Glazed" }, + { "id": "5004", "type": "Maple" } + ] + } +] +}` + +func TestHyperComplexSame(t *testing.T) { + patch, e := CreatePatch([]byte(hyperComplexBase), []byte(hyperComplexBase)) + assert.NoError(t, e) + assert.Equal(t, len(patch), 0, "they should be equal") +} + +func TestHyperComplexBoolReplace(t *testing.T) { + patch, e := CreatePatch([]byte(hyperComplexBase), []byte(hyperComplexA)) + assert.NoError(t, e) + assert.Equal(t, 3, len(patch), "they should be equal") + sort.Sort(ByPath(patch)) + + change := patch[0] + assert.Equal(t, "replace", change.Operation, "they should be equal") + assert.Equal(t, "/goods/0/batters/batter/2/type", change.Path, "they should be equal") + assert.Equal(t, "Strawberry", change.Value, "they should be equal") + change = patch[1] + assert.Equal(t, "add", change.Operation, "they should be equal") + assert.Equal(t, "/goods/2/batters/batter/2", change.Path, "they should be equal") + assert.Equal(t, map[string]interface{}{"id": "1003", "type": "Vanilla"}, change.Value, "they should be equal") + change = patch[2] + assert.Equal(t, change.Operation, "remove", "they should be equal") + assert.Equal(t, change.Path, "/goods/2/topping/2", "they should be equal") + assert.Equal(t, nil, change.Value, "they should be equal") +} diff --git a/vendor/github.com/mattbaird/jsonpatch/jsonpatch_json_test.go b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_json_test.go new file mode 100644 index 00000000..4f8617f7 --- /dev/null +++ b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_json_test.go @@ -0,0 +1,31 @@ +package jsonpatch + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestMarshalNullableValue(t *testing.T) { + p1 := JsonPatchOperation{ + Operation: "replace", + Path: "/a1", + Value: nil, + } + assert.JSONEq(t, `{"op":"replace", "path":"/a1","value":null}`, p1.Json()) + + p2 := JsonPatchOperation{ + Operation: "replace", + Path: "/a2", + Value: "v2", + } + assert.JSONEq(t, `{"op":"replace", "path":"/a2", "value":"v2"}`, p2.Json()) +} + +func TestMarshalNonNullableValue(t *testing.T) { + p1 := JsonPatchOperation{ + Operation: "remove", + Path: "/a1", + } + assert.JSONEq(t, `{"op":"remove", "path":"/a1"}`, p1.Json()) + +} diff --git a/vendor/github.com/mattbaird/jsonpatch/jsonpatch_simple_test.go b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_simple_test.go new file mode 100644 index 00000000..1a6ec297 --- /dev/null +++ b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_simple_test.go @@ -0,0 +1,120 @@ +package jsonpatch + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/assert" +) + +var simpleA = `{"a":100, "b":200, "c":"hello"}` +var simpleB = `{"a":100, "b":200, "c":"goodbye"}` +var simpleC = `{"a":100, "b":100, "c":"hello"}` +var simpleD = `{"a":100, "b":200, "c":"hello", "d":"foo"}` +var simpleE = `{"a":100, "b":200}` +var simplef = `{"a":100, "b":100, "d":"foo"}` +var simpleG = `{"a":100, "b":null, "d":"foo"}` +var empty = `{}` + +func TestOneNullReplace(t *testing.T) { + patch, e := CreatePatch([]byte(simplef), []byte(simpleG)) + assert.NoError(t, e) + assert.Equal(t, len(patch), 1, "they should be equal") + change := patch[0] + assert.Equal(t, change.Operation, "replace", "they should be equal") + assert.Equal(t, change.Path, "/b", "they should be equal") + assert.Equal(t, change.Value, nil, "they should be equal") +} + +func TestSame(t *testing.T) { + patch, e := CreatePatch([]byte(simpleA), []byte(simpleA)) + assert.NoError(t, e) + assert.Equal(t, len(patch), 0, "they should be equal") +} + +func TestOneStringReplace(t *testing.T) { + patch, e := CreatePatch([]byte(simpleA), []byte(simpleB)) + assert.NoError(t, e) + assert.Equal(t, len(patch), 1, "they should be equal") + change := patch[0] + assert.Equal(t, change.Operation, "replace", "they should be equal") + assert.Equal(t, change.Path, "/c", "they should be equal") + assert.Equal(t, change.Value, "goodbye", "they should be equal") +} + +func TestOneIntReplace(t *testing.T) { + patch, e := CreatePatch([]byte(simpleA), []byte(simpleC)) + assert.NoError(t, e) + assert.Equal(t, len(patch), 1, "they should be equal") + change := patch[0] + assert.Equal(t, change.Operation, "replace", "they should be equal") + assert.Equal(t, change.Path, "/b", "they should be equal") + var expected float64 = 100 + assert.Equal(t, change.Value, expected, "they should be equal") +} + +func TestOneAdd(t *testing.T) { + patch, e := CreatePatch([]byte(simpleA), []byte(simpleD)) + assert.NoError(t, e) + assert.Equal(t, len(patch), 1, "they should be equal") + change := patch[0] + assert.Equal(t, change.Operation, "add", "they should be equal") + assert.Equal(t, change.Path, "/d", "they should be equal") + assert.Equal(t, change.Value, "foo", "they should be equal") +} + +func TestOneRemove(t *testing.T) { + patch, e := CreatePatch([]byte(simpleA), []byte(simpleE)) + assert.NoError(t, e) + assert.Equal(t, len(patch), 1, "they should be equal") + change := patch[0] + assert.Equal(t, change.Operation, "remove", "they should be equal") + assert.Equal(t, change.Path, "/c", "they should be equal") + assert.Equal(t, change.Value, nil, "they should be equal") +} + +func TestVsEmpty(t *testing.T) { + patch, e := CreatePatch([]byte(simpleA), []byte(empty)) + assert.NoError(t, e) + assert.Equal(t, len(patch), 3, "they should be equal") + sort.Sort(ByPath(patch)) + change := patch[0] + assert.Equal(t, change.Operation, "remove", "they should be equal") + assert.Equal(t, change.Path, "/a", "they should be equal") + + change = patch[1] + assert.Equal(t, change.Operation, "remove", "they should be equal") + assert.Equal(t, change.Path, "/b", "they should be equal") + + change = patch[2] + assert.Equal(t, change.Operation, "remove", "they should be equal") + assert.Equal(t, change.Path, "/c", "they should be equal") +} + +func BenchmarkBigArrays(b *testing.B) { + var a1, a2 []interface{} + a1 = make([]interface{}, 100) + a2 = make([]interface{}, 101) + + for i := 0; i < 100; i++ { + a1[i] = i + a2[i+1] = i + } + for i := 0; i < b.N; i++ { + compareArray(a1, a2, "/") + } +} + +func BenchmarkBigArrays2(b *testing.B) { + var a1, a2 []interface{} + a1 = make([]interface{}, 100) + a2 = make([]interface{}, 101) + + for i := 0; i < 100; i++ { + a1[i] = i + a2[i] = i + } + for i := 0; i < b.N; i++ { + compareArray(a1, a2, "/") + } +} diff --git a/vendor/github.com/mattbaird/jsonpatch/jsonpatch_supercomplex_test.go b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_supercomplex_test.go new file mode 100644 index 00000000..6e98f3a6 --- /dev/null +++ b/vendor/github.com/mattbaird/jsonpatch/jsonpatch_supercomplex_test.go @@ -0,0 +1,506 @@ +package jsonpatch + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +var superComplexBase = ` +{ + "annotations": { + "annotation": [ + { + "name": "version", + "value": "8" + }, + { + "name": "versionTag", + "value": "Published on May 13, 2015 at 8:48pm (MST)" + } + ] + }, + "attributes": { + "attribute-key": [ + { + "id": "3b05c943-d81a-436f-b242-8b519e7a6f30", + "properties": { + "visible": true + } + }, + { + "id": "d794c7ee-2a4b-4da4-bba7-e8b973d50c4b", + "properties": { + "visible": true + } + }, + { + "id": "a0259458-517c-480f-9f04-9b54b1b2af1f", + "properties": { + "visible": true + } + }, + { + "id": "9415f39d-c396-4458-9019-fc076c847964", + "properties": { + "visible": true + } + }, + { + "id": "0a2e49a9-8989-42fb-97da-cc66334f828b", + "properties": { + "visible": true + } + }, + { + "id": "27f5f14a-ea97-4feb-b22a-6ff754a31212", + "properties": { + "visible": true + } + }, + { + "id": "6f810508-4615-4fd0-9e87-80f9c94f9ad8", + "properties": { + "visible": true + } + }, + { + "id": "3451b1b2-7365-455c-8bb1-0b464d4d3ba1", + "properties": { + "visible": true + } + }, + { + "id": "a82ec957-8c26-41ea-8af6-6dd75c384801", + "properties": { + "visible": true + } + }, + { + "id": "736c5496-9a6e-4a82-aa00-456725796432", + "properties": { + "visible": true + } + }, + { + "id": "2d428b3c-9d3b-4ec1-bf98-e00673599d60", + "properties": { + "visible": true + } + }, + { + "id": "68566ebb-811d-4337-aba9-a8a8baf90e4b", + "properties": { + "visible": true + } + }, + { + "id": "ca88bab1-a1ea-40cc-8f96-96d1e9f1217d", + "properties": { + "visible": true + } + }, + { + "id": "c63a12c8-542d-47f3-bee1-30b5fe2b0690", + "properties": { + "visible": true + } + }, + { + "id": "cbd9e3bc-6a49-432a-a906-b1674c1de24c", + "properties": { + "visible": true + } + }, + { + "id": "03262f07-8a15-416d-a3f5-e2bf561c78f9", + "properties": { + "visible": true + } + }, + { + "id": "e5c93b87-83fc-45b6-b4d5-bf1e3f523075", + "properties": { + "visible": true + } + }, + { + "id": "72260ac5-3d51-49d7-bb31-f794dd129f1c", + "properties": { + "visible": true + } + }, + { + "id": "d856bde1-1b42-4935-9bee-c37e886c9ecf", + "properties": { + "visible": true + } + }, + { + "id": "62380509-bedf-4134-95c3-77ff377a4a6a", + "properties": { + "visible": true + } + }, + { + "id": "f4ed5ac9-b386-49a6-a0a0-6f3341ce9021", + "properties": { + "visible": true + } + }, + { + "id": "528d2bd2-87fe-4a49-954a-c93a03256929", + "properties": { + "visible": true + } + }, + { + "id": "ff8951f1-61a7-416b-9223-fac4bb6dac50", + "properties": { + "visible": true + } + }, + { + "id": "95c2b011-d782-4042-8a07-6aa4a5765c2e", + "properties": { + "visible": true + } + }, + { + "id": "dbe5837b-0624-4a05-91f3-67b5bd9b812a", + "properties": { + "visible": true + } + }, + { + "id": "13f198ed-82ab-4e51-8144-bfaa5bf77fd5", + "properties": { + "visible": true + } + }, + { + "id": "025312eb-12b6-47e6-9750-0fb31ddc2111", + "properties": { + "visible": true + } + }, + { + "id": "24292d58-db66-4ef3-8f4f-005d7b719433", + "properties": { + "visible": true + } + }, + { + "id": "22e5b5c4-821c-413a-a5b1-ab866d9a03bb", + "properties": { + "visible": true + } + }, + { + "id": "2fde0aac-df89-403d-998e-854b949c7b57", + "properties": { + "visible": true + } + }, + { + "id": "8b576876-5c16-4178-805e-24984c24fac3", + "properties": { + "visible": true + } + }, + { + "id": "415b7d2a-b362-4f1e-b83a-927802328ecb", + "properties": { + "visible": true + } + }, + { + "id": "8ef24fc2-ab25-4f22-9d9f-61902b49dc01", + "properties": { + "visible": true + } + }, + { + "id": "2299b09e-9f8e-4b79-a55c-a7edacde2c85", + "properties": { + "visible": true + } + }, + { + "id": "bf506538-f438-425c-be85-5aa2f9b075b8", + "properties": { + "visible": true + } + }, + { + "id": "2b501dc6-799d-4675-9144-fac77c50c57c", + "properties": { + "visible": true + } + }, + { + "id": "c0446da1-e069-417e-bd5a-34edcd028edc", + "properties": { + "visible": true + } + } + ] + } +}` + +var superComplexA = ` +{ + "annotations": { + "annotation": [ + { + "name": "version", + "value": "8" + }, + { + "name": "versionTag", + "value": "Published on May 13, 2015 at 8:48pm (MST)" + } + ] + }, + "attributes": { + "attribute-key": [ + { + "id": "3b05c943-d81a-436f-b242-8b519e7a6f30", + "properties": { + "visible": true + } + }, + { + "id": "d794c7ee-2a4b-4da4-bba7-e8b973d50c4b", + "properties": { + "visible": true + } + }, + { + "id": "a0259458-517c-480f-9f04-9b54b1b2af1f", + "properties": { + "visible": true + } + }, + { + "id": "9415f39d-c396-4458-9019-fc076c847964", + "properties": { + "visible": true + } + }, + { + "id": "0a2e49a9-8989-42fb-97da-cc66334f828b", + "properties": { + "visible": true + } + }, + { + "id": "27f5f14a-ea97-4feb-b22a-6ff754a31212", + "properties": { + "visible": true + } + }, + { + "id": "6f810508-4615-4fd0-9e87-80f9c94f9ad8", + "properties": { + "visible": true + } + }, + { + "id": "3451b1b2-7365-455c-8bb1-0b464d4d3ba1", + "properties": { + "visible": true + } + }, + { + "id": "a82ec957-8c26-41ea-8af6-6dd75c384801", + "properties": { + "visible": true + } + }, + { + "id": "736c5496-9a6e-4a82-aa00-456725796432", + "properties": { + "visible": true + } + }, + { + "id": "2d428b3c-9d3b-4ec1-bf98-e00673599d60", + "properties": { + "visible": true + } + }, + { + "id": "68566ebb-811d-4337-aba9-a8a8baf90e4b", + "properties": { + "visible": true + } + }, + { + "id": "ca88bab1-a1ea-40cc-8f96-96d1e9f1217d", + "properties": { + "visible": true + } + }, + { + "id": "c63a12c8-542d-47f3-bee1-30b5fe2b0690", + "properties": { + "visible": true + } + }, + { + "id": "cbd9e3bc-6a49-432a-a906-b1674c1de24c", + "properties": { + "visible": true + } + }, + { + "id": "03262f07-8a15-416d-a3f5-e2bf561c78f9", + "properties": { + "visible": true + } + }, + { + "id": "e5c93b87-83fc-45b6-b4d5-bf1e3f523075", + "properties": { + "visible": true + } + }, + { + "id": "72260ac5-3d51-49d7-bb31-f794dd129f1c", + "properties": { + "visible": true + } + }, + { + "id": "d856bde1-1b42-4935-9bee-c37e886c9ecf", + "properties": { + "visible": true + } + }, + { + "id": "62380509-bedf-4134-95c3-77ff377a4a6a", + "properties": { + "visible": true + } + }, + { + "id": "f4ed5ac9-b386-49a6-a0a0-6f3341ce9021", + "properties": { + "visible": true + } + }, + { + "id": "528d2bd2-87fe-4a49-954a-c93a03256929", + "properties": { + "visible": true + } + }, + { + "id": "ff8951f1-61a7-416b-9223-fac4bb6dac50", + "properties": { + "visible": true + } + }, + { + "id": "95c2b011-d782-4042-8a07-6aa4a5765c2e", + "properties": { + "visible": true + } + }, + { + "id": "dbe5837b-0624-4a05-91f3-67b5bd9b812a", + "properties": { + "visible": true + } + }, + { + "id": "13f198ed-82ab-4e51-8144-bfaa5bf77fd5", + "properties": { + "visible": true + } + }, + { + "id": "025312eb-12b6-47e6-9750-0fb31ddc2111", + "properties": { + "visible": true + } + }, + { + "id": "24292d58-db66-4ef3-8f4f-005d7b719433", + "properties": { + "visible": true + } + }, + { + "id": "22e5b5c4-821c-413a-a5b1-ab866d9a03bb", + "properties": { + "visible": true + } + }, + { + "id": "2fde0aac-df89-403d-998e-854b949c7b57", + "properties": { + "visible": true + } + }, + { + "id": "8b576876-5c16-4178-805e-24984c24fac3", + "properties": { + "visible": true + } + }, + { + "id": "415b7d2a-b362-4f1e-b83a-927802328ecb", + "properties": { + "visible": true + } + }, + { + "id": "8ef24fc2-ab25-4f22-9d9f-61902b49dc01", + "properties": { + "visible": true + } + }, + { + "id": "2299b09e-9f8e-4b79-a55c-a7edacde2c85", + "properties": { + "visible": true + } + }, + { + "id": "bf506538-f438-425c-be85-5aa2f9b075b8", + "properties": { + "visible": true + } + }, + { + "id": "2b501dc6-799d-4675-9144-fac77c50c57c", + "properties": { + "visible": true + } + }, + { + "id": "c0446da1-e069-417e-bd5a-34edcd028edc", + "properties": { + "visible": false + } + } + ] + } +}` + +func TestSuperComplexSame(t *testing.T) { + patch, e := CreatePatch([]byte(superComplexBase), []byte(superComplexBase)) + assert.NoError(t, e) + assert.Equal(t, 0, len(patch), "they should be equal") +} + +func TestSuperComplexBoolReplace(t *testing.T) { + patch, e := CreatePatch([]byte(superComplexBase), []byte(superComplexA)) + assert.NoError(t, e) + assert.Equal(t, 1, len(patch), "they should be equal") + change := patch[0] + assert.Equal(t, "replace", change.Operation, "they should be equal") + assert.Equal(t, "/attributes/attribute-key/36/properties/visible", change.Path, "they should be equal") + assert.Equal(t, false, change.Value, "they should be equal") +}