├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .dockerignore ├── .github └── workflows │ └── 4-action-acr-aci.yml ├── .gitignore ├── .vscode └── launch.json ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── TODO.md ├── azure ├── DEPLOY.sh ├── DEPLOY.txt └── README.md ├── charts └── cscaler-proxy │ ├── Chart.yaml │ ├── templates │ ├── _helpers.tpl │ ├── keda-components.yaml │ ├── proxy-admin-service.yaml │ ├── proxy-deployment.yaml │ ├── proxy-grpc-service.yaml │ ├── proxy-http-service.yaml │ ├── proxy-role-binding.yaml │ ├── proxy-role.yaml │ └── proxy-svc-account.yaml │ └── values.yaml ├── cli ├── Cargo.toml └── src │ ├── commands │ ├── client.rs │ ├── mod.rs │ ├── rm.rs │ └── run.rs │ └── main.rs ├── cmd └── proxy │ ├── admin_handlers.go │ ├── grpc_impl.go │ ├── handlers.go │ ├── main.go │ ├── middleware.go │ └── req_counter.go ├── docker-compose.yml ├── docs └── COMPONENTS.md ├── externalscaler ├── kedascaler.external.pb.go └── kedascaler.external_grpc.pb.go ├── go.mod ├── go.sum ├── kedascaler.external.proto ├── oss-improvements.md ├── pkg ├── k8s │ ├── client.go │ ├── deployment.go │ ├── meta.go │ ├── scaledobject.go │ ├── service.go │ └── val_ptrs.go └── srv │ └── config.go ├── readme.txt └── scripts ├── _helpers └── logo.txt └── install.sh /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------------------------------------- 2 | # Copyright (c) Microsoft Corporation. All rights reserved. 3 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. 4 | #------------------------------------------------------------------------------------------------------------- 5 | 6 | FROM golang:1 7 | 8 | # Avoid warnings by switching to noninteractive 9 | ENV DEBIAN_FRONTEND=noninteractive 10 | 11 | # This Dockerfile adds a non-root user with sudo access. Use the "remoteUser" 12 | # property in devcontainer.json to use it. On Linux, the container user's GID/UIDs 13 | # will be updated to match your local UID/GID (when using the dockerFile property). 14 | # See https://aka.ms/vscode-remote/containers/non-root-user for details. 15 | ARG USERNAME=vscode 16 | ARG USER_UID=1000 17 | ARG USER_GID=$USER_UID 18 | 19 | # Configure apt, install packages and tools 20 | RUN apt-get update \ 21 | && apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \ 22 | # 23 | # Verify git, process tools, lsb-release (common in install instructions for CLIs) installed 24 | && apt-get -y install git openssh-client less iproute2 procps lsb-release \ 25 | # 26 | # Build Go tools w/module support 27 | && mkdir -p /tmp/gotools \ 28 | && cd /tmp/gotools \ 29 | && GOPATH=/tmp/gotools GO111MODULE=on go get -v golang.org/x/tools/gopls@latest 2>&1 \ 30 | && GOPATH=/tmp/gotools GO111MODULE=on go get -v \ 31 | honnef.co/go/tools/...@latest \ 32 | golang.org/x/tools/cmd/gorename@latest \ 33 | golang.org/x/tools/cmd/goimports@latest \ 34 | golang.org/x/tools/cmd/guru@latest \ 35 | golang.org/x/lint/golint@latest \ 36 | github.com/mdempsky/gocode@latest \ 37 | github.com/cweill/gotests/...@latest \ 38 | github.com/haya14busa/goplay/cmd/goplay@latest \ 39 | github.com/sqs/goreturns@latest \ 40 | github.com/josharian/impl@latest \ 41 | github.com/davidrjenni/reftools/cmd/fillstruct@latest \ 42 | github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest \ 43 | github.com/ramya-rao-a/go-outline@latest \ 44 | github.com/acroca/go-symbols@latest \ 45 | github.com/godoctor/godoctor@latest \ 46 | github.com/rogpeppe/godef@latest \ 47 | github.com/zmb3/gogetdoc@latest \ 48 | github.com/fatih/gomodifytags@latest \ 49 | github.com/mgechev/revive@latest \ 50 | github.com/go-delve/delve/cmd/dlv@latest 2>&1 \ 51 | # 52 | # Build Go tools w/o module support 53 | && GOPATH=/tmp/gotools go get -v github.com/alecthomas/gometalinter 2>&1 \ 54 | # 55 | # Build gocode-gomod 56 | && GOPATH=/tmp/gotools go get -x -d github.com/stamblerre/gocode 2>&1 \ 57 | && GOPATH=/tmp/gotools go build -o gocode-gomod github.com/stamblerre/gocode \ 58 | # 59 | # Install Go tools 60 | && mv /tmp/gotools/bin/* /usr/local/bin/ \ 61 | && mv gocode-gomod /usr/local/bin/ \ 62 | # 63 | # Install golangci-lint 64 | && curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin 2>&1 \ 65 | # 66 | # Create a non-root user to use if preferred - see https://aka.ms/vscode-remote/containers/non-root-user. 67 | && groupadd --gid $USER_GID $USERNAME \ 68 | && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \ 69 | # [Optional] Add sudo support 70 | && apt-get install -y sudo \ 71 | && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ 72 | && chmod 0440 /etc/sudoers.d/$USERNAME \ 73 | # 74 | # Clean up 75 | && apt-get autoremove -y \ 76 | && apt-get clean -y \ 77 | && rm -rf /var/lib/apt/lists/* /tmp/gotools 78 | 79 | # Update this to "on" or "off" as appropriate 80 | ENV GO111MODULE=auto 81 | 82 | # Switch back to dialog for any ad-hoc use of apt-get 83 | ENV DEBIAN_FRONTEND=dialog -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Go", 3 | "dockerComposeFile": [ 4 | "../docker-compose.yml" 5 | ], 6 | "service": "containerscaler", 7 | "runArgs": [ 8 | "--cap-add=SYS_PTRACE", 9 | "--security-opt", 10 | "seccomp=unconfined" 11 | ], 12 | "settings": { 13 | "terminal.integrated.shell.linux": "/bin/bash", 14 | "go.gopath": "/go" 15 | }, 16 | "extensions": [ 17 | "ms-vscode.go" 18 | ], 19 | "workspaceFolder": "/workspace/", 20 | "shutdownAction": "none" 21 | } -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | README.md 3 | .github 4 | azure 5 | .vscode 6 | docs 7 | LICENSE 8 | Makefile 9 | README.md 10 | proxy 11 | /cli 12 | /target 13 | -------------------------------------------------------------------------------- /.github/workflows/4-action-acr-aci.yml: -------------------------------------------------------------------------------- 1 | name: 4-action-acr-aci 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - azure/* 9 | - '*' 10 | 11 | jobs: 12 | build-and-deploy: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v1 16 | - uses: azure/login@v1 17 | with: 18 | creds: ${{ secrets.AZURE_CREDENTIALS }} 19 | - name: bash DEPLOY.sh 20 | env: 21 | AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} 22 | GITHUB_SHA: ${{ github.sha }} 23 | run: | 24 | cd $GITHUB_WORKSPACE/ 25 | bash azure/DEPLOY.sh 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | cscaler.db 17 | containerscaler 18 | controller 19 | bin 20 | /proxy 21 | /capps 22 | .envrc 23 | 24 | # rust ignores 25 | 26 | # Created by https://www.toptal.com/developers/gitignore/api/rust 27 | # Edit at https://www.toptal.com/developers/gitignore?templates=rust 28 | 29 | ### Rust ### 30 | # Generated by Cargo 31 | # will have compiled files and executables 32 | cli/target/ 33 | 34 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 35 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 36 | cli/Cargo.lock 37 | 38 | # End of https://www.toptal.com/developers/gitignore/api/rust 39 | 40 | # the cli binary 41 | /cscaler 42 | 43 | # rust build directory from `make cli` 44 | /target 45 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${fileDirname}", 13 | "env": {}, 14 | "args": [] 15 | }, 16 | // the Rust task for the CLI. might be a little older, thanks 17 | // to @nyxiative on Twitch for the source. 18 | { 19 | "name": "cargo run", 20 | "type": "shell", 21 | "command": "cargo", 22 | "args": [ 23 | "run", 24 | // "--release", 25 | // "--", 26 | // "arg1" 27 | ], 28 | "group": { 29 | "kind": "build", 30 | "isDefault": true 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ansi_term" 5 | version = "0.11.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 8 | dependencies = [ 9 | "winapi 0.3.9", 10 | ] 11 | 12 | [[package]] 13 | name = "async-trait" 14 | version = "0.1.42" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" 17 | dependencies = [ 18 | "proc-macro2", 19 | "quote", 20 | "syn", 21 | ] 22 | 23 | [[package]] 24 | name = "atty" 25 | version = "0.2.14" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 28 | dependencies = [ 29 | "hermit-abi", 30 | "libc", 31 | "winapi 0.3.9", 32 | ] 33 | 34 | [[package]] 35 | name = "autocfg" 36 | version = "1.0.1" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 39 | 40 | [[package]] 41 | name = "base64" 42 | version = "0.13.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 45 | 46 | [[package]] 47 | name = "bitflags" 48 | version = "1.2.1" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 51 | 52 | [[package]] 53 | name = "bumpalo" 54 | version = "3.4.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 57 | 58 | [[package]] 59 | name = "bytes" 60 | version = "0.5.6" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" 63 | 64 | [[package]] 65 | name = "cc" 66 | version = "1.0.66" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" 69 | 70 | [[package]] 71 | name = "cfg-if" 72 | version = "0.1.10" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 75 | 76 | [[package]] 77 | name = "cfg-if" 78 | version = "1.0.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 81 | 82 | [[package]] 83 | name = "clap" 84 | version = "2.33.3" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 87 | dependencies = [ 88 | "ansi_term", 89 | "atty", 90 | "bitflags", 91 | "strsim", 92 | "textwrap", 93 | "unicode-width", 94 | "vec_map", 95 | ] 96 | 97 | [[package]] 98 | name = "cli" 99 | version = "0.1.0" 100 | dependencies = [ 101 | "async-trait", 102 | "futures", 103 | "reqwest", 104 | "structopt", 105 | "tokio", 106 | ] 107 | 108 | [[package]] 109 | name = "core-foundation" 110 | version = "0.9.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 113 | dependencies = [ 114 | "core-foundation-sys", 115 | "libc", 116 | ] 117 | 118 | [[package]] 119 | name = "core-foundation-sys" 120 | version = "0.8.2" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 123 | 124 | [[package]] 125 | name = "encoding_rs" 126 | version = "0.8.26" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" 129 | dependencies = [ 130 | "cfg-if 1.0.0", 131 | ] 132 | 133 | [[package]] 134 | name = "fnv" 135 | version = "1.0.7" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 138 | 139 | [[package]] 140 | name = "foreign-types" 141 | version = "0.3.2" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 144 | dependencies = [ 145 | "foreign-types-shared", 146 | ] 147 | 148 | [[package]] 149 | name = "foreign-types-shared" 150 | version = "0.1.1" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 153 | 154 | [[package]] 155 | name = "form_urlencoded" 156 | version = "1.0.0" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" 159 | dependencies = [ 160 | "matches", 161 | "percent-encoding", 162 | ] 163 | 164 | [[package]] 165 | name = "fuchsia-zircon" 166 | version = "0.3.3" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 169 | dependencies = [ 170 | "bitflags", 171 | "fuchsia-zircon-sys", 172 | ] 173 | 174 | [[package]] 175 | name = "fuchsia-zircon-sys" 176 | version = "0.3.3" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 179 | 180 | [[package]] 181 | name = "futures" 182 | version = "0.3.8" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" 185 | dependencies = [ 186 | "futures-channel", 187 | "futures-core", 188 | "futures-executor", 189 | "futures-io", 190 | "futures-sink", 191 | "futures-task", 192 | "futures-util", 193 | ] 194 | 195 | [[package]] 196 | name = "futures-channel" 197 | version = "0.3.8" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" 200 | dependencies = [ 201 | "futures-core", 202 | "futures-sink", 203 | ] 204 | 205 | [[package]] 206 | name = "futures-core" 207 | version = "0.3.8" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" 210 | 211 | [[package]] 212 | name = "futures-executor" 213 | version = "0.3.8" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" 216 | dependencies = [ 217 | "futures-core", 218 | "futures-task", 219 | "futures-util", 220 | ] 221 | 222 | [[package]] 223 | name = "futures-io" 224 | version = "0.3.8" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" 227 | 228 | [[package]] 229 | name = "futures-macro" 230 | version = "0.3.8" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" 233 | dependencies = [ 234 | "proc-macro-hack", 235 | "proc-macro2", 236 | "quote", 237 | "syn", 238 | ] 239 | 240 | [[package]] 241 | name = "futures-sink" 242 | version = "0.3.8" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" 245 | 246 | [[package]] 247 | name = "futures-task" 248 | version = "0.3.8" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" 251 | dependencies = [ 252 | "once_cell", 253 | ] 254 | 255 | [[package]] 256 | name = "futures-util" 257 | version = "0.3.8" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" 260 | dependencies = [ 261 | "futures-channel", 262 | "futures-core", 263 | "futures-io", 264 | "futures-macro", 265 | "futures-sink", 266 | "futures-task", 267 | "memchr", 268 | "pin-project 1.0.2", 269 | "pin-utils", 270 | "proc-macro-hack", 271 | "proc-macro-nested", 272 | "slab", 273 | ] 274 | 275 | [[package]] 276 | name = "getrandom" 277 | version = "0.1.16" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 280 | dependencies = [ 281 | "cfg-if 1.0.0", 282 | "libc", 283 | "wasi", 284 | ] 285 | 286 | [[package]] 287 | name = "h2" 288 | version = "0.2.7" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" 291 | dependencies = [ 292 | "bytes", 293 | "fnv", 294 | "futures-core", 295 | "futures-sink", 296 | "futures-util", 297 | "http", 298 | "indexmap", 299 | "slab", 300 | "tokio", 301 | "tokio-util", 302 | "tracing", 303 | "tracing-futures", 304 | ] 305 | 306 | [[package]] 307 | name = "hashbrown" 308 | version = "0.9.1" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 311 | 312 | [[package]] 313 | name = "heck" 314 | version = "0.3.2" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" 317 | dependencies = [ 318 | "unicode-segmentation", 319 | ] 320 | 321 | [[package]] 322 | name = "hermit-abi" 323 | version = "0.1.17" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" 326 | dependencies = [ 327 | "libc", 328 | ] 329 | 330 | [[package]] 331 | name = "http" 332 | version = "0.2.2" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26" 335 | dependencies = [ 336 | "bytes", 337 | "fnv", 338 | "itoa", 339 | ] 340 | 341 | [[package]] 342 | name = "http-body" 343 | version = "0.3.1" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" 346 | dependencies = [ 347 | "bytes", 348 | "http", 349 | ] 350 | 351 | [[package]] 352 | name = "httparse" 353 | version = "1.3.4" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 356 | 357 | [[package]] 358 | name = "httpdate" 359 | version = "0.3.2" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" 362 | 363 | [[package]] 364 | name = "hyper" 365 | version = "0.13.9" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" 368 | dependencies = [ 369 | "bytes", 370 | "futures-channel", 371 | "futures-core", 372 | "futures-util", 373 | "h2", 374 | "http", 375 | "http-body", 376 | "httparse", 377 | "httpdate", 378 | "itoa", 379 | "pin-project 1.0.2", 380 | "socket2", 381 | "tokio", 382 | "tower-service", 383 | "tracing", 384 | "want", 385 | ] 386 | 387 | [[package]] 388 | name = "hyper-tls" 389 | version = "0.4.3" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" 392 | dependencies = [ 393 | "bytes", 394 | "hyper", 395 | "native-tls", 396 | "tokio", 397 | "tokio-tls", 398 | ] 399 | 400 | [[package]] 401 | name = "idna" 402 | version = "0.2.0" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 405 | dependencies = [ 406 | "matches", 407 | "unicode-bidi", 408 | "unicode-normalization", 409 | ] 410 | 411 | [[package]] 412 | name = "indexmap" 413 | version = "1.6.1" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" 416 | dependencies = [ 417 | "autocfg", 418 | "hashbrown", 419 | ] 420 | 421 | [[package]] 422 | name = "iovec" 423 | version = "0.1.4" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 426 | dependencies = [ 427 | "libc", 428 | ] 429 | 430 | [[package]] 431 | name = "ipnet" 432 | version = "2.3.0" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" 435 | 436 | [[package]] 437 | name = "itoa" 438 | version = "0.4.7" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 441 | 442 | [[package]] 443 | name = "js-sys" 444 | version = "0.3.46" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" 447 | dependencies = [ 448 | "wasm-bindgen", 449 | ] 450 | 451 | [[package]] 452 | name = "kernel32-sys" 453 | version = "0.2.2" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 456 | dependencies = [ 457 | "winapi 0.2.8", 458 | "winapi-build", 459 | ] 460 | 461 | [[package]] 462 | name = "lazy_static" 463 | version = "1.4.0" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 466 | 467 | [[package]] 468 | name = "libc" 469 | version = "0.2.81" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" 472 | 473 | [[package]] 474 | name = "log" 475 | version = "0.4.11" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 478 | dependencies = [ 479 | "cfg-if 0.1.10", 480 | ] 481 | 482 | [[package]] 483 | name = "matches" 484 | version = "0.1.8" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 487 | 488 | [[package]] 489 | name = "memchr" 490 | version = "2.3.4" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 493 | 494 | [[package]] 495 | name = "mime" 496 | version = "0.3.16" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 499 | 500 | [[package]] 501 | name = "mime_guess" 502 | version = "2.0.3" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 505 | dependencies = [ 506 | "mime", 507 | "unicase", 508 | ] 509 | 510 | [[package]] 511 | name = "mio" 512 | version = "0.6.23" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" 515 | dependencies = [ 516 | "cfg-if 0.1.10", 517 | "fuchsia-zircon", 518 | "fuchsia-zircon-sys", 519 | "iovec", 520 | "kernel32-sys", 521 | "libc", 522 | "log", 523 | "miow", 524 | "net2", 525 | "slab", 526 | "winapi 0.2.8", 527 | ] 528 | 529 | [[package]] 530 | name = "miow" 531 | version = "0.2.2" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" 534 | dependencies = [ 535 | "kernel32-sys", 536 | "net2", 537 | "winapi 0.2.8", 538 | "ws2_32-sys", 539 | ] 540 | 541 | [[package]] 542 | name = "native-tls" 543 | version = "0.2.7" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" 546 | dependencies = [ 547 | "lazy_static", 548 | "libc", 549 | "log", 550 | "openssl", 551 | "openssl-probe", 552 | "openssl-sys", 553 | "schannel", 554 | "security-framework", 555 | "security-framework-sys", 556 | "tempfile", 557 | ] 558 | 559 | [[package]] 560 | name = "net2" 561 | version = "0.2.37" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" 564 | dependencies = [ 565 | "cfg-if 0.1.10", 566 | "libc", 567 | "winapi 0.3.9", 568 | ] 569 | 570 | [[package]] 571 | name = "num_cpus" 572 | version = "1.13.0" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 575 | dependencies = [ 576 | "hermit-abi", 577 | "libc", 578 | ] 579 | 580 | [[package]] 581 | name = "once_cell" 582 | version = "1.5.2" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" 585 | 586 | [[package]] 587 | name = "openssl" 588 | version = "0.10.32" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" 591 | dependencies = [ 592 | "bitflags", 593 | "cfg-if 1.0.0", 594 | "foreign-types", 595 | "lazy_static", 596 | "libc", 597 | "openssl-sys", 598 | ] 599 | 600 | [[package]] 601 | name = "openssl-probe" 602 | version = "0.1.2" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 605 | 606 | [[package]] 607 | name = "openssl-sys" 608 | version = "0.9.60" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" 611 | dependencies = [ 612 | "autocfg", 613 | "cc", 614 | "libc", 615 | "pkg-config", 616 | "vcpkg", 617 | ] 618 | 619 | [[package]] 620 | name = "percent-encoding" 621 | version = "2.1.0" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 624 | 625 | [[package]] 626 | name = "pin-project" 627 | version = "0.4.27" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" 630 | dependencies = [ 631 | "pin-project-internal 0.4.27", 632 | ] 633 | 634 | [[package]] 635 | name = "pin-project" 636 | version = "1.0.2" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" 639 | dependencies = [ 640 | "pin-project-internal 1.0.2", 641 | ] 642 | 643 | [[package]] 644 | name = "pin-project-internal" 645 | version = "0.4.27" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" 648 | dependencies = [ 649 | "proc-macro2", 650 | "quote", 651 | "syn", 652 | ] 653 | 654 | [[package]] 655 | name = "pin-project-internal" 656 | version = "1.0.2" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" 659 | dependencies = [ 660 | "proc-macro2", 661 | "quote", 662 | "syn", 663 | ] 664 | 665 | [[package]] 666 | name = "pin-project-lite" 667 | version = "0.1.11" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" 670 | 671 | [[package]] 672 | name = "pin-project-lite" 673 | version = "0.2.0" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" 676 | 677 | [[package]] 678 | name = "pin-utils" 679 | version = "0.1.0" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 682 | 683 | [[package]] 684 | name = "pkg-config" 685 | version = "0.3.19" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 688 | 689 | [[package]] 690 | name = "ppv-lite86" 691 | version = "0.2.10" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 694 | 695 | [[package]] 696 | name = "proc-macro-error" 697 | version = "1.0.4" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 700 | dependencies = [ 701 | "proc-macro-error-attr", 702 | "proc-macro2", 703 | "quote", 704 | "syn", 705 | "version_check", 706 | ] 707 | 708 | [[package]] 709 | name = "proc-macro-error-attr" 710 | version = "1.0.4" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 713 | dependencies = [ 714 | "proc-macro2", 715 | "quote", 716 | "version_check", 717 | ] 718 | 719 | [[package]] 720 | name = "proc-macro-hack" 721 | version = "0.5.19" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 724 | 725 | [[package]] 726 | name = "proc-macro-nested" 727 | version = "0.1.6" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" 730 | 731 | [[package]] 732 | name = "proc-macro2" 733 | version = "1.0.24" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 736 | dependencies = [ 737 | "unicode-xid", 738 | ] 739 | 740 | [[package]] 741 | name = "quote" 742 | version = "1.0.8" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" 745 | dependencies = [ 746 | "proc-macro2", 747 | ] 748 | 749 | [[package]] 750 | name = "rand" 751 | version = "0.7.3" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 754 | dependencies = [ 755 | "getrandom", 756 | "libc", 757 | "rand_chacha", 758 | "rand_core", 759 | "rand_hc", 760 | ] 761 | 762 | [[package]] 763 | name = "rand_chacha" 764 | version = "0.2.2" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 767 | dependencies = [ 768 | "ppv-lite86", 769 | "rand_core", 770 | ] 771 | 772 | [[package]] 773 | name = "rand_core" 774 | version = "0.5.1" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 777 | dependencies = [ 778 | "getrandom", 779 | ] 780 | 781 | [[package]] 782 | name = "rand_hc" 783 | version = "0.2.0" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 786 | dependencies = [ 787 | "rand_core", 788 | ] 789 | 790 | [[package]] 791 | name = "redox_syscall" 792 | version = "0.1.57" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 795 | 796 | [[package]] 797 | name = "remove_dir_all" 798 | version = "0.5.3" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 801 | dependencies = [ 802 | "winapi 0.3.9", 803 | ] 804 | 805 | [[package]] 806 | name = "reqwest" 807 | version = "0.10.10" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c" 810 | dependencies = [ 811 | "base64", 812 | "bytes", 813 | "encoding_rs", 814 | "futures-core", 815 | "futures-util", 816 | "http", 817 | "http-body", 818 | "hyper", 819 | "hyper-tls", 820 | "ipnet", 821 | "js-sys", 822 | "lazy_static", 823 | "log", 824 | "mime", 825 | "mime_guess", 826 | "native-tls", 827 | "percent-encoding", 828 | "pin-project-lite 0.2.0", 829 | "serde", 830 | "serde_json", 831 | "serde_urlencoded", 832 | "tokio", 833 | "tokio-tls", 834 | "url", 835 | "wasm-bindgen", 836 | "wasm-bindgen-futures", 837 | "web-sys", 838 | "winreg", 839 | ] 840 | 841 | [[package]] 842 | name = "ryu" 843 | version = "1.0.5" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 846 | 847 | [[package]] 848 | name = "schannel" 849 | version = "0.1.19" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 852 | dependencies = [ 853 | "lazy_static", 854 | "winapi 0.3.9", 855 | ] 856 | 857 | [[package]] 858 | name = "security-framework" 859 | version = "2.0.0" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" 862 | dependencies = [ 863 | "bitflags", 864 | "core-foundation", 865 | "core-foundation-sys", 866 | "libc", 867 | "security-framework-sys", 868 | ] 869 | 870 | [[package]] 871 | name = "security-framework-sys" 872 | version = "2.0.0" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" 875 | dependencies = [ 876 | "core-foundation-sys", 877 | "libc", 878 | ] 879 | 880 | [[package]] 881 | name = "serde" 882 | version = "1.0.118" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" 885 | 886 | [[package]] 887 | name = "serde_json" 888 | version = "1.0.61" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" 891 | dependencies = [ 892 | "itoa", 893 | "ryu", 894 | "serde", 895 | ] 896 | 897 | [[package]] 898 | name = "serde_urlencoded" 899 | version = "0.7.0" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" 902 | dependencies = [ 903 | "form_urlencoded", 904 | "itoa", 905 | "ryu", 906 | "serde", 907 | ] 908 | 909 | [[package]] 910 | name = "slab" 911 | version = "0.4.2" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 914 | 915 | [[package]] 916 | name = "socket2" 917 | version = "0.3.19" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" 920 | dependencies = [ 921 | "cfg-if 1.0.0", 922 | "libc", 923 | "winapi 0.3.9", 924 | ] 925 | 926 | [[package]] 927 | name = "strsim" 928 | version = "0.8.0" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 931 | 932 | [[package]] 933 | name = "structopt" 934 | version = "0.3.21" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" 937 | dependencies = [ 938 | "clap", 939 | "lazy_static", 940 | "structopt-derive", 941 | ] 942 | 943 | [[package]] 944 | name = "structopt-derive" 945 | version = "0.4.14" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" 948 | dependencies = [ 949 | "heck", 950 | "proc-macro-error", 951 | "proc-macro2", 952 | "quote", 953 | "syn", 954 | ] 955 | 956 | [[package]] 957 | name = "syn" 958 | version = "1.0.57" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" 961 | dependencies = [ 962 | "proc-macro2", 963 | "quote", 964 | "unicode-xid", 965 | ] 966 | 967 | [[package]] 968 | name = "tempfile" 969 | version = "3.1.0" 970 | source = "registry+https://github.com/rust-lang/crates.io-index" 971 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 972 | dependencies = [ 973 | "cfg-if 0.1.10", 974 | "libc", 975 | "rand", 976 | "redox_syscall", 977 | "remove_dir_all", 978 | "winapi 0.3.9", 979 | ] 980 | 981 | [[package]] 982 | name = "textwrap" 983 | version = "0.11.0" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 986 | dependencies = [ 987 | "unicode-width", 988 | ] 989 | 990 | [[package]] 991 | name = "tinyvec" 992 | version = "1.1.0" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" 995 | dependencies = [ 996 | "tinyvec_macros", 997 | ] 998 | 999 | [[package]] 1000 | name = "tinyvec_macros" 1001 | version = "0.1.0" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1004 | 1005 | [[package]] 1006 | name = "tokio" 1007 | version = "0.2.24" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" 1010 | dependencies = [ 1011 | "bytes", 1012 | "fnv", 1013 | "futures-core", 1014 | "iovec", 1015 | "lazy_static", 1016 | "memchr", 1017 | "mio", 1018 | "num_cpus", 1019 | "pin-project-lite 0.1.11", 1020 | "slab", 1021 | "tokio-macros", 1022 | ] 1023 | 1024 | [[package]] 1025 | name = "tokio-macros" 1026 | version = "0.2.6" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" 1029 | dependencies = [ 1030 | "proc-macro2", 1031 | "quote", 1032 | "syn", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "tokio-tls" 1037 | version = "0.3.1" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" 1040 | dependencies = [ 1041 | "native-tls", 1042 | "tokio", 1043 | ] 1044 | 1045 | [[package]] 1046 | name = "tokio-util" 1047 | version = "0.3.1" 1048 | source = "registry+https://github.com/rust-lang/crates.io-index" 1049 | checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" 1050 | dependencies = [ 1051 | "bytes", 1052 | "futures-core", 1053 | "futures-sink", 1054 | "log", 1055 | "pin-project-lite 0.1.11", 1056 | "tokio", 1057 | ] 1058 | 1059 | [[package]] 1060 | name = "tower-service" 1061 | version = "0.3.0" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" 1064 | 1065 | [[package]] 1066 | name = "tracing" 1067 | version = "0.1.22" 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" 1069 | checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" 1070 | dependencies = [ 1071 | "cfg-if 1.0.0", 1072 | "log", 1073 | "pin-project-lite 0.2.0", 1074 | "tracing-core", 1075 | ] 1076 | 1077 | [[package]] 1078 | name = "tracing-core" 1079 | version = "0.1.17" 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" 1081 | checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" 1082 | dependencies = [ 1083 | "lazy_static", 1084 | ] 1085 | 1086 | [[package]] 1087 | name = "tracing-futures" 1088 | version = "0.2.4" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" 1091 | dependencies = [ 1092 | "pin-project 0.4.27", 1093 | "tracing", 1094 | ] 1095 | 1096 | [[package]] 1097 | name = "try-lock" 1098 | version = "0.2.3" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1101 | 1102 | [[package]] 1103 | name = "unicase" 1104 | version = "2.6.0" 1105 | source = "registry+https://github.com/rust-lang/crates.io-index" 1106 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1107 | dependencies = [ 1108 | "version_check", 1109 | ] 1110 | 1111 | [[package]] 1112 | name = "unicode-bidi" 1113 | version = "0.3.4" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1116 | dependencies = [ 1117 | "matches", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "unicode-normalization" 1122 | version = "0.1.16" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" 1125 | dependencies = [ 1126 | "tinyvec", 1127 | ] 1128 | 1129 | [[package]] 1130 | name = "unicode-segmentation" 1131 | version = "1.7.1" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" 1134 | 1135 | [[package]] 1136 | name = "unicode-width" 1137 | version = "0.1.8" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 1140 | 1141 | [[package]] 1142 | name = "unicode-xid" 1143 | version = "0.2.1" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1146 | 1147 | [[package]] 1148 | name = "url" 1149 | version = "2.2.0" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" 1152 | dependencies = [ 1153 | "form_urlencoded", 1154 | "idna", 1155 | "matches", 1156 | "percent-encoding", 1157 | ] 1158 | 1159 | [[package]] 1160 | name = "vcpkg" 1161 | version = "0.2.11" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" 1164 | 1165 | [[package]] 1166 | name = "vec_map" 1167 | version = "0.8.2" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 1170 | 1171 | [[package]] 1172 | name = "version_check" 1173 | version = "0.9.2" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 1176 | 1177 | [[package]] 1178 | name = "want" 1179 | version = "0.3.0" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1182 | dependencies = [ 1183 | "log", 1184 | "try-lock", 1185 | ] 1186 | 1187 | [[package]] 1188 | name = "wasi" 1189 | version = "0.9.0+wasi-snapshot-preview1" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1192 | 1193 | [[package]] 1194 | name = "wasm-bindgen" 1195 | version = "0.2.69" 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" 1197 | checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" 1198 | dependencies = [ 1199 | "cfg-if 1.0.0", 1200 | "serde", 1201 | "serde_json", 1202 | "wasm-bindgen-macro", 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "wasm-bindgen-backend" 1207 | version = "0.2.69" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" 1210 | dependencies = [ 1211 | "bumpalo", 1212 | "lazy_static", 1213 | "log", 1214 | "proc-macro2", 1215 | "quote", 1216 | "syn", 1217 | "wasm-bindgen-shared", 1218 | ] 1219 | 1220 | [[package]] 1221 | name = "wasm-bindgen-futures" 1222 | version = "0.4.19" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35" 1225 | dependencies = [ 1226 | "cfg-if 1.0.0", 1227 | "js-sys", 1228 | "wasm-bindgen", 1229 | "web-sys", 1230 | ] 1231 | 1232 | [[package]] 1233 | name = "wasm-bindgen-macro" 1234 | version = "0.2.69" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" 1237 | dependencies = [ 1238 | "quote", 1239 | "wasm-bindgen-macro-support", 1240 | ] 1241 | 1242 | [[package]] 1243 | name = "wasm-bindgen-macro-support" 1244 | version = "0.2.69" 1245 | source = "registry+https://github.com/rust-lang/crates.io-index" 1246 | checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" 1247 | dependencies = [ 1248 | "proc-macro2", 1249 | "quote", 1250 | "syn", 1251 | "wasm-bindgen-backend", 1252 | "wasm-bindgen-shared", 1253 | ] 1254 | 1255 | [[package]] 1256 | name = "wasm-bindgen-shared" 1257 | version = "0.2.69" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" 1260 | 1261 | [[package]] 1262 | name = "web-sys" 1263 | version = "0.3.46" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" 1266 | dependencies = [ 1267 | "js-sys", 1268 | "wasm-bindgen", 1269 | ] 1270 | 1271 | [[package]] 1272 | name = "winapi" 1273 | version = "0.2.8" 1274 | source = "registry+https://github.com/rust-lang/crates.io-index" 1275 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1276 | 1277 | [[package]] 1278 | name = "winapi" 1279 | version = "0.3.9" 1280 | source = "registry+https://github.com/rust-lang/crates.io-index" 1281 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1282 | dependencies = [ 1283 | "winapi-i686-pc-windows-gnu", 1284 | "winapi-x86_64-pc-windows-gnu", 1285 | ] 1286 | 1287 | [[package]] 1288 | name = "winapi-build" 1289 | version = "0.1.1" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1292 | 1293 | [[package]] 1294 | name = "winapi-i686-pc-windows-gnu" 1295 | version = "0.4.0" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1298 | 1299 | [[package]] 1300 | name = "winapi-x86_64-pc-windows-gnu" 1301 | version = "0.4.0" 1302 | source = "registry+https://github.com/rust-lang/crates.io-index" 1303 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1304 | 1305 | [[package]] 1306 | name = "winreg" 1307 | version = "0.7.0" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" 1310 | dependencies = [ 1311 | "winapi 0.3.9", 1312 | ] 1313 | 1314 | [[package]] 1315 | name = "ws2_32-sys" 1316 | version = "0.2.1" 1317 | source = "registry+https://github.com/rust-lang/crates.io-index" 1318 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1319 | dependencies = [ 1320 | "winapi 0.2.8", 1321 | "winapi-build", 1322 | ] 1323 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "cli" 5 | ] 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # taken from Athens 2 | # https://github.com/gomods/athens/blob/main/cmd/proxy/Dockerfile 3 | ARG GOLANG_VERSION=1.14 4 | ARG ALPINE_VERSION=3.11.5 5 | 6 | FROM golang:${GOLANG_VERSION}-alpine AS builder 7 | 8 | WORKDIR $GOPATH/src/github.com/arschles/containerscaler 9 | 10 | COPY . . 11 | COPY kedascaler.external.proto /bin/proto.proto 12 | 13 | ARG VERSION="unset" 14 | 15 | RUN GO111MODULE=on CGO_ENABLED=0 GOPROXY="https://proxy.golang.org" go build -o /bin/containerscalerproxy ./cmd/proxy 16 | 17 | FROM alpine:${ALPINE_VERSION} 18 | 19 | RUN apk add -U curl 20 | 21 | ENV GO111MODULE=on 22 | 23 | COPY --from=builder /bin/containerscalerproxy /bin/containerscalerproxy 24 | COPY --from=builder /bin/proto.proto /bin/proto.proto 25 | 26 | RUN curl -o grpcurl.tgz -L https://github.com/fullstorydev/grpcurl/releases/download/v1.7.0/grpcurl_1.7.0_linux_x86_64.tar.gz && \ 27 | tar -xzf grpcurl.tgz && \ 28 | mv grpcurl /bin/grpcurl 29 | 30 | EXPOSE 8080 31 | 32 | ENTRYPOINT ["/bin/containerscalerproxy"] 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 FullStory, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN_DIR := ./bin 2 | GIT_SHA := $(shell git rev-parse --short HEAD) 3 | 4 | .PHONY: proxy 5 | proxy: 6 | go build -o ${BIN_DIR}/proxy ./cmd/proxy 7 | 8 | .PHONY: runproxy 9 | runproxy: 10 | go run ./cmd/proxy 11 | 12 | .PHONY: proto 13 | proto: 14 | protoc \ 15 | --go_out=externalscaler \ 16 | --go_opt=paths=source_relative \ 17 | --go-grpc_out=externalscaler \ 18 | --go-grpc_opt=paths=source_relative \ 19 | kedascaler.external.proto 20 | 21 | .PHONY: dockerbuild 22 | dockerbuild: 23 | docker build -t arschles/cscaler . 24 | 25 | .PHONY: dockerpush 26 | dockerpush: dockerbuild 27 | docker push arschles/cscaler 28 | 29 | .PHONY: cli 30 | cli: 31 | cargo build --bin cli 32 | cp target/debug/cli ./cscaler 33 | 34 | .PHONY: clean-cli 35 | clean-cli: 36 | rm -rf ${BIN_DIR}/cscaler 37 | 38 | .PHONY: clean-bin 39 | clean-bin: clean-cli 40 | 41 | .PHONY: clean 42 | clean: clean-bin 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Notice: this repository is moved to [github.com/kedacore/http-add-on](https://github.com/kedacore/http-add-on). Do not commit here anymore** 2 | 3 | # KEDA HTTP 4 | 5 | This project implements a prototype of auto-scaling containers based on HTTP requests. As requests come into the system, the container(s) that are equipped to handle that request may or may not be running and ready to accept it. If there are sufficient containers available, the request is routed to one of them. If there are not, a container is started and the request is routed to it when it's ready, courtesy of KEDA. 6 | ## Installation 7 | 8 | To install the application you'll need a __Kubernetes Cluster__ up and running. 9 | ### Install KEDA 10 | 11 | You need to install KEDA first. Do so with these commands: 12 | 13 | ```shell 14 | helm repo add kedacore https://kedacore.github.io/charts 15 | helm repo update 16 | helm install keda kedacore/keda --n capps --create-namespace 17 | ``` 18 | 19 | >These commands are similar to those on the [official install page](https://keda.sh/docs/1.5/deploy/#helm), but we're installing in a different namespace. 20 | 21 | ### Install the Proxy 22 | 23 | The proxy is responsible for receiving the requests, so you'll need to install it. 24 | 25 | ```shell 26 | helm upgrade --install capps ./charts/cscaler-proxy -n capps --create-namespace 27 | ``` 28 | 29 | >You can use the above command to upgrade your installation as well 30 | 31 | After the install, run the following command to fetch the public IP of the proxy service: 32 | 33 | ```shell 34 | kubectl get svc cscaler-proxy -n cscaler -o=jsonpath='{.status.loadBalancer.ingress[*].ip}' 35 | ``` 36 | 37 | To delete the proxy, but not KEDA, run this: 38 | 39 | ```shell 40 | helm delete -n capps capps 41 | 42 | 43 | ### Build the app 44 | 45 | Just simply run ```make cli``` command within the root directory. This will create a new binary file called `capps` in the `bin` directory. 46 | 47 | You can then install it into your ```PATH``` or add the ```./bin``` to your ```PATH```, or you can just run it by typing `./bin/capps` (assuming you're on the root). 48 | 49 | ## CLI API 50 | 51 | ```shell 52 | ./bin/capps 53 | ``` 54 | 55 | Running with no parameters will give you the general help for the commands 56 | 57 | __Root Commands__: 58 | 59 | - `help`: General help, use it as `./bin/capps help ` to get help on any command 60 | - `rm`: Removes a created app, has its own set of flags 61 | - `run`: Creates a new app, has its own set of flags 62 | - `version`: Provides the version name and number 63 | 64 | ### Create an App 65 | 66 | ```shell 67 | ./bin/capps run --image /: --port --server-url 68 | ``` 69 | 70 | Runs a new application based on parameters. 71 | 72 | __Flags__ 73 | 74 | - `-i`, `--image`: The image to be downloaded from the repository. 75 | > Since this command will create a new set of workloads, all the logged Docker repositories within the current cluster will work 76 | 77 | - `-s`, `--server-url`: (__Required__) The URL for the admin server. To get this, run `kubectl get svc cscaler-proxy -n cscaler -o=jsonpath="{.status.loadBalancer.ingress[*].ip}"` 78 | > Without the correct admin url the scaler __will not__ work 79 | 80 | - `-p`, `--port`: Port number to be exposed, should be the port where the app listens to incoming connections. 81 | 82 | - `--use-http`: When set, the server URL will use the `HTTP` protocol instead of `HTTPS` (_default: false_) 83 | 84 | ### Remove an App 85 | 86 | ```shell 87 | ./bin/capps rm --server-url 88 | ``` 89 | 90 | Removes a previously created app 91 | 92 | - `-s`, `--server-url`: (__Required__) The URL for the admin server. To get this, run `kubectl get svc cscaler-proxy -n cscaler -o=jsonpath="{.status.loadBalancer.ingress[*].ip}"` 93 | > Without the correct admin url the scaler __will not__ work 94 | 95 | - `--use-http`: When set, the server URL will use the `HTTP` protocol instead of `HTTPS` (_default: false_) 96 | 97 | ## Access the app 98 | 99 | Once deployed with `capps run` you'll be able to access the application through the __[proxy IP](#install-the-proxy)__. 100 | 101 | However, the proxy only understands DNS hostnames, which means that, if your service is called `foo`, you'll have to access it through a DNS name like `foo.domain.com` and this DNS Zone needs to have an `A` record with the name `foo` pointing to the proxy IP. This is an implementation of an automatic ingress rule. 102 | 103 | You can either use your own domain or an Azure provided one. 104 | 105 | > __Important__: If you could not access the endpoint and it didn't work, probably you didn't have HTTPS enabled, try to use the `--use-http` flag to test again 106 | 107 | ### Access through your domain 108 | 109 | 1. Go to your DNS zone settings in your domain registrar 110 | 2. Add a new `A` record with the __same name as your service__ – it should point to `.yourdomain.com` 111 | 3. Point this DNS record to the proxy IP 112 | 4. Give it some minutes or check [dnschecker.org](https://dnschecker.org) for the propagation 113 | 5. Access the domain 114 | 115 | You can check the logs on `kubectl logs deploy/cscaler-proxy -f -n cscaler` to check for incoming requests 116 | 117 | ### Access through Azure-provided domains 118 | 119 | 1. If your cluster is not created in Azure, check the "Enable HTTP Application Routing Addon" box when creating it 120 | 2. If your cluster already exists, run `az aks enable-addons -n -g --addons http_application_routing` 121 | 3. Once the execution is complete, open the Azure Portal, navigate to the resource group named `MC___` 122 | 4. Find the Azure DNS zone the Addon created for you 123 | 5. Follow steps 2 to 4 from the [previous section](#access-through-your-domain) 124 | 6. Access the service using `.` 125 | 126 | You can check the logs on `kubectl logs deploy/cscaler-proxy -f -n cscaler` to check for incoming requests 127 | 128 | ## Debugging 129 | 130 | If you are using [vscode ](https://code.visualstudio.com/) you can open up the dev environment in container using the [remote container extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) the nats server will be avalible in the dev container via `nats-server:4222` 131 | 132 | If you need to do any DNS work from inside a container that's running Alpine linux, use this command: 133 | 134 | ```shell 135 | curl -L https://github.com/sequenceiq/docker-alpine-dig/releases/download/v9.10.2/dig.tgz|tar -xzv -C /usr/local/bin/ 136 | ``` 137 | 138 | Courtesy https://github.com/sequenceiq/docker-alpine-dig 139 | 140 | ## More Information 141 | 142 | See [this document](./docs/COMPONENTS.md) for details on the components of this system. 143 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODOs 2 | 3 | - [ ] Build an [external scaler](https://keda.sh/docs/1.5/concepts/external-scalers/) 4 | KEDA is built for the to-be-scaled container to consume events from the same place that KEDA looks for them, so it makes certain assumptions that tie it more tightly to the applications than we'd like. Building out an external scaler breaks this tight binding 5 | - [ ] Run the proxy behind an ingress controller 6 | - [ ] Make the proxy aware of multiple hosts 7 | - This is a start to making it multi-tenant 8 | - It should use the `Ingress` object as the lookup table for where to forward requests 9 | - [ ] Build an releases/versioning API. It should do this: 10 | - Create a _new_ app: create a new `Deployment`, `Service`, `ScaledObject`, and rule for the new hostname in the `Ingress` rule for the proxy's ingress controller 11 | - Upgrade an existing app: update the image name on the app's `Deployment` and triggering a rollout 12 | - Future: figure out how to host multiple versions, do traffic splitting, etc... This probably involves service mesh support, see below 13 | - Delete an app: delete the app's `Deployment`, `Service`, and `ScaledObject` 14 | - [ ] Hook the CLI up to the releases/versioning API 15 | - [ ] Hook the CLI up to ACR tasks. It should be able to build a container in ACR, then trigger a deploy (either create a new app or upgrade one) 16 | 17 | ## Future Ideas (notes from @asw101 and @arschles discussion) 18 | 19 | - [ ] Add admin "control plane" API to this, and a CLI for it 20 | - `csclr deploy hello-world:latest --platform=VMSS or --platform=ACI ...` 21 | - Also provide a standards-compliant YAML deployment (i.e. KEDA YAML or KNative `Service` YAML) 22 | - [ ] Express container network policy API - translate it to underlying service mesh API 23 | - Use LinkerD for service mesh? 24 | - [ ] Figure out the Front Door ingress situation 25 | - [ ] Once ^^ is done, modify the scaling controller to know (a) what ideal region to create new containers and (b) the "backup" regions (i.e. priority list) to spin containers up in 26 | - [ ] Make the proxy / controller multi-tenant 27 | - [ ] Similar to ^^, add some strategies for general scaling. For example: 28 | - Primary/secondary: scale first in regionA, then in regionB. i.e. a "backup" scenario 29 | - Geographic redundancy: on any scale event, scale up in regionA, regionB and regionC 30 | - Geographic load balancing: scale up in the region closest to me, where there is capacity available. have a "backup" list of regions to scale up in, if the preferred region on that scale event is unavailable 31 | - [ ] Logging mechanism to aggregate all of the requests from the "proxies" (e.g. edge locations) and dump reports into blob store, kusto(?), Azure Arc(?), big data analytics of other kind 32 | - Probably in structured JSON 33 | - Also implement prometheus API (statsd?) 34 | - Also dump scale events 35 | - [ ] Dump traces from containers in from edge to your container, and from your container to other Azure services / generic URLs to App Insights 36 | - [ ] Support versions and rollout events for the customer's app. A/B, Green/Blue and canary. Integrate with tracing and logging 37 | - Tied to "native" scaling events? 38 | - Lean on Front Door for the routing? Just need to clean up the versioned endpoints. See [this ARM template from AaronW](https://github.com/aaronmsft/aaronmsft-com/blob/master/azure-front-door-container-instances-arm/azuredeploy.json) for example on how to do this 39 | - [ ] Preview your code in a PR using a GitHub action 40 | - Have it roll out a new version 41 | 42 | -------------------------------------------------------------------------------- /azure/DEPLOY.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # variables 5 | RESOURCE_GROUP='07-2020-containerscaler' 6 | LOCATION='eastus' 7 | RANDOM_STR='09cscale' 8 | # [[ -z "$RANDOM_STR" ]] && RANDOM_STR=$(openssl rand -hex 3) 9 | REPOSITORY_NAME="containerscaler" 10 | 11 | # create container registry 12 | REGISTRY_NAME="acr${RANDOM_STR}" 13 | az acr create -g $RESOURCE_GROUP -l $LOCATION --name $REGISTRY_NAME --sku Basic --admin-enabled true 14 | # build image 15 | [[ -z "${GITHUB_SHA:-}" ]] && GITHUB_SHA=$(git rev-parse --short HEAD) 16 | CONTAINER_IMAGE=$REPOSITORY_NAME:$(date +%y%m%d)-${GITHUB_SHA} 17 | az acr build -r $REGISTRY_NAME -t $CONTAINER_IMAGE --file Dockerfile . 18 | # create container instance 19 | REGISTRY_PASSWORD=$(az acr credential show -n $REGISTRY_NAME | jq -r .passwords[0].value) 20 | CONTAINER_NAME="aci-${REPOSITORY_NAME}-${RANDOM_STR}" 21 | az container create --resource-group $RESOURCE_GROUP --location $LOCATION \ 22 | --name $CONTAINER_NAME \ 23 | --image "${REGISTRY_NAME}.azurecr.io/${CONTAINER_IMAGE}" \ 24 | --registry-login-server "${REGISTRY_NAME}.azurecr.io" \ 25 | --registry-username $REGISTRY_NAME \ 26 | --registry-password $REGISTRY_PASSWORD \ 27 | --cpu 1 \ 28 | --memory 1 \ 29 | --ports 80 \ 30 | --environment-variables LISTEN_PORT=80 \ 31 | --dns-name-label ${REPOSITORY_NAME}-${RANDOM_STR} 32 | FQDN=$(az container show -g $RESOURCE_GROUP --name $CONTAINER_NAME | jq -r .ipAddress.fqdn) 33 | echo "http://${FQDN}" 34 | -------------------------------------------------------------------------------- /azure/DEPLOY.txt: -------------------------------------------------------------------------------- 1 | ping 2 | -------------------------------------------------------------------------------- /azure/README.md: -------------------------------------------------------------------------------- 1 | # Deploy to Azure with GitHub Actions 2 | 3 | ## 1. Create Azure Service Principal 4 | 5 | Open your local [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) (+[jq](https://stedolan.github.io/jq/download/)), the [Azure Cloud Shell (bash)](https://docs.microsoft.com/en-us/azure/cloud-shell/quickstart) or and run the following snippet: 6 | ```bash 7 | RESOURCE_GROUP='07-2020-containerscaler' 8 | LOCATION='eastus' 9 | SUBSCRIPTION_ID=$(az account show | jq -r .id) 10 | 11 | az group create -n $RESOURCE_GROUP -l $LOCATION 12 | 13 | SP=$(az ad sp create-for-rbac --sdk-auth -n $RESOURCE_GROUP --role contributor \ 14 | --scopes "/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}") 15 | echo $SP | jq -c 16 | ``` 17 | 18 | ## 2. Create GitHub Actions Secret 19 | 20 | Copy the JSON output above and create a secret named `AZURE_CREDENTIALS` under `Settings > Secrets` in your GitHub repository's [Settings](../../../settings/secrets). 21 | 22 | ## 3. Modify [DEPLOY.sh](DEPLOY.sh) and trigger a build 23 | 24 | 1. Set the correct variables for `RESOURCE_GROUP`, etc, under `# variables` in [DEPLOY.sh](DEPLOY.sh). 25 | 1. Modify [DEPLOY.txt](DEPLOY.txt) to trigger a build. 26 | -------------------------------------------------------------------------------- /charts/cscaler-proxy/Chart.yaml: -------------------------------------------------------------------------------- 1 | name: cscaler 2 | apiVersion: v2 3 | type: application 4 | version: 0.0.1 5 | appVersion: 0.0.1 6 | description: CScaler 7 | engine: gotpl 8 | maintainers: 9 | - name: foo 10 | email: bar@gmail.com 11 | metadata: 12 | a: b -------------------------------------------------------------------------------- /charts/cscaler-proxy/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Create a default fully qualified app name. 3 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 4 | */}} 5 | {{- define "fullname" -}} 6 | {{- if .Values.fullnameOverride -}} 7 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 8 | {{- else -}} 9 | {{- $name := default .Chart.Name .Values.nameOverride -}} 10 | {{- if contains $name .Release.Name -}} 11 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 12 | {{- else -}} 13 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 14 | {{- end -}} 15 | {{- end -}} 16 | {{- end -}} 17 | {{- define "readinessPath" -}} 18 | {{- if contains "v0.2.0" .Values.image.tag -}}/{{- else -}}/readyz{{- end -}} 19 | {{- end -}} 20 | -------------------------------------------------------------------------------- /charts/cscaler-proxy/templates/keda-components.yaml: -------------------------------------------------------------------------------- 1 | # this is here so that keda can scale the proxy itself in the future 2 | apiVersion: keda.sh/v1alpha1 3 | kind: ScaledObject 4 | metadata: 5 | name: cscaler-external-scaler 6 | spec: 7 | scaleTargetRef: 8 | kind: Deployment 9 | name: cscaler-proxy 10 | maxReplicaCount: 1000 # Optional. Default: 100 11 | minReplicaCount: 1 12 | cooldownPeriod: 10 13 | pollingInterval: 1 14 | triggers: 15 | - type: external 16 | metadata: 17 | scalerAddress: "{{ .Values.proxyGRPCSvcName }}.{{ .Release.Namespace }}:{{ .Values.proxyGRPCSvcPort }}" 18 | -------------------------------------------------------------------------------- /charts/cscaler-proxy/templates/proxy-admin-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ .Values.proxyAdminSvcName }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | name: {{ .Values.proxyAdminSvcName }} 8 | app: cscaler 9 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 10 | release: "{{ .Release.Name }}" 11 | heritage: "{{ .Release.Service }}" 12 | spec: 13 | type: LoadBalancer 14 | ports: 15 | - name: web 16 | protocol: TCP 17 | port: 80 18 | targetPort: {{ .Values.proxyAdminSvcPort }} 19 | selector: 20 | name: cscaler-proxy 21 | app: {{ template "fullname" . }} 22 | release: "{{ .Release.Name }}" -------------------------------------------------------------------------------- /charts/cscaler-proxy/templates/proxy-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ .Values.proxyDeployName }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | app: cscaler 8 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 9 | release: "{{ .Release.Name }}" 10 | heritage: "{{ .Release.Service }}" 11 | spec: 12 | replicas: {{ .Values.replicaCount }} 13 | selector: 14 | matchLabels: 15 | name: cscaler-proxy 16 | app: {{ template "fullname" . }} 17 | release: "{{ .Release.Name }}" 18 | template: 19 | metadata: 20 | labels: 21 | name: cscaler-proxy 22 | app: {{ template "fullname" . }} 23 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 24 | release: "{{ .Release.Name }}" 25 | spec: 26 | serviceAccountName: cscaler-proxy 27 | containers: 28 | - name: {{ template "fullname" . }} 29 | image: "arschles/cscaler:latest" 30 | imagePullPolicy: Always 31 | ports: 32 | - containerPort: {{ .Values.proxyHTTPSvcPort }} 33 | - containerPort: {{ .Values.proxyAdminSvcPort }} 34 | - containerPort: {{ .Values.proxyGRPCSvcPort }} 35 | env: 36 | - name: "CSCALER_SCALER_ADDRESS" 37 | value: "{{ .Values.proxyGRPCSvcName }}.{{ .Release.Namespace }}:{{ .Values.proxyGRPCSvcPort}}" 38 | - name: ADMIN_PORT 39 | value: {{ .Values.proxyAdminSvcPort | quote }} 40 | - name: PROXY_PORT 41 | value: {{ .Values.proxyHTTPSvcPort | quote }} 42 | - name: GRPC_PORT 43 | value: {{ .Values.proxyGRPCSvcPort | quote }} 44 | - name: CSCALER_NAMESPACE 45 | value: {{ .Release.Namespace }} 46 | -------------------------------------------------------------------------------- /charts/cscaler-proxy/templates/proxy-grpc-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ .Values.proxyGRPCSvcName }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | name: {{ .Values.proxyGRPCSvcName }} 8 | app: cscaler 9 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 10 | release: "{{ .Release.Name }}" 11 | heritage: "{{ .Release.Service }}" 12 | spec: 13 | type: ClusterIP 14 | ports: 15 | - name: grpc 16 | protocol: TCP 17 | port: 9090 18 | targetPort: {{ .Values.proxyGRPCPort }} 19 | selector: 20 | name: cscaler-proxy 21 | app: {{ template "fullname" . }} 22 | release: "{{ .Release.Name }}" -------------------------------------------------------------------------------- /charts/cscaler-proxy/templates/proxy-http-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ .Values.proxyHTTPSvcName }} 5 | namespace: {{ .Release.Namespace }} 6 | labels: 7 | name: {{ .Values.proxyHTTPSvcName }} 8 | app: cscaler 9 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 10 | release: "{{ .Release.Name }}" 11 | heritage: "{{ .Release.Service }}" 12 | spec: 13 | type: LoadBalancer 14 | ports: 15 | - name: web 16 | protocol: TCP 17 | port: 80 18 | targetPort: {{ .Values.proxyHTTPSvcPort }} 19 | selector: 20 | name: cscaler-proxy 21 | app: {{ template "fullname" . }} 22 | release: "{{ .Release.Name }}" -------------------------------------------------------------------------------- /charts/cscaler-proxy/templates/proxy-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | # This role binding allows "jane" to read pods in the "default" namespace. 3 | # You need to already have a Role named "pod-reader" in that namespace. 4 | kind: ClusterRoleBinding 5 | metadata: 6 | name: cscaler-proxy 7 | # namespace: {{ .Release.Namespace }} 8 | subjects: 9 | # You can specify more than one "subject" 10 | - kind: ServiceAccount 11 | name: cscaler-proxy # "name" is case sensitive 12 | namespace: {{ .Release.Namespace }} 13 | roleRef: 14 | # "roleRef" specifies the binding to a Role / ClusterRole 15 | kind: ClusterRole #this must be Role or ClusterRole 16 | name: cscaler-proxy # this must match the name of the Role or ClusterRole you wish to bind to 17 | apiGroup: rbac.authorization.k8s.io 18 | -------------------------------------------------------------------------------- /charts/cscaler-proxy/templates/proxy-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: cscaler-proxy 5 | namespace: {{ .Release.Namespace }} 6 | rules: 7 | - apiGroups: ["", "apps", "scaledobjects.keda.sh", "keda.sh"] # "" indicates the core API group 8 | resources: ["deployments", "services", "scaledobjects"] 9 | verbs: ["get", "watch", "list", "create", "delete"] 10 | -------------------------------------------------------------------------------- /charts/cscaler-proxy/templates/proxy-svc-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: cscaler-proxy 5 | -------------------------------------------------------------------------------- /charts/cscaler-proxy/values.yaml: -------------------------------------------------------------------------------- 1 | replicaCount: 1 2 | 3 | proxyHTTPSvcName: cscaler-proxy 4 | proxyHTTPSvcPort: 8080 5 | 6 | proxyAdminSvcName: cscaler-admin 7 | proxyAdminSvcPort: 8081 8 | 9 | proxyGRPCSvcName: cscaler-proxy-internal 10 | proxyGRPCSvcPort: 9090 11 | 12 | proxyDeployName: cscaler-proxy -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli" 3 | version = "0.1.0" 4 | authors = ["Aaron Schlesinger "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | async-trait = "0.1.41" 11 | futures = "0.3.7" 12 | reqwest = {version = "0.10.8", features = ["json", "blocking"]} 13 | structopt = "0.3.20" 14 | tokio = { version = "0.2.22", features = ["macros"] } 15 | -------------------------------------------------------------------------------- /cli/src/commands/client.rs: -------------------------------------------------------------------------------- 1 | use reqwest::{Error}; 2 | use std::result::Result; 3 | use std::collections::HashMap; 4 | 5 | pub type Res = Result<(), Error>; 6 | 7 | pub trait AppClient { 8 | fn add_app(&mut self, app_name: &str, app_image: &str, port: u32) -> Res; 9 | fn rm_app(&mut self, app_name: &str) -> Res; 10 | } 11 | 12 | pub struct ProdAppClient { 13 | base_deploy_url: String, 14 | client: reqwest::blocking::Client 15 | } 16 | 17 | impl ProdAppClient { 18 | pub fn new(base_deploy_url: &str) -> ProdAppClient { 19 | ProdAppClient{ 20 | base_deploy_url: base_deploy_url.to_string(), 21 | client: reqwest::blocking::Client::new(), 22 | } 23 | } 24 | } 25 | 26 | impl AppClient for ProdAppClient { 27 | fn add_app(&mut self, app_name: &str, app_image: &str, port: u32) 28 | -> Res { 29 | let port_string = port.to_string(); 30 | let mut map = HashMap::new(); 31 | map.insert("name", app_name); 32 | map.insert("image", &app_image); 33 | map.insert("port", &port_string); 34 | 35 | let request_url = format!("{}?name={}", self.base_deploy_url, app_name); 36 | self.client.post(&request_url) 37 | .json(&map) 38 | .send() 39 | .map(|_| ()) 40 | } 41 | 42 | fn rm_app(&mut self, app_name: &str) 43 | -> Res { 44 | let mut map = HashMap::new(); 45 | map.insert("name", app_name); 46 | 47 | let request_url = format!("{}?name={}", self.base_deploy_url, app_name); 48 | self.client.delete(&request_url) 49 | .json(&map) 50 | .send() 51 | .map(|_| ()) 52 | } 53 | } 54 | 55 | pub mod test { 56 | use super::{Res, AppClient}; 57 | use std::vec::Vec; 58 | 59 | #[derive(Debug, PartialEq)] 60 | pub struct AddCall { 61 | pub app_name: String, 62 | pub app_image: String, 63 | pub port: u32, 64 | } 65 | 66 | pub struct TestAppClient { 67 | pub add_counter: u32, 68 | pub add_calls: Vec, 69 | pub rm_counter: u32, 70 | pub rm_calls: Vec, 71 | 72 | // TODO: this is for stubbing out return values 73 | // add_return: Result<(), Error>, 74 | // rm_return: Result<(), Error>, 75 | } 76 | 77 | impl TestAppClient { 78 | pub fn new() 79 | ->TestAppClient { 80 | TestAppClient{ 81 | add_counter: 0, 82 | add_calls: vec![], 83 | rm_counter: 0, 84 | rm_calls: vec![], 85 | } 86 | } 87 | } 88 | 89 | impl AppClient for TestAppClient { 90 | fn add_app(&mut self, app_name: &str, app_image: &str, port: u32) 91 | -> Res { 92 | self.add_counter+=1; 93 | self.add_calls.push(AddCall{ 94 | app_name: app_name.to_string(), 95 | app_image: app_image.to_string(), 96 | port: port, 97 | }); 98 | Ok(()) 99 | } 100 | fn rm_app(&mut self, app_name: &str) 101 | -> Res { 102 | self.rm_counter+=1; 103 | self.rm_calls.push(app_name.to_string()); 104 | Ok(()) 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /cli/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod run; 2 | pub mod rm; 3 | pub mod client; 4 | 5 | pub const DEPLOY_URL: &str ="https://admin.containerapps.dev"; 6 | -------------------------------------------------------------------------------- /cli/src/commands/rm.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::client::{AppClient, Res}; 2 | 3 | pub fn rm(ac: &mut impl AppClient, app_name: &str) 4 | -> Res { 5 | ac.rm_app(app_name) 6 | } 7 | 8 | #[cfg(test)] 9 | mod tests { 10 | use super::*; 11 | use crate::commands::client::test::TestAppClient; 12 | 13 | 14 | #[test] 15 | fn test_run() { 16 | let mut cl = TestAppClient::new(); 17 | let res = rm(&mut cl, "testapp").unwrap(); 18 | 19 | assert_eq!(res, ()); 20 | assert_eq!(cl.rm_counter, 1); 21 | assert_eq!(cl.rm_calls.len(), 1); 22 | assert_eq!(cl.rm_calls[0], "testapp".to_string()); 23 | assert_eq!(cl.add_counter, 0); 24 | assert_eq!(cl.add_calls.len(), 0); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /cli/src/commands/run.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::client::{AppClient, Res}; 2 | 3 | 4 | pub fn run(ac: &mut impl AppClient, app_name: &str, image: &str, port: u32) 5 | -> Res { 6 | ac.add_app(app_name, image, port) 7 | } 8 | 9 | #[cfg(test)] 10 | mod tests { 11 | use super::*; 12 | use crate::commands::client::test::{TestAppClient, AddCall}; 13 | 14 | 15 | #[test] 16 | fn test_run() { 17 | let mut cl = TestAppClient::new(); 18 | let app_name = "testapp"; 19 | let app_image = "testimage"; 20 | let port = 9090; 21 | let res = run(&mut cl, app_name, app_image, port).unwrap(); 22 | 23 | assert_eq!(res, ()); 24 | assert_eq!(cl.rm_counter, 0); 25 | assert_eq!(cl.rm_calls.len(), 0); 26 | assert_eq!(cl.add_counter, 1); 27 | assert_eq!(cl.add_calls.len(), 1); 28 | assert_eq!(cl.add_calls[0], AddCall{ 29 | app_name: app_name.to_string(), 30 | app_image: app_image.to_string(), 31 | port: port, 32 | }) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use structopt::StructOpt; 2 | use std::fmt::Debug; 3 | mod commands; 4 | use reqwest::Error; 5 | use std::result::Result; 6 | use commands::client::ProdAppClient; 7 | 8 | #[derive(Debug, StructOpt)] 9 | enum Command { 10 | Rm { 11 | app_name: String, 12 | }, 13 | Run { 14 | app_name: String, 15 | #[structopt(name="image", short)] 16 | image: String, 17 | #[structopt(name="port", short)] 18 | port: u32, 19 | }, 20 | } 21 | 22 | #[derive(Debug, StructOpt)] 23 | #[structopt(about = "Deploy scalable, production ready containers to Kubernetes")] 24 | struct KedaHTTP { 25 | #[structopt(subcommand)] 26 | cmd: Command, 27 | } 28 | 29 | fn main() -> Result<(), Error> { 30 | let keda = KedaHTTP::from_args(); 31 | let admin_url = format!("{}/app", commands::DEPLOY_URL); 32 | let mut app_client = ProdAppClient::new(&admin_url); 33 | match keda.cmd { 34 | Command::Rm{app_name} => { 35 | match commands::rm::rm(&mut app_client, &app_name) { 36 | Ok(_) => { 37 | println!("Removed {}", app_name) 38 | }, 39 | Err(e) => { 40 | println!("Error removing app ({})", e) 41 | }, 42 | } 43 | }, 44 | Command::Run{app_name, image, port} => { 45 | match commands::run::run(&mut app_client, &app_name, &image, port) { 46 | Ok(_) => { 47 | println!("Deployed app") 48 | 49 | }, 50 | Err(e) => { 51 | println!("Error deploying ({})", e) 52 | }, 53 | }; 54 | }, 55 | }; 56 | Ok(()) 57 | } 58 | -------------------------------------------------------------------------------- /cmd/proxy/admin_handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "strconv" 6 | 7 | "github.com/arschles/containerscaler/pkg/k8s" 8 | echo "github.com/labstack/echo/v4" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | "k8s.io/client-go/dynamic" 11 | "k8s.io/client-go/kubernetes" 12 | ) 13 | 14 | func newAdminDeleteAppHandler( 15 | k8sCl *kubernetes.Clientset, 16 | dynCl dynamic.Interface, 17 | namespace string, 18 | ) echo.HandlerFunc { 19 | return func(c echo.Context) error { 20 | logger := c.Logger() 21 | ctx := c.Request().Context() 22 | deployName := c.QueryParam("name") 23 | scaledObjectCl := k8s.NewScaledObjectClient(dynCl).Namespace(namespace) 24 | if deployName == "" { 25 | logger.Errorf("'name' query param not found") 26 | return c.String(400, "'name' query param required") 27 | } 28 | delSvcErr := k8s.DeleteService( 29 | ctx, 30 | namespace, 31 | deployName, 32 | k8sCl.CoreV1().Services(namespace), 33 | ) 34 | if delSvcErr != nil { 35 | logger.Errorf("Deleting service %s (%s)", delSvcErr) 36 | return c.String(500, "deleting service") 37 | } 38 | if err := k8s.DeleteDeployment(ctx, deployName, k8sCl.AppsV1().Deployments(namespace)); err != nil { 39 | logger.Errorf("Deleting deployment %s (%s)", deployName, err) 40 | return c.String(500, "deleting deployment") 41 | } 42 | if err := k8s.DeleteScaledObject(ctx, deployName, scaledObjectCl); err != nil { 43 | logger.Errorf("Deleting scaledobject %s (%s)", deployName, err) 44 | return c.String(500, "deleting scaledobject") 45 | } 46 | c.String(200, "deleted") 47 | return nil 48 | } 49 | } 50 | 51 | func newAdminCreateAppHandler( 52 | k8sCl *kubernetes.Clientset, 53 | dynCl dynamic.Interface, 54 | scalerAddress, 55 | namespace string, 56 | ) echo.HandlerFunc { 57 | 58 | type reqBody struct { 59 | Name string `json:"name"` 60 | ContainerImage string `json:"image"` 61 | Port string `json:"port"` 62 | } 63 | 64 | return func(c echo.Context) error { 65 | r := c.Request() 66 | ctx := c.Request().Context() 67 | req := new(reqBody) 68 | defer r.Body.Close() 69 | if err := json.NewDecoder(r.Body).Decode(req); err != nil { 70 | c.Logger().Errorf("Decoding request (%s)", err) 71 | return c.String(400, "decoding request") 72 | } 73 | 74 | portInt, err := strconv.Atoi(req.Port) 75 | if err != nil { 76 | c.Logger().Errorf("Invalid port %s (%s)", req.Port, err) 77 | return c.String(400, "invalid port") 78 | } 79 | 80 | appsCl := k8sCl.AppsV1().Deployments(namespace) 81 | deployment := k8s.NewDeployment(ctx, namespace, req.Name, req.ContainerImage, int32(portInt)) 82 | // TODO: watch the deployment until it reaches ready state 83 | if _, err := appsCl.Create(ctx, deployment, metav1.CreateOptions{}); err != nil { 84 | c.Logger().Errorf("Creating deployment (%s)", err) 85 | return c.String(500, "creating deployment") 86 | } 87 | 88 | coreCl := k8sCl.CoreV1().Services(namespace) 89 | service := k8s.NewService(namespace, req.Name, int32(portInt)) 90 | if _, err := coreCl.Create(ctx, service, metav1.CreateOptions{}); err != nil { 91 | c.Logger().Errorf("Creating service (%s)", err) 92 | return c.String(500, "creating service") 93 | } 94 | scaledObjectCl := k8s.NewScaledObjectClient(dynCl) 95 | _, err = scaledObjectCl.Namespace(namespace).Create(ctx, k8s.NewScaledObject( 96 | namespace, 97 | req.Name, 98 | req.Name, 99 | scalerAddress, 100 | ), metav1.CreateOptions{}) 101 | if err != nil { 102 | c.Logger().Errorf("Creating scaledobject (%s)", err) 103 | return c.String(500, "creating scaledobject") 104 | } 105 | 106 | return nil 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /cmd/proxy/grpc_impl.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | context "context" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/arschles/containerscaler/externalscaler" 9 | empty "github.com/golang/protobuf/ptypes/empty" 10 | ) 11 | 12 | func init() { 13 | rand.Seed(time.Now().UnixNano()) 14 | } 15 | 16 | type impl struct { 17 | reqCounter *reqCounter 18 | externalscaler.UnimplementedExternalScalerServer 19 | } 20 | 21 | func newImpl(reqCounter *reqCounter) *impl { 22 | return &impl{reqCounter: reqCounter} 23 | } 24 | 25 | func (e *impl) Ping(context.Context, *empty.Empty) (*empty.Empty, error) { 26 | return &empty.Empty{}, nil 27 | } 28 | 29 | func (e *impl) IsActive( 30 | ctx context.Context, 31 | in *externalscaler.ScaledObjectRef, 32 | ) (*externalscaler.IsActiveResponse, error) { 33 | return &externalscaler.IsActiveResponse{ 34 | Result: true, 35 | }, nil 36 | } 37 | 38 | func (e *impl) StreamIsActive( 39 | in *externalscaler.ScaledObjectRef, 40 | server externalscaler.ExternalScaler_StreamIsActiveServer, 41 | ) error { 42 | ticker := time.NewTicker(200 * time.Millisecond) 43 | defer ticker.Stop() 44 | for { 45 | select { 46 | case <-server.Context().Done(): 47 | return nil 48 | case <-ticker.C: 49 | server.Send(&externalscaler.IsActiveResponse{ 50 | Result: true, 51 | }) 52 | } 53 | } 54 | return nil 55 | } 56 | 57 | func (e *impl) GetMetricSpec( 58 | ctx context.Context, 59 | in *externalscaler.ScaledObjectRef, 60 | ) (*externalscaler.GetMetricSpecResponse, error) { 61 | return &externalscaler.GetMetricSpecResponse{ 62 | MetricSpecs: []*externalscaler.MetricSpec{ 63 | { 64 | MetricName: "proxyCounter", 65 | TargetSize: 100, 66 | }, 67 | }, 68 | }, nil 69 | } 70 | func (e *impl) GetMetrics( 71 | ctx context.Context, 72 | in *externalscaler.GetMetricsRequest, 73 | ) (*externalscaler.GetMetricsResponse, error) { 74 | return &externalscaler.GetMetricsResponse{ 75 | MetricValues: []*externalscaler.MetricValue{ 76 | { 77 | MetricName: "proxyCounter", 78 | MetricValue: int64(e.reqCounter.get()), 79 | }, 80 | }, 81 | }, nil 82 | } 83 | -------------------------------------------------------------------------------- /cmd/proxy/handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "net/http/httputil" 8 | "net/url" 9 | "strings" 10 | 11 | echo "github.com/labstack/echo/v4" 12 | ) 13 | 14 | func getSvcName(host string) (string, error) { 15 | hostSpl := strings.Split(host, ".") 16 | log.Printf("split for host %s: %v", host, hostSpl) 17 | if len(hostSpl) != 3 { 18 | return "", fmt.Errorf("Host string %s malformed", host) 19 | } 20 | return hostSpl[0], nil 21 | } 22 | 23 | // TODO: use proxy handler: https://echo.labstack.com/middleware/proxy ?? 24 | func newForwardingHandler() echo.HandlerFunc { 25 | return func(c echo.Context) error { 26 | 27 | svcName, err := getSvcName(c.Request().Host) 28 | if err != nil { 29 | log.Printf("Couldn't find service name (%s)", err) 30 | return c.String(400, "No service name given") 31 | } 32 | hostPortStr := fmt.Sprintf("http://%s:8080", svcName) 33 | log.Printf("using container URL %s", hostPortStr) 34 | 35 | // forward the request 36 | svcURL, err := url.Parse(hostPortStr) 37 | if err != nil { 38 | log.Printf( 39 | "Error parsing container URL string %s (%s)", 40 | hostPortStr, 41 | err, 42 | ) 43 | return c.String(400, fmt.Sprintf("Error parsing URL string %s (%s)", hostPortStr, err)) 44 | } 45 | 46 | r := c.Request() 47 | 48 | proxy := httputil.NewSingleHostReverseProxy(svcURL) 49 | proxy.Director = func(req *http.Request) { 50 | req.URL = svcURL 51 | req.Host = svcURL.Host 52 | // req.URL.Scheme = "https" 53 | req.URL.Path = r.URL.Path 54 | req.URL.RawQuery = r.URL.RawQuery 55 | // req.URL.Host = containerURL.Host 56 | // req.URL.Path = containerURL.Path 57 | reqBytes, _ := httputil.DumpRequest(req, false) 58 | log.Printf("Proxying request to %v", *req.URL) 59 | log.Printf("with body %s", string(reqBytes)) 60 | } 61 | proxy.ModifyResponse = func(res *http.Response) error { 62 | respBody, _ := httputil.DumpResponse(res, true) 63 | log.Printf("Proxied response: %v", string(respBody)) 64 | return nil 65 | } 66 | 67 | log.Printf( 68 | "Proxying request to %s to host %s", 69 | r.URL.Path, 70 | hostPortStr, 71 | ) 72 | w := c.Response() 73 | proxy.ServeHTTP(w, r) 74 | return nil 75 | } 76 | } 77 | 78 | func newHealthCheckHandler() echo.HandlerFunc { 79 | return func(c echo.Context) error { 80 | // handle Azure Front Door health checks 81 | return c.String(200, "OK") 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /cmd/proxy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "net" 8 | "net/http/httputil" 9 | "os" 10 | "sync" 11 | "time" 12 | 13 | "github.com/arschles/containerscaler/externalscaler" 14 | "github.com/arschles/containerscaler/pkg/k8s" 15 | "github.com/arschles/containerscaler/pkg/srv" 16 | echo "github.com/labstack/echo/v4" 17 | "github.com/labstack/echo/v4/middleware" 18 | "google.golang.org/grpc" 19 | ) 20 | 21 | const ( 22 | clusterID = "test-cluster" 23 | clientID = "cscaler-client" 24 | ) 25 | 26 | func init() { 27 | rand.Seed(time.Now().UnixNano()) 28 | } 29 | 30 | func main() { 31 | namespace := os.Getenv("CSCALER_NAMESPACE") 32 | if namespace == "" { 33 | log.Fatalf("CSCALER_NAMESPACE missing") 34 | } 35 | scalerAddress := os.Getenv("CSCALER_SCALER_ADDRESS") 36 | if scalerAddress == "" { 37 | log.Fatalf("Need CSCALER_SCALER_ADDRESS") 38 | } 39 | log.Printf("Using CSCALER_SCALER_ADDRESS %s", scalerAddress) 40 | reqCounter := &reqCounter{i: 0, mut: new(sync.RWMutex)} 41 | 42 | e := echo.New() 43 | 44 | e.Use(middleware.Logger()) 45 | e.Use(userAgentHandler()) 46 | countM := countMiddleware(reqCounter) 47 | 48 | e.Any("/*", newForwardingHandler(), countM) 49 | 50 | adminE := echo.New() 51 | adminE.Use(middleware.Logger()) 52 | 53 | clientset, dynCl, err := k8s.NewClientset() 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | 58 | adminE.POST("/app", newAdminCreateAppHandler(clientset, dynCl, scalerAddress, namespace)) 59 | adminE.DELETE("/app", newAdminDeleteAppHandler(clientset, dynCl, namespace)) 60 | adminE.GET("/pong", pongHandler) 61 | adminE.GET("/counter", func(c echo.Context) error { 62 | fmt.Fprintf(c.Response(), "%d", reqCounter.get()) 63 | return nil 64 | }) 65 | 66 | go func() { 67 | port := fmt.Sprintf(":%s", srv.EnvOr("ADMIN_PORT", "8081")) 68 | log.Printf("admin server listening on port %s", port) 69 | log.Fatal(adminE.Start(port)) 70 | }() 71 | go func() { 72 | port := fmt.Sprintf(":%s", srv.EnvOr("PROXY_PORT", "8080")) 73 | log.Printf("proxy listening on port %s", port) 74 | log.Fatal(e.Start(port)) 75 | }() 76 | go func() { 77 | port := fmt.Sprintf(":%s", srv.EnvOr("GRPC_PORT", "9090")) 78 | log.Printf("GRPC listening on port %s", port) 79 | log.Fatal(startGrpcServer(port, reqCounter)) 80 | }() 81 | select {} 82 | } 83 | 84 | func startGrpcServer(port string, ctr *reqCounter) error { 85 | lis, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0%s", port)) 86 | if err != nil { 87 | log.Fatalf("failed to listen: %v", err) 88 | } 89 | grpcServer := grpc.NewServer() 90 | externalscaler.RegisterExternalScalerServer(grpcServer, newImpl(ctr)) 91 | return grpcServer.Serve(lis) 92 | } 93 | 94 | func pongHandler(c echo.Context) error { 95 | reqBytes, err := httputil.DumpRequest(c.Request(), true) 96 | if err != nil { 97 | return c.String(500, err.Error()) 98 | } 99 | return c.String(200, string(reqBytes)) 100 | } 101 | -------------------------------------------------------------------------------- /cmd/proxy/middleware.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/labstack/echo/v4" 5 | ) 6 | 7 | func countMiddleware(counter *reqCounter) echo.MiddlewareFunc { 8 | countMiddleware := func(fn echo.HandlerFunc) echo.HandlerFunc { 9 | return func(c echo.Context) error { 10 | // TODO: need to figure out a way to get the increment 11 | // to happen before fn(w, r) happens below. otherwise, 12 | // the counter won't get incremented right away and the actual 13 | // handler will hang longer than it needs to 14 | go func() { 15 | counter.inc() 16 | }() 17 | defer func() { 18 | counter.dec() 19 | }() 20 | fn(c) 21 | return nil 22 | } 23 | } 24 | return countMiddleware 25 | } 26 | 27 | func userAgentHandler() echo.MiddlewareFunc { 28 | return func(fn echo.HandlerFunc) echo.HandlerFunc { 29 | return func(c echo.Context) error { 30 | c.Request().Header.Set("User-Agent", "cscaler-echo") 31 | return fn(c) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cmd/proxy/req_counter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | type reqCounter struct { 6 | i int 7 | mut *sync.RWMutex 8 | } 9 | 10 | func (r *reqCounter) inc() { 11 | r.mut.Lock() 12 | defer r.mut.Unlock() 13 | r.i++ 14 | } 15 | 16 | func (r *reqCounter) dec() { 17 | r.mut.Lock() 18 | defer r.mut.Unlock() 19 | r.i-- 20 | } 21 | 22 | func (r *reqCounter) get() int { 23 | r.mut.RLock() 24 | defer r.mut.RUnlock() 25 | return r.i 26 | } 27 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | nats-server: 4 | image: 'nats:2.1.7-scratch' 5 | expose: 6 | - "4222" 7 | hostname: nats-server 8 | containerscaler: 9 | build: 10 | context: . 11 | dockerfile: .devcontainer/Dockerfile 12 | depends_on: 13 | - nats-server 14 | volumes: 15 | - .:/workspace:cached 16 | command: /bin/sh -c "while sleep 1000; do :; done" 17 | -------------------------------------------------------------------------------- /docs/COMPONENTS.md: -------------------------------------------------------------------------------- 1 | # Components of the System 2 | 3 | The entire system runs inside of Kubernetes and takes advantage of Kubernetes features. It cannot run outside of Kubernetes, but this functionality might be built in the future. 4 | 5 | ## The Proxy 6 | 7 | The proxy is primarily responsible for accepting requests from the internet and forwarding them to the right _backend_. A backend is a set of containers that can scale up and down, including to 0 containers. 8 | 9 | ### Processing a Request 10 | 11 | An incoming request to the system will reach the proxy first. When it does, the proxy first publishes an event to [Redis](https://redis.io). After this point, it forwards the request to a [Kubernetes `Service`](https://kubernetes.io/docs/concepts/services-networking/service/) for the backend intended to serve the request. This backend might not currently have any running containers. 12 | 13 | KEDA (detailed below) is responsible for scaling (up and down) the pods that the `Service` load balances over. 14 | 15 | ### Creating a New Backend 16 | 17 | The proxy also has an "admin" API that is not intended to be exposed to the internet without authentication. Generally speaking, a command line tool or cloud portal would do operations on this API. 18 | 19 | The API supports two major operations: (a) create new backend and (b) delete backend. 20 | 21 | #### Create new Backend 22 | 23 | When the proxy gets a request to create a new backend, it does the following: 24 | 25 | - Create a new Kubernetes [`Deployment`](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)s and [`Service`](https://kubernetes.io/docs/concepts/services-networking/service/)s when a new application is created 26 | - Create a Kubernetes [`Service`](https://kubernetes.io/docs/concepts/services-networking/service/) that forwards to and load-balances over the pods in the `Deployment` 27 | - Create a new KEDA [`ScaledObject`](https://keda.sh/docs/1.5/concepts/scaling-deployments/#scaledobject-spec) to indicate that the pods in the deployment should be scaled based on NATS traffic (more on all of this next) 28 | 29 | #### Delete a Backend 30 | 31 | Deleting a backend reverses the steps in the "create" step. That means it will delete all of the resources that it created there. 32 | 33 | ## [KEDA](https://keda.sh) 34 | 35 | Behind the scenes, the prometheus the proxy publishes are consumed by [KEDA](https://keda.sh). KEDA is responsible for responding to these events and scale up / down the number of pods in the `Deployment` mentioned above. The `Service` that the proxy forwards to provides a stable endpoint that load balances over all of the pods, regardless of how many there are. 36 | 37 | -------------------------------------------------------------------------------- /externalscaler/kedascaler.external.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.25.0 4 | // protoc v3.11.4 5 | // source: kedascaler.external.proto 6 | 7 | package externalscaler 8 | 9 | import ( 10 | proto "github.com/golang/protobuf/proto" 11 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 12 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 13 | reflect "reflect" 14 | sync "sync" 15 | ) 16 | 17 | const ( 18 | // Verify that this generated code is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 20 | // Verify that runtime/protoimpl is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 22 | ) 23 | 24 | // This is a compile-time assertion that a sufficiently up-to-date version 25 | // of the legacy proto package is being used. 26 | const _ = proto.ProtoPackageIsVersion4 27 | 28 | type ScaledObjectRef struct { 29 | state protoimpl.MessageState 30 | sizeCache protoimpl.SizeCache 31 | unknownFields protoimpl.UnknownFields 32 | 33 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 34 | Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` 35 | 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"` 36 | } 37 | 38 | func (x *ScaledObjectRef) Reset() { 39 | *x = ScaledObjectRef{} 40 | if protoimpl.UnsafeEnabled { 41 | mi := &file_kedascaler_external_proto_msgTypes[0] 42 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 43 | ms.StoreMessageInfo(mi) 44 | } 45 | } 46 | 47 | func (x *ScaledObjectRef) String() string { 48 | return protoimpl.X.MessageStringOf(x) 49 | } 50 | 51 | func (*ScaledObjectRef) ProtoMessage() {} 52 | 53 | func (x *ScaledObjectRef) ProtoReflect() protoreflect.Message { 54 | mi := &file_kedascaler_external_proto_msgTypes[0] 55 | if protoimpl.UnsafeEnabled && x != nil { 56 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 57 | if ms.LoadMessageInfo() == nil { 58 | ms.StoreMessageInfo(mi) 59 | } 60 | return ms 61 | } 62 | return mi.MessageOf(x) 63 | } 64 | 65 | // Deprecated: Use ScaledObjectRef.ProtoReflect.Descriptor instead. 66 | func (*ScaledObjectRef) Descriptor() ([]byte, []int) { 67 | return file_kedascaler_external_proto_rawDescGZIP(), []int{0} 68 | } 69 | 70 | func (x *ScaledObjectRef) GetName() string { 71 | if x != nil { 72 | return x.Name 73 | } 74 | return "" 75 | } 76 | 77 | func (x *ScaledObjectRef) GetNamespace() string { 78 | if x != nil { 79 | return x.Namespace 80 | } 81 | return "" 82 | } 83 | 84 | func (x *ScaledObjectRef) GetScalerMetadata() map[string]string { 85 | if x != nil { 86 | return x.ScalerMetadata 87 | } 88 | return nil 89 | } 90 | 91 | type IsActiveResponse struct { 92 | state protoimpl.MessageState 93 | sizeCache protoimpl.SizeCache 94 | unknownFields protoimpl.UnknownFields 95 | 96 | Result bool `protobuf:"varint,1,opt,name=result,proto3" json:"result,omitempty"` 97 | } 98 | 99 | func (x *IsActiveResponse) Reset() { 100 | *x = IsActiveResponse{} 101 | if protoimpl.UnsafeEnabled { 102 | mi := &file_kedascaler_external_proto_msgTypes[1] 103 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 104 | ms.StoreMessageInfo(mi) 105 | } 106 | } 107 | 108 | func (x *IsActiveResponse) String() string { 109 | return protoimpl.X.MessageStringOf(x) 110 | } 111 | 112 | func (*IsActiveResponse) ProtoMessage() {} 113 | 114 | func (x *IsActiveResponse) ProtoReflect() protoreflect.Message { 115 | mi := &file_kedascaler_external_proto_msgTypes[1] 116 | if protoimpl.UnsafeEnabled && x != nil { 117 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 118 | if ms.LoadMessageInfo() == nil { 119 | ms.StoreMessageInfo(mi) 120 | } 121 | return ms 122 | } 123 | return mi.MessageOf(x) 124 | } 125 | 126 | // Deprecated: Use IsActiveResponse.ProtoReflect.Descriptor instead. 127 | func (*IsActiveResponse) Descriptor() ([]byte, []int) { 128 | return file_kedascaler_external_proto_rawDescGZIP(), []int{1} 129 | } 130 | 131 | func (x *IsActiveResponse) GetResult() bool { 132 | if x != nil { 133 | return x.Result 134 | } 135 | return false 136 | } 137 | 138 | type GetMetricSpecResponse struct { 139 | state protoimpl.MessageState 140 | sizeCache protoimpl.SizeCache 141 | unknownFields protoimpl.UnknownFields 142 | 143 | MetricSpecs []*MetricSpec `protobuf:"bytes,1,rep,name=metricSpecs,proto3" json:"metricSpecs,omitempty"` 144 | } 145 | 146 | func (x *GetMetricSpecResponse) Reset() { 147 | *x = GetMetricSpecResponse{} 148 | if protoimpl.UnsafeEnabled { 149 | mi := &file_kedascaler_external_proto_msgTypes[2] 150 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 151 | ms.StoreMessageInfo(mi) 152 | } 153 | } 154 | 155 | func (x *GetMetricSpecResponse) String() string { 156 | return protoimpl.X.MessageStringOf(x) 157 | } 158 | 159 | func (*GetMetricSpecResponse) ProtoMessage() {} 160 | 161 | func (x *GetMetricSpecResponse) ProtoReflect() protoreflect.Message { 162 | mi := &file_kedascaler_external_proto_msgTypes[2] 163 | if protoimpl.UnsafeEnabled && x != nil { 164 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 165 | if ms.LoadMessageInfo() == nil { 166 | ms.StoreMessageInfo(mi) 167 | } 168 | return ms 169 | } 170 | return mi.MessageOf(x) 171 | } 172 | 173 | // Deprecated: Use GetMetricSpecResponse.ProtoReflect.Descriptor instead. 174 | func (*GetMetricSpecResponse) Descriptor() ([]byte, []int) { 175 | return file_kedascaler_external_proto_rawDescGZIP(), []int{2} 176 | } 177 | 178 | func (x *GetMetricSpecResponse) GetMetricSpecs() []*MetricSpec { 179 | if x != nil { 180 | return x.MetricSpecs 181 | } 182 | return nil 183 | } 184 | 185 | type MetricSpec struct { 186 | state protoimpl.MessageState 187 | sizeCache protoimpl.SizeCache 188 | unknownFields protoimpl.UnknownFields 189 | 190 | MetricName string `protobuf:"bytes,1,opt,name=metricName,proto3" json:"metricName,omitempty"` 191 | TargetSize int64 `protobuf:"varint,2,opt,name=targetSize,proto3" json:"targetSize,omitempty"` 192 | } 193 | 194 | func (x *MetricSpec) Reset() { 195 | *x = MetricSpec{} 196 | if protoimpl.UnsafeEnabled { 197 | mi := &file_kedascaler_external_proto_msgTypes[3] 198 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 199 | ms.StoreMessageInfo(mi) 200 | } 201 | } 202 | 203 | func (x *MetricSpec) String() string { 204 | return protoimpl.X.MessageStringOf(x) 205 | } 206 | 207 | func (*MetricSpec) ProtoMessage() {} 208 | 209 | func (x *MetricSpec) ProtoReflect() protoreflect.Message { 210 | mi := &file_kedascaler_external_proto_msgTypes[3] 211 | if protoimpl.UnsafeEnabled && x != nil { 212 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 213 | if ms.LoadMessageInfo() == nil { 214 | ms.StoreMessageInfo(mi) 215 | } 216 | return ms 217 | } 218 | return mi.MessageOf(x) 219 | } 220 | 221 | // Deprecated: Use MetricSpec.ProtoReflect.Descriptor instead. 222 | func (*MetricSpec) Descriptor() ([]byte, []int) { 223 | return file_kedascaler_external_proto_rawDescGZIP(), []int{3} 224 | } 225 | 226 | func (x *MetricSpec) GetMetricName() string { 227 | if x != nil { 228 | return x.MetricName 229 | } 230 | return "" 231 | } 232 | 233 | func (x *MetricSpec) GetTargetSize() int64 { 234 | if x != nil { 235 | return x.TargetSize 236 | } 237 | return 0 238 | } 239 | 240 | type GetMetricsRequest struct { 241 | state protoimpl.MessageState 242 | sizeCache protoimpl.SizeCache 243 | unknownFields protoimpl.UnknownFields 244 | 245 | ScaledObjectRef *ScaledObjectRef `protobuf:"bytes,1,opt,name=scaledObjectRef,proto3" json:"scaledObjectRef,omitempty"` 246 | MetricName string `protobuf:"bytes,2,opt,name=metricName,proto3" json:"metricName,omitempty"` 247 | } 248 | 249 | func (x *GetMetricsRequest) Reset() { 250 | *x = GetMetricsRequest{} 251 | if protoimpl.UnsafeEnabled { 252 | mi := &file_kedascaler_external_proto_msgTypes[4] 253 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 254 | ms.StoreMessageInfo(mi) 255 | } 256 | } 257 | 258 | func (x *GetMetricsRequest) String() string { 259 | return protoimpl.X.MessageStringOf(x) 260 | } 261 | 262 | func (*GetMetricsRequest) ProtoMessage() {} 263 | 264 | func (x *GetMetricsRequest) ProtoReflect() protoreflect.Message { 265 | mi := &file_kedascaler_external_proto_msgTypes[4] 266 | if protoimpl.UnsafeEnabled && x != nil { 267 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 268 | if ms.LoadMessageInfo() == nil { 269 | ms.StoreMessageInfo(mi) 270 | } 271 | return ms 272 | } 273 | return mi.MessageOf(x) 274 | } 275 | 276 | // Deprecated: Use GetMetricsRequest.ProtoReflect.Descriptor instead. 277 | func (*GetMetricsRequest) Descriptor() ([]byte, []int) { 278 | return file_kedascaler_external_proto_rawDescGZIP(), []int{4} 279 | } 280 | 281 | func (x *GetMetricsRequest) GetScaledObjectRef() *ScaledObjectRef { 282 | if x != nil { 283 | return x.ScaledObjectRef 284 | } 285 | return nil 286 | } 287 | 288 | func (x *GetMetricsRequest) GetMetricName() string { 289 | if x != nil { 290 | return x.MetricName 291 | } 292 | return "" 293 | } 294 | 295 | type GetMetricsResponse struct { 296 | state protoimpl.MessageState 297 | sizeCache protoimpl.SizeCache 298 | unknownFields protoimpl.UnknownFields 299 | 300 | MetricValues []*MetricValue `protobuf:"bytes,1,rep,name=metricValues,proto3" json:"metricValues,omitempty"` 301 | } 302 | 303 | func (x *GetMetricsResponse) Reset() { 304 | *x = GetMetricsResponse{} 305 | if protoimpl.UnsafeEnabled { 306 | mi := &file_kedascaler_external_proto_msgTypes[5] 307 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 308 | ms.StoreMessageInfo(mi) 309 | } 310 | } 311 | 312 | func (x *GetMetricsResponse) String() string { 313 | return protoimpl.X.MessageStringOf(x) 314 | } 315 | 316 | func (*GetMetricsResponse) ProtoMessage() {} 317 | 318 | func (x *GetMetricsResponse) ProtoReflect() protoreflect.Message { 319 | mi := &file_kedascaler_external_proto_msgTypes[5] 320 | if protoimpl.UnsafeEnabled && x != nil { 321 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 322 | if ms.LoadMessageInfo() == nil { 323 | ms.StoreMessageInfo(mi) 324 | } 325 | return ms 326 | } 327 | return mi.MessageOf(x) 328 | } 329 | 330 | // Deprecated: Use GetMetricsResponse.ProtoReflect.Descriptor instead. 331 | func (*GetMetricsResponse) Descriptor() ([]byte, []int) { 332 | return file_kedascaler_external_proto_rawDescGZIP(), []int{5} 333 | } 334 | 335 | func (x *GetMetricsResponse) GetMetricValues() []*MetricValue { 336 | if x != nil { 337 | return x.MetricValues 338 | } 339 | return nil 340 | } 341 | 342 | type MetricValue struct { 343 | state protoimpl.MessageState 344 | sizeCache protoimpl.SizeCache 345 | unknownFields protoimpl.UnknownFields 346 | 347 | MetricName string `protobuf:"bytes,1,opt,name=metricName,proto3" json:"metricName,omitempty"` 348 | MetricValue int64 `protobuf:"varint,2,opt,name=metricValue,proto3" json:"metricValue,omitempty"` 349 | } 350 | 351 | func (x *MetricValue) Reset() { 352 | *x = MetricValue{} 353 | if protoimpl.UnsafeEnabled { 354 | mi := &file_kedascaler_external_proto_msgTypes[6] 355 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 356 | ms.StoreMessageInfo(mi) 357 | } 358 | } 359 | 360 | func (x *MetricValue) String() string { 361 | return protoimpl.X.MessageStringOf(x) 362 | } 363 | 364 | func (*MetricValue) ProtoMessage() {} 365 | 366 | func (x *MetricValue) ProtoReflect() protoreflect.Message { 367 | mi := &file_kedascaler_external_proto_msgTypes[6] 368 | if protoimpl.UnsafeEnabled && x != nil { 369 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 370 | if ms.LoadMessageInfo() == nil { 371 | ms.StoreMessageInfo(mi) 372 | } 373 | return ms 374 | } 375 | return mi.MessageOf(x) 376 | } 377 | 378 | // Deprecated: Use MetricValue.ProtoReflect.Descriptor instead. 379 | func (*MetricValue) Descriptor() ([]byte, []int) { 380 | return file_kedascaler_external_proto_rawDescGZIP(), []int{6} 381 | } 382 | 383 | func (x *MetricValue) GetMetricName() string { 384 | if x != nil { 385 | return x.MetricName 386 | } 387 | return "" 388 | } 389 | 390 | func (x *MetricValue) GetMetricValue() int64 { 391 | if x != nil { 392 | return x.MetricValue 393 | } 394 | return 0 395 | } 396 | 397 | var File_kedascaler_external_proto protoreflect.FileDescriptor 398 | 399 | var file_kedascaler_external_proto_rawDesc = []byte{ 400 | 0x0a, 0x19, 0x6b, 0x65, 0x64, 0x61, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x2e, 0x65, 0x78, 0x74, 401 | 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x65, 0x78, 0x74, 402 | 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x22, 0xe3, 0x01, 0x0a, 0x0f, 403 | 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x12, 404 | 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 405 | 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 406 | 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 407 | 0x65, 0x12, 0x5b, 0x0a, 0x0e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 408 | 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x65, 0x78, 0x74, 0x65, 409 | 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x2e, 0x53, 0x63, 0x61, 0x6c, 0x65, 410 | 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x2e, 0x53, 0x63, 0x61, 0x6c, 0x65, 411 | 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 412 | 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x41, 413 | 0x0a, 0x13, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 414 | 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 415 | 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 416 | 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 417 | 0x01, 0x22, 0x2a, 0x0a, 0x10, 0x49, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x73, 418 | 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 419 | 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x55, 0x0a, 420 | 0x15, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x70, 0x65, 0x63, 0x52, 0x65, 421 | 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 422 | 0x53, 0x70, 0x65, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x65, 0x78, 423 | 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 424 | 0x72, 0x69, 0x63, 0x53, 0x70, 0x65, 0x63, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 425 | 0x70, 0x65, 0x63, 0x73, 0x22, 0x4c, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x70, 426 | 0x65, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 427 | 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 428 | 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 429 | 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x69, 430 | 0x7a, 0x65, 0x22, 0x7e, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 431 | 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x0f, 0x73, 0x63, 0x61, 0x6c, 0x65, 432 | 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 433 | 0x32, 0x1f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 434 | 0x72, 0x2e, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 435 | 0x66, 0x52, 0x0f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 436 | 0x65, 0x66, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 437 | 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 438 | 0x6d, 0x65, 0x22, 0x55, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 439 | 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0c, 0x6d, 0x65, 0x74, 0x72, 440 | 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 441 | 0x2e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x2e, 442 | 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x6d, 0x65, 0x74, 443 | 0x72, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 444 | 0x72, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x74, 0x72, 445 | 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 446 | 0x74, 0x72, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 447 | 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6d, 448 | 0x65, 0x74, 0x72, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x32, 0xec, 0x02, 0x0a, 0x0e, 0x45, 449 | 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x12, 0x4f, 0x0a, 450 | 0x08, 0x49, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x1f, 0x2e, 0x65, 0x78, 0x74, 0x65, 451 | 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x2e, 0x53, 0x63, 0x61, 0x6c, 0x65, 452 | 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x1a, 0x20, 0x2e, 0x65, 0x78, 0x74, 453 | 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x2e, 0x49, 0x73, 0x41, 0x63, 454 | 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 455 | 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 456 | 0x12, 0x1f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 457 | 0x72, 0x2e, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 458 | 0x66, 0x1a, 0x20, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 459 | 0x65, 0x72, 0x2e, 0x49, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 460 | 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x59, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4d, 0x65, 461 | 0x74, 0x72, 0x69, 0x63, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x72, 462 | 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x2e, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x64, 463 | 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x1a, 0x25, 0x2e, 0x65, 0x78, 0x74, 0x65, 464 | 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 465 | 0x74, 0x72, 0x69, 0x63, 0x53, 0x70, 0x65, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 466 | 0x22, 0x00, 0x12, 0x55, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 467 | 0x12, 0x21, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 468 | 0x72, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 469 | 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 470 | 0x61, 0x6c, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 471 | 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x12, 0x5a, 0x10, 0x2e, 0x3b, 0x65, 472 | 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x62, 0x06, 0x70, 473 | 0x72, 0x6f, 0x74, 0x6f, 0x33, 474 | } 475 | 476 | var ( 477 | file_kedascaler_external_proto_rawDescOnce sync.Once 478 | file_kedascaler_external_proto_rawDescData = file_kedascaler_external_proto_rawDesc 479 | ) 480 | 481 | func file_kedascaler_external_proto_rawDescGZIP() []byte { 482 | file_kedascaler_external_proto_rawDescOnce.Do(func() { 483 | file_kedascaler_external_proto_rawDescData = protoimpl.X.CompressGZIP(file_kedascaler_external_proto_rawDescData) 484 | }) 485 | return file_kedascaler_external_proto_rawDescData 486 | } 487 | 488 | var file_kedascaler_external_proto_msgTypes = make([]protoimpl.MessageInfo, 8) 489 | var file_kedascaler_external_proto_goTypes = []interface{}{ 490 | (*ScaledObjectRef)(nil), // 0: externalscaler.ScaledObjectRef 491 | (*IsActiveResponse)(nil), // 1: externalscaler.IsActiveResponse 492 | (*GetMetricSpecResponse)(nil), // 2: externalscaler.GetMetricSpecResponse 493 | (*MetricSpec)(nil), // 3: externalscaler.MetricSpec 494 | (*GetMetricsRequest)(nil), // 4: externalscaler.GetMetricsRequest 495 | (*GetMetricsResponse)(nil), // 5: externalscaler.GetMetricsResponse 496 | (*MetricValue)(nil), // 6: externalscaler.MetricValue 497 | nil, // 7: externalscaler.ScaledObjectRef.ScalerMetadataEntry 498 | } 499 | var file_kedascaler_external_proto_depIdxs = []int32{ 500 | 7, // 0: externalscaler.ScaledObjectRef.scalerMetadata:type_name -> externalscaler.ScaledObjectRef.ScalerMetadataEntry 501 | 3, // 1: externalscaler.GetMetricSpecResponse.metricSpecs:type_name -> externalscaler.MetricSpec 502 | 0, // 2: externalscaler.GetMetricsRequest.scaledObjectRef:type_name -> externalscaler.ScaledObjectRef 503 | 6, // 3: externalscaler.GetMetricsResponse.metricValues:type_name -> externalscaler.MetricValue 504 | 0, // 4: externalscaler.ExternalScaler.IsActive:input_type -> externalscaler.ScaledObjectRef 505 | 0, // 5: externalscaler.ExternalScaler.StreamIsActive:input_type -> externalscaler.ScaledObjectRef 506 | 0, // 6: externalscaler.ExternalScaler.GetMetricSpec:input_type -> externalscaler.ScaledObjectRef 507 | 4, // 7: externalscaler.ExternalScaler.GetMetrics:input_type -> externalscaler.GetMetricsRequest 508 | 1, // 8: externalscaler.ExternalScaler.IsActive:output_type -> externalscaler.IsActiveResponse 509 | 1, // 9: externalscaler.ExternalScaler.StreamIsActive:output_type -> externalscaler.IsActiveResponse 510 | 2, // 10: externalscaler.ExternalScaler.GetMetricSpec:output_type -> externalscaler.GetMetricSpecResponse 511 | 5, // 11: externalscaler.ExternalScaler.GetMetrics:output_type -> externalscaler.GetMetricsResponse 512 | 8, // [8:12] is the sub-list for method output_type 513 | 4, // [4:8] is the sub-list for method input_type 514 | 4, // [4:4] is the sub-list for extension type_name 515 | 4, // [4:4] is the sub-list for extension extendee 516 | 0, // [0:4] is the sub-list for field type_name 517 | } 518 | 519 | func init() { file_kedascaler_external_proto_init() } 520 | func file_kedascaler_external_proto_init() { 521 | if File_kedascaler_external_proto != nil { 522 | return 523 | } 524 | if !protoimpl.UnsafeEnabled { 525 | file_kedascaler_external_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 526 | switch v := v.(*ScaledObjectRef); i { 527 | case 0: 528 | return &v.state 529 | case 1: 530 | return &v.sizeCache 531 | case 2: 532 | return &v.unknownFields 533 | default: 534 | return nil 535 | } 536 | } 537 | file_kedascaler_external_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 538 | switch v := v.(*IsActiveResponse); i { 539 | case 0: 540 | return &v.state 541 | case 1: 542 | return &v.sizeCache 543 | case 2: 544 | return &v.unknownFields 545 | default: 546 | return nil 547 | } 548 | } 549 | file_kedascaler_external_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 550 | switch v := v.(*GetMetricSpecResponse); i { 551 | case 0: 552 | return &v.state 553 | case 1: 554 | return &v.sizeCache 555 | case 2: 556 | return &v.unknownFields 557 | default: 558 | return nil 559 | } 560 | } 561 | file_kedascaler_external_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { 562 | switch v := v.(*MetricSpec); i { 563 | case 0: 564 | return &v.state 565 | case 1: 566 | return &v.sizeCache 567 | case 2: 568 | return &v.unknownFields 569 | default: 570 | return nil 571 | } 572 | } 573 | file_kedascaler_external_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { 574 | switch v := v.(*GetMetricsRequest); i { 575 | case 0: 576 | return &v.state 577 | case 1: 578 | return &v.sizeCache 579 | case 2: 580 | return &v.unknownFields 581 | default: 582 | return nil 583 | } 584 | } 585 | file_kedascaler_external_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { 586 | switch v := v.(*GetMetricsResponse); i { 587 | case 0: 588 | return &v.state 589 | case 1: 590 | return &v.sizeCache 591 | case 2: 592 | return &v.unknownFields 593 | default: 594 | return nil 595 | } 596 | } 597 | file_kedascaler_external_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { 598 | switch v := v.(*MetricValue); i { 599 | case 0: 600 | return &v.state 601 | case 1: 602 | return &v.sizeCache 603 | case 2: 604 | return &v.unknownFields 605 | default: 606 | return nil 607 | } 608 | } 609 | } 610 | type x struct{} 611 | out := protoimpl.TypeBuilder{ 612 | File: protoimpl.DescBuilder{ 613 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 614 | RawDescriptor: file_kedascaler_external_proto_rawDesc, 615 | NumEnums: 0, 616 | NumMessages: 8, 617 | NumExtensions: 0, 618 | NumServices: 1, 619 | }, 620 | GoTypes: file_kedascaler_external_proto_goTypes, 621 | DependencyIndexes: file_kedascaler_external_proto_depIdxs, 622 | MessageInfos: file_kedascaler_external_proto_msgTypes, 623 | }.Build() 624 | File_kedascaler_external_proto = out.File 625 | file_kedascaler_external_proto_rawDesc = nil 626 | file_kedascaler_external_proto_goTypes = nil 627 | file_kedascaler_external_proto_depIdxs = nil 628 | } 629 | -------------------------------------------------------------------------------- /externalscaler/kedascaler.external_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | 3 | package externalscaler 4 | 5 | import ( 6 | context "context" 7 | grpc "google.golang.org/grpc" 8 | codes "google.golang.org/grpc/codes" 9 | status "google.golang.org/grpc/status" 10 | ) 11 | 12 | // This is a compile-time assertion to ensure that this generated file 13 | // is compatible with the grpc package it is being compiled against. 14 | const _ = grpc.SupportPackageIsVersion7 15 | 16 | // ExternalScalerClient is the client API for ExternalScaler service. 17 | // 18 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 19 | type ExternalScalerClient interface { 20 | IsActive(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*IsActiveResponse, error) 21 | StreamIsActive(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (ExternalScaler_StreamIsActiveClient, error) 22 | GetMetricSpec(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*GetMetricSpecResponse, error) 23 | GetMetrics(ctx context.Context, in *GetMetricsRequest, opts ...grpc.CallOption) (*GetMetricsResponse, error) 24 | } 25 | 26 | type externalScalerClient struct { 27 | cc grpc.ClientConnInterface 28 | } 29 | 30 | func NewExternalScalerClient(cc grpc.ClientConnInterface) ExternalScalerClient { 31 | return &externalScalerClient{cc} 32 | } 33 | 34 | func (c *externalScalerClient) IsActive(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*IsActiveResponse, error) { 35 | out := new(IsActiveResponse) 36 | err := c.cc.Invoke(ctx, "/externalscaler.ExternalScaler/IsActive", in, out, opts...) 37 | if err != nil { 38 | return nil, err 39 | } 40 | return out, nil 41 | } 42 | 43 | func (c *externalScalerClient) StreamIsActive(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (ExternalScaler_StreamIsActiveClient, error) { 44 | stream, err := c.cc.NewStream(ctx, &_ExternalScaler_serviceDesc.Streams[0], "/externalscaler.ExternalScaler/StreamIsActive", opts...) 45 | if err != nil { 46 | return nil, err 47 | } 48 | x := &externalScalerStreamIsActiveClient{stream} 49 | if err := x.ClientStream.SendMsg(in); err != nil { 50 | return nil, err 51 | } 52 | if err := x.ClientStream.CloseSend(); err != nil { 53 | return nil, err 54 | } 55 | return x, nil 56 | } 57 | 58 | type ExternalScaler_StreamIsActiveClient interface { 59 | Recv() (*IsActiveResponse, error) 60 | grpc.ClientStream 61 | } 62 | 63 | type externalScalerStreamIsActiveClient struct { 64 | grpc.ClientStream 65 | } 66 | 67 | func (x *externalScalerStreamIsActiveClient) Recv() (*IsActiveResponse, error) { 68 | m := new(IsActiveResponse) 69 | if err := x.ClientStream.RecvMsg(m); err != nil { 70 | return nil, err 71 | } 72 | return m, nil 73 | } 74 | 75 | func (c *externalScalerClient) GetMetricSpec(ctx context.Context, in *ScaledObjectRef, opts ...grpc.CallOption) (*GetMetricSpecResponse, error) { 76 | out := new(GetMetricSpecResponse) 77 | err := c.cc.Invoke(ctx, "/externalscaler.ExternalScaler/GetMetricSpec", in, out, opts...) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return out, nil 82 | } 83 | 84 | func (c *externalScalerClient) GetMetrics(ctx context.Context, in *GetMetricsRequest, opts ...grpc.CallOption) (*GetMetricsResponse, error) { 85 | out := new(GetMetricsResponse) 86 | err := c.cc.Invoke(ctx, "/externalscaler.ExternalScaler/GetMetrics", in, out, opts...) 87 | if err != nil { 88 | return nil, err 89 | } 90 | return out, nil 91 | } 92 | 93 | // ExternalScalerServer is the server API for ExternalScaler service. 94 | // All implementations must embed UnimplementedExternalScalerServer 95 | // for forward compatibility 96 | type ExternalScalerServer interface { 97 | IsActive(context.Context, *ScaledObjectRef) (*IsActiveResponse, error) 98 | StreamIsActive(*ScaledObjectRef, ExternalScaler_StreamIsActiveServer) error 99 | GetMetricSpec(context.Context, *ScaledObjectRef) (*GetMetricSpecResponse, error) 100 | GetMetrics(context.Context, *GetMetricsRequest) (*GetMetricsResponse, error) 101 | mustEmbedUnimplementedExternalScalerServer() 102 | } 103 | 104 | // UnimplementedExternalScalerServer must be embedded to have forward compatible implementations. 105 | type UnimplementedExternalScalerServer struct { 106 | } 107 | 108 | func (UnimplementedExternalScalerServer) IsActive(context.Context, *ScaledObjectRef) (*IsActiveResponse, error) { 109 | return nil, status.Errorf(codes.Unimplemented, "method IsActive not implemented") 110 | } 111 | func (UnimplementedExternalScalerServer) StreamIsActive(*ScaledObjectRef, ExternalScaler_StreamIsActiveServer) error { 112 | return status.Errorf(codes.Unimplemented, "method StreamIsActive not implemented") 113 | } 114 | func (UnimplementedExternalScalerServer) GetMetricSpec(context.Context, *ScaledObjectRef) (*GetMetricSpecResponse, error) { 115 | return nil, status.Errorf(codes.Unimplemented, "method GetMetricSpec not implemented") 116 | } 117 | func (UnimplementedExternalScalerServer) GetMetrics(context.Context, *GetMetricsRequest) (*GetMetricsResponse, error) { 118 | return nil, status.Errorf(codes.Unimplemented, "method GetMetrics not implemented") 119 | } 120 | func (UnimplementedExternalScalerServer) mustEmbedUnimplementedExternalScalerServer() {} 121 | 122 | // UnsafeExternalScalerServer may be embedded to opt out of forward compatibility for this service. 123 | // Use of this interface is not recommended, as added methods to ExternalScalerServer will 124 | // result in compilation errors. 125 | type UnsafeExternalScalerServer interface { 126 | mustEmbedUnimplementedExternalScalerServer() 127 | } 128 | 129 | func RegisterExternalScalerServer(s grpc.ServiceRegistrar, srv ExternalScalerServer) { 130 | s.RegisterService(&_ExternalScaler_serviceDesc, srv) 131 | } 132 | 133 | func _ExternalScaler_IsActive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 134 | in := new(ScaledObjectRef) 135 | if err := dec(in); err != nil { 136 | return nil, err 137 | } 138 | if interceptor == nil { 139 | return srv.(ExternalScalerServer).IsActive(ctx, in) 140 | } 141 | info := &grpc.UnaryServerInfo{ 142 | Server: srv, 143 | FullMethod: "/externalscaler.ExternalScaler/IsActive", 144 | } 145 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 146 | return srv.(ExternalScalerServer).IsActive(ctx, req.(*ScaledObjectRef)) 147 | } 148 | return interceptor(ctx, in, info, handler) 149 | } 150 | 151 | func _ExternalScaler_StreamIsActive_Handler(srv interface{}, stream grpc.ServerStream) error { 152 | m := new(ScaledObjectRef) 153 | if err := stream.RecvMsg(m); err != nil { 154 | return err 155 | } 156 | return srv.(ExternalScalerServer).StreamIsActive(m, &externalScalerStreamIsActiveServer{stream}) 157 | } 158 | 159 | type ExternalScaler_StreamIsActiveServer interface { 160 | Send(*IsActiveResponse) error 161 | grpc.ServerStream 162 | } 163 | 164 | type externalScalerStreamIsActiveServer struct { 165 | grpc.ServerStream 166 | } 167 | 168 | func (x *externalScalerStreamIsActiveServer) Send(m *IsActiveResponse) error { 169 | return x.ServerStream.SendMsg(m) 170 | } 171 | 172 | func _ExternalScaler_GetMetricSpec_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 173 | in := new(ScaledObjectRef) 174 | if err := dec(in); err != nil { 175 | return nil, err 176 | } 177 | if interceptor == nil { 178 | return srv.(ExternalScalerServer).GetMetricSpec(ctx, in) 179 | } 180 | info := &grpc.UnaryServerInfo{ 181 | Server: srv, 182 | FullMethod: "/externalscaler.ExternalScaler/GetMetricSpec", 183 | } 184 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 185 | return srv.(ExternalScalerServer).GetMetricSpec(ctx, req.(*ScaledObjectRef)) 186 | } 187 | return interceptor(ctx, in, info, handler) 188 | } 189 | 190 | func _ExternalScaler_GetMetrics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 191 | in := new(GetMetricsRequest) 192 | if err := dec(in); err != nil { 193 | return nil, err 194 | } 195 | if interceptor == nil { 196 | return srv.(ExternalScalerServer).GetMetrics(ctx, in) 197 | } 198 | info := &grpc.UnaryServerInfo{ 199 | Server: srv, 200 | FullMethod: "/externalscaler.ExternalScaler/GetMetrics", 201 | } 202 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 203 | return srv.(ExternalScalerServer).GetMetrics(ctx, req.(*GetMetricsRequest)) 204 | } 205 | return interceptor(ctx, in, info, handler) 206 | } 207 | 208 | var _ExternalScaler_serviceDesc = grpc.ServiceDesc{ 209 | ServiceName: "externalscaler.ExternalScaler", 210 | HandlerType: (*ExternalScalerServer)(nil), 211 | Methods: []grpc.MethodDesc{ 212 | { 213 | MethodName: "IsActive", 214 | Handler: _ExternalScaler_IsActive_Handler, 215 | }, 216 | { 217 | MethodName: "GetMetricSpec", 218 | Handler: _ExternalScaler_GetMetricSpec_Handler, 219 | }, 220 | { 221 | MethodName: "GetMetrics", 222 | Handler: _ExternalScaler_GetMetrics_Handler, 223 | }, 224 | }, 225 | Streams: []grpc.StreamDesc{ 226 | { 227 | StreamName: "StreamIsActive", 228 | Handler: _ExternalScaler_StreamIsActive_Handler, 229 | ServerStreams: true, 230 | }, 231 | }, 232 | Metadata: "kedascaler.external.proto", 233 | } 234 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/arschles/containerscaler 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/golang/protobuf v1.4.2 7 | github.com/hashicorp/go-multierror v1.0.0 8 | github.com/labstack/echo v3.3.10+incompatible 9 | github.com/labstack/echo/v4 v4.1.17 10 | github.com/mitchellh/go-homedir v1.1.0 11 | github.com/parnurzeal/gorequest v0.2.16 12 | github.com/pkg/errors v0.9.1 13 | github.com/spf13/cobra v1.0.0 14 | github.com/spf13/viper v1.7.0 15 | github.com/stretchr/testify v1.6.1 // indirect 16 | google.golang.org/grpc v1.34.0 17 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1 // indirect 18 | google.golang.org/protobuf v1.25.0 19 | k8s.io/api v0.19.0 20 | k8s.io/apimachinery v0.19.0 21 | // k8s.io/client-go v12.0.0+incompatible 22 | k8s.io/client-go v0.19.0 23 | moul.io/http2curl v1.0.0 // indirect 24 | 25 | ) 26 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= 10 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 11 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 12 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 13 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 14 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 15 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 16 | github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= 17 | github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= 18 | github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= 19 | github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= 20 | github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= 21 | github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= 22 | github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= 23 | github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= 24 | github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= 25 | github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= 26 | github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= 27 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 28 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 29 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 30 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 31 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 32 | github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 33 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 34 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 35 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 36 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 37 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 38 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 39 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 40 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 41 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 42 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 43 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 44 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 45 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 46 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 47 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 48 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 49 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 50 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 51 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 52 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 53 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 54 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 55 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 56 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 57 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 58 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 59 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 60 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 61 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 62 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= 63 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 64 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 65 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 66 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 67 | github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= 68 | github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 69 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 70 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 71 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 72 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 73 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 74 | github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 75 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 76 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 77 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 78 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 79 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 80 | github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 81 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 82 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 83 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 84 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 85 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 86 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 87 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 88 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 89 | github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= 90 | github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= 91 | github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= 92 | github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= 93 | github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= 94 | github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= 95 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 96 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 97 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 98 | github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= 99 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 100 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 101 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 102 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 103 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 104 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 105 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 106 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 107 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 108 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 109 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 110 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 111 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 112 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 113 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 114 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 115 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 116 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 117 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 118 | github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= 119 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 120 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 121 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 122 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 123 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 124 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 125 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 126 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 127 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 128 | github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= 129 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 130 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 131 | github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= 132 | github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 133 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 134 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 135 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 136 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 137 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 138 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 139 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 140 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 141 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 142 | github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= 143 | github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= 144 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 145 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 146 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 147 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 148 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 149 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 150 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 151 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 152 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 153 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 154 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 155 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 156 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 157 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 158 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 159 | github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= 160 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 161 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 162 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 163 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 164 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 165 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 166 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 167 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 168 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 169 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 170 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 171 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 172 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 173 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 174 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 175 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 176 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 177 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 178 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 179 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 180 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 181 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 182 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 183 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 184 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 185 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 186 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 187 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 188 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 189 | github.com/kedacore/keda v1.5.0 h1:c8xA1Vo3H7rPwFiWUX3CBXnjBSrbYDmUs9iEfDlf4bQ= 190 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 191 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 192 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 193 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 194 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 195 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 196 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 197 | github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= 198 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 199 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 200 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 201 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 202 | github.com/labstack/echo v1.4.4 h1:1bEiBNeGSUKxcPDGfZ/7IgdhJJZx8wV/pICJh4W2NJI= 203 | github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= 204 | github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= 205 | github.com/labstack/echo/v4 v4.1.17 h1:PQIBaRplyRy3OjwILGkPg89JRtH2x5bssi59G2EL3fo= 206 | github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ= 207 | github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= 208 | github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= 209 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 210 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 211 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 212 | github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 213 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 214 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 215 | github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= 216 | github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 217 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 218 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 219 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= 220 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 221 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 222 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 223 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 224 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 225 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 226 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 227 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 228 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 229 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 230 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 231 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 232 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 233 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 234 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 235 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 236 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 237 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 238 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 239 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 240 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 241 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 242 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 243 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 244 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 245 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 246 | github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 247 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 248 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 249 | github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ= 250 | github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= 251 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 252 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 253 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 254 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 255 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 256 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 257 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 258 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 259 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 260 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 261 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 262 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 263 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 264 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 265 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 266 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 267 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 268 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 269 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 270 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 271 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 272 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 273 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 274 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 275 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 276 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 277 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 278 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 279 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 280 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 281 | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 282 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 283 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 284 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 285 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= 286 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 287 | github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= 288 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 289 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 290 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 291 | github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= 292 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 293 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 294 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 295 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 296 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 297 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 298 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 299 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 300 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 301 | github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= 302 | github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 303 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 304 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 305 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 306 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 307 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 308 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 309 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 310 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 311 | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= 312 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 313 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 314 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 315 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 316 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 317 | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 318 | github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= 319 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 320 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 321 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 322 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 323 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 324 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 325 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 326 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 327 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 328 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 329 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 330 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 331 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 332 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 333 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 334 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 335 | golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 336 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 337 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 338 | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= 339 | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 340 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 341 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 342 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 343 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 344 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 345 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 346 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 347 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 348 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 349 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 350 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 351 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 352 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 353 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 354 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 355 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 356 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 357 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 358 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 359 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 360 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 361 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 362 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 363 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 364 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 365 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 366 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 367 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 368 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 369 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 370 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 371 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 372 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 373 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 374 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 375 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 376 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 377 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 378 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 379 | golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= 380 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 381 | golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= 382 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 383 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 384 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 385 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= 386 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 387 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= 388 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 389 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 390 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 391 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 392 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 393 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 394 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 395 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 396 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 397 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 398 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 399 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 400 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 401 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 402 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 403 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 404 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 405 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 406 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 407 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 408 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 409 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 410 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 411 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 412 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 413 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 414 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 415 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 416 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 417 | golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8= 418 | golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 419 | golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8= 420 | golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 421 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 422 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 423 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 424 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 425 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 426 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 427 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 428 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 429 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= 430 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 431 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 432 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 433 | golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 434 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 435 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 436 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 437 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 438 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 439 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 440 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 441 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 442 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 443 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 444 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 445 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 446 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 447 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 448 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 449 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 450 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 451 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 452 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 453 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 454 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 455 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 456 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 457 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 458 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 459 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 460 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 461 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 462 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 463 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 464 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 465 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 466 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 467 | google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= 468 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 469 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 470 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 471 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 472 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 473 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 474 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 475 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 476 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 477 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= 478 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 479 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 480 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= 481 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 482 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 483 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 484 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 485 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 486 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 487 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 488 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 489 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 490 | google.golang.org/grpc v1.28.0-pre h1:PVZrwq0Uyu/ItyjGm2UJMD65GYT5F32WIornvBYEPrE= 491 | google.golang.org/grpc v1.28.0-pre/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 492 | google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI= 493 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 494 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1 h1:M8spwkmx0pHrPq+uMdl22w5CvJ/Y+oAJTIs9oGoCpOE= 495 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 496 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 497 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 498 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 499 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 500 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 501 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 502 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 503 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 504 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 505 | google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= 506 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 507 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 508 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 509 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 510 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 511 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 512 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 513 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 514 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 515 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 516 | gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= 517 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 518 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 519 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 520 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 521 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 522 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 523 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 524 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 525 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 526 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 527 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 528 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 529 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 530 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 531 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 532 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 533 | k8s.io/api v0.19.0 h1:XyrFIJqTYZJ2DU7FBE/bSPz7b1HvbVBuBf07oeo6eTc= 534 | k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= 535 | k8s.io/apimachinery v0.19.0 h1:gjKnAda/HZp5k4xQYjL0K/Yb66IvNqjthCb03QlKpaQ= 536 | k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= 537 | k8s.io/client-go v0.19.0 h1:1+0E0zfWFIWeyRhQYWzimJOyAk2UT7TiARaLNwJCf7k= 538 | k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU= 539 | k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 540 | k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= 541 | k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= 542 | k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= 543 | k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= 544 | k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg= 545 | k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= 546 | moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= 547 | moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= 548 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 549 | sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA= 550 | sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= 551 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 552 | sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= 553 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 554 | -------------------------------------------------------------------------------- /kedascaler.external.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package externalscaler; 4 | option go_package = ".;externalscaler"; 5 | 6 | service ExternalScaler { 7 | rpc IsActive(ScaledObjectRef) returns (IsActiveResponse) {} 8 | rpc StreamIsActive(ScaledObjectRef) returns (stream IsActiveResponse) {} 9 | rpc GetMetricSpec(ScaledObjectRef) returns (GetMetricSpecResponse) {} 10 | rpc GetMetrics(GetMetricsRequest) returns (GetMetricsResponse) {} 11 | } 12 | 13 | message ScaledObjectRef { 14 | string name = 1; 15 | string namespace = 2; 16 | map scalerMetadata = 3; 17 | } 18 | 19 | message IsActiveResponse { 20 | bool result = 1; 21 | } 22 | 23 | message GetMetricSpecResponse { 24 | repeated MetricSpec metricSpecs = 1; 25 | } 26 | 27 | message MetricSpec { 28 | string metricName = 1; 29 | int64 targetSize = 2; 30 | } 31 | 32 | message GetMetricsRequest { 33 | ScaledObjectRef scaledObjectRef = 1; 34 | string metricName = 2; 35 | } 36 | 37 | message GetMetricsResponse { 38 | repeated MetricValue metricValues = 1; 39 | } 40 | 41 | message MetricValue { 42 | string metricName = 1; 43 | int64 metricValue = 2; 44 | } 45 | -------------------------------------------------------------------------------- /oss-improvements.md: -------------------------------------------------------------------------------- 1 | # Keda improvements 2 | 3 | ## Docs for external scaling 4 | 5 | - Example with real metrics in the [external scaling docs overview](https://keda.sh/docs/1.5/concepts/external-scalers/#overview) - plug it in to the [HPA formula](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#algorithm-details) 6 | - Mention how `maxReplicaCount`, `minReplicaCount`, `cooldownPeriod`, and `pollingInterval` factor in to external scaling 7 | - Include how to start the Go gRPC server on the [scaling docs](https://keda.sh/docs/1.5/concepts/external-scalers/#overview) 8 | - KEDA redis issue (https://github.com/kedacore/keda/issues/905) 9 | 10 | 11 | ## Helm 12 | 13 | - How to configure dependency configs (same as subchart configs) 14 | - You can install helm via linuxbrew 15 | 16 | This on the getting started page: 17 | 18 | ```shell 19 | DESKTOP-DQP07VM :: ~/src/containerscaler ‹controller*› » helm install stable/mysql --generate-name 20 | Error: failed to download "stable/mysql" (hint: running `helm repo update` may help) 21 | ``` 22 | -------------------------------------------------------------------------------- /pkg/k8s/client.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | "k8s.io/client-go/dynamic" 6 | "k8s.io/client-go/kubernetes" 7 | "k8s.io/client-go/rest" 8 | ) 9 | 10 | // NewClientset gets a new Kubernetes clientset, or calls log.Fatal 11 | // if it couldn't 12 | func NewClientset() (*kubernetes.Clientset, dynamic.Interface, error) { 13 | // creates the in-cluster config 14 | config, err := rest.InClusterConfig() 15 | if err != nil { 16 | return nil, nil, errors.Wrap(err, "Getting in-cluster config") 17 | } 18 | // creates the clientset 19 | clientset, err := kubernetes.NewForConfig(config) 20 | if err != nil { 21 | return nil, nil, errors.Wrap(err, "Creating k8s clientset") 22 | } 23 | dynamic, err := dynamic.NewForConfig(config) 24 | 25 | if err != nil { 26 | return nil, nil, err 27 | } 28 | return clientset, dynamic, nil 29 | } 30 | -------------------------------------------------------------------------------- /pkg/k8s/deployment.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | context "context" 5 | 6 | appsv1 "k8s.io/api/apps/v1" 7 | corev1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | k8sappsv1 "k8s.io/client-go/kubernetes/typed/apps/v1" 10 | ) 11 | 12 | // DeleteDeployment deletes the deployment given using the client given 13 | func DeleteDeployment(ctx context.Context, name string, cl k8sappsv1.DeploymentInterface) error { 14 | return cl.Delete(ctx, name, metav1.DeleteOptions{}) 15 | } 16 | 17 | // NewDeployment creates a new deployment object in the given namespace 18 | // with the given name and the given image. This does not actually create 19 | // the deployment in the cluster, it just creates the deployment object 20 | // in memory 21 | func NewDeployment(ctx context.Context, namespace, name, image string, port int32) *appsv1.Deployment { 22 | deployment := &appsv1.Deployment{ 23 | TypeMeta: metav1.TypeMeta{ 24 | Kind: "Deployment", 25 | }, 26 | ObjectMeta: metav1.ObjectMeta{ 27 | Name: name, 28 | Namespace: namespace, 29 | Labels: labels(name), 30 | }, 31 | Spec: appsv1.DeploymentSpec{ 32 | Selector: &metav1.LabelSelector{ 33 | MatchLabels: labels(name), 34 | }, 35 | Replicas: int32P(1), 36 | Template: corev1.PodTemplateSpec{ 37 | ObjectMeta: metav1.ObjectMeta{ 38 | Labels: labels(name), 39 | }, 40 | Spec: corev1.PodSpec{ 41 | Containers: []corev1.Container{ 42 | { 43 | Image: image, 44 | Name: name, 45 | ImagePullPolicy: "Always", 46 | Ports: []corev1.ContainerPort{ 47 | { 48 | ContainerPort: port, 49 | }, 50 | }, 51 | Env: []corev1.EnvVar{ 52 | { 53 | Name: "ENV", 54 | Value: "cscaler", 55 | }, 56 | }, 57 | }, 58 | }, 59 | }, 60 | }, 61 | }, 62 | } 63 | 64 | return deployment 65 | } 66 | -------------------------------------------------------------------------------- /pkg/k8s/meta.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import "fmt" 4 | 5 | func labels(name string) map[string]string { 6 | return map[string]string{ 7 | "name": name, 8 | "app": fmt.Sprintf("cscaler-%s", name), 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /pkg/k8s/scaledobject.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | context "context" 5 | 6 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 8 | "k8s.io/apimachinery/pkg/runtime/schema" 9 | "k8s.io/client-go/dynamic" 10 | ) 11 | 12 | func kedaGVR() schema.GroupVersionResource { 13 | return schema.GroupVersionResource{ 14 | Group: "keda.sh", 15 | Version: "v1alpha1", 16 | Resource: "scaledobjects", 17 | } 18 | } 19 | 20 | // NewScaledObjectClient returns a new dynamic client capable 21 | // of interacting with ScaledObjects in a cluster 22 | func NewScaledObjectClient(cl dynamic.Interface) dynamic.NamespaceableResourceInterface { 23 | return cl.Resource(kedaGVR()) 24 | } 25 | 26 | // DeleteScaledObject deletes a scaled object with the given name 27 | func DeleteScaledObject(ctx context.Context, name string, cl dynamic.ResourceInterface) error { 28 | return cl.Delete(ctx, name, v1.DeleteOptions{}) 29 | } 30 | 31 | // NewScaledObject creates a new ScaledObject in memory 32 | func NewScaledObject(namespace, name, deploymentName, scalerAddress string) *unstructured.Unstructured { 33 | // https://keda.sh/docs/1.5/faq/ 34 | // https://github.com/kedacore/keda/blob/v2/api/v1alpha1/scaledobject_types.go 35 | return &unstructured.Unstructured{ 36 | Object: map[string]interface{}{ 37 | "apiVersion": "keda.sh/v1alpha1", 38 | "kind": "ScaledObject", 39 | "metadata": map[string]interface{}{ 40 | "name": name, 41 | "namespace": namespace, 42 | "labels": labels(name), 43 | }, 44 | "spec": map[string]interface{}{ 45 | "minReplicaCount": 0, 46 | "maxReplicaCount": 1000, 47 | "pollingInterval": 1, 48 | "scaleTargetRef": map[string]string{ 49 | "kind": "Deployment", 50 | "name": deploymentName, 51 | }, 52 | "triggers": []interface{}{ 53 | map[string]interface{}{ 54 | "type": "external", 55 | "metadata": map[string]string{ 56 | "scalerAddress": scalerAddress, 57 | }, 58 | }, 59 | }, 60 | }, 61 | }, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /pkg/k8s/service.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | context "context" 5 | 6 | corev1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | intstr "k8s.io/apimachinery/pkg/util/intstr" 9 | k8scorev1 "k8s.io/client-go/kubernetes/typed/core/v1" 10 | ) 11 | 12 | func DeleteService(ctx context.Context, namespace, name string, cl k8scorev1.ServiceInterface) error { 13 | return cl.Delete(ctx, name, metav1.DeleteOptions{}) 14 | } 15 | 16 | func NewService(namespace, name string, port int32) *corev1.Service { 17 | return &corev1.Service{ 18 | TypeMeta: metav1.TypeMeta{ 19 | Kind: "Service", 20 | }, 21 | ObjectMeta: metav1.ObjectMeta{ 22 | Name: name, 23 | Namespace: namespace, 24 | Labels: labels(name), 25 | }, 26 | Spec: corev1.ServiceSpec{ 27 | Ports: []corev1.ServicePort{ 28 | { 29 | Name: "standard", 30 | Protocol: corev1.ProtocolTCP, 31 | Port: 8080, 32 | TargetPort: intstr.IntOrString{ 33 | Type: intstr.Int, 34 | IntVal: port, 35 | }, 36 | }, 37 | }, 38 | Selector: labels(name), 39 | Type: corev1.ServiceTypeClusterIP, 40 | }, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pkg/k8s/val_ptrs.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | func int32P(i int32) *int32 { 4 | return &i 5 | } 6 | 7 | func str(s string) *string { 8 | return &s 9 | } 10 | -------------------------------------------------------------------------------- /pkg/srv/config.go: -------------------------------------------------------------------------------- 1 | package srv 2 | 3 | import "os" 4 | 5 | func EnvOr(envName, otherwise string) string { 6 | fromEnv := os.Getenv(envName) 7 | if fromEnv == "" { 8 | return otherwise 9 | } 10 | return fromEnv 11 | } 12 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | Protocol Buffers - Google's data interchange format 2 | Copyright 2008 Google Inc. 3 | https://developers.google.com/protocol-buffers/ 4 | 5 | This package contains a precompiled binary version of the protocol buffer 6 | compiler (protoc). This binary is intended for users who want to use Protocol 7 | Buffers in languages other than C++ but do not want to compile protoc 8 | themselves. To install, simply place this binary somewhere in your PATH. 9 | 10 | If you intend to use the included well known types then don't forget to 11 | copy the contents of the 'include' directory somewhere as well, for example 12 | into '/usr/local/include/'. 13 | 14 | Please refer to our official github site for more installation instructions: 15 | https://github.com/protocolbuffers/protobuf 16 | -------------------------------------------------------------------------------- /scripts/_helpers/logo.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | ..,,,***,.. ..,,**,.. 4 | ,(##%#%%####%(////**,,.. .*/###%%%###%#(////*,,.. 5 | ,#(/**(#***/#(/****///////,.((/**/#/***##(/*/////////* 6 | ,#(/**(#***/##////////////,.((/**/#/***##(///////////* 7 | ,#(/**(#***/##////////////,.((/**/#/***##(///////////* 8 | ,#(/**(#***/##////////////,.((/**/#/***##(///////////* 9 | ,#(/**(#***/##////////////,.((/**/#/***##(/*/////////* 10 | ,##((/##***/##/////////*,. .(##//(#/***##(/////////*,. 11 | .,**//(##%%%%(/**,,.. ,*//(######%#//***,.. 12 | .,,. .. .,*,. 13 | .,*/(#%%%%#***,. .,*/(#%%%%#//**,. .,*/(#%%%#(/*,. 14 | %##((##///(##*//////****,..(#((/(%(///##////////***,. *(#(((#(///(#(/*//////**,, 15 | %(***(#***/##**//////////,.((/**/%/***##////////////* *((**/#(/**(#(//////////// 16 | %(***(#***/##*///////////,.((/**/%/***##////////////* *((**/#(/**(#(//////////// 17 | %(***(#***/##**//////////,.((/**/%/***##////////////* *((**/#(/**(#(/*////////// 18 | %(***(#***/##*///////////,.((/**/%/***##////////////* *((**/#(/**(#(//////////// 19 | %(**/(#***/##/*/////////*,.((/**/%/***##////////////, *((**/#(/**(#(//////////// 20 | #######(//(##/////***,. ./##%%%%#(((##////****,. *(####%#(//(#(/*///***,. 21 | .,*/(#%(*,. .,*(###/*. ,*/(##(/,. 22 | .,,*, ... 23 | .//((#######%(****,,.. **//((#####%#//**,,.. 24 | *%(///((**/(#(**/////////,..##(//(#/**/##///////////*. 25 | *%/**/((**/(#(**/////////*..#(/**(#***/##////////////, 26 | *%/**/((**/(#(**/////////*..#(/,*(#/**/##////////////, 27 | *%/**/((**/(#(**/////////*..#(/**(#/**/##////////////, 28 | *%/**/((**/(#(**/////////*..#(/**(#/**/##////////////, 29 | *%(**/((**/(#(***////////,..#(/,*(#***/##////////////, 30 | ./(##%%%####%(*//**,.. ./(##%%%####%#///***,.. 31 | ..,***,.. .,,**/*,.. 32 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #!/bin/bash 3 | 4 | function shoutln { 5 | echo 6 | printf '%s\n' "$1" | awk '{ print toupper($0) }' 7 | echo 8 | } 9 | 10 | function shout { 11 | printf '%s' "$1" | awk '{ print toupper($0) }' 12 | } 13 | 14 | function log { 15 | echo -n "-> $1" 16 | } 17 | 18 | function logln () { 19 | echo "-> $1" 20 | } 21 | 22 | commandExists () { 23 | type "$1" &> /dev/null ; 24 | } 25 | 26 | function pause () { 27 | read -s -n 1 -p "$*" 28 | } 29 | 30 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 31 | REPOSITORY_LOCATION=/tmp/containerscaler 32 | rm -rf $REPOSITORY_LOCATION 33 | 34 | curl -sL https://raw.githubusercontent.com/arschles/containerscaler/main/scripts/_helpers/logo.txt?token=AAYNMMFUJFX4XC2ZT3FTXDC7SB344 | cat 35 | echo 36 | 37 | shoutln "=== container scaler init script ===" 38 | echo 39 | shoutln "Checking for prerequisites" 40 | 41 | log "Checking for Helm... " 42 | if ! commandExists helm ; then 43 | echo 44 | echo " Helm is not installed. Installing it now..." 45 | curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash 46 | fi 47 | echo "done" 48 | 49 | log "Checking for the Azure CLI... " 50 | if ! commandExists az ; then 51 | echo 52 | echo " Azure CLI is not installed. Installing it now..." 53 | curl -L https://aka.ms/InstallAzureCli | bash 54 | echo " I'll prompt you to log in to your Azure Account, please follow the next steps" 55 | az login 56 | fi 57 | echo "done" 58 | 59 | logln "This script will create all resources under your current default subscription in Azure CLI, if that's not what you want, please stop it and use \"az account set --subscription {your subscription id}\" to set a new subscription and then run this command again." 60 | 61 | pause " If that's the correct subscription press [Enter] to continue" 62 | 63 | log "Checking for the Kubectl... " 64 | if ! commandExists kubectl ; then 65 | echo 66 | echo " Kubectl is not installed. Installing it now..." 67 | az aks install-cli 68 | fi 69 | echo "done" 70 | 71 | log "Checking for Git... " 72 | if ! commandExists git ; then 73 | echo 74 | echo " Git is not installed. Please refer to https://git-scm.com/book/en/v2/Getting-Started-Installing-Git to install it and run this command again" 75 | exit 1 76 | fi 77 | echo "done" 78 | 79 | shoutln "=== creating kubernetes cluster ===" 80 | while read -p "==> What is the RESOURCE GROUP name I should use to create this cluster? " RESOURCE_GROUP_NAME && [[ -z "$RESOURCE_GROUP_NAME" ]] 81 | do 82 | echo " Resource group name is required" 83 | done 84 | 85 | while read -p "==> What is the LOCATION I should use to create this cluster? " LOCATION && [[ -z "$LOCATION" ]] 86 | do 87 | echo " Location is required" 88 | done 89 | 90 | resource_group_exists=$(az group list --query "[?name =='$RESOURCE_GROUP_NAME'].name" -o tsv) 91 | if [ ! -z "$resource_group_exists" ] 92 | then 93 | logln "Resource group with this name already exists, using it..." 94 | else 95 | az group create -n $RESOURCE_GROUP_NAME --location $LOCATION 96 | fi 97 | 98 | while read -p "==> What will be the name of the CLUSTER? " AKS_NAME && [[ -z "$AKS_NAME" ]] 99 | do 100 | echo " Name is required" 101 | done 102 | 103 | logln "Creating Azure Resource" 104 | echo " Be patient, this can take a while..." 105 | az aks create -g $RESOURCE_GROUP_NAME -n $AKS_NAME --node-count=2 --generate-ssh-keys --node-vm-size=Standard_B2s --location $LOCATION --enable-addons http_application_routing 106 | [ $? -ne 0 ] && echo "ERROR: Error creating Azure Resources, stopping script" 107 | exit 2 108 | 109 | echo " Cluster install finished!" 110 | 111 | logln "Getting credentials" 112 | az aks get-credentials -g $RESOURCE_GROUP_NAME -n $AKS_NAME 113 | kubectl config set-context $AKS_NAME 114 | 115 | shoutln "=== adding helm repos ===" 116 | helm repo add kedacore https://kedacore.github.io/charts 117 | helm repo update 118 | 119 | shoutln "=== cloning repository ===" 120 | git clone https://github.com/arschles/containerscaler $REPOSITORY_LOCATION 121 | 122 | shoutln "=== installing cscaler ===" 123 | 124 | read -p "==> What will be the name of the NAMESPACE where I should install everything? [cscaler]" NAMESPACE 125 | NAMESPACE=${NAMESPACE:-cscaler} 126 | echo $NAMESPACE 127 | 128 | logln "Installing KEDA" 129 | helm install keda kedacore/keda --namespace $NAMESPACE --create-namespace 130 | 131 | logln "Installing Proxy" 132 | helm install cscaler-proxy $REPOSITORY_LOCATION/charts/cscaler-proxy -n $NAMESPACE --create-namespace 133 | 134 | shoutln "=== compiling binary ===" 135 | 136 | make -C $REPOSITORY_LOCATION cli 137 | if [[ ":$PATH:" == *":/usr/local/bin:"* ]]; then 138 | [ ! -d "/usr/local/bin" ] && mkdir -p /usr/local/bin 139 | mv $REPOSITORY_LOCATION/bin/capps /usr/local/bin 140 | rm -rf $REPOSITORY_LOCATION 141 | else 142 | logln "You don't seem to have \"/usr/local/bin\" in your PATH variable, you might want to add that." 143 | logln "The CLI is built, please copy it from \"$REPOSITORY_LOCATION/bin/capps\" to where you'd like to use it" 144 | fi 145 | 146 | shout "===! finished !===" 147 | 148 | logln "Use \"capps\" to access the CLI" 149 | exit 0 150 | --------------------------------------------------------------------------------