From d7fa70e7930823568ad3507311e124f93edd667e Mon Sep 17 00:00:00 2001 From: Ahmed ElSayed Date: Tue, 16 Jun 2020 00:06:41 -0700 Subject: [PATCH] Add PushScaler interface and impl external-push scaler (#865) * Add PushScaler interface and impl external-push scaler Closes #820 Signed-off-by: Ahmed ElSayed * pass metadata to scaler Signed-off-by: Ahmed ElSayed * add section for breaking changes in the CHANGELOG Signed-off-by: Ahmed ElSayed * use correct protoc-gen-go version Signed-off-by: Ahmed ElSayed --- CHANGELOG.md | 7 + deploy/crds/keda.sh_withpods_crd.yaml | 42 --- go.mod | 1 + go.sum | 183 ++++++++++++ pkg/scalers/external_scaler.go | 247 +++++++++++------ pkg/scalers/external_scaler_test.go | 144 +++++++++- .../externalscaler/externalscaler.pb.go | 262 +++++++----------- .../externalscaler/externalscaler.proto | 12 +- pkg/scalers/scaler.go | 7 + pkg/scaling/executor/scale_executor.go | 9 +- pkg/scaling/executor/scale_jobs.go | 45 +-- pkg/scaling/executor/scale_scaledobjects.go | 20 +- pkg/scaling/scale_handler.go | 120 +++++++- 13 files changed, 736 insertions(+), 363 deletions(-) delete mode 100644 deploy/crds/keda.sh_withpods_crd.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e1ad129777..d7de32eb43d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ## History +- [v2.0.0](#v200) - [v1.5.0](#v150) - [v1.4.1](#v141) - [v1.4.0](#v140) @@ -14,6 +15,12 @@ - [v1.1.0](#v110) - [v1.0.0](#v100) +## v2.0.0 + +### Breaking Changes + +- Remove `New()` and `Close()` from the interface of `service ExternalScaler` in `externalscaler.proto`. + ## v1.5.0 Learn more about our release in [our milestone](https://github.com/kedacore/keda/milestone/12). diff --git a/deploy/crds/keda.sh_withpods_crd.yaml b/deploy/crds/keda.sh_withpods_crd.yaml deleted file mode 100644 index a2ea199ffeb..00000000000 --- a/deploy/crds/keda.sh_withpods_crd.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: withpods.keda.sh -spec: - group: keda.sh - names: - kind: WithPod - listKind: WithPodList - plural: withpods - singular: withpod - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: WithPod is the shell that demonstrates how PodSpecable types - wrap a PodSpec. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: WithPodSpec is the shell around the PodSpecable within WithPod. - properties: - template: - $ref: '#/definitions/k8s.io~1api~1core~1v1~0PodTemplateSpec' - description: PodSpecable is implemented by types containing a PodTemplateSpec - in the manner of ReplicaSet, Deployment, DaemonSet, StatefulSet. - type: object - type: object - served: true - storage: true diff --git a/go.mod b/go.mod index b70f842d7d8..52cb90ecf4a 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,7 @@ require ( github.com/imdario/mergo v0.3.9 github.com/kubernetes-incubator/custom-metrics-apiserver v0.0.0-20200323093244-5046ce1afe6b github.com/lib/pq v1.3.0 + github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452 github.com/operator-framework/operator-sdk v0.17.1-0.20200501204426-0f43da11b8c0 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 diff --git a/go.sum b/go.sum index 7ff98d25a37..863190072ff 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,32 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.49.0/go.mod h1:hGvAdzcWNbyuxS3nWhD7H2cIJxjRRTRLQVB0bdputVY= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.55.0 h1:eoz/lYxKSL4CNAiaUJ0ZfD1J3bfMYbU5B3rwM1C1EIU= +cloud.google.com/go v0.55.0/go.mod h1:ZHmoY+/lIMNkN2+fBmuTiqZ4inFhvQad8ft7MT8IV5Y= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.3.0/go.mod h1:9IAwXhoyBJ7z9LcAwkj0/7NnPzYaPeZxxVp3zm+5IqA= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= @@ -170,9 +189,13 @@ github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tj github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= @@ -284,6 +307,10 @@ github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful-swagger12 v0.0.0-20170208215640-dcef7f557305 h1:2vAWk0wMCWb/pYiyat2rRZp5I5ZM+efPlagySNZ3JeM= github.com/emicklei/go-restful-swagger12 v0.0.0-20170208215640-dcef7f557305/go.mod h1:qr0VowGBT4CS4Q8vFF8BSeKz34PuqKGxs/L0IAQA9DQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -309,6 +336,8 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -417,10 +446,14 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -428,8 +461,18 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= @@ -454,8 +497,12 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -464,6 +511,7 @@ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= @@ -551,6 +599,7 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= @@ -678,6 +727,7 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go. github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452 h1:hOY53G+kBFhbYFpRVxHl5eS7laP6B1+Cq+Z9Dry1iMU= github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -941,6 +991,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= @@ -964,11 +1015,14 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1013,12 +1067,20 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -1026,14 +1088,19 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1066,16 +1133,25 @@ golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1083,8 +1159,11 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1119,19 +1198,31 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180805044716-cb6730876b98/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -1141,15 +1232,18 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190118193359-16909d206f00/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1159,6 +1253,7 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190813034749-528a2984e271/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1173,8 +1268,20 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191111182352-50fa39b762bc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200403190813-44a64ad78b9b h1:AFZdJUT7jJYXQEC29hYH/WZkoV7+KhwxQGmdZ19yYoY= golang.org/x/tools v0.0.0-20200403190813-44a64ad78b9b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1190,8 +1297,23 @@ gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40 gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.10.0 h1:7tmAxx3oKE98VMZ+SBZzvYYWRQ9HODBxmC8mXUsraSQ= google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1200,10 +1322,67 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191002211648-c459b9ce5143 h1:tikhlQEJeezbnu0Zcblj7g5vm/L7xt6g1vnfq8mRCS4= google.golang.org/genproto v0.0.0-20191002211648-c459b9ce5143/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.0 h1:2pJjwYOdkZ9HlN4sWRYBg9ttH5bCOlsueaM+b/oYjwo= +google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.0/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.20.1/go.mod h1:KqelGeouBkcbcuB3HCk4/YH2tmNLk6YSWA5LIWeI/lY= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc h1:TnonUr8u3himcMY0vSh23jFOXA+cnucl1gB6EQTReBI= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= @@ -1256,11 +1435,14 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= helm.sh/helm/v3 v3.2.0/go.mod h1:ZaXz/vzktgwjyGGFbUWtIQkscfE7WYoRGP2szqAFHR0= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= k8s.io/api v0.0.0-20190813020757-36bff7324fb7/go.mod h1:3Iy+myeAORNCLgjd/Xu9ebwN7Vh59Bw0vh9jhoX+V58= @@ -1364,6 +1546,7 @@ pack.ag/amqp v0.8.0/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= pack.ag/amqp v0.11.0/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= pack.ag/amqp v0.12.5 h1:WjH1KZ0hHZbT62nzDpvFCQD+jgSwRqj6FUOc2/GlqHM= pack.ag/amqp v0.12.5/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/scalers/external_scaler.go b/pkg/scalers/external_scaler.go index 4aff2dd62c3..424d9bf8ccd 100644 --- a/pkg/scalers/external_scaler.go +++ b/pkg/scalers/external_scaler.go @@ -3,6 +3,9 @@ package scalers import ( "context" "fmt" + "github.com/mitchellh/hashstructure" + "sync" + "time" pb "github.com/kedacore/keda/pkg/scalers/externalscaler" "google.golang.org/grpc" @@ -16,95 +19,95 @@ import ( ) type externalScaler struct { - metadata *externalScalerMetadata + metadata externalScalerMetadata scaledObjectRef pb.ScaledObjectRef - grpcClient pb.ExternalScalerClient - grpcConnection *grpc.ClientConn +} + +type externalPushScaler struct { + externalScaler } type externalScalerMetadata struct { scalerAddress string tlsCertFile string - metadata map[string]string + originalMetadata map[string]string } +type connectionGroup struct { + grpcConnection *grpc.ClientConn + waitGroup *sync.WaitGroup +} + +// a pool of connectionGroup per metadata hash +var connectionPool sync.Map + var externalLog = logf.Log.WithName("external_scaler") // NewExternalScaler creates a new external scaler - calls the GRPC interface // to create a new scaler -func NewExternalScaler(name, namespace string, resolvedEnv, metadata map[string]string) (Scaler, error) { - - meta, err := parseExternalScalerMetadata(metadata, resolvedEnv) +func NewExternalScaler(name, namespace string, metadata map[string]string) (Scaler, error) { + meta, err := parseExternalScalerMetadata(metadata) if err != nil { return nil, fmt.Errorf("error parsing external scaler metadata: %s", err) } - scaler := &externalScaler{ + return &externalScaler{ metadata: meta, scaledObjectRef: pb.ScaledObjectRef{ Name: name, Namespace: namespace, }, - } - - // TODO: Pass Context - ctx := context.Background() - - // Call GRPC Interface to parse metadata - err = scaler.getGRPCClient() - if err != nil { - return nil, err - } - - request := &pb.NewRequest{ - ScaledObjectRef: &scaler.scaledObjectRef, - Metadata: scaler.metadata.metadata, - } + }, nil +} - _, err = scaler.grpcClient.New(ctx, request) +func NewExternalPushScaler(name, namespace string, metadata map[string]string) (PushScaler, error) { + meta, err := parseExternalScalerMetadata(metadata) if err != nil { - return nil, err + return nil, fmt.Errorf("error parsing external scaler metadata: %s", err) } - return scaler, nil + return &externalPushScaler{ + externalScaler{ + metadata: meta, + scaledObjectRef: pb.ScaledObjectRef{ + Name: name, + Namespace: namespace, + ScalerMetadata: meta.originalMetadata, + }, + }, + }, nil } -func parseExternalScalerMetadata(metadata, resolvedEnv map[string]string) (*externalScalerMetadata, error) { - meta := externalScalerMetadata{} +func parseExternalScalerMetadata(metadata map[string]string) (externalScalerMetadata, error) { + meta := externalScalerMetadata{ + originalMetadata: metadata, + } // Check if scalerAddress is present if val, ok := metadata["scalerAddress"]; ok && val != "" { meta.scalerAddress = val } else { - return nil, fmt.Errorf("Scaler Address is a required field") + return meta, fmt.Errorf("scaler Address is a required field") } if val, ok := metadata["tlsCertFile"]; ok && val != "" { meta.tlsCertFile = val } - meta.metadata = make(map[string]string) - - // Add elements to metadata - for key, value := range metadata { - // Check if key is in resolved environment and resolve - if val, ok := resolvedEnv[value]; ok && val != "" { - meta.metadata[key] = val - } else { - meta.metadata[key] = value - } - } - - return &meta, nil + return meta, nil } // IsActive checks if there are any messages in the subscription func (s *externalScaler) IsActive(ctx context.Context) (bool, error) { + grpcClient, done, err := getClientForConnectionPool(s.metadata) + if err != nil { + return false, err + } + defer done() - // Call GRPC Interface to check if active - response, err := s.grpcClient.IsActive(ctx, &s.scaledObjectRef) + response, err := grpcClient.IsActive(ctx, &s.scaledObjectRef) if err != nil { - externalLog.Error(err, "error") + externalLog.Error(err, "error calling IsActive on external scaler") return false, err } @@ -112,36 +115,26 @@ func (s *externalScaler) IsActive(ctx context.Context) (bool, error) { } func (s *externalScaler) Close() error { - // Call GRPC Interface to close connection - - // TODO: Pass Context - ctx := context.Background() - - _, err := s.grpcClient.Close(ctx, &s.scaledObjectRef) - if err != nil { - externalLog.Error(err, "error") - return err - } - defer s.grpcConnection.Close() - return nil } // GetMetricSpecForScaling returns the metric spec for the HPA func (s *externalScaler) GetMetricSpecForScaling() []v2beta2.MetricSpec { + var result []v2beta2.MetricSpec - // TODO: Pass Context - ctx := context.Background() + grpcClient, done, err := getClientForConnectionPool(s.metadata) + if err != nil { + externalLog.Error(err, "error building grpc connection") + return result + } + defer done() - // Call GRPC Interface to get metric specs - response, err := s.grpcClient.GetMetricSpec(ctx, &s.scaledObjectRef) + response, err := grpcClient.GetMetricSpec(context.TODO(), &s.scaledObjectRef) if err != nil { externalLog.Error(err, "error") return nil } - var result []v2beta2.MetricSpec - for _, spec := range response.MetricSpecs { // Construct the target subscription size as a quantity qty := resource.NewQuantity(int64(spec.TargetSize), resource.DecimalSI) @@ -170,16 +163,19 @@ func (s *externalScaler) GetMetricSpecForScaling() []v2beta2.MetricSpec { // GetMetrics connects calls the gRPC interface to get the metrics with a specific name func (s *externalScaler) GetMetrics(ctx context.Context, metricName string, metricSelector labels.Selector) ([]external_metrics.ExternalMetricValue, error) { - var metrics []external_metrics.ExternalMetricValue - // Call GRPC Interface to get metric specs + grpcClient, done, err := getClientForConnectionPool(s.metadata) + if err != nil { + return metrics, err + } + defer done() request := &pb.GetMetricsRequest{ MetricName: metricName, ScaledObjectRef: &s.scaledObjectRef, } - response, err := s.grpcClient.GetMetrics(ctx, request) + response, err := grpcClient.GetMetrics(ctx, request) if err != nil { externalLog.Error(err, "error") return []external_metrics.ExternalMetricValue{}, err @@ -198,27 +194,122 @@ func (s *externalScaler) GetMetrics(ctx context.Context, metricName string, metr return metrics, nil } -// getGRPCClient creates a new gRPC client -func (s *externalScaler) getGRPCClient() error { +// handleIsActiveStream is the only writer to the active channel and will close it on return. +func (s *externalPushScaler) Run(ctx context.Context, active chan<- bool) { + defer close(active) + // It's possible for the connection to get terminated anytime, we need to run this in a retry loop + runWithLog := func() { + grpcClient, done, err := getClientForConnectionPool(s.metadata) + if err != nil { + externalLog.Error(err, "error running internalRun") + return + } + handleIsActiveStream(ctx, s.scaledObjectRef, grpcClient, active) + done() + } + + // retry on error from runWithLog() starting by 2 sec backing off * 2 with a max of 1 minute + retryDuration := time.Second * 2 + retryBackoff := func() <-chan time.Time { + ch := time.After(retryDuration) + retryDuration *= time.Second * 2 + if retryDuration > time.Minute*1 { + retryDuration = time.Minute * 1 + } + return ch + } - var err error + // start the first run without delay + runWithLog() - if s.metadata.tlsCertFile != "" { - certFile := fmt.Sprintf("/grpccerts/%s", s.metadata.tlsCertFile) - creds, err := credentials.NewClientTLSFromFile(certFile, "") + for { + select { + case <-ctx.Done(): + return + case <-retryBackoff(): + runWithLog() + } + } +} + +// handleIsActiveStream calls blocks on a stream call from the GRPC server. It'll only terminate on error, stream completion, or ctx cancellation. +func handleIsActiveStream(ctx context.Context, scaledObjectRef pb.ScaledObjectRef, grpcClient pb.ExternalScalerClient, active chan<- bool) error { + stream, err := grpcClient.StreamIsActive(ctx, &scaledObjectRef) + if err != nil { + return err + } + + for { + resp, err := stream.Recv() if err != nil { return err } - s.grpcConnection, err = grpc.Dial(s.metadata.scalerAddress, grpc.WithTransportCredentials(creds)) - } else { - s.grpcConnection, err = grpc.Dial(s.metadata.scalerAddress, grpc.WithInsecure()) + + active <- resp.Result + } +} + +var connectionPoolMutex sync.Mutex + +// getClientForConnectionPool returns a grpcClient and a done() Func. The done() function must be called once the client is no longer +// in use to clean up the shared grpc.ClientConn +func getClientForConnectionPool(metadata externalScalerMetadata) (pb.ExternalScalerClient, func(), error) { + connectionPoolMutex.Lock() + defer connectionPoolMutex.Unlock() + + buildGRPCConnection := func(metadata externalScalerMetadata) (*grpc.ClientConn, error) { + if metadata.tlsCertFile != "" { + creds, err := credentials.NewClientTLSFromFile(metadata.tlsCertFile, "") + if err != nil { + return nil, err + } + return grpc.Dial(metadata.scalerAddress, grpc.WithTransportCredentials(creds)) + } + + return grpc.Dial(metadata.scalerAddress, grpc.WithInsecure()) } + // create a unique key per-metadata. If scaledObjects share the same connection properties + // in the metadata, they will share the same grpc.ClientConn + key, err := hashstructure.Hash(metadata, nil) if err != nil { - return fmt.Errorf("cannot connect to external scaler over grpc interface: %s", err) + return nil, nil, err } - s.grpcClient = pb.NewExternalScalerClient(s.grpcConnection) + if i, ok := connectionPool.Load(key); ok { + if connGroup, ok := i.(*connectionGroup); ok { + connGroup.waitGroup.Add(1) + return pb.NewExternalScalerClient(connGroup.grpcConnection), func() { + connGroup.waitGroup.Done() + }, nil + } + } - return nil + conn, err := buildGRPCConnection(metadata) + if err != nil { + return nil, nil, err + } + + waitGroup := &sync.WaitGroup{} + waitGroup.Add(1) + connGroup := connectionGroup{ + grpcConnection: conn, + waitGroup: waitGroup, + } + + connectionPool.Store(key, connGroup) + + go func() { + // clean up goroutine. + // once all waitGroup is done, remove the connection from the pool and Close() grpc.ClientConn + connGroup.waitGroup.Wait() + connectionPoolMutex.Lock() + defer connectionPoolMutex.Unlock() + connectionPool.Delete(key) + connGroup.grpcConnection.Close() + }() + + return pb.NewExternalScalerClient(connGroup.grpcConnection), func() { + connGroup.waitGroup.Done() + }, nil } diff --git a/pkg/scalers/external_scaler_test.go b/pkg/scalers/external_scaler_test.go index f8e948e2024..304c2979a6d 100644 --- a/pkg/scalers/external_scaler_test.go +++ b/pkg/scalers/external_scaler_test.go @@ -1,7 +1,16 @@ package scalers import ( + "context" + "fmt" + pb "github.com/kedacore/keda/pkg/scalers/externalscaler" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "net" + "sync/atomic" "testing" + "time" ) var testExternalScalerResolvedEnv map[string]string @@ -21,7 +30,7 @@ var testExternalScalerMetadata = []parseExternalScalerMetadataTestData{ func TestExternalScalerParseMetadata(t *testing.T) { for _, testData := range testExternalScalerMetadata { - _, err := parseExternalScalerMetadata(testData.metadata, testExternalScalerResolvedEnv) + _, err := parseExternalScalerMetadata(testData.metadata) if err != nil && !testData.isError { t.Error("Expected success but got error", err) } @@ -30,3 +39,136 @@ func TestExternalScalerParseMetadata(t *testing.T) { } } } + +func TestExternalPushScaler_Run(t *testing.T) { + const serverCount = 5 + const iterationCount = 500 + + servers := createGRPCServers(serverCount, t) + replyCh := createIsActiveChannels(serverCount * iterationCount) + + // we will send serverCount * iterationCount 'isActiveResponse' and expect resultCount == serverCount * iterationCount + var resultCount int64 + + ctx, cancel := context.WithCancel(context.Background()) + for i := 0; i < serverCount*iterationCount; i++ { + id := i % serverCount + pushScaler, _ := NewExternalPushScaler("app", "namespace", map[string]string{"scalerAddress": servers[id].address}) + go pushScaler.Run(ctx, replyCh[i]) + } + + // scaler consumer + for i, ch := range replyCh { + go func(c chan bool, id int) { + for msg := range c { + if msg { + atomic.AddInt64(&resultCount, 1) + } + } + }(ch, i) + } + + // producer + for _, s := range servers { + go func(c chan bool) { + for i := 0; i < iterationCount; i++ { + c <- true + } + }(s.publish) + } + + retries := 0 + defer cancel() + for { + select { + case <-time.After(time.Second * 1): + if resultCount == serverCount*iterationCount { + t.Logf("resultCount == %d", resultCount) + return + } + + retries++ + if retries > 10 { + t.Fatalf("Expected resultCount to be %d after %d retries, but got %d", serverCount*iterationCount, retries, resultCount) + return + } + } + } +} + +type testServer struct { + grpcServer *grpc.Server + address string + publish chan bool +} + +func createGRPCServers(count int, t *testing.T) []testServer { + result := make([]testServer, 0, count) + + for i := 0; i < count; i++ { + grpcServer := grpc.NewServer() + address := fmt.Sprintf("127.0.0.1:%d", 5050+i) + lis, _ := net.Listen("tcp", address) + activeCh := make(chan bool) + pb.RegisterExternalScalerServer(grpcServer, &testExternalScaler{ + t: t, + active: activeCh, + }) + + go func() { + if err := grpcServer.Serve(lis); err != nil { + t.Error(err, "error from grpcServer") + } + }() + + result = append(result, testServer{ + grpcServer: grpcServer, + address: address, + publish: activeCh, + }) + } + + return result +} + +func createIsActiveChannels(count int) []chan bool { + result := make([]chan bool, 0, count) + for i := 0; i < count; i++ { + result = append(result, make(chan bool)) + } + + return result +} + +type testExternalScaler struct { + t *testing.T + active chan bool +} + +func (e *testExternalScaler) IsActive(context.Context, *pb.ScaledObjectRef) (*pb.IsActiveResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method IsActive not implemented") +} +func (e *testExternalScaler) StreamIsActive(_ *pb.ScaledObjectRef, epsServer pb.ExternalScaler_StreamIsActiveServer) error { + for { + select { + case <-epsServer.Context().Done(): + // the call completed? exit + return nil + case i := <-e.active: + err := epsServer.Send(&pb.IsActiveResponse{ + Result: i, + }) + if err != nil { + e.t.Error(err) + } + } + } +} + +func (e *testExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*pb.GetMetricSpecResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMetricSpec not implemented") +} + +func (e *testExternalScaler) GetMetrics(context.Context, *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMetrics not implemented") +} diff --git a/pkg/scalers/externalscaler/externalscaler.pb.go b/pkg/scalers/externalscaler/externalscaler.pb.go index 6263daec943..c01bc76e674 100644 --- a/pkg/scalers/externalscaler/externalscaler.pb.go +++ b/pkg/scalers/externalscaler/externalscaler.pb.go @@ -7,7 +7,6 @@ import ( context "context" fmt "fmt" proto "github.com/golang/protobuf/proto" - empty "github.com/golang/protobuf/ptypes/empty" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -26,11 +25,12 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type ScaledObjectRef struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` + ScalerMetadata map[string]string `protobuf:"bytes,3,rep,name=scalerMetadata,proto3" json:"scalerMetadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ScaledObjectRef) Reset() { *m = ScaledObjectRef{} } @@ -72,49 +72,9 @@ func (m *ScaledObjectRef) GetNamespace() string { return "" } -type NewRequest struct { - ScaledObjectRef *ScaledObjectRef `protobuf:"bytes,1,opt,name=scaledObjectRef,proto3" json:"scaledObjectRef,omitempty"` - Metadata map[string]string `protobuf:"bytes,2,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *NewRequest) Reset() { *m = NewRequest{} } -func (m *NewRequest) String() string { return proto.CompactTextString(m) } -func (*NewRequest) ProtoMessage() {} -func (*NewRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3d382708546499d1, []int{1} -} - -func (m *NewRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_NewRequest.Unmarshal(m, b) -} -func (m *NewRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_NewRequest.Marshal(b, m, deterministic) -} -func (m *NewRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_NewRequest.Merge(m, src) -} -func (m *NewRequest) XXX_Size() int { - return xxx_messageInfo_NewRequest.Size(m) -} -func (m *NewRequest) XXX_DiscardUnknown() { - xxx_messageInfo_NewRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_NewRequest proto.InternalMessageInfo - -func (m *NewRequest) GetScaledObjectRef() *ScaledObjectRef { - if m != nil { - return m.ScaledObjectRef - } - return nil -} - -func (m *NewRequest) GetMetadata() map[string]string { +func (m *ScaledObjectRef) GetScalerMetadata() map[string]string { if m != nil { - return m.Metadata + return m.ScalerMetadata } return nil } @@ -130,7 +90,7 @@ func (m *IsActiveResponse) Reset() { *m = IsActiveResponse{} } func (m *IsActiveResponse) String() string { return proto.CompactTextString(m) } func (*IsActiveResponse) ProtoMessage() {} func (*IsActiveResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3d382708546499d1, []int{2} + return fileDescriptor_3d382708546499d1, []int{1} } func (m *IsActiveResponse) XXX_Unmarshal(b []byte) error { @@ -169,7 +129,7 @@ func (m *GetMetricSpecResponse) Reset() { *m = GetMetricSpecResponse{} } func (m *GetMetricSpecResponse) String() string { return proto.CompactTextString(m) } func (*GetMetricSpecResponse) ProtoMessage() {} func (*GetMetricSpecResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3d382708546499d1, []int{3} + return fileDescriptor_3d382708546499d1, []int{2} } func (m *GetMetricSpecResponse) XXX_Unmarshal(b []byte) error { @@ -209,7 +169,7 @@ func (m *MetricSpec) Reset() { *m = MetricSpec{} } func (m *MetricSpec) String() string { return proto.CompactTextString(m) } func (*MetricSpec) ProtoMessage() {} func (*MetricSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_3d382708546499d1, []int{4} + return fileDescriptor_3d382708546499d1, []int{3} } func (m *MetricSpec) XXX_Unmarshal(b []byte) error { @@ -256,7 +216,7 @@ func (m *GetMetricsRequest) Reset() { *m = GetMetricsRequest{} } func (m *GetMetricsRequest) String() string { return proto.CompactTextString(m) } func (*GetMetricsRequest) ProtoMessage() {} func (*GetMetricsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_3d382708546499d1, []int{5} + return fileDescriptor_3d382708546499d1, []int{4} } func (m *GetMetricsRequest) XXX_Unmarshal(b []byte) error { @@ -302,7 +262,7 @@ func (m *GetMetricsResponse) Reset() { *m = GetMetricsResponse{} } func (m *GetMetricsResponse) String() string { return proto.CompactTextString(m) } func (*GetMetricsResponse) ProtoMessage() {} func (*GetMetricsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3d382708546499d1, []int{6} + return fileDescriptor_3d382708546499d1, []int{5} } func (m *GetMetricsResponse) XXX_Unmarshal(b []byte) error { @@ -342,7 +302,7 @@ func (m *MetricValue) Reset() { *m = MetricValue{} } func (m *MetricValue) String() string { return proto.CompactTextString(m) } func (*MetricValue) ProtoMessage() {} func (*MetricValue) Descriptor() ([]byte, []int) { - return fileDescriptor_3d382708546499d1, []int{7} + return fileDescriptor_3d382708546499d1, []int{6} } func (m *MetricValue) XXX_Unmarshal(b []byte) error { @@ -379,8 +339,7 @@ func (m *MetricValue) GetMetricValue() int64 { func init() { proto.RegisterType((*ScaledObjectRef)(nil), "externalscaler.ScaledObjectRef") - proto.RegisterType((*NewRequest)(nil), "externalscaler.NewRequest") - proto.RegisterMapType((map[string]string)(nil), "externalscaler.NewRequest.MetadataEntry") + proto.RegisterMapType((map[string]string)(nil), "externalscaler.ScaledObjectRef.ScalerMetadataEntry") proto.RegisterType((*IsActiveResponse)(nil), "externalscaler.IsActiveResponse") proto.RegisterType((*GetMetricSpecResponse)(nil), "externalscaler.GetMetricSpecResponse") proto.RegisterType((*MetricSpec)(nil), "externalscaler.MetricSpec") @@ -392,38 +351,35 @@ func init() { func init() { proto.RegisterFile("externalscaler.proto", fileDescriptor_3d382708546499d1) } var fileDescriptor_3d382708546499d1 = []byte{ - // 484 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x53, 0x4d, 0x6f, 0xd3, 0x40, - 0x10, 0x8d, 0x63, 0x5a, 0xa5, 0x13, 0xfa, 0xc1, 0xa8, 0x54, 0x91, 0x8b, 0x20, 0xac, 0x84, 0x14, - 0x71, 0x70, 0xa5, 0x70, 0x41, 0x14, 0x09, 0x41, 0x89, 0x50, 0x25, 0x9a, 0x48, 0x1b, 0xa5, 0x12, - 0xc7, 0x8d, 0x3b, 0x8d, 0x02, 0x4e, 0x6c, 0xbc, 0xeb, 0x96, 0x70, 0xe0, 0x57, 0xf0, 0xeb, 0xf8, - 0x35, 0xc8, 0xeb, 0xef, 0x55, 0x43, 0xb8, 0xf4, 0x64, 0xef, 0xbc, 0x37, 0xb3, 0xf3, 0xde, 0xcc, - 0xc2, 0x21, 0xfd, 0x50, 0x14, 0x2d, 0x85, 0x2f, 0x3d, 0xe1, 0x53, 0xe4, 0x86, 0x51, 0xa0, 0x02, - 0xdc, 0xab, 0x47, 0x9d, 0xe3, 0x59, 0x10, 0xcc, 0x7c, 0x3a, 0xd1, 0xe8, 0x34, 0xbe, 0x3e, 0xa1, - 0x45, 0xa8, 0x56, 0x29, 0x99, 0x9d, 0xc1, 0xfe, 0x38, 0xa1, 0x5d, 0x8d, 0xa6, 0x5f, 0xc9, 0x53, - 0x9c, 0xae, 0x11, 0xe1, 0xc1, 0x52, 0x2c, 0xa8, 0x63, 0x75, 0xad, 0xde, 0x0e, 0xd7, 0xff, 0xf8, - 0x04, 0x76, 0x92, 0xaf, 0x0c, 0x85, 0x47, 0x9d, 0xa6, 0x06, 0xca, 0x00, 0xfb, 0x63, 0x01, 0x0c, - 0xe9, 0x96, 0xd3, 0xf7, 0x98, 0xa4, 0xc2, 0x73, 0xd8, 0x97, 0xf5, 0x9a, 0xba, 0x56, 0xbb, 0xff, - 0xcc, 0x35, 0x1a, 0x36, 0xae, 0xe6, 0x66, 0x1e, 0x7e, 0x84, 0xd6, 0x82, 0x94, 0xb8, 0x12, 0x4a, - 0x74, 0x9a, 0x5d, 0xbb, 0xd7, 0xee, 0xf7, 0xcc, 0x1a, 0xe5, 0xc5, 0xee, 0x45, 0x46, 0x1d, 0x2c, - 0x55, 0xb4, 0xe2, 0x45, 0xa6, 0x73, 0x0a, 0xbb, 0x35, 0x08, 0x0f, 0xc0, 0xfe, 0x46, 0xab, 0x4c, - 0x61, 0xf2, 0x8b, 0x87, 0xb0, 0x75, 0x23, 0xfc, 0x38, 0x17, 0x97, 0x1e, 0xde, 0x34, 0x5f, 0x5b, - 0xec, 0x25, 0x1c, 0x9c, 0xcb, 0xf7, 0x9e, 0x9a, 0xdf, 0x10, 0x27, 0x19, 0x06, 0x4b, 0x49, 0x78, - 0x04, 0xdb, 0x11, 0xc9, 0xd8, 0x57, 0xba, 0x44, 0x8b, 0x67, 0x27, 0x36, 0x81, 0xc7, 0x9f, 0x48, - 0x5d, 0x90, 0x8a, 0xe6, 0xde, 0x38, 0x24, 0xaf, 0x48, 0x78, 0x0b, 0xed, 0x45, 0x11, 0x95, 0x1d, - 0x4b, 0x4b, 0x71, 0x4c, 0x29, 0x95, 0xc4, 0x2a, 0x9d, 0x7d, 0x06, 0x28, 0x21, 0x7c, 0x0a, 0x90, - 0x82, 0xc3, 0x72, 0x4a, 0x95, 0x48, 0x82, 0x2b, 0x11, 0xcd, 0x48, 0x8d, 0xe7, 0x3f, 0x53, 0x3d, - 0x36, 0xaf, 0x44, 0xd8, 0x2f, 0x78, 0x54, 0x34, 0x29, 0xef, 0x61, 0x66, 0xf5, 0xfe, 0x9a, 0x66, - 0x7f, 0x6c, 0x02, 0x58, 0xbd, 0x3f, 0x73, 0xe8, 0x1d, 0x3c, 0x4c, 0x39, 0x97, 0x89, 0xf3, 0xb9, - 0x45, 0xc7, 0x77, 0x5b, 0xa4, 0x39, 0xbc, 0x96, 0xc0, 0x46, 0xd0, 0xae, 0x80, 0x1b, 0x5d, 0xea, - 0xe6, 0x13, 0xb9, 0x2c, 0xc6, 0x6e, 0xf3, 0x6a, 0xa8, 0xff, 0xdb, 0x86, 0xbd, 0x41, 0x76, 0xbb, - 0x16, 0x1d, 0xe1, 0x29, 0xd8, 0x43, 0xba, 0x45, 0x67, 0xfd, 0x0e, 0x3a, 0x47, 0x6e, 0xfa, 0xdc, - 0xdc, 0xfc, 0xb9, 0xb9, 0x83, 0xe4, 0xb9, 0xb1, 0x06, 0x8e, 0xa0, 0x95, 0x2f, 0x12, 0x6e, 0x72, - 0xd5, 0xe9, 0x9a, 0x04, 0x73, 0x07, 0x59, 0x03, 0xbf, 0xc0, 0x6e, 0x6d, 0xdb, 0x36, 0x57, 0x7d, - 0x61, 0x12, 0xee, 0xdc, 0x56, 0xd6, 0xc0, 0x09, 0x40, 0x39, 0x23, 0x7c, 0xbe, 0x36, 0x2d, 0xdf, - 0x1f, 0x87, 0xfd, 0x8b, 0x52, 0x94, 0xfd, 0x00, 0x5b, 0x67, 0x7e, 0x20, 0xff, 0x43, 0xff, 0x5a, - 0x1b, 0xa7, 0xdb, 0x3a, 0xf2, 0xea, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x69, 0x62, 0xa3, 0x07, - 0xfd, 0x04, 0x00, 0x00, + // 442 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xdf, 0x6b, 0xd4, 0x40, + 0x10, 0x6e, 0x12, 0x2d, 0xed, 0x44, 0xd3, 0x73, 0xac, 0x12, 0xa2, 0x68, 0x5c, 0x10, 0x8a, 0x0f, + 0x87, 0x5c, 0x5f, 0x44, 0x05, 0xa9, 0x50, 0xa4, 0x60, 0x3d, 0xd8, 0x70, 0x15, 0xf5, 0x69, 0x9b, + 0x8e, 0x72, 0x9a, 0xcb, 0xc5, 0xdd, 0xbd, 0xe2, 0xf9, 0xe0, 0x3f, 0xeb, 0xab, 0x7f, 0x84, 0xe4, + 0x77, 0xb2, 0x9c, 0xe6, 0xa5, 0x4f, 0xd9, 0x9d, 0xf9, 0xe6, 0xdb, 0x99, 0x6f, 0x3e, 0x02, 0xfb, + 0xf4, 0x43, 0x93, 0x4c, 0x45, 0xa2, 0x62, 0x91, 0x90, 0x1c, 0x67, 0x72, 0xa9, 0x97, 0xe8, 0xf5, + 0xa3, 0xec, 0xb7, 0x05, 0x7b, 0x51, 0x7e, 0xbc, 0x98, 0x9e, 0x7f, 0xa5, 0x58, 0x73, 0xfa, 0x8c, + 0x08, 0xd7, 0x52, 0xb1, 0x20, 0xdf, 0x0a, 0xad, 0x83, 0x5d, 0x5e, 0x9c, 0xf1, 0x3e, 0xec, 0xe6, + 0x5f, 0x95, 0x89, 0x98, 0x7c, 0xbb, 0x48, 0xb4, 0x01, 0xfc, 0x04, 0x5e, 0xc9, 0x77, 0x4a, 0x5a, + 0x5c, 0x08, 0x2d, 0x7c, 0x27, 0x74, 0x0e, 0xdc, 0xc9, 0xe1, 0xd8, 0x68, 0xc2, 0x78, 0xaa, 0xbc, + 0x37, 0x55, 0xc7, 0xa9, 0x96, 0x6b, 0x6e, 0x50, 0x05, 0x47, 0x70, 0x7b, 0x03, 0x0c, 0x47, 0xe0, + 0x7c, 0xa3, 0x75, 0xd5, 0x64, 0x7e, 0xc4, 0x7d, 0xb8, 0x7e, 0x29, 0x92, 0x55, 0xdd, 0x5f, 0x79, + 0x79, 0x6e, 0x3f, 0xb3, 0xd8, 0x13, 0x18, 0x9d, 0xa8, 0xa3, 0x58, 0xcf, 0x2f, 0x89, 0x93, 0xca, + 0x96, 0xa9, 0x22, 0xbc, 0x0b, 0xdb, 0x92, 0xd4, 0x2a, 0xd1, 0x05, 0xc5, 0x0e, 0xaf, 0x6e, 0x6c, + 0x06, 0x77, 0xde, 0x90, 0x3e, 0x25, 0x2d, 0xe7, 0x71, 0x94, 0x51, 0xdc, 0x14, 0xbc, 0x04, 0x77, + 0xd1, 0x44, 0x95, 0x6f, 0x15, 0x13, 0x06, 0xe6, 0x84, 0x9d, 0xc2, 0x2e, 0x9c, 0xbd, 0x05, 0x68, + 0x53, 0xf8, 0x00, 0xa0, 0x4c, 0xbe, 0x6b, 0x85, 0xee, 0x44, 0xf2, 0xbc, 0x16, 0xf2, 0x0b, 0xe9, + 0x68, 0xfe, 0xb3, 0x9c, 0xc7, 0xe1, 0x9d, 0x08, 0xfb, 0x05, 0xb7, 0x9a, 0x26, 0x15, 0xa7, 0xef, + 0x2b, 0x52, 0x1a, 0x4f, 0x60, 0x4f, 0xf5, 0xf5, 0x2d, 0x98, 0xdd, 0xc9, 0xc3, 0x81, 0x35, 0x70, + 0xb3, 0xce, 0xe8, 0xcf, 0x36, 0xfb, 0x63, 0x33, 0xc0, 0xee, 0xfb, 0x95, 0x42, 0xaf, 0xe0, 0x46, + 0x89, 0x39, 0xcb, 0x95, 0xaf, 0x25, 0xba, 0xb7, 0x59, 0xa2, 0x02, 0xc3, 0x7b, 0x05, 0x6c, 0x0a, + 0x6e, 0x27, 0x39, 0xa8, 0x52, 0x58, 0x6f, 0xe4, 0xac, 0x59, 0xbb, 0xc3, 0xbb, 0xa1, 0xc9, 0x1f, + 0x1b, 0xbc, 0xe3, 0xea, 0xf5, 0xd2, 0x44, 0x38, 0x85, 0x9d, 0xda, 0x0b, 0x38, 0x24, 0x4c, 0x10, + 0x9a, 0x00, 0xd3, 0x46, 0x6c, 0x0b, 0xdf, 0x83, 0x17, 0x69, 0x49, 0x62, 0x71, 0xa5, 0xb4, 0x4f, + 0x2d, 0xfc, 0x00, 0x37, 0x7b, 0x4e, 0x1c, 0xe6, 0x7d, 0x6c, 0x02, 0x36, 0x3a, 0x99, 0x6d, 0xe1, + 0x0c, 0xa0, 0xdd, 0x1f, 0x3e, 0xfa, 0x67, 0x59, 0xed, 0xad, 0x80, 0xfd, 0x0f, 0x52, 0xd3, 0xbe, + 0xc6, 0x8f, 0xa3, 0xf1, 0x8b, 0x3e, 0xf0, 0x7c, 0xbb, 0xf8, 0xf1, 0x1c, 0xfe, 0x0d, 0x00, 0x00, + 0xff, 0xff, 0xdc, 0xf1, 0x73, 0xce, 0x90, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -438,11 +394,10 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ExternalScalerClient interface { - New(ctx context.Context, in *NewRequest, opts ...grpc.CallOption) (*empty.Empty, error) IsActive(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*IsActiveResponse, error) + StreamIsActive(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (ExternalScaler_StreamIsActiveClient, error) GetMetricSpec(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*GetMetricSpecResponse, error) GetMetrics(ctx context.Context, in *GetMetricsRequest, opts ...grpc.CallOption) (*GetMetricsResponse, error) - Close(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*empty.Empty, error) } type externalScalerClient struct { @@ -453,22 +408,45 @@ func NewExternalScalerClient(cc *grpc.ClientConn) ExternalScalerClient { return &externalScalerClient{cc} } -func (c *externalScalerClient) New(ctx context.Context, in *NewRequest, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/externalscaler.ExternalScaler/New", in, out, opts...) +func (c *externalScalerClient) IsActive(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*IsActiveResponse, error) { + out := new(IsActiveResponse) + err := c.cc.Invoke(ctx, "/externalscaler.ExternalScaler/IsActive", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *externalScalerClient) IsActive(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*IsActiveResponse, error) { - out := new(IsActiveResponse) - err := c.cc.Invoke(ctx, "/externalscaler.ExternalScaler/IsActive", in, out, opts...) +func (c *externalScalerClient) StreamIsActive(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (ExternalScaler_StreamIsActiveClient, error) { + stream, err := c.cc.NewStream(ctx, &_ExternalScaler_serviceDesc.Streams[0], "/externalscaler.ExternalScaler/StreamIsActive", opts...) if err != nil { return nil, err } - return out, nil + x := &externalScalerStreamIsActiveClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type ExternalScaler_StreamIsActiveClient interface { + Recv() (*IsActiveResponse, error) + grpc.ClientStream +} + +type externalScalerStreamIsActiveClient struct { + grpc.ClientStream +} + +func (x *externalScalerStreamIsActiveClient) Recv() (*IsActiveResponse, error) { + m := new(IsActiveResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil } func (c *externalScalerClient) GetMetricSpec(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*GetMetricSpecResponse, error) { @@ -489,66 +467,35 @@ func (c *externalScalerClient) GetMetrics(ctx context.Context, in *GetMetricsReq return out, nil } -func (c *externalScalerClient) Close(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/externalscaler.ExternalScaler/Close", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - // ExternalScalerServer is the server API for ExternalScaler service. type ExternalScalerServer interface { - New(context.Context, *NewRequest) (*empty.Empty, error) IsActive(context.Context, *ScaledObjectRef) (*IsActiveResponse, error) + StreamIsActive(*ScaledObjectRef, ExternalScaler_StreamIsActiveServer) error GetMetricSpec(context.Context, *ScaledObjectRef) (*GetMetricSpecResponse, error) GetMetrics(context.Context, *GetMetricsRequest) (*GetMetricsResponse, error) - Close(context.Context, *ScaledObjectRef) (*empty.Empty, error) } // UnimplementedExternalScalerServer can be embedded to have forward compatible implementations. type UnimplementedExternalScalerServer struct { } -func (*UnimplementedExternalScalerServer) New(ctx context.Context, req *NewRequest) (*empty.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method New not implemented") -} func (*UnimplementedExternalScalerServer) IsActive(ctx context.Context, req *ScaledObjectRef) (*IsActiveResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method IsActive not implemented") } +func (*UnimplementedExternalScalerServer) StreamIsActive(req *ScaledObjectRef, srv ExternalScaler_StreamIsActiveServer) error { + return status.Errorf(codes.Unimplemented, "method StreamIsActive not implemented") +} func (*UnimplementedExternalScalerServer) GetMetricSpec(ctx context.Context, req *ScaledObjectRef) (*GetMetricSpecResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetMetricSpec not implemented") } func (*UnimplementedExternalScalerServer) GetMetrics(ctx context.Context, req *GetMetricsRequest) (*GetMetricsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetMetrics not implemented") } -func (*UnimplementedExternalScalerServer) Close(ctx context.Context, req *ScaledObjectRef) (*empty.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Close not implemented") -} func RegisterExternalScalerServer(s *grpc.Server, srv ExternalScalerServer) { s.RegisterService(&_ExternalScaler_serviceDesc, srv) } -func _ExternalScaler_New_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(NewRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ExternalScalerServer).New(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/externalscaler.ExternalScaler/New", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ExternalScalerServer).New(ctx, req.(*NewRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _ExternalScaler_IsActive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ScaledObjectRef) if err := dec(in); err != nil { @@ -567,6 +514,27 @@ func _ExternalScaler_IsActive_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _ExternalScaler_StreamIsActive_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ScaledObjectRef) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ExternalScalerServer).StreamIsActive(m, &externalScalerStreamIsActiveServer{stream}) +} + +type ExternalScaler_StreamIsActiveServer interface { + Send(*IsActiveResponse) error + grpc.ServerStream +} + +type externalScalerStreamIsActiveServer struct { + grpc.ServerStream +} + +func (x *externalScalerStreamIsActiveServer) Send(m *IsActiveResponse) error { + return x.ServerStream.SendMsg(m) +} + func _ExternalScaler_GetMetricSpec_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ScaledObjectRef) if err := dec(in); err != nil { @@ -603,32 +571,10 @@ func _ExternalScaler_GetMetrics_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } -func _ExternalScaler_Close_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ScaledObjectRef) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ExternalScalerServer).Close(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/externalscaler.ExternalScaler/Close", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ExternalScalerServer).Close(ctx, req.(*ScaledObjectRef)) - } - return interceptor(ctx, in, info, handler) -} - var _ExternalScaler_serviceDesc = grpc.ServiceDesc{ ServiceName: "externalscaler.ExternalScaler", HandlerType: (*ExternalScalerServer)(nil), Methods: []grpc.MethodDesc{ - { - MethodName: "New", - Handler: _ExternalScaler_New_Handler, - }, { MethodName: "IsActive", Handler: _ExternalScaler_IsActive_Handler, @@ -641,11 +587,13 @@ var _ExternalScaler_serviceDesc = grpc.ServiceDesc{ MethodName: "GetMetrics", Handler: _ExternalScaler_GetMetrics_Handler, }, + }, + Streams: []grpc.StreamDesc{ { - MethodName: "Close", - Handler: _ExternalScaler_Close_Handler, + StreamName: "StreamIsActive", + Handler: _ExternalScaler_StreamIsActive_Handler, + ServerStreams: true, }, }, - Streams: []grpc.StreamDesc{}, Metadata: "externalscaler.proto", } diff --git a/pkg/scalers/externalscaler/externalscaler.proto b/pkg/scalers/externalscaler/externalscaler.proto index b72d2242c10..1df4495455a 100644 --- a/pkg/scalers/externalscaler/externalscaler.proto +++ b/pkg/scalers/externalscaler/externalscaler.proto @@ -1,25 +1,19 @@ syntax = "proto3"; package externalscaler; - -import "google/protobuf/empty.proto"; +option go_package = ".;externalscaler"; service ExternalScaler { - rpc New(NewRequest) returns (google.protobuf.Empty) {} rpc IsActive(ScaledObjectRef) returns (IsActiveResponse) {} + rpc StreamIsActive(ScaledObjectRef) returns (stream IsActiveResponse) {} rpc GetMetricSpec(ScaledObjectRef) returns (GetMetricSpecResponse) {} rpc GetMetrics(GetMetricsRequest) returns (GetMetricsResponse) {} - rpc Close(ScaledObjectRef) returns (google.protobuf.Empty) {} } message ScaledObjectRef { string name = 1; string namespace = 2; -} - -message NewRequest { - ScaledObjectRef scaledObjectRef = 1; - map metadata = 2; + map scalerMetadata = 3; } message IsActiveResponse { diff --git a/pkg/scalers/scaler.go b/pkg/scalers/scaler.go index c11755d157a..0357dc41f17 100644 --- a/pkg/scalers/scaler.go +++ b/pkg/scalers/scaler.go @@ -22,3 +22,10 @@ type Scaler interface { // Close any resources that need disposing when scaler is no longer used or destroyed Close() error } + +type PushScaler interface { + Scaler + + // Run is the only writer to the active channel and must close it once done. + Run(ctx context.Context, active chan<- bool) +} diff --git a/pkg/scaling/executor/scale_executor.go b/pkg/scaling/executor/scale_executor.go index b0c33beecbd..8e88b3da9ce 100644 --- a/pkg/scaling/executor/scale_executor.go +++ b/pkg/scaling/executor/scale_executor.go @@ -3,11 +3,8 @@ package executor import ( "context" "fmt" - - kedav1alpha1 "github.com/kedacore/keda/pkg/apis/keda/v1alpha1" - "github.com/kedacore/keda/pkg/scalers" - "github.com/go-logr/logr" + kedav1alpha1 "github.com/kedacore/keda/pkg/apis/keda/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/scale" @@ -21,8 +18,8 @@ const ( ) type ScaleExecutor interface { - RequestJobScale(ctx context.Context, scalers []scalers.Scaler, scaledObject *kedav1alpha1.ScaledJob) - RequestScale(ctx context.Context, scalers []scalers.Scaler, scaledObject *kedav1alpha1.ScaledObject) + RequestJobScale(ctx context.Context, scaledJob *kedav1alpha1.ScaledJob, isActive bool, scaleTo int64, maxScale int64) + RequestScale(ctx context.Context, scaledObject *kedav1alpha1.ScaledObject, isActive bool) } type scaleExecutor struct { diff --git a/pkg/scaling/executor/scale_jobs.go b/pkg/scaling/executor/scale_jobs.go index be0057177aa..4ce60706a80 100644 --- a/pkg/scaling/executor/scale_jobs.go +++ b/pkg/scaling/executor/scale_jobs.go @@ -5,7 +5,6 @@ import ( //"fmt" kedav1alpha1 "github.com/kedacore/keda/pkg/apis/keda/v1alpha1" - "github.com/kedacore/keda/pkg/scalers" version "github.com/kedacore/keda/version" batchv1 "k8s.io/api/batch/v1" @@ -16,49 +15,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) -func (e *scaleExecutor) RequestJobScale(ctx context.Context, scalers []scalers.Scaler, scaledJob *kedav1alpha1.ScaledJob) { - var queueLength int64 - var maxValue int64 - isActive := false - - for _, scaler := range scalers { - defer scaler.Close() - scalerLogger := e.logger.WithValues("Scaler", scaler) - - isTriggerActive, err := scaler.IsActive(ctx) - scalerLogger.Info("Active trigger", "isTriggerActive", isTriggerActive) - metricSpecs := scaler.GetMetricSpecForScaling() - - var metricValue int64 - for _, metric := range metricSpecs { - metricValue, _ = metric.External.Target.AverageValue.AsInt64() - maxValue += metricValue - } - scalerLogger.Info("Scaler max value", "MaxValue", maxValue) - - metrics, _ := scaler.GetMetrics(ctx, "queueLength", nil) - - for _, m := range metrics { - if m.MetricName == "queueLength" { - metricValue, _ = m.Value.AsInt64() - queueLength += metricValue - } - } - scalerLogger.Info("QueueLength Metric value", "queueLength", queueLength) - - if err != nil { - scalerLogger.V(1).Info("Error getting scale decision, but continue", "Error", err) - continue - } else if isTriggerActive { - isActive = true - scalerLogger.Info("Scaler is active") - } - } - - e.scaleJobs(ctx, scaledJob, isActive, queueLength, maxValue) -} - -func (e *scaleExecutor) scaleJobs(ctx context.Context, scaledJob *kedav1alpha1.ScaledJob, isActive bool, scaleTo int64, maxScale int64) { +func (e *scaleExecutor) RequestJobScale(ctx context.Context, scaledJob *kedav1alpha1.ScaledJob, isActive bool, scaleTo int64, maxScale int64) { runningJobCount := e.getRunningJobCount(scaledJob, maxScale) e.logger.Info("Scaling Jobs", "Number of running Jobs ", runningJobCount) diff --git a/pkg/scaling/executor/scale_scaledobjects.go b/pkg/scaling/executor/scale_scaledobjects.go index 08633f8b966..edff334227b 100644 --- a/pkg/scaling/executor/scale_scaledobjects.go +++ b/pkg/scaling/executor/scale_scaledobjects.go @@ -4,33 +4,17 @@ import ( "context" "time" - kedav1alpha1 "github.com/kedacore/keda/pkg/apis/keda/v1alpha1" - "github.com/kedacore/keda/pkg/scalers" - "github.com/go-logr/logr" + kedav1alpha1 "github.com/kedacore/keda/pkg/apis/keda/v1alpha1" autoscalingv1 "k8s.io/api/autoscaling/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func (e *scaleExecutor) RequestScale(ctx context.Context, scalers []scalers.Scaler, scaledObject *kedav1alpha1.ScaledObject) { +func (e *scaleExecutor) RequestScale(ctx context.Context, scaledObject *kedav1alpha1.ScaledObject, isActive bool) { logger := e.logger.WithValues("Scaledobject.Name", scaledObject.Name, "ScaledObject.Namespace", scaledObject.Namespace, "ScaleTarget.Name", scaledObject.Spec.ScaleTargetRef.Name) - isActive := false - for _, scaler := range scalers { - defer scaler.Close() - isTriggerActive, err := scaler.IsActive(ctx) - - if err != nil { - logger.V(1).Info("Error getting scale decision", "Error", err) - continue - } else if isTriggerActive { - isActive = true - logger.V(1).Info("Scaler for scaledObject is active", "Scaler", scaler) - } - } - currentScale, err := e.getScaleTargetScale(ctx, scaledObject) if err != nil { logger.Error(err, "Error getting Scale") diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 293598e79e4..b47fe9a77f9 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -87,7 +87,10 @@ func (h *scaleHandler) HandleScalableObject(scalableObject interface{}) error { h.scaleLoopContexts.Store(key, cancel) } - go h.startScaleLoop(ctx, withTriggers, scalableObject) + // a mutex is used to synchronize scale requests per scalableObject + scalingMutex := &sync.Mutex{} + go h.startPushScalers(ctx, withTriggers, scalableObject, scalingMutex) + go h.startScaleLoop(ctx, withTriggers, scalableObject, scalingMutex) return nil } @@ -115,11 +118,11 @@ func (h *scaleHandler) DeleteScalableObject(scalableObject interface{}) error { } // startScaleLoop blocks forever and checks the scaledObject based on its pollingInterval -func (h *scaleHandler) startScaleLoop(ctx context.Context, withTriggers *kedav1alpha1.WithTriggers, scalableObject interface{}) { +func (h *scaleHandler) startScaleLoop(ctx context.Context, withTriggers *kedav1alpha1.WithTriggers, scalableObject interface{}, scalingMutex *sync.Mutex) { logger := h.logger.WithValues("type", withTriggers.Kind, "namespace", withTriggers.Namespace, "name", withTriggers.Name) // kick off one check to the scalers now - h.checkScalers(ctx, scalableObject) + h.checkScalers(ctx, scalableObject, scalingMutex) pollingInterval := getPollingInterval(withTriggers) logger.V(1).Info("Watching with pollingInterval", "PollingInterval", pollingInterval) @@ -127,7 +130,7 @@ func (h *scaleHandler) startScaleLoop(ctx context.Context, withTriggers *kedav1a for { select { case <-time.After(pollingInterval): - h.checkScalers(ctx, scalableObject) + h.checkScalers(ctx, scalableObject, scalingMutex) case <-ctx.Done(): logger.V(1).Info("Context canceled") return @@ -135,21 +138,120 @@ func (h *scaleHandler) startScaleLoop(ctx context.Context, withTriggers *kedav1a } } +func (h *scaleHandler) startPushScalers(ctx context.Context, withTriggers *kedav1alpha1.WithTriggers, scalableObject interface{}, scalingMutex *sync.Mutex) { + logger := h.logger.WithValues("type", withTriggers.Kind, "namespace", withTriggers.Namespace, "name", withTriggers.Name) + ss, err := h.GetScalers(scalableObject) + if err != nil { + logger.Error(err, "Error getting scalers", "object", scalableObject) + return + } + + for _, s := range ss { + scaler, ok := s.(scalers.PushScaler) + if !ok { + continue + } + + go func() { + activeCh := make(chan bool) + go scaler.Run(ctx, activeCh) + for { + select { + case <-ctx.Done(): + return + case active := <-activeCh: + scalingMutex.Lock() + switch obj := scalableObject.(type) { + case *kedav1alpha1.ScaledObject: + h.scaleExecutor.RequestScale(ctx, obj, active) + case *kedav1alpha1.ScaledJob: + // TODO: revisit when implementing ScaledJob + h.scaleExecutor.RequestJobScale(ctx, obj, active, 1, 1) + } + scalingMutex.Unlock() + } + } + }() + } +} + // checkScalers contains the main logic for the ScaleHandler scaling logic. // It'll check each trigger active status then call RequestScale -func (h *scaleHandler) checkScalers(ctx context.Context, scalableObject interface{}) { +func (h *scaleHandler) checkScalers(ctx context.Context, scalableObject interface{}, scalingMutex *sync.Mutex) { scalers, err := h.GetScalers(scalableObject) if err != nil { h.logger.Error(err, "Error getting scalers", "object", scalableObject) return } + scalingMutex.Lock() + defer scalingMutex.Unlock() switch obj := scalableObject.(type) { case *kedav1alpha1.ScaledObject: - h.scaleExecutor.RequestScale(ctx, scalers, obj) + h.scaleExecutor.RequestScale(ctx, obj, h.checkScaledObjectScalers(ctx, scalers)) case *kedav1alpha1.ScaledJob: - h.scaleExecutor.RequestJobScale(ctx, scalers, obj) + isActive, scaleTo, maxScale := h.checkScaledJobScalers(ctx, scalers) + h.scaleExecutor.RequestJobScale(ctx, obj, isActive, scaleTo, maxScale) + } +} + +func (h *scaleHandler) checkScaledObjectScalers(ctx context.Context, scalers []scalers.Scaler) bool { + isActive := false + for _, scaler := range scalers { + isTriggerActive, err := scaler.IsActive(ctx) + scaler.Close() + + if err != nil { + h.logger.V(1).Info("Error getting scale decision", "Error", err) + continue + } else if isTriggerActive { + isActive = true + h.logger.V(1).Info("Scaler for scaledObject is active", "Scaler", scaler) + } } + return isActive +} + +func (h *scaleHandler) checkScaledJobScalers(ctx context.Context, scalers []scalers.Scaler) (bool, int64, int64) { + var queueLength int64 + var maxValue int64 + isActive := false + + for _, scaler := range scalers { + scalerLogger := h.logger.WithValues("Scaler", scaler) + + isTriggerActive, err := scaler.IsActive(ctx) + scaler.Close() + scalerLogger.Info("Active trigger", "isTriggerActive", isTriggerActive) + metricSpecs := scaler.GetMetricSpecForScaling() + + var metricValue int64 + for _, metric := range metricSpecs { + metricValue, _ = metric.External.Target.AverageValue.AsInt64() + maxValue += metricValue + } + scalerLogger.Info("Scaler max value", "MaxValue", maxValue) + + metrics, _ := scaler.GetMetrics(ctx, "queueLength", nil) + + for _, m := range metrics { + if m.MetricName == "queueLength" { + metricValue, _ = m.Value.AsInt64() + queueLength += metricValue + } + } + scalerLogger.Info("QueueLength Metric value", "queueLength", queueLength) + + if err != nil { + scalerLogger.V(1).Info("Error getting scale decision, but continue", "Error", err) + continue + } else if isTriggerActive { + isActive = true + scalerLogger.Info("Scaler is active") + } + } + + return isActive, queueLength, maxValue } // buildScalers returns list of Scalers for the specified triggers @@ -249,7 +351,9 @@ func buildScaler(name, namespace, triggerType string, resolvedEnv, triggerMetada case "gcp-pubsub": return scalers.NewPubSubScaler(resolvedEnv, triggerMetadata) case "external": - return scalers.NewExternalScaler(name, namespace, resolvedEnv, triggerMetadata) + return scalers.NewExternalScaler(name, namespace, triggerMetadata) + case "external-push": + return scalers.NewExternalPushScaler(name, namespace, triggerMetadata) case "liiklus": return scalers.NewLiiklusScaler(resolvedEnv, triggerMetadata) case "stan":