Compare commits

...

159 Commits
3.2.4 ... 6.0.1

Author SHA1 Message Date
Stefan Prodan
68fd4e245a Merge pull request #156 from stefanprodan/release-6.0.1
Release v6.0.1
2021-10-20 13:19:47 +03:00
Stefan Prodan
b718809f3b Release v6.0.1
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-10-20 13:14:39 +03:00
Stefan Prodan
26379a5589 Merge pull request #155 from stefanprodan/update-deps
Update dependencies
2021-10-20 13:12:55 +03:00
Stefan Prodan
8d37bcfa32 Update cert-manager to v1.5.3
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-10-20 11:28:30 +03:00
Stefan Prodan
f168e1909b Update dependencies
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1
go.uber.org/zap v1.19.1

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-10-20 11:03:43 +03:00
Stefan Prodan
627d5c4bb6 Merge pull request #140 from stefanprodan/release-6.0.0
Release v6.0.0
2021-06-16 15:30:23 +03:00
Stefan Prodan
29f3e7f430 Release v6.0.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-16 15:24:35 +03:00
Stefan Prodan
8a7d5689e5 Merge pull request #139 from stefanprodan/alpine-3.14
Update Alpine to v3.14
2021-06-16 15:21:02 +03:00
Stefan Prodan
70ab46cd6e Update Alpine to v3.14
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-16 15:16:43 +03:00
Stefan Prodan
d8effad747 Merge pull request #138 from stefanprodan/deps-update
Update dependencies
2021-06-16 14:54:25 +03:00
Stefan Prodan
dc97765557 Update dependencies
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-16 14:37:41 +03:00
Stefan Prodan
685371108d Merge pull request #136 from monotek/ingressClassName
chart: Upgrade Ingress to networking.k8s.io/v1 and add ingressClassName
2021-06-08 19:08:16 +03:00
André Bauer
b6f1555176 added ingressClassName
Signed-off-by: André Bauer <monotek23@gmail.com>
2021-06-04 15:51:59 +02:00
Stefan Prodan
deadf87be8 Merge pull request #134 from stefanprodan/release-v5.2.1
Release v5.2.1
2021-05-13 15:33:46 +03:00
Stefan Prodan
1d75661e45 Release v5.2.1
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-05-13 15:33:21 +03:00
Stefan Prodan
6a78560d28 Update dgrijalva/jwt-go to v4 (#133)
Fix for CVE-2020-26160 https://avd.aquasec.com/nvd/cve-2020-26160/

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-05-13 15:30:43 +03:00
Hendrik Ferber
9c4df129c6 added additional labels to servicemonitor template (#132)
chart: Add additional labels to servicemonitor template

Co-authored-by: Hendrik Ferber <HFerber@anexia-it.com>
2021-05-06 16:39:41 +03:00
Stefan Prodan
21c8dfbb69 Merge pull request #130 from onematchfox/master
Add option to bind service to specific host
2021-04-21 14:45:36 +03:00
Brian Fox
8c93f05fa9 feat: add option to bind service to specific host 2021-04-16 08:07:56 +02:00
Stefan Prodan
ef98a040c8 Merge pull request #129 from stefanprodan/release-v5.2.0
Release v5.2.0
2021-03-22 11:45:01 +02:00
Stefan Prodan
bcd8e65416 Release v5.2.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-22 11:40:18 +02:00
Stefan Prodan
79ab71bcf9 Merge pull request #128 from stefanprodan/cd-docs
Add Continuous Delivery section to readme
2021-03-21 20:31:49 +02:00
Stefan Prodan
a67c482a87 Add Continuous Delivery section to readme
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-21 19:27:27 +02:00
Stefan Prodan
7d89cbfd07 Merge pull request #127 from stefanprodan/chart-improvements
Helm chart improvements
2021-03-21 18:09:39 +02:00
Stefan Prodan
0b73f44190 chart: Add container security context
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-21 15:58:29 +02:00
Stefan Prodan
5751990e1a chart: Rearrange values
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-21 15:35:32 +02:00
Stefan Prodan
6d1d8c1271 chart: Add service annotations
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-21 15:21:37 +02:00
Stefan Prodan
81a311281e Merge pull request #126 from stefanprodan/helm-action
Refactor Helm GitHub Action
2021-03-21 14:51:32 +02:00
Stefan Prodan
71647cee9a Refactor Helm GitHub Action
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-21 13:49:32 +02:00
Stefan Prodan
2487019631 Merge pull request #125 from stefanprodan/go-1.16
Update Go to v1.16
2021-03-21 13:42:36 +02:00
Stefan Prodan
885a35eebf Update Go to v1.16
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-21 13:37:26 +02:00
Stefan Prodan
d36c523e67 Merge pull request #124 from runyontr/redis-connection-fix
chart: Fix Redis backend url
2021-03-05 16:45:20 +02:00
Thomas Runyon
43b05fb948 Update redis backend url
The connection information for redis does not match the service entry.
2021-03-05 08:56:23 -05:00
Stefan Prodan
6856337c57 Merge pull request #123 from mhulscher/servicemonitor-should-select-own-namespace-only
fix(chart): ensure that the servicemonitor only selects its own namespace
2021-03-01 16:51:08 +02:00
Mitch Hulscher
2b603bedcd fix(chart): ensure that the servicemonitor only selects its own namespace 2021-03-01 14:59:19 +01:00
Stefan Prodan
e43ebfa5bf Merge pull request #121 from stefanprodan/release-5.1.4
Release v5.1.4
2021-02-02 16:26:20 +02:00
Stefan Prodan
c914acb34d Release v5.1.4
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-02 16:20:22 +02:00
Stefan Prodan
982713606e Merge pull request #120 from 181192/service-monitor-selector
chart: Fix ServiceMonitor matchLabels for service
2021-02-02 10:03:57 +02:00
181192
63c0d0afe5 Fix servicemonitor matchLabels for service
Signed-off-by: 181192 <k@kalli.no>
2021-02-01 18:47:54 +01:00
Stefan Prodan
4407939ea3 Merge pull request #119 from stefanprodan/release-5.1.3
Release v5.1.3
2021-01-31 12:41:08 +02:00
Stefan Prodan
ee6df13f57 Release v5.1.3
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-31 12:36:06 +02:00
Stefan Prodan
97f9aca039 Merge pull request #118 from stefanprodan/remove-helmv2-tests
Refactor e2e tests
2021-01-28 16:11:27 +02:00
Stefan Prodan
f30e3f89ed Refactor e2e tests
- remove Helm v2 and Tiller
- update Helm v3 to 3.5.0
- rename scripts dir to test

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-28 13:05:34 +02:00
Stefan Prodan
f220644263 Merge pull request #117 from a-vorobiev/fix/use-group
Fix adduser param to actually use 'app' group
2021-01-26 10:57:35 +02:00
Anton Vorobiev
9e066ebc7b Fix adduser param to actually use 'app' group 2021-01-23 21:58:49 +01:00
Stefan Prodan
855f7724be Merge pull request #113 from stefanprodan/release-5.1.2
Release v5.1.2
2020-12-14 13:22:23 +02:00
Stefan Prodan
33d1e950a9 Release v5.1.2
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-12-14 13:13:47 +02:00
Stefan Prodan
ec57c11356 Merge pull request #112 from ErezArbell/patch-1
Add quoting support in ingress to allow wildcard domain
2020-12-14 10:18:00 +02:00
ErezArbell
f0d7f0adca Add quoting support in ingress to allow wildcard domain
YAML requires asterisk to be quoted.  
If we want to use wildcard host in the ingress so we put in values.yaml something like
```
ingress:
  tls:
    - secretName: my-secret-tls
      hosts:
        - '*.example.com'
```
then Helm removes the quote mark before putting the hosts value in the ingress, so the ingress will have this unquoted and will give error:
```
Error: YAML parse error on podinfo/templates/ingress.yaml: error converting YAML to JSON: yaml: line 21: did not find expected alphabetic or numeric character
```

See same issue : <https://github.com/helm/helm/issues/3936>.
and this PR that fixes it in Helm code: <https://github.com/helm/helm/pull/3956>.

This fix applies the same to podinfo.
2020-12-14 09:44:50 +02:00
Stefan Prodan
9f4d31a8c4 Merge pull request #111 from stefanprodan/release-v5.1.1
Release v5.1.1
2020-12-09 11:11:53 +02:00
Stefan Prodan
95eafd32f9 Release v5.1.1
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-12-09 10:25:36 +02:00
Stefan Prodan
86dbbf7254 Merge pull request #109 from ut8ia/patch-1
Issue : silence on config file missing error
2020-12-08 18:05:26 +02:00
Stefan Prodan
b13ec2ddb4 Merge pull request #110 from stefanprodan/release-v5.1.0
Release v5.1.0
2020-12-08 17:43:37 +02:00
Stefan Prodan
0f7a876dae Release v5.1.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-12-08 17:36:23 +02:00
Eugene Anufriev
837ff33ee0 Issue : silence on config file missing error
Any information in case when config file not exist or path\filename configured incorrectly.
It is not clear now.
Propose: handle such situation and inform about that.
2020-12-02 17:46:56 +02:00
Stefan Prodan
4735f54368 Merge pull request #108 from dmccaffery/feat/secure-port-chart
feat(charts): add tls support to helm chart
2020-11-20 15:43:54 +02:00
Deavon M. McCaffery
35c9128bca fix(charts): fix default tls port in deployment 2020-11-19 15:41:06 +00:00
Deavon M. McCaffery
411bce81c0 build: update e2e tests to validate secure-port
* pull out script blocks into `hack` path
* update e2e workflow to use scripts in `hack`
* install cert manager and self-signed cluster issuer in e2e
* deploy podinfo with secure port and certificate enabled
* add `hack/e2e.sh` script, which can be used to execute the github
  workflow locally
2020-11-19 03:04:36 +00:00
Deavon M. McCaffery
1ac286c3b0 feat(charts): add tls support to helm chart
* add tls variable block to configure service and pod with secure-port
* add ability to create cert-manager certificate
* add support for host ports (both http and https)
* add helm test for tls port
* add example values for secure-port deployment
  - this assumes certificate manager is deployed to the cluster
2020-11-19 02:46:25 +00:00
Stefan Prodan
b496853d32 Merge pull request #106 from dmccaffery/feat/secure-port
feat(podinfo): add secure port for end-to-end tls
2020-11-18 15:16:19 +02:00
Deavon M. McCaffery
9ca49aa442 build: replace add-path with environment files 2020-11-18 12:44:58 +00:00
Deavon M. McCaffery
bc809cd763 docs(deploy): add script to test podinfo using kind 2020-11-17 23:04:55 +00:00
Deavon M. McCaffery
046ac8a4a5 docs(deploy): add kube manifests for secure-port example 2020-11-17 23:04:35 +00:00
Deavon M. McCaffery
c38f357872 feat(podinfo): add secure port for end-to-end tls
* add `secure-port` argument to podinfo
* add `cert-path` argument to podinfo
* add http server for secure port
* normalise http/https server start
2020-11-17 23:01:41 +00:00
Stefan Prodan
95be17be1d Merge pull request #103 from stefanprodan/release-v5.0.3
Release v5.0.3
2020-10-28 11:50:48 +02:00
Stefan Prodan
5c30dfefc7 Release v5.0.3
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-28 11:43:11 +02:00
Stefan Prodan
d1829ea9ec Merge pull request #102 from stefanprodan/clean-up
Update modules and swagger docs
2020-10-28 11:41:02 +02:00
Stefan Prodan
06f2276501 Update packages
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-28 11:24:52 +02:00
Stefan Prodan
13f7ec7ba1 Update swagger docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-28 11:22:26 +02:00
Stefan Prodan
e9d5f7965f Remove deprecated CLI code cmd
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-28 10:59:50 +02:00
Stefan Prodan
6596ed08de Merge pull request #101 from stefanprodan/release-5.0.2
Release v5.0.2
2020-10-06 09:59:15 +03:00
stefanprodan
4c0dfaef0e Release v5.0.2 2020-10-06 09:51:18 +03:00
Stefan Prodan
36e5ceaee2 Merge pull request #100 from hiddeco/chart/introduce-prod-values
chart: introduce prod values values
2020-10-06 00:53:10 +03:00
Hidde Beydals
5281c2d9a8 chart: introduce prod values 2020-10-05 23:39:16 +02:00
Stefan Prodan
7411da595c Merge pull request #99 from stefanprodan/release-v5.0.1
Release v5.0.1
2020-09-21 10:48:25 +03:00
stefanprodan
44f8ae96eb Release v5.0.1 2020-09-21 09:57:49 +03:00
Stefan Prodan
0cab9bf6b2 Merge pull request #98 from stefanprodan/arm-v6
Drop support for linux/arm/v6
2020-09-21 09:35:01 +03:00
stefanprodan
7111121165 Drop support for linux/arm/v6 2020-09-21 09:21:55 +03:00
stefanprodan
9299a2d1f3 Push semver to GHCR 2020-09-20 15:10:57 +03:00
Stefan Prodan
8d90770909 Merge pull request #97 from stefanprodan/release-v5.0.0
Release v5.0.0
2020-09-20 14:37:24 +03:00
stefanprodan
16a9f6f84c Release v5.0.0
Set the default container registry to GHCR
2020-09-20 14:32:05 +03:00
Stefan Prodan
6f4447fb8b Merge pull request #96 from stefanprodan/apache-license
Change license from MIT to Apache v2
2020-09-20 14:18:59 +03:00
stefanprodan
70e31587bb Change license from MIT to Apache v2 2020-09-20 14:08:41 +03:00
Stefan Prodan
6dca3b2743 Merge pull request #95 from stefanprodan/go-1.15
Update Go to v1.15
2020-09-20 13:50:01 +03:00
stefanprodan
5cd072243d Update Go to v1.15 2020-09-20 13:36:57 +03:00
Stefan Prodan
fbf20b8ac1 Merge pull request #94 from stefanprodan/ghcr
Publish multi-arch image to GHCR
2020-09-20 13:33:11 +03:00
stefanprodan
5833d41e85 Publish multi-arch image to GHCR 2020-09-20 13:27:05 +03:00
Stefan Prodan
a77b43479a Merge pull request #93 from hiddeco/patch-1
Always quote the UI color
2020-09-19 15:41:04 +03:00
Stefan Prodan
3b884b02c9 Merge pull request #92 from monotek/labels
updated chart labels to new helm standard
2020-09-19 15:40:46 +03:00
Hidde Beydals
1a56086320 Always quote the UI color
As otherwise the value will render to `null` for the default chart value due to the `#`.
2020-09-17 15:08:13 +02:00
André Bauer
692df9e5b7 changed selector label and added new labels to tests too
Signed-off-by: André Bauer <monotek23@gmail.com>
2020-09-11 14:07:47 +02:00
André Bauer
103929b14a updated chart labels to new helm standard
Signed-off-by: André Bauer <monotek23@gmail.com>
2020-09-11 12:23:24 +02:00
Stefan Prodan
d3865f9247 Merge pull request #91 from dirien/master
Update HPA to autoscaling/v2beta2
2020-08-06 10:02:06 +03:00
Engin Diri
3507958932 uodate autoscaling/v2beta1 to autoscaling/v2beta2 2020-08-05 21:01:09 +02:00
dirien
a8b8bad0e4 Update hpa.yaml
update apiVersion: autoscaling/v2beta2
2020-08-05 12:10:36 +02:00
Stefan Prodan
26a630c0b4 Merge pull request #89 from stefanprodan/release-4.0.6
Release v4.0.6
2020-06-26 13:42:49 +03:00
stefanprodan
76c18c588f Release v4.0.6 2020-06-26 13:26:28 +03:00
Stefan Prodan
df5ece51ef Merge pull request #88 from commixon/random-delay-revamp
Revisit random-delay
2020-06-26 10:43:17 +03:00
Chris Loukas
f7d1c5639c Validate config parameters for random delay
- random-delay-max should not be less than random-delay-min
- random-delay-unit accepted values: s|ms
2020-06-25 20:06:03 +03:00
Chris Loukas
20a136a73c Revisit random-delay
If enabled it will still delay randomly between 0-5 seconds.

However, the functionality to fine grain this is added.
Both seconds and milliseconds are supported now. Moreover,
min/max values for random delay can be condigured through
pflag params
2020-06-25 11:41:21 +03:00
Stefan Prodan
f43f9b2eb6 Merge pull request #87 from stefanprodan/release-4.0.5
Release v4.0.5
2020-06-15 10:34:21 +03:00
stefanprodan
9a46ed3182 Release v4.0.5 2020-06-15 09:56:10 +03:00
Stefan Prodan
82b7007c5d Merge pull request #86 from stefanprodan/redis-chart
Add Redis deployment to Helm chart
2020-06-15 09:54:44 +03:00
stefanprodan
806f0dbe82 Add Redis deployment to Helm chart 2020-06-14 15:35:34 +03:00
Stefan Prodan
936018e5bb Merge pull request #85 from stefanprodan/alpine-3.12
Update Alpine to 3.12
2020-06-12 16:55:54 +03:00
stefanprodan
1b4131b5ad Update Alpine to 3.12 2020-06-12 13:49:06 +03:00
Stefan Prodan
b98a9dcc1a Merge pull request #84 from stefanprodan/release-4.0.4
Release v4.0.4
2020-06-12 13:46:58 +03:00
stefanprodan
8860e57362 Release v4.0.4 2020-06-12 13:40:07 +03:00
Stefan Prodan
f1ecea6b53 Merge pull request #83 from stefanprodan/pod-annotations
Add pod annotations to chart options
2020-06-12 13:33:31 +03:00
stefanprodan
ebc6493990 Add pod annotations to chart options 2020-06-12 13:00:54 +03:00
Stefan Prodan
a2f9216fe4 Merge pull request #82 from stefanprodan/release-4.0.3
Release v4.0.3
2020-06-06 13:31:28 +03:00
stefanprodan
27436ed538 Release v4.0.3 2020-06-06 09:55:21 +03:00
Stefan Prodan
c103a50423 Merge pull request #81 from alaa/master
Enable gRPC reflection protocol
2020-06-06 09:01:24 +03:00
Alaa Qutaish
5ac16f0f98 Enable gRPC reflection protocol 2020-06-05 17:16:29 +02:00
Stefan Prodan
b4138fdb4d Merge pull request #80 from stefanprodan/release-4.0.2
Release v4.0.2
2020-05-29 13:50:31 +03:00
stefanprodan
a2e6fd0ef1 Release v4.0.2 2020-05-29 13:24:11 +03:00
Stefan Prodan
c2aaf7a962 Merge pull request #79 from stefanprodan/cve-scan
Add CVE scanning with trivy
2020-05-29 12:46:07 +03:00
stefanprodan
a066ff5385 Add CVE scanning with trivy 2020-05-29 12:39:55 +03:00
Stefan Prodan
113360052b Merge pull request #78 from stefanprodan/release-4.0.1
Release 4.0.1
2020-05-28 10:46:58 +03:00
stefanprodan
a24e3e539c Release 4.0.1 2020-05-28 10:41:56 +03:00
Stefan Prodan
ed8a14d4d9 Merge pull request #77 from stefanprodan/e2e-helm
Consolidate e2e and unit tests
2020-05-28 10:36:37 +03:00
stefanprodan
d2798e1a24 Consolidate tests 2020-05-28 10:21:50 +03:00
stefanprodan
369014455c Use helm-gh-pages action 2020-05-28 10:13:45 +03:00
Stefan Prodan
db1b8a7acd Merge pull request #76 from seaneagan/helm2_tests
Add end-to-end tests for Helm v2 and v3
2020-05-28 10:11:45 +03:00
Sean Eagan
cc9231ae10 Test for helm 2 support 2020-05-27 15:59:38 -05:00
Sean Eagan
03ba47a0be helm tests: Helm 2 support
The `test-success` hook is supported by Helm 2 and 3.
2020-05-27 11:34:56 -05:00
Stefan Prodan
ab953493ee Merge pull request #74 from stefanprodan/release-4.0.0
Release 4.0.0
2020-05-27 18:28:14 +03:00
stefanprodan
c04ee365e6 Release 4.0.0 2020-05-27 18:14:55 +03:00
Stefan Prodan
26e8935520 Merge pull request #73 from stefanprodan/gh-actions-e2e
Migrate CI to GitHub Actions
2020-05-27 18:13:19 +03:00
stefanprodan
dd027359e6 Add goreleaser to release workflow 2020-05-27 17:56:18 +03:00
stefanprodan
cf26a9cefc Remove CircleCI e2e tests 2020-05-27 17:30:57 +03:00
stefanprodan
026b40876c Add linting workflow 2020-05-27 17:18:54 +03:00
stefanprodan
fd1814052a Add opencontainers metadata 2020-05-27 17:02:23 +03:00
stefanprodan
98c2853ec3 Publish Helm chart on release 2020-05-27 16:45:07 +03:00
stefanprodan
b2ca15b8af Add Helm publish action 2020-05-27 16:26:08 +03:00
stefanprodan
55e7178dad Refactor Helm action 2020-05-27 13:06:54 +03:00
stefanprodan
ea55d3facf Run end-to-end tests with Github Actions 2020-05-22 11:49:04 +03:00
stefanprodan
a72aa7a184 Remove ngrok chart 2020-05-22 10:45:22 +03:00
Stefan Prodan
b4248cae1e Merge pull request #72 from stefanprodan/multi-arch-build
Push releases to Docker Hub for AMD64, ARM64 and ARM v6/v7
2020-05-21 12:59:40 +03:00
stefanprodan
7d2bc4905a Push releases to Docker Hub for ARM64 and ARM v6/v7 2020-05-20 17:35:38 +03:00
stefanprodan
f75f6e9fbc Publish multi-arch image with Docker buildx 2020-05-20 15:56:11 +03:00
Stefan Prodan
713d1094a2 Merge pull request #71 from stefanprodan/register-instance
Register hostname and version in cache
2020-05-20 13:57:19 +03:00
stefanprodan
3197ad3e45 Register hostname and version in cache
If the caching server is online, podinfo registers its hostname and version in Redis. The set expires after one minute and it's refreshed every 30 seconds.
2020-05-20 13:51:07 +03:00
Stefan Prodan
92f415d633 Merge pull request #70 from stefanprodan/redis-cache-api
Add cache CRUD API
2020-05-20 13:15:03 +03:00
stefanprodan
0352a3c822 Add Helm test for the cache routes 2020-05-20 13:05:50 +03:00
stefanprodan
5ba5808722 Add cache CRUD API 2020-05-20 12:59:27 +03:00
Stefan Prodan
1d416a8513 Merge pull request #69 from seaneagan/helm2and3tests
Reverts tests as Jobs
2020-05-20 12:04:19 +03:00
Sean Eagan
95028a0fb0 Reverts tests as Jobs
This reverts the #61 change to use test Jobs, which was premature
since this feature hasn't been back ported to Helm 2 yet, which
leads to the tests not being run there.

It would be possible to use presence of .Capabilities.TillerVersion
to implement tests differently for Helm 2 vs 3, but this seems
not worth the trouble.
2020-05-19 15:27:08 -05:00
Stefan Prodan
b45cc75329 Merge pull request #67 from stefanprodan/release-3.3.1
Release v3.3.1
2020-05-16 11:46:00 +03:00
stefanprodan
79bbf76ece Release v3.3.1 2020-05-16 11:01:21 +03:00
Stefan Prodan
a8c7300174 Merge pull request #66 from stefanprodan/linkerd-profile-update
Add cache routes to Linkerd profile
2020-05-16 10:58:26 +03:00
stefanprodan
a60f28ac2f Update Kubernetes Kind to v0.8.1 2020-05-16 10:16:49 +03:00
stefanprodan
adba061f77 Update ingress API version 2020-05-16 10:15:36 +03:00
stefanprodan
8f15e4e00a Fix Helm tests 2020-05-16 10:14:45 +03:00
stefanprodan
07db5a6583 Add cache routes to Linkerd profile 2020-05-16 10:14:22 +03:00
Stefan Prodan
3e6d61e77e Merge pull request #65 from stefanprodan/release-3.3.0
Release v3.3.0
2020-05-16 10:07:12 +03:00
stefanprodan
c7c7d699c9 Release v3.3.0 2020-05-16 10:00:51 +03:00
Stefan Prodan
067751c67d Merge pull request #64 from stefanprodan/cache-api
Add cache API
2020-05-16 09:59:19 +03:00
stefanprodan
73b658d711 Add cache API
- implement cache with Redis
- add cache-server to args and config
- add Redis deployment to webapp overlays
2020-05-16 09:53:17 +03:00
98 changed files with 3079 additions and 1438 deletions

View File

@@ -1,113 +0,0 @@
version: 2.1
jobs:
e2e-kubernetes:
machine: true
steps:
- checkout
- run:
name: Build podinfo container
command: e2e/build.sh
- run:
name: Start Kubernetes Kind cluster
command: e2e/bootstrap.sh
- run:
name: Install podinfo with Helm v3
command: e2e/install.sh
- run:
name: Run Helm tests
command: e2e/test.sh
push-container:
docker:
- image: circleci/golang:1.14
working_directory: ~/build
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: false
- run: make build-container
- run: |
if [ -z "$CIRCLE_TAG" ]; then
echo "Not a release, skipping container push";
else
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin;
echo $QUAY_PASS | docker login -u $QUAY_USER --password-stdin quay.io;
make push-container;
make push-base;
fi
push-binary:
docker:
- image: circleci/golang:1.14
steps:
- checkout
- run: curl -sL https://git.io/goreleaser | bash
push-helm-charts:
docker:
- image: circleci/golang:1.14
steps:
- checkout
- run:
name: Install kubectl
command: sudo curl -L https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl && sudo chmod +x /usr/local/bin/kubectl
- run:
name: Install helm
command: sudo curl -L https://storage.googleapis.com/kubernetes-helm/helm-v2.14.2-linux-amd64.tar.gz | tar xz && sudo mv linux-amd64/helm /bin/helm && sudo rm -rf linux-amd64
- run:
name: Initialize helm
command: helm init --client-only --kubeconfig=$HOME/.kube/kubeconfig
- run:
name: Lint charts
command: |
helm lint ./charts/*
- run:
name: Package charts
command: |
mkdir $HOME/charts
helm package ./charts/* --destination $HOME/charts
- run:
name: Publish charts
command: |
if echo "${CIRCLE_TAG}" | grep -Eq "[0-9]+(\.[0-9]+)*(-[a-z]+)?$"; then
REPOSITORY="https://stefanprodan:${GITHUB_TOKEN}@github.com/stefanprodan/podinfo.git"
git config user.email stefanprodan@users.noreply.github.com
git config user.name stefanprodan
git remote set-url origin ${REPOSITORY}
git checkout gh-pages
mv -f $HOME/charts/*.tgz .
helm repo index . --url https://stefanprodan.github.io/podinfo
git add .
git commit -m "Publish Helm charts v${CIRCLE_TAG}"
git push origin gh-pages
else
echo "Not a release! Skip charts publish"
fi
workflows:
version: 2
build-test:
jobs:
- e2e-kubernetes
release:
jobs:
- push-binary:
filters:
branches:
ignore: /.*/
tags:
ignore: /^chart.*/
- push-container:
filters:
branches:
ignore: /.*/
tags:
ignore: /^chart.*/
- push-helm-charts:
requires:
- push-container
filters:
branches:
ignore: /.*/
tags:
ignore: /^chart.*/

33
.github/actions/helm/action.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Setup Helm CLI
description: A GitHub Action for running Helm commands
author: Stefan Prodan
branding:
color: blue
icon: command
inputs:
version:
description: "Helm version"
required: true
runs:
using: composite
steps:
- name: "Download helm binary to tmp"
shell: bash
run: |
VERSION=${{ inputs.version }}
BIN_URL="https://get.helm.sh/helm-v${VERSION}-linux-amd64.tar.gz"
curl -sL ${BIN_URL} -o /tmp/helm.tar.gz
mkdir -p /tmp/helm
tar -C /tmp/helm/ -zxvf /tmp/helm.tar.gz
- name: "Add helm binary to /usr/local/bin"
shell: bash
run: |
sudo cp /tmp/helm/linux-amd64/helm /usr/local/bin
- name: "Cleanup tmp"
shell: bash
run: |
rm -rf /tmp/helm/ /tmp/helm.tar.gz
- name: "Verify correct installation of binary"
shell: bash
run: |
helm version

View File

@@ -0,0 +1,6 @@
FROM stefanprodan/alpine-base:latest
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -0,0 +1,9 @@
name: 'github-release-notes'
description: 'A GitHub Action to run github-release-notes commands'
author: 'Stefan Prodan'
branding:
icon: 'command'
color: 'blue'
runs:
using: 'docker'
image: 'Dockerfile'

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -o errexit
set -o pipefail
VERSION=0.2.0
BIN_DIR="$GITHUB_WORKSPACE/bin"
main() {
mkdir -p ${BIN_DIR}
tmpDir=$(mktemp -d)
pushd $tmpDir >& /dev/null
curl -sSL https://github.com/buchanae/github-release-notes/releases/download/${VERSION}/github-release-notes-linux-amd64-${VERSION}.tar.gz | tar xz
cp github-release-notes ${BIN_DIR}/github-release-notes
popd >& /dev/null
rm -rf $tmpDir
}
main
echo "$BIN_DIR" >> $GITHUB_PATH
echo "$RUNNER_WORKSPACE/$(basename $GITHUB_REPOSITORY)/bin" >> $GITHUB_PATH

23
.github/workflows/cve-scan.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: cve-scan
on:
push:
branches:
- 'master'
jobs:
trivy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build image
id: build
run: |
IMAGE=test/podinfo:${GITHUB_SHA}
docker build -t ${IMAGE} .
echo "::set-output name=image::$IMAGE"
- name: Scan image
uses: docker://docker.io/aquasec/trivy:latest
with:
args: --cache-dir /var/lib/trivy --no-progress --exit-code 1 --severity MEDIUM,HIGH,CRITICAL ${{ steps.build.outputs.image }}

38
.github/workflows/e2e.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: e2e
on:
pull_request:
push:
branches:
- 'master'
jobs:
kind-helm:
strategy:
matrix:
helm-version:
- 3.6.0
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Kubernetes
uses: engineerd/setup-kind@v0.5.0
with:
version: v0.11.1
- name: Build container image
run: |
./test/build.sh
kind load docker-image test/podinfo:latest
- name: Setup Helm
uses: ./.github/actions/helm
with:
version: ${{ matrix.helm-version }}
- name: Deploy
run: ./test/deploy.sh
- name: Run integration tests
run: ./test/test.sh
- name: Debug failure
if: failure()
run: |
kubectl logs -l app=podinfo || true

85
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,85 @@
name: release
on:
push:
tags: '*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup QEMU
uses: docker/setup-qemu-action@v1
with:
platforms: all
- name: Setup Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
buildkitd-flags: "--debug"
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Prepare
id: prep
run: |
VERSION=sha-${GITHUB_SHA::8}
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF/refs\/tags\//}
fi
echo ::set-output name=BUILD_DATE::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
echo ::set-output name=VERSION::${VERSION}
- name: Publish multi-arch image
uses: docker/build-push-action@v2
with:
push: true
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm/v7,linux/arm64
tags: |
docker.io/stefanprodan/podinfo:${{ steps.prep.outputs.VERSION }}
docker.io/stefanprodan/podinfo:latest
ghcr.io/stefanprodan/podinfo:${{ steps.prep.outputs.VERSION }}
labels: |
org.opencontainers.image.title=${{ github.event.repository.name }}
org.opencontainers.image.description=${{ github.event.repository.description }}
org.opencontainers.image.source=${{ github.event.repository.html_url }}
org.opencontainers.image.url=${{ github.event.repository.html_url }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.version=${{ steps.prep.outputs.VERSION }}
org.opencontainers.image.created=${{ steps.prep.outputs.BUILD_DATE }}
- name: Publish base image
uses: docker/build-push-action@v2
with:
push: true
builder: ${{ steps.buildx.outputs.name }}
context: .
platforms: linux/amd64
file: ./Dockerfile.base
tags: docker.io/stefanprodan/podinfo-base:latest
- name: Publish helm chart
uses: stefanprodan/helm-gh-pages@master
with:
token: ${{ secrets.GITHUB_TOKEN }}
- uses: ./.github/actions/release-notes
- name: Generate release notes
run: |
echo 'CHANGELOG' > /tmp/release.txt
github-release-notes -org stefanprodan -repo podinfo -since-latest-release >> /tmp/release.txt
- name: Publish release
uses: goreleaser/goreleaser-action@v1
with:
version: latest
args: release --release-notes=/tmp/release.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,17 +1,47 @@
on: [push, pull_request]
name: test
on:
pull_request:
push:
branches:
- 'master'
jobs:
validate:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: kubeval
- name: Checkout
uses: actions/checkout@v2
- name: Restore Go cache
uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.16.x
- name: Run unit tests
run: make test
- name: Check if working tree is dirty
run: |
if [[ $(git diff --stat) != '' ]]; then
echo 'run make test and commit changes'
exit 1
fi
- name: Validate Helm chart
uses: stefanprodan/kube-tools@v1
with:
kubectl: 1.19.11
helm: 2.17.0
helmv3: 3.6.0
command: |
kustomize build ./kustomize | kubeval --strict
- name: conftest
helmv3 template ./charts/podinfo | kubeval --strict --kubernetes-version 1.19.11 --schema-location https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master
- name: Validate kustomization
uses: stefanprodan/kube-tools@v1
with:
kubectl: 1.19.11
command: |
kustomize build ./kustomize | kubeval --strict --kubernetes-version 1.19.11 --schema-location https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master
kustomize build ./kustomize | conftest test -p .github/policy -

View File

@@ -1,4 +1,6 @@
FROM golang:1.14 as builder
FROM golang:1.16-alpine as builder
ARG REVISION
RUN mkdir -p /podinfo/
@@ -8,24 +10,26 @@ COPY . .
RUN go mod download
RUN go test -v -race ./...
RUN GIT_COMMIT=$(git rev-list -1 HEAD) && \
CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w \
-X github.com/stefanprodan/podinfo/pkg/version.REVISION=${GIT_COMMIT}" \
RUN CGO_ENABLED=0 go build -ldflags "-s -w \
-X github.com/stefanprodan/podinfo/pkg/version.REVISION=${REVISION}" \
-a -o bin/podinfo cmd/podinfo/*
RUN GIT_COMMIT=$(git rev-list -1 HEAD) && \
CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w \
-X github.com/stefanprodan/podinfo/pkg/version.REVISION=${GIT_COMMIT}" \
RUN CGO_ENABLED=0 go build -ldflags "-s -w \
-X github.com/stefanprodan/podinfo/pkg/version.REVISION=${REVISION}" \
-a -o bin/podcli cmd/podcli/*
FROM alpine:3.11
FROM alpine:3.14
ARG BUILD_DATE
ARG VERSION
ARG REVISION
LABEL maintainer="stefanprodan"
RUN addgroup -S app \
&& adduser -S -g app app \
&& adduser -S -G app app \
&& apk --no-cache add \
curl openssl netcat-openbsd
ca-certificates curl netcat-openbsd
WORKDIR /home/app

View File

@@ -1,4 +1,4 @@
FROM golang:1.14
FROM golang:1.16
WORKDIR /workspace

214
LICENSE
View File

@@ -1,21 +1,201 @@
MIT License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2018 Stefan Prodan
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Stefan Prodan. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -15,8 +15,9 @@ run:
--level=debug --grpc-port=9999 --backend-url=https://httpbin.org/status/401 --backend-url=https://httpbin.org/status/500 \
--ui-logo=https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif $(EXTRA_RUN_ARGS)
.PHONY: test
test:
go test -v -race ./...
go test ./... -coverprofile cover.out
build:
GIT_COMMIT=$$(git rev-list -1 HEAD) && CGO_ENABLED=0 go build -ldflags "-s -w -X github.com/stefanprodan/podinfo/pkg/version.REVISION=$(GIT_COMMIT)" -a -o ./bin/podinfo ./cmd/podinfo/*
@@ -60,6 +61,7 @@ version-set:
current="$(VERSION)" && \
sed -i '' "s/$$current/$$next/g" pkg/version/version.go && \
sed -i '' "s/tag: $$current/tag: $$next/g" charts/podinfo/values.yaml && \
sed -i '' "s/tag: $$current/tag: $$next/g" charts/podinfo/values-prod.yaml && \
sed -i '' "s/appVersion: $$current/appVersion: $$next/g" charts/podinfo/Chart.yaml && \
sed -i '' "s/version: $$current/version: $$next/g" charts/podinfo/Chart.yaml && \
sed -i '' "s/podinfo:$$current/podinfo:$$next/g" kustomize/deployment.yaml && \
@@ -75,4 +77,4 @@ release:
swagger:
go get github.com/swaggo/swag/cmd/swag
cd pkg/api && $$(go env GOPATH)/bin/swag init -g server.go
cd pkg/api && $$(go env GOPATH)/bin/swag init -g server.go

View File

@@ -1,11 +1,14 @@
# podinfo
[![CircleCI](https://circleci.com/gh/stefanprodan/podinfo.svg?style=svg)](https://circleci.com/gh/stefanprodan/podinfo)
[![conftest](https://github.com/stefanprodan/podinfo/workflows/test/badge.svg)](https://github.com/stefanprodan/podinfo/blob/master/.github/workflows/test.yml)
[![e2e](https://github.com/stefanprodan/podinfo/workflows/e2e/badge.svg)](https://github.com/stefanprodan/podinfo/blob/master/.github/workflows/e2e.yml)
[![test](https://github.com/stefanprodan/podinfo/workflows/test/badge.svg)](https://github.com/stefanprodan/podinfo/blob/master/.github/workflows/test.yml)
[![cve-scan](https://github.com/stefanprodan/podinfo/workflows/cve-scan/badge.svg)](https://github.com/stefanprodan/podinfo/blob/master/.github/workflows/cve-scan.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/stefanprodan/podinfo)](https://goreportcard.com/report/github.com/stefanprodan/podinfo)
[![Docker Pulls](https://img.shields.io/docker/pulls/stefanprodan/podinfo)](https://hub.docker.com/r/stefanprodan/podinfo)
Podinfo is a tiny web application made with Go that showcases best practices of running microservices in Kubernetes.
Podinfo is used by CNCF projects like [Flux](https://github.com/fluxcd/flux2) and [Flagger](https://github.com/fluxcd/flagger)
for end-to-end testing and workshops.
Specifications:
@@ -22,6 +25,8 @@ Specifications:
* Helm and Kustomize installers
* End-to-End testing with Kubernetes Kind and Helm
* Kustomize testing with GitHub Actions and Open Policy Agent
* Multi-arch container image with Docker buildx and Github Actions
* CVE scanning with trivy
Web API:
@@ -41,6 +46,9 @@ Web API:
* `POST /token` issues a JWT token valid for one minute `JWT=$(curl -sd 'anon' podinfo:9898/token | jq -r .token)`
* `GET /token/validate` validates the JWT token `curl -H "Authorization: Bearer $JWT" podinfo:9898/token/validate`
* `GET /configs` returns a JSON with configmaps and/or secrets mounted in the `config` volume
* `POST/PUT /cache/{key}` saves the posted content to Redis
* `GET /cache/{key}` returns the content from Redis if the key exists
* `DELETE /cache/{key}` deletes the key from Redis if exists
* `POST /store` writes the posted content to disk at /data/hash and returns the SHA1 hash of the content
* `GET /store/{hash}` returns the content of the file /data/hash if exists
* `GET /ws/echo` echos content via websockets `podcli ws ws://localhost:9898/ws/echo`
@@ -80,12 +88,11 @@ helm upgrade --install --wait frontend \
--set backend=http://backend-podinfo:9898/echo \
podinfo/podinfo
# Test pods have hook-delete-policy: hook-succeeded
helm test frontend
helm upgrade --install --wait backend \
--namespace test \
--set hpa.enabled=true \
--set redis.enabled=true \
podinfo/podinfo
```
@@ -99,4 +106,82 @@ Docker:
```bash
docker run -dp 9898:9898 stefanprodan/podinfo
```
```
### Continuous Delivery
In order to install podinfo on a Kubernetes cluster and keep it up to date with the latest
release in an automated manner, you can use [Flux](https://fluxcd.io).
Install the Flux CLI on MacOS and Linux using Homebrew:
```sh
brew install fluxcd/tap/flux
```
Install the Flux controllers needed for Helm operations:
```sh
flux install \
--namespace=flux-system \
--network-policy=false \
--components=source-controller,helm-controller
```
Add podinfo's Helm repository to your cluster and
configure Flux to check for new chart releases every ten minutes:
```sh
flux create source helm podinfo \
--namespace=default \
--url=https://stefanprodan.github.io/podinfo \
--interval=10m
```
Create a `podinfo-values.yaml` file locally:
```sh
cat > podinfo-values.yaml <<EOL
replicaCount: 2
resources:
limits:
memory: 256Mi
requests:
cpu: 100m
memory: 64Mi
EOL
```
Create a Helm release for deploying podinfo in the default namespace:
```sh
flux create helmrelease podinfo \
--namespace=default \
--source=HelmRepository/podinfo \
--release-name=podinfo \
--chart=podinfo \
--chart-version=">5.0.0" \
--values=podinfo-values.yaml
```
Based on the above definition, Flux will upgrade the release automatically
when a new version of podinfo is released. If the upgrade fails, Flux
can [rollback](https://toolkit.fluxcd.io/components/helm/helmreleases/#configuring-failure-remediation)
to the previous working version.
You can check what version is currently deployed with:
```sh
flux get helmreleases -n default
```
To delete podinfo's Helm repository and release from your cluster run:
```sh
flux -n default delete source helm podinfo
flux -n default delete helmrelease podinfo
```
If you wish to manage the lifecycle of your applications in a **GitOps** manner, check out
this [workflow example](https://github.com/fluxcd/flux2-kustomize-helm-example)
for multi-env deployments with Flux, Kustomize and Helm.

View File

@@ -1,21 +0,0 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj

View File

@@ -1,5 +0,0 @@
apiVersion: v1
appVersion: "1.0"
description: A Ngrok Helm chart for Kubernetes
name: ngrok
version: 0.2.0

View File

@@ -1,64 +0,0 @@
# Ngrok
Expose Kubernetes service with [Ngrok](https://ngrok.com).
## Installing the Chart
To install the chart with the release name `my-release`:
```console
$ helm install sp/ngrok --name my-release \
--set token=NGROK-TOKEN \
--set expose.service=podinfo:9898
```
The command deploys Ngrok on the Kubernetes cluster in the default namespace.
The [configuration](#configuration) section lists the parameters that can be configured during installation.
## Uninstalling the Chart
To uninstall/delete the `my-release` deployment:
```console
$ helm delete --purge my-release
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
## Configuration
The following tables lists the configurable parameters of the Grafana chart and their default values.
Parameter | Description | Default
--- | --- | ---
`image.repository` | Image repository | `stefanprodan/ngrok`
`image.pullPolicy` | Image pull policy | `IfNotPresent`
`image.tag` | Image tag | `latest`
`replicaCount` | desired number of pods | `1`
`tolerations` | List of node taints to tolerate | `[]`
`affinity` | node/pod affinities | `node`
`nodeSelector` | node labels for pod assignment | `{}`
`service.type` | type of service | `ClusterIP`
`token` | Ngrok auth token | `none`
`expose.service` | Service address to be exposed as in `service-name:port` | `none`
`subdomain` | Ngrok subdomain | `none`
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
```console
$ helm upgrade --install --wait tunel \
--set token=NGROK-TOKEN \
--set service.type=NodePort \
--set expose.service=podinfo:9898 \
sp/ngrok
```
Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
```console
$ helm install sp/grafana --name my-release -f values.yaml
```
> **Tip**: You can use the default [values.yaml](values.yaml)
```

View File

@@ -1,15 +0,0 @@
1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "ngrok.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "ngrok.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "ngrok.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "ngrok.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80
{{- end }}

View File

@@ -1,32 +0,0 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "ngrok.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "ngrok.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "ngrok.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@@ -1,12 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "ngrok.fullname" . }}
data:
ngrok.yml: |-
web_addr: 0.0.0.0:4040
update: false
log: stdout
{{- if .Values.token }}
authtoken: {{ .Values.token }}
{{- end }}

View File

@@ -1,65 +0,0 @@
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ template "ngrok.fullname" . }}
labels:
app: {{ template "ngrok.name" . }}
chart: {{ template "ngrok.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "ngrok.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "ngrok.name" . }}
release: {{ .Release.Name }}
annotations:
prometheus.io/scrape: 'false'
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- ./ngrok
- http
{{- if .Values.subdomain }}
- --subdomain={{ .Values.subdomain }}
{{- end }}
- {{ .Values.expose.service }}
volumeMounts:
- name: config
mountPath: /home/ngrok/.ngrok2
ports:
- name: http
containerPort: 4040
protocol: TCP
livenessProbe:
httpGet:
path: /api/tunnels
port: http
initialDelaySeconds: 10
periodSeconds: 30
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
volumes:
- name: config
configMap:
name: {{ template "ngrok.fullname" . }}

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "ngrok.fullname" . }}
labels:
app: {{ template "ngrok.name" . }}
chart: {{ template "ngrok.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app: {{ template "ngrok.name" . }}
release: {{ .Release.Name }}

View File

@@ -1,27 +0,0 @@
# Default values for ngrok.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: stefanprodan/ngrok
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 4040
expose:
service: ga-podinfo:9898
token: 4i3rDinhLqMHtvez71N9S_38rkS7onwv77VFNZTaUR6
nodeSelector: {}
tolerations: []
affinity: {}
subdomain:

View File

@@ -1,6 +1,6 @@
apiVersion: v1
version: 3.2.4
appVersion: 3.2.4
version: 6.0.1
appVersion: 6.0.1
name: podinfo
engine: gotpl
description: Podinfo Helm chart for Kubernetes
@@ -10,3 +10,4 @@ maintainers:
name: stefanprodan
sources:
- https://github.com/stefanprodan/podinfo
kubeVersion: ">=1.19.0-0"

201
charts/podinfo/LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Stefan Prodan. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,15 +1,20 @@
# Podinfo
Podinfo is a tiny web application made with Go
Podinfo is a tiny web application made with Go
that showcases best practices of running microservices in Kubernetes.
Podinfo is used by CNCF projects like [Flux](https://github.com/fluxcd/flux2)
and [Flagger](https://github.com/fluxcd/flagger)
for end-to-end testing and workshops.
## Installing the Chart
To install the chart with the release name `my-release`:
```console
$ helm repo add sp https://stefanprodan.github.io/podinfo
$ helm upgrade my-release --install sp/podinfo
$ helm repo add podinfo https://stefanprodan.github.io/podinfo
$ helm upgrade -i my-release podinfo/podinfo
```
The command deploys podinfo on the Kubernetes cluster in the default namespace.
@@ -20,7 +25,7 @@ The [configuration](#configuration) section lists the parameters that can be con
To uninstall/delete the `my-release` deployment:
```console
$ helm delete --purge my-release
$ helm delete my-release
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
@@ -32,9 +37,11 @@ The following tables lists the configurable parameters of the podinfo chart and
Parameter | Default | Description
--- | --- | ---
`replicaCount` | `1` | Desired number of pods
`logLevel` | `info` | Log level: `debug`, `info`, `warn`, `error`, `flat` or `panic`
`logLevel` | `info` | Log level: `debug`, `info`, `warn`, `error`
`backend` | `None` | Echo backend URL
`backends` | `[]` | Array of echo backend URLs
`cache` | `None` | Redis address in the format `<host>:<port>`
`redis.enabled` | `false` | Create Redis deployment for caching purposes
`ui.color` | `#34577c` | UI color
`ui.message` | `None` | UI greetings message
`ui.logo` | `None` | UI logo
@@ -44,7 +51,6 @@ Parameter | Default | Description
`faults.unready` | `false` | When set, the ready state is never reached
`faults.testFail` | `false` | When set, a helm test is included which always fails
`faults.testTimeout` | `false` | When set, a helm test is included which always times out
`h2c.enabled` | `false` | Allow upgrading to h2c
`image.repository` | `stefanprodan/podinfo` | Image repository
`image.tag` | `<VERSION>` | Image tag
`image.pullPolicy` | `IfNotPresent` | Image pull policy
@@ -56,6 +62,7 @@ Parameter | Default | Description
`service.grpcPort` | `9999` | ClusterIP gPRC port
`service.grpcService` | `podinfo` | gPRC service name
`service.nodePort` | `31198` | NodePort for the HTTP endpoint
`h2c.enabled` | `false` | Allow upgrading to h2c (non-TLS version of HTTP/2)
`hpa.enabled` | `false` | Enables the Kubernetes HPA
`hpa.maxReplicas` | `10` | Maximum amount of pods
`hpa.cpu` | `None` | Target CPU usage per pod
@@ -63,12 +70,14 @@ Parameter | Default | Description
`hpa.requests` | `None` | Target HTTP requests per second per pod
`serviceAccount.enabled` | `false` | Whether a service account should be created
`serviceAccount.name` | `None` | The name of the service account to use, if not set and create is true, a name is generated using the fullname template
`securityContext` | `{}` | The security context to be set on the podinfo container
`linkerd.profile.enabled` | `false` | Create Linkerd service profile
`serviceMonitor.enabled` | `false` | Whether a Prometheus Operator service monitor should be created
`serviceMonitor.interval` | `15s` | Prometheus scraping interval
`serviceMonitor.additionalLabels` | `{}` | Add additional labels to the service monitor |
`ingress.enabled` | `false` | Enables Ingress
`ingress.className ` | `""` | Use ingressClassName
`ingress.annotations` | `{}` | Ingress annotations
`ingress.path` | `/*` | Ingress path
`ingress.hosts` | `[]` | Ingress accepted hosts
`ingress.tls` | `[]` | Ingress TLS configuration
`resources.requests.cpu` | `1m` | Pod CPU request
@@ -78,18 +87,37 @@ Parameter | Default | Description
`nodeSelector` | `{}` | Node labels for pod assignment
`tolerations` | `[]` | List of node taints to tolerate
`affinity` | `None` | Node/pod affinities
`podAnnotations` | `{}` | Pod annotations
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
```console
$ helm install stable/podinfo --name my-release \
$ helm install my-release podinfo/podinfo \
--set=serviceMonitor.enabled=true,serviceMonitor.interval=5s
```
To add custom annotations you need to escape the annotation key string:
```console
$ helm upgrade -i my-release podinfo/podinfo \
--set podAnnotations."appmesh\.k8s\.aws\/preview"=enabled
```
Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
```console
$ helm install stable/podinfo --name my-release -f values.yaml
$ helm install my-release podinfo/podinfo -f values.yaml
```
> **Tip**: You can use the default [values.yaml](values.yaml)
## Upgrading the chart
### To =< 5.0.0
Version 5.0.0 is a major update.
* The chart now follows the new Kubernetes label recommendations:
<https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/>
The simplest way to update is to do a force upgrade, which recreates the resources by doing a delete and an install.

View File

@@ -1,10 +1,9 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "podinfo.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
@@ -12,32 +11,59 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this
If release name contains chart name it will be used as a full name.
*/}}
{{- define "podinfo.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "podinfo.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "podinfo.labels" -}}
helm.sh/chart: {{ include "podinfo.chart" . }}
{{ include "podinfo.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "podinfo.selectorLabels" -}}
app.kubernetes.io/name: {{ include "podinfo.fullname" . }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "podinfo.serviceAccountName" -}}
{{- if .Values.serviceAccount.enabled -}}
{{ default (include "podinfo.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{- if .Values.serviceAccount.enabled }}
{{- default (include "podinfo.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Create the name of the tls secret for secure port
*/}}
{{- define "podinfo.tlsSecretName" -}}
{{- $fullname := include "podinfo.fullname" . -}}
{{- default (printf "%s-tls" $fullname) .Values.tls.secretName }}
{{- end }}

View File

@@ -0,0 +1,16 @@
{{- if .Values.certificate.create -}}
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: {{ template "podinfo.fullname" . }}
labels:
{{- include "podinfo.labels" . | nindent 4 }}
spec:
dnsNames:
{{- range .Values.certificate.dnsNames }}
- {{ . | quote }}
{{- end }}
secretName: {{ template "podinfo.tlsSecretName" . }}
issuerRef:
{{- .Values.certificate.issuerRef | toYaml | trimSuffix "\n" | nindent 4 }}
{{- end }}

View File

@@ -3,10 +3,7 @@ kind: Deployment
metadata:
name: {{ template "podinfo.fullname" . }}
labels:
app: {{ template "podinfo.fullname" . }}
chart: {{ template "podinfo.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- include "podinfo.labels" . | nindent 4 }}
spec:
{{- if not .Values.hpa.enabled }}
replicas: {{ .Values.replicaCount }}
@@ -17,14 +14,17 @@ spec:
maxUnavailable: 1
selector:
matchLabels:
app: {{ template "podinfo.fullname" . }}
{{- include "podinfo.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
app: {{ template "podinfo.fullname" . }}
{{- include "podinfo.selectorLabels" . | nindent 8 }}
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "{{ .Values.service.httpPort }}"
{{- range $key, $value := .Values.podAnnotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
terminationGracePeriodSeconds: 30
{{- if .Values.serviceAccount.enabled }}
@@ -34,9 +34,30 @@ spec:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- if .Values.securityContext }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
{{- else if (or .Values.service.hostPort .Values.tls.hostPort) }}
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
{{- end }}
command:
- ./podinfo
- --port={{ .Values.service.httpPort | default 9898 }}
{{- if .Values.host }}
- --host={{ .Values.host }}
{{- end }}
{{- if .Values.tls.enabled }}
- --secure-port={{ .Values.tls.port }}
{{- end }}
{{- if .Values.tls.certPath }}
- --cert-path={{ .Values.tls.certPath }}
{{- end }}
{{- if .Values.service.metricsPort }}
- --port-metrics={{ .Values.service.metricsPort }}
{{- end }}
@@ -49,6 +70,11 @@ spec:
{{- range .Values.backends }}
- --backend-url={{ . }}
{{- end }}
{{- if .Values.cache }}
- --cache-server={{ .Values.cache }}
{{- else if .Values.redis.enabled }}
- --cache-server={{ template "podinfo.fullname" . }}-redis:6379
{{- end }}
- --level={{ .Values.logLevel }}
- --random-delay={{ .Values.faults.delay }}
- --random-error={{ .Values.faults.error }}
@@ -72,7 +98,7 @@ spec:
{{- end }}
{{- if .Values.ui.color }}
- name: PODINFO_UI_COLOR
value: {{ .Values.ui.color }}
value: {{ quote .Values.ui.color }}
{{- end }}
{{- if .Values.backend }}
- name: PODINFO_BACKEND_URL
@@ -82,6 +108,17 @@ spec:
- name: http
containerPort: {{ .Values.service.httpPort | default 9898 }}
protocol: TCP
{{- if .Values.service.hostPort }}
hostPort: {{ .Values.service.hostPort }}
{{- end }}
{{- if .Values.tls.enabled }}
- name: https
containerPort: {{ .Values.tls.port | default 9899 }}
protocol: TCP
{{- if .Values.tls.hostPort }}
hostPort: {{ .Values.tls.hostPort }}
{{- end }}
{{- end }}
{{- if .Values.service.metricsPort }}
- name: http-metrics
containerPort: {{ .Values.service.metricsPort }}
@@ -113,6 +150,11 @@ spec:
volumeMounts:
- name: data
mountPath: /data
{{- if .Values.tls.enabled }}
- name: tls
mountPath: {{ .Values.tls.certPath | default "/data/cert" }}
readOnly: true
{{- end }}
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
@@ -130,3 +172,8 @@ spec:
volumes:
- name: data
emptyDir: {}
{{- if .Values.tls.enabled }}
- name: tls
secret:
secretName: {{ template "podinfo.tlsSecretName" . }}
{{- end }}

View File

@@ -1,8 +1,10 @@
{{- if .Values.hpa.enabled -}}
apiVersion: autoscaling/v2beta1
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: {{ template "podinfo.fullname" . }}
labels:
{{- include "podinfo.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
@@ -15,18 +17,25 @@ spec:
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.hpa.cpu }}
target:
type: Utilization
averageUtilization: {{ .Values.hpa.cpu }}
{{- end }}
{{- if .Values.hpa.memory }}
- type: Resource
resource:
name: memory
targetAverageValue: {{ .Values.hpa.memory }}
target:
type: AverageValue
averageValue: {{ .Values.hpa.memory }}
{{- end }}
{{- if .Values.hpa.requests }}
- type: Pod
pods:
metricName: http_requests
targetAverageValue: {{ .Values.hpa.requests }}
- type: Pods
pods:
metric:
name: http_requests
target:
type: AverageValue
averageValue: {{ .Values.hpa.requests }}
{{- end }}
{{- end }}

View File

@@ -1,46 +1,41 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "podinfo.fullname" . -}}
{{- $ingressPath := .Values.ingress.path -}}
apiVersion: extensions/v1beta1
{{- $svcPort := .Values.service.externalPort -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app: {{ template "podinfo.name" . }}
chart: {{ template "podinfo.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
{{- include "podinfo.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . }}
{{- end }}
secretName: {{ .secretName }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ . }}
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
- path: {{ $ingressPath }}
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
serviceName: {{ $fullName }}
servicePort: http
{{- end }}
{{- if not .Values.ingress.hosts }}
- http:
paths:
- path: {{ $ingressPath }}
backend:
serviceName: {{ $fullName }}
servicePort: http
{{- end }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -3,6 +3,8 @@ apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: {{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
labels:
{{- include "podinfo.labels" . | nindent 4 }}
spec:
routes:
- condition:
@@ -61,6 +63,14 @@ spec:
method: GET
pathRegex: /status/[^/]*
name: GET /status/{code}
- condition:
method: POST
pathRegex: /cache
name: POST /cache
- condition:
method: GET
pathRegex: /cache/[^/]*
name: GET /cache/{hash}
- condition:
method: POST
pathRegex: /store

View File

@@ -0,0 +1,12 @@
{{- if .Values.redis.enabled -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "podinfo.fullname" . }}-redis
data:
redis.conf: |
maxmemory 64mb
maxmemory-policy allkeys-lru
save ""
appendonly no
{{- end }}

View File

@@ -0,0 +1,68 @@
{{- if .Values.redis.enabled -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "podinfo.fullname" . }}-redis
labels:
app: {{ template "podinfo.fullname" . }}-redis
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: {{ template "podinfo.fullname" . }}-redis
template:
metadata:
labels:
app: {{ template "podinfo.fullname" . }}-redis
annotations:
checksum/config: {{ include (print $.Template.BasePath "/redis/config.yaml") . | sha256sum | quote }}
spec:
{{- if .Values.serviceAccount.enabled }}
serviceAccountName: {{ template "podinfo.serviceAccountName" . }}
{{- end }}
containers:
- name: redis
image: "{{ .Values.redis.repository }}:{{ .Values.redis.tag }}"
imagePullPolicy: IfNotPresent
command:
- redis-server
- "/redis-master/redis.conf"
ports:
- name: redis
containerPort: 6379
protocol: TCP
livenessProbe:
tcpSocket:
port: redis
initialDelaySeconds: 5
timeoutSeconds: 5
readinessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 5
timeoutSeconds: 5
resources:
limits:
cpu: 1000m
memory: 128Mi
requests:
cpu: 100m
memory: 32Mi
volumeMounts:
- mountPath: /var/lib/redis
name: data
- mountPath: /redis-master
name: config
volumes:
- name: data
emptyDir: {}
- name: config
configMap:
name: {{ template "podinfo.fullname" . }}-redis
items:
- key: redis.conf
path: redis.conf
{{- end }}

View File

@@ -0,0 +1,17 @@
{{- if .Values.redis.enabled -}}
apiVersion: v1
kind: Service
metadata:
name: {{ template "podinfo.fullname" . }}-redis
labels:
app: {{ template "podinfo.fullname" . }}-redis
spec:
type: ClusterIP
selector:
app: {{ template "podinfo.fullname" . }}-redis
ports:
- name: redis
port: 6379
protocol: TCP
targetPort: redis
{{- end }}

View File

@@ -4,10 +4,11 @@ kind: Service
metadata:
name: {{ template "podinfo.fullname" . }}
labels:
app: {{ template "podinfo.name" . }}
chart: {{ template "podinfo.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- include "podinfo.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
@@ -18,6 +19,12 @@ spec:
{{- if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort))) }}
nodePort: {{ .Values.service.nodePort }}
{{- end }}
{{- if .Values.tls.enabled }}
- port: {{ .Values.tls.port | default 9899 }}
targetPort: https
protocol: TCP
name: https
{{- end }}
{{- if .Values.service.grpcPort }}
- port: {{ .Values.service.grpcPort }}
targetPort: grpc
@@ -25,5 +32,5 @@ spec:
name: grpc
{{- end }}
selector:
app: {{ template "podinfo.fullname" . }}
{{- end }}
{{- include "podinfo.selectorLabels" . | nindent 4 }}
{{- end }}

View File

@@ -4,8 +4,5 @@ kind: ServiceAccount
metadata:
name: {{ template "podinfo.serviceAccountName" . }}
labels:
app: {{ template "podinfo.name" . }}
chart: {{ template "podinfo.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- include "podinfo.labels" . | nindent 4 }}
{{- end -}}

View File

@@ -4,16 +4,19 @@ kind: ServiceMonitor
metadata:
name: {{ template "podinfo.fullname" . }}
labels:
app: {{ template "podinfo.name" . }}
chart: {{ template "podinfo.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- include "podinfo.labels" . | nindent 4 }}
{{- with .Values.serviceMonitor.additionalLabels }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
endpoints:
- path: /metrics
port: http
interval: {{ .Values.serviceMonitor.interval }}
namespaceSelector:
matchNames:
- {{ .Release.Namespace }}
selector:
matchLabels:
app: {{ template "podinfo.fullname" . }}
{{- include "podinfo.selectorLabels" . | nindent 6 }}
{{- end }}

View File

@@ -0,0 +1,29 @@
{{- if .Values.cache }}
apiVersion: v1
kind: Pod
metadata:
name: {{ template "podinfo.fullname" . }}-cache-test-{{ randAlphaNum 5 | lower }}
labels:
{{- include "podinfo.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
sidecar.istio.io/inject: "false"
linkerd.io/inject: disabled
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
spec:
containers:
- name: curl
image: curlimages/curl:7.69.0
command:
- sh
- -c
- |
curl -sd 'data' ${PODINFO_SVC}/cache/test &&
curl -s ${PODINFO_SVC}/cache/test | grep data &&
curl -s -XDELETE ${PODINFO_SVC}/cache/test
env:
- name: PODINFO_SVC
value: "{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.externalPort }}"
restartPolicy: Never
{{- end }}

View File

@@ -1,26 +1,21 @@
{{- if .Values.faults.test }}
apiVersion: batch/v1
kind: Job
{{- if .Values.faults.testFail }}
apiVersion: v1
kind: Pod
metadata:
name: {{ template "podinfo.fullname" . }}-fault-test-{{ randAlphaNum 5 | lower }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "podinfo.name" . }}
{{- include "podinfo.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
"helm.sh/hook": test-success
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
sidecar.istio.io/inject: "false"
linkerd.io/inject: disabled
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
spec:
template:
spec:
containers:
- name: fault
image: alpine:3.11
command: ['/bin/sh']
args: ['-c', 'exit 1']
restartPolicy: Never
containers:
- name: fault
image: alpine:3.11
command: ['/bin/sh']
args: ['-c', 'exit 1']
restartPolicy: Never
{{- end }}

View File

@@ -1,24 +1,19 @@
apiVersion: batch/v1
kind: Job
apiVersion: v1
kind: Pod
metadata:
name: {{ template "podinfo.fullname" . }}-grpc-test-{{ randAlphaNum 5 | lower }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "podinfo.name" . }}
{{- include "podinfo.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
"helm.sh/hook": test-success
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
sidecar.istio.io/inject: "false"
linkerd.io/inject: disabled
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
spec:
template:
spec:
containers:
- name: grpc-health-probe
image: stefanprodan/grpc_health_probe:v0.3.0
command: ['grpc_health_probe']
args: ['-addr={{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.grpcPort }}']
restartPolicy: Never
containers:
- name: grpc-health-probe
image: stefanprodan/grpc_health_probe:v0.3.0
command: ['grpc_health_probe']
args: ['-addr={{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.grpcPort }}']
restartPolicy: Never

View File

@@ -1,31 +1,26 @@
apiVersion: batch/v1
kind: Job
apiVersion: v1
kind: Pod
metadata:
name: {{ template "podinfo.fullname" . }}-jwt-test-{{ randAlphaNum 5 | lower }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "podinfo.name" . }}
{{- include "podinfo.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
"helm.sh/hook": test-success
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
sidecar.istio.io/inject: "false"
linkerd.io/inject: disabled
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
spec:
template:
spec:
containers:
- name: tools
image: giantswarm/tiny-tools
command:
- sh
- -c
- |
TOKEN=$(curl -sd 'test' ${PODINFO_SVC}/token | jq -r .token) &&
curl -sH "Authorization: Bearer ${TOKEN}" ${PODINFO_SVC}/token/validate | grep test
env:
- name: PODINFO_SVC
value: "{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.externalPort }}"
restartPolicy: Never
containers:
- name: tools
image: giantswarm/tiny-tools
command:
- sh
- -c
- |
TOKEN=$(curl -sd 'test' ${PODINFO_SVC}/token | jq -r .token) &&
curl -sH "Authorization: Bearer ${TOKEN}" ${PODINFO_SVC}/token/validate | grep test
env:
- name: PODINFO_SVC
value: "{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.externalPort }}"
restartPolicy: Never

View File

@@ -1,30 +1,25 @@
apiVersion: batch/v1
kind: Job
apiVersion: v1
kind: Pod
metadata:
name: {{ template "podinfo.fullname" . }}-service-test-{{ randAlphaNum 5 | lower }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "podinfo.name" . }}
{{- include "podinfo.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
"helm.sh/hook": test-success
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
sidecar.istio.io/inject: "false"
linkerd.io/inject: disabled
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
spec:
template:
spec:
containers:
- name: curl
image: curlimages/curl:7.69.0
command:
- sh
- -c
- |
curl -s ${PODINFO_SVC}/api/info | grep version
env:
- name: PODINFO_SVC
value: "{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.externalPort }}"
restartPolicy: Never
containers:
- name: curl
image: curlimages/curl:7.69.0
command:
- sh
- -c
- |
curl -s ${PODINFO_SVC}/api/info | grep version
env:
- name: PODINFO_SVC
value: "{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.externalPort }}"
restartPolicy: Never

View File

@@ -1,26 +1,21 @@
{{- if .Values.faults.test }}
apiVersion: batch/v1
kind: Job
{{- if .Values.faults.testTimeout }}
apiVersion: v1
kind: Pod
metadata:
name: {{ template "podinfo.fullname" . }}-fault-test-{{ randAlphaNum 5 | lower }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "podinfo.name" . }}
{{- include "podinfo.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
"helm.sh/hook": test-success
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
sidecar.istio.io/inject: "false"
linkerd.io/inject: disabled
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
spec:
template:
spec:
containers:
- name: fault
image: alpine:3.11
command: ['/bin/sh']
args: ['-c', 'while sleep 3600; do :; done']
restartPolicy: Never
containers:
- name: fault
image: alpine:3.11
command: ['/bin/sh']
args: ['-c', 'while sleep 3600; do :; done']
restartPolicy: Never
{{- end }}

View File

@@ -0,0 +1,27 @@
{{- if .Values.tls.enabled -}}
apiVersion: v1
kind: Pod
metadata:
name: {{ template "podinfo.fullname" . }}-tls-test-{{ randAlphaNum 5 | lower }}
labels:
{{- include "podinfo.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test-success
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
sidecar.istio.io/inject: "false"
linkerd.io/inject: disabled
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
spec:
containers:
- name: curl
image: curlimages/curl:7.69.0
command:
- sh
- -c
- |
curl -sk ${PODINFO_SVC}/api/info | grep version
env:
- name: PODINFO_SVC
value: "https://{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.tls.port }}"
restartPolicy: Never
{{- end }}

View File

@@ -0,0 +1,137 @@
# Production values for podinfo.
# Includes Redis deployment and memory limits.
replicaCount: 1
logLevel: info
backend: #http://backend-podinfo:9898/echo
backends: []
image:
repository: ghcr.io/stefanprodan/podinfo
tag: 6.0.1
pullPolicy: IfNotPresent
ui:
color: "#34577c"
message: ""
logo: ""
# failure conditions
faults:
delay: false
error: false
unhealthy: false
unready: false
testFail: false
testTimeout: false
# Kubernetes Service settings
service:
enabled: true
annotations: {}
type: ClusterIP
metricsPort: 9797
httpPort: 9898
externalPort: 9898
grpcPort: 9999
grpcService: podinfo
nodePort: 31198
# enable h2c protocol (non-TLS version of HTTP/2)
h2c:
enabled: false
# enable tls on the podinfo service
tls:
enabled: false
# the name of the secret used to mount the certificate key pair
secretName:
# the path where the certificate key pair will be mounted
certPath: /data/cert
# the port used to host the tls endpoint on the service
port: 9899
# the port used to bind the tls port to the host
# NOTE: requires privileged container with NET_BIND_SERVICE capability -- this is useful for testing
# in local clusters such as kind without port forwarding
hostPort:
# create a certificate manager certificate (cert-manager required)
certificate:
create: false
# the issuer used to issue the certificate
issuerRef:
kind: ClusterIssuer
name: self-signed
# the hostname / subject alternative names for the certificate
dnsNames:
- podinfo
# metrics-server add-on required
hpa:
enabled: true
maxReplicas: 5
# average total CPU usage per pod (1-100)
cpu: 99
# average memory usage per pod (100Mi-1Gi)
memory:
# average http requests per second per pod (k8s-prometheus-adapter)
requests:
# Redis address in the format <host>:<port>
cache: ""
# Redis deployment
redis:
enabled: true
repository: redis
tag: 6.0.8
serviceAccount:
# Specifies whether a service account should be created
enabled: false
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name:
# set container security context
securityContext: {}
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: podinfo.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
linkerd:
profile:
enabled: false
# create Prometheus Operator monitor
serviceMonitor:
enabled: false
interval: 15s
additionalLabels: {}
resources:
limits:
memory: 256Mi
requests:
cpu: 100m
memory: 64Mi
nodeSelector: {}
tolerations: []
affinity: {}
podAnnotations: {}

View File

@@ -2,14 +2,21 @@
replicaCount: 1
logLevel: info
host: #0.0.0.0
backend: #http://backend-podinfo:9898/echo
backends: []
image:
repository: ghcr.io/stefanprodan/podinfo
tag: 6.0.1
pullPolicy: IfNotPresent
ui:
color: "#34577c"
message: ""
logo: ""
# failure conditions
faults:
delay: false
error: false
@@ -18,16 +25,10 @@ faults:
testFail: false
testTimeout: false
h2c:
enabled: false
image:
repository: stefanprodan/podinfo
tag: 3.2.4
pullPolicy: IfNotPresent
# Kubernetes Service settings
service:
enabled: true
annotations: {}
type: ClusterIP
metricsPort: 9797
httpPort: 9898
@@ -35,6 +36,39 @@ service:
grpcPort: 9999
grpcService: podinfo
nodePort: 31198
# the port used to bind the http port to the host
# NOTE: requires privileged container with NET_BIND_SERVICE capability -- this is useful for testing
# in local clusters such as kind without port forwarding
hostPort:
# enable h2c protocol (non-TLS version of HTTP/2)
h2c:
enabled: false
# enable tls on the podinfo service
tls:
enabled: false
# the name of the secret used to mount the certificate key pair
secretName:
# the path where the certificate key pair will be mounted
certPath: /data/cert
# the port used to host the tls endpoint on the service
port: 9899
# the port used to bind the tls port to the host
# NOTE: requires privileged container with NET_BIND_SERVICE capability -- this is useful for testing
# in local clusters such as kind without port forwarding
hostPort:
# create a certificate manager certificate (cert-manager required)
certificate:
create: false
# the issuer used to issue the certificate
issuerRef:
kind: ClusterIssuer
name: self-signed
# the hostname / subject alternative names for the certificate
dnsNames:
- podinfo
# metrics-server add-on required
hpa:
@@ -47,6 +81,14 @@ hpa:
# average http requests per second per pod (k8s-prometheus-adapter)
requests:
# Redis address in the format <host>:<port>
cache: ""
# Redis deployment
redis:
enabled: false
repository: redis
tag: 6.0.8
serviceAccount:
# Specifies whether a service account should be created
enabled: false
@@ -54,27 +96,35 @@ serviceAccount:
# If not set and create is true, a name is generated using the fullname template
name:
linkerd:
profile:
enabled: false
serviceMonitor:
enabled: false
interval: 15s
# set container security context
securityContext: {}
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
path: /*
hosts: []
# - podinfo.local
hosts:
- host: podinfo.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
linkerd:
profile:
enabled: false
# create Prometheus Operator monitor
serviceMonitor:
enabled: false
interval: 15s
additionalLabels: {}
resources:
limits:
requests:
@@ -87,3 +137,4 @@ tolerations: []
affinity: {}
podAnnotations: {}

View File

@@ -1,365 +0,0 @@
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/hashicorp/go-getter"
"github.com/spf13/cobra"
)
var (
codeProjectName string
codeGitUser string
codeVersion string
codeProjectPath string
)
var codeCmd = &cobra.Command{
Use: `code`,
Short: "Code commands",
}
var codeInitCmd = &cobra.Command{
Use: `init [name]`,
Short: "initialize podinfo code repo",
Example: ` code init demo-app --version=v1.2.0 --git-user=stefanprodan`,
RunE: runCodeInit,
}
func init() {
codeInitCmd.Flags().StringVar(&codeGitUser, "git-user", "", "GitHub user or org")
codeInitCmd.Flags().StringVar(&codeVersion, "version", "master", "podinfo repo tag or branch name")
codeInitCmd.Flags().StringVar(&codeProjectPath, "path", ".", "destination repo")
codeCmd.AddCommand(codeInitCmd)
rootCmd.AddCommand(codeCmd)
}
func runCodeInit(cmd *cobra.Command, args []string) error {
if len(codeGitUser) < 0 {
return fmt.Errorf("--git-user is required")
}
if len(args) < 1 {
return fmt.Errorf("project name is required")
}
codeProjectName = args[0]
pwd, err := os.Getwd()
if err != nil {
log.Fatalf("Error getting pwd: %s", err)
os.Exit(1)
}
tmpPath := "/tmp/k8s-podinfo"
versionName := fmt.Sprintf("k8s-podinfo-%s", codeVersion)
downloadURL := fmt.Sprintf("https://github.com/stefanprodan/podinfo/archive/%s.zip", codeVersion)
client := &getter.Client{
Src: downloadURL,
Dst: tmpPath,
Pwd: pwd,
Mode: getter.ClientModeAny,
}
fmt.Printf("Downloading %s\n", downloadURL)
if err := client.Get(); err != nil {
log.Fatalf("Error downloading: %s", err)
os.Exit(1)
}
pkgFrom := "github.com/stefanprodan/podinfo"
pkgTo := fmt.Sprintf("github.com/%s/%s", codeGitUser, codeProjectName)
if err := replaceImports(tmpPath, pkgFrom, pkgTo); err != nil {
log.Fatalf("Error parsing imports: %s", err)
os.Exit(1)
}
dirs := []string{"pkg", "cmd", "ui", "vendor", ".github"}
for _, dir := range dirs {
err = os.MkdirAll(path.Join(codeProjectPath, dir), os.ModePerm)
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
if err := copyDir(path.Join(tmpPath, versionName, dir), path.Join(codeProjectPath, dir)); err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
}
files := []string{"Gopkg.toml", "Gopkg.lock"}
for _, file := range files {
if err := copyFile(path.Join(tmpPath, versionName, file), path.Join(codeProjectPath, file)); err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
fileContent, err := ioutil.ReadFile(path.Join(codeProjectPath, file))
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
newContent := strings.Replace(string(fileContent), pkgFrom, pkgTo, -1)
err = ioutil.WriteFile(path.Join(codeProjectPath, file), []byte(newContent), os.ModePerm)
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
}
projFrom := "stefanprodan/podinfo"
projTo := fmt.Sprintf("%s/%s", codeGitUser, codeProjectName)
makeFiles := []string{"Makefile.gh", "Dockerfile.gh"}
for _, file := range makeFiles {
fileContent, err := ioutil.ReadFile(path.Join(tmpPath, versionName, file))
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
destFile := strings.Replace(file, ".gh", "", -1)
newContent := strings.Replace(string(fileContent), projFrom, projTo, -1)
err = ioutil.WriteFile(path.Join(codeProjectPath, destFile), []byte(newContent), os.ModePerm)
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
}
workflows := []string{".github/main.workflow"}
for _, file := range workflows {
fileContent, err := ioutil.ReadFile(path.Join(codeProjectPath, file))
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
newContent := strings.Replace(string(fileContent), "Dockerfile.gh", "Dockerfile", -1)
err = ioutil.WriteFile(path.Join(codeProjectPath, file), []byte(newContent), os.ModePerm)
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
}
dockerFiles := []string{"Dockerfile.ci"}
for _, file := range dockerFiles {
fileContent, err := ioutil.ReadFile(path.Join(tmpPath, versionName, file))
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
newContent := strings.Replace(string(fileContent), projFrom, projTo, -1)
err = ioutil.WriteFile(path.Join(codeProjectPath, file), []byte(newContent), os.ModePerm)
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
}
travisFiles := []string{"travis.lite.yml"}
for _, file := range travisFiles {
fileContent, err := ioutil.ReadFile(path.Join(tmpPath, versionName, file))
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
destFile := strings.Replace(file, "travis.lite.yml", ".travis.yml", -1)
newContent := strings.Replace(string(fileContent), projFrom, projTo, -1)
err = ioutil.WriteFile(path.Join(codeProjectPath, destFile), []byte(newContent), os.ModePerm)
if err != nil {
log.Fatalf("Error: %s", err)
os.Exit(1)
}
}
err = gitPush()
if err != nil {
log.Fatalf("git push error: %s", err)
os.Exit(1)
}
fmt.Println("Initialization finished")
return nil
}
func gitPush() error {
cmdPush := fmt.Sprintf("git add . && git commit -m \"sync %s\" && git push", codeVersion)
cmd := exec.Command("sh", "-c", cmdPush)
output, err := cmd.Output()
if err != nil {
return err
}
fmt.Println(string(output))
return nil
}
func replaceImports(projectPath string, pkgFrom string, pkgTo string) error {
regexImport, err := regexp.Compile(`(?s)(import(.*?)\)|import.*$)`)
if err != nil {
return err
}
regexImportedPackage, err := regexp.Compile(`"(.*?)"`)
if err != nil {
return err
}
found := []string{}
err = filepath.Walk(projectPath, func(path string, info os.FileInfo, err error) error {
if filepath.Ext(path) == ".go" {
bts, err := ioutil.ReadFile(path)
if err != nil {
return err
}
content := string(bts)
matches := regexImport.FindAllString(content, -1)
isExists := false
isReplaceable:
for _, each := range matches {
for _, eachLine := range strings.Split(each, "\n") {
matchesInline := regexImportedPackage.FindAllString(eachLine, -1)
if err != nil {
return err
}
for _, eachSubline := range matchesInline {
if strings.Contains(eachSubline, pkgFrom) {
isExists = true
break isReplaceable
}
}
}
}
if isExists {
content = strings.Replace(content, `"`+pkgFrom+`"`, `"`+pkgTo+`"`, -1)
content = strings.Replace(content, `"`+pkgFrom+`/`, `"`+pkgTo+`/`, -1)
found = append(found, path)
}
err = ioutil.WriteFile(path, []byte(content), info.Mode())
if err != nil {
return err
}
}
return nil
})
if err != nil {
fmt.Println("ERROR", err.Error())
}
if len(found) == 0 {
fmt.Println("Nothing replaced")
} else {
fmt.Printf("Go imports total %d file replaced\n", len(found))
}
return nil
}
func copyDir(src string, dst string) error {
si, err := os.Stat(src)
if err != nil {
return err
}
if !si.IsDir() {
return fmt.Errorf("source is not a directory")
}
err = os.MkdirAll(dst, si.Mode())
if err != nil {
return err
}
entries, err := ioutil.ReadDir(src)
if err != nil {
return err
}
for _, entry := range entries {
srcPath := filepath.Join(src, entry.Name())
dstPath := filepath.Join(dst, entry.Name())
if entry.IsDir() {
err = copyDir(srcPath, dstPath)
if err != nil {
return err
}
} else {
// Skip symlinks.
if entry.Mode()&os.ModeSymlink != 0 {
continue
}
err = copyFile(srcPath, dstPath)
if err != nil {
return err
}
}
}
return nil
}
func copyFile(src, dst string) (err error) {
in, err := os.Open(src)
if err != nil {
return
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return
}
defer func() {
if e := out.Close(); e != nil {
err = e
}
}()
_, err = io.Copy(out, in)
if err != nil {
return
}
err = out.Sync()
if err != nil {
return
}
si, err := os.Stat(src)
if err != nil {
return
}
err = os.Chmod(dst, si.Mode())
if err != nil {
return
}
return
}

View File

@@ -23,7 +23,9 @@ import (
func main() {
// flags definition
fs := pflag.NewFlagSet("default", pflag.ContinueOnError)
fs.Int("port", 9898, "HTTP port")
fs.String("host", "", "Host to bind service to")
fs.Int("port", 9898, "HTTP port to bind service to")
fs.Int("secure-port", 0, "HTTPS port")
fs.Int("port-metrics", 0, "metrics port")
fs.Int("grpc-port", 0, "gRPC port")
fs.String("grpc-service-name", "podinfo", "gPRC service name")
@@ -34,18 +36,23 @@ func main() {
fs.Duration("http-server-shutdown-timeout", 5*time.Second, "server graceful shutdown timeout duration")
fs.String("data-path", "/data", "data local path")
fs.String("config-path", "", "config dir path")
fs.String("cert-path", "/data/cert", "certificate path for HTTPS port")
fs.String("config", "config.yaml", "config file name")
fs.String("ui-path", "./ui", "UI local path")
fs.String("ui-logo", "", "UI logo")
fs.String("ui-color", "#34577c", "UI color")
fs.String("ui-message", fmt.Sprintf("greetings from podinfo v%v", version.VERSION), "UI message")
fs.Bool("h2c", false, "allow upgrading to H2C")
fs.Bool("random-delay", false, "between 0 and 5 seconds random delay")
fs.Bool("random-delay", false, "between 0 and 5 seconds random delay by default")
fs.String("random-delay-unit", "s", "either s(seconds) or ms(milliseconds")
fs.Int("random-delay-min", 0, "min for random delay: 0 by default")
fs.Int("random-delay-max", 5, "max for random delay: 5 by default")
fs.Bool("random-error", false, "1/3 chances of a random response error")
fs.Bool("unhealthy", false, "when set, healthy state is never reached")
fs.Bool("unready", false, "when set, ready state is never reached")
fs.Int("stress-cpu", 0, "number of CPU cores with 100 load")
fs.Int("stress-memory", 0, "MB of data to load into memory")
fs.String("cache-server", "", "Redis address in the format <host>:<port>")
versionFlag := fs.BoolP("version", "v", false, "get version number")
@@ -77,11 +84,11 @@ func main() {
viper.AutomaticEnv()
// load config from file
if _, err := os.Stat(filepath.Join(viper.GetString("config-path"), viper.GetString("config"))); err == nil {
if _, fileErr := os.Stat(filepath.Join(viper.GetString("config-path"), viper.GetString("config"))); fileErr == nil {
viper.SetConfigName(strings.Split(viper.GetString("config"), ".")[0])
viper.AddConfigPath(viper.GetString("config-path"))
if err := viper.ReadInConfig(); err != nil {
fmt.Printf("Error reading config file, %v\n", err)
if readErr := viper.ReadInConfig(); readErr != nil {
fmt.Printf("Error reading config file, %v\n", readErr)
}
}
@@ -100,6 +107,26 @@ func main() {
viper.Set("port", strconv.Itoa(port))
}
// validate secure port
if _, err := strconv.Atoi(viper.GetString("secure-port")); err != nil {
securePort, _ := fs.GetInt("secure-port")
viper.Set("secure-port", strconv.Itoa(securePort))
}
// validate random delay options
if viper.GetInt("random-delay-max") < viper.GetInt("random-delay-min") {
logger.Panic("`--random-delay-max` should be greater than `--random-delay-min`")
}
switch delayUnit := viper.GetString("random-delay-unit"); delayUnit {
case
"s",
"ms":
break
default:
logger.Panic("`random-delay-unit` accepted values are: s|ms")
}
// load gRPC server config
var grpcCfg grpc.Config
if err := viper.Unmarshal(&grpcCfg); err != nil {

View File

@@ -1,6 +1,7 @@
# Deploy demo webapp
# Deploy demo webapp
Demo webapp manifests:
- [common](webapp/common)
- [frontend](webapp/frontend)
- [backend](webapp/backend)
@@ -30,3 +31,15 @@ Deploy the demo in the `production` namespace:
```bash
kustomize build ./overlays/production | kubectl apply -f-
```
## Testing Locally Using Kind
> NOTE: You can install [kind from here](https://kind.sigs.k8s.io/docs/user/quick-start/#installation)
The following will create a new cluster called "podinfo" and configure host ports on 80 and 443. You can access the
endpoints on localhost. The example also deploys cert-manager within the cluster along with a self-signed cluster issuer
used to generate the certificate to validate the secure port.
```sh
./kind.sh
```

View File

@@ -23,7 +23,7 @@ spec:
spec:
containers:
- name: backend
image: stefanprodan/podinfo:3.2.4
image: ghcr.io/stefanprodan/podinfo:6.0.1
imagePullPolicy: IfNotPresent
ports:
- name: http
@@ -42,6 +42,7 @@ spec:
- --grpc-port=9999
- --grpc-service-name=backend
- --level=info
- --cache-server=cache:6379
env:
- name: PODINFO_UI_COLOR
value: "#34577c"

57
deploy/bases/cache/deployment.yaml vendored Normal file
View File

@@ -0,0 +1,57 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: cache
spec:
selector:
matchLabels:
app: cache
template:
metadata:
labels:
app: cache
spec:
containers:
- name: redis
image: redis:6.0.1
imagePullPolicy: IfNotPresent
command:
- redis-server
- "/redis-master/redis.conf"
ports:
- name: redis
containerPort: 6379
protocol: TCP
livenessProbe:
tcpSocket:
port: redis
initialDelaySeconds: 5
timeoutSeconds: 5
readinessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 5
timeoutSeconds: 5
resources:
limits:
cpu: 1000m
memory: 128Mi
requests:
cpu: 100m
memory: 32Mi
volumeMounts:
- mountPath: /var/lib/redis
name: data
- mountPath: /redis-master
name: config
volumes:
- name: data
emptyDir: {}
- name: config
configMap:
name: redis-config
items:
- key: redis.conf
path: redis.conf

9
deploy/bases/cache/kustomization.yaml vendored Normal file
View File

@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- service.yaml
- deployment.yaml
configMapGenerator:
- name: redis-config
files:
- redis.conf

4
deploy/bases/cache/redis.conf vendored Normal file
View File

@@ -0,0 +1,4 @@
maxmemory 64mb
maxmemory-policy allkeys-lru
save ""
appendonly no

13
deploy/bases/cache/service.yaml vendored Normal file
View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: cache
spec:
type: ClusterIP
selector:
app: cache
ports:
- name: redis
port: 6379
protocol: TCP
targetPort: redis

View File

@@ -23,7 +23,7 @@ spec:
spec:
containers:
- name: frontend
image: stefanprodan/podinfo:3.2.4
image: ghcr.io/stefanprodan/podinfo:6.0.1
imagePullPolicy: IfNotPresent
ports:
- name: http
@@ -41,6 +41,7 @@ spec:
- --port-metrics=9797
- --level=info
- --backend-url=http://backend:9898/echo
- --cache-server=cache:6379
env:
- name: PODINFO_UI_COLOR
value: "#34577c"

48
deploy/kind.sh Executable file
View File

@@ -0,0 +1,48 @@
#! /usr/bin/env sh
mkdir -p bin
cat > ./bin/kind.yaml <<EOF
apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
EOF
# create the kind cluster
kind create cluster --config=kind.yaml
# add certificate manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.0.4/cert-manager.yaml
# wait for cert manager
kubectl rollout status --namespace cert-manager deployment/cert-manager --timeout=2m
kubectl rollout status --namespace cert-manager deployment/cert-manager-webhook --timeout=2m
kubectl rollout status --namespace cert-manager deployment/cert-manager-cainjector --timeout=2m
# # apply the secure webapp
kubectl apply -f ./secure/common
kubectl apply -f ./secure/backend
kubectl apply -f ./secure/frontend
# # wait for the podinfo frontend to come up
kubectl rollout status --namespace secure deployment/frontend --timeout=1m
# curl the endpoints (responds with info due to header regexp on route handler)
echo
echo "http enpdoint:"
echo "curl http://localhost"
echo
curl http://localhost
echo
echo "https (secure) enpdoint:"
echo "curl --insecure https://localhost"
echo
curl --insecure https://localhost

View File

@@ -4,6 +4,7 @@ namespace: dev
resources:
- ../../bases/backend
- ../../bases/frontend
- ../../bases/cache
- namespace.yaml
transformers:
- labels.yaml

View File

@@ -4,6 +4,7 @@ namespace: production
resources:
- ../../bases/backend
- ../../bases/frontend
- ../../bases/cache
- namespace.yaml
transformers:
- labels.yaml

View File

@@ -4,6 +4,7 @@ namespace: staging
resources:
- ../../bases/backend
- ../../bases/frontend
- ../../bases/cache
- namespace.yaml
transformers:
- labels.yaml

View File

@@ -0,0 +1,74 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
namespace: secure
spec:
minReadySeconds: 3
revisionHistoryLimit: 5
progressDeadlineSeconds: 60
strategy:
rollingUpdate:
maxUnavailable: 0
type: RollingUpdate
selector:
matchLabels:
app: backend
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9797"
labels:
app: backend
spec:
serviceAccountName: secure
containers:
- name: backend
image: ghcr.io/stefanprodan/podinfo:5.0.3
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 9898
protocol: TCP
- name: http-metrics
containerPort: 9797
protocol: TCP
- name: grpc
containerPort: 9999
protocol: TCP
command:
- ./podinfo
- --port=9898
- --port-metrics=9797
- --grpc-port=9999
- --grpc-service-name=backend
- --level=info
env:
- name: PODINFO_UI_COLOR
value: "#34577c"
livenessProbe:
exec:
command:
- podcli
- check
- http
- localhost:9898/healthz
initialDelaySeconds: 5
timeoutSeconds: 5
readinessProbe:
exec:
command:
- podcli
- check
- http
- localhost:9898/readyz
initialDelaySeconds: 5
timeoutSeconds: 5
resources:
limits:
cpu: 2000m
memory: 512Mi
requests:
cpu: 100m
memory: 32Mi

View File

@@ -0,0 +1,19 @@
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: backend
namespace: secure
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: backend
minReplicas: 1
maxReplicas: 2
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 99

View File

@@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: backend
namespace: secure
spec:
type: ClusterIP
selector:
app: backend
ports:
- name: http
port: 9898
protocol: TCP
targetPort: http
- port: 9999
targetPort: grpc
protocol: TCP
name: grpc

View File

@@ -0,0 +1,6 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: self-signed
spec:
selfSigned: {}

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: secure

View File

@@ -0,0 +1,29 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: reconciler
namespace: secure
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: reconciler
namespace: secure
rules:
- apiGroups: ['*']
resources: ['*']
verbs: ['*']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: reconciler
namespace: secure
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: reconciler
subjects:
- kind: ServiceAccount
name: reconciler
namespace: secure

View File

@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: secure
namespace: secure

View File

@@ -0,0 +1,15 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: podinfo-frontend
namespace: secure
spec:
dnsNames:
- frontend
- frontend.secure
- frontend.secure.cluster.local
- localhost
secretName: podinfo-frontend-tls
issuerRef:
name: self-signed
kind: ClusterIssuer

View File

@@ -0,0 +1,95 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: secure
spec:
minReadySeconds: 3
revisionHistoryLimit: 5
progressDeadlineSeconds: 60
strategy:
rollingUpdate:
maxUnavailable: 0
type: RollingUpdate
selector:
matchLabels:
app: frontend
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9797"
labels:
app: frontend
spec:
serviceAccountName: secure
volumes:
- name: tls
secret:
secretName: podinfo-frontend-tls
containers:
- name: frontend
image: deavon/podinfo:secure-port
imagePullPolicy: IfNotPresent
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
allowPrivilegeEscalation: true
ports:
- name: http
containerPort: 9898
protocol: TCP
hostPort: 80
- name: https
containerPort: 9899
protocol: TCP
hostPort: 443
- name: http-metrics
containerPort: 9797
protocol: TCP
- name: grpc
containerPort: 9999
protocol: TCP
volumeMounts:
- name: tls
mountPath: /data/cert
readOnly: true
command:
- ./podinfo
- --port=9898
- --secure-port=9899
- --port-metrics=9797
- --level=info
- --cert-path=/data/cert
- --backend-url=http://backend:9898/echo
env:
- name: PODINFO_UI_COLOR
value: "#34577c"
livenessProbe:
exec:
command:
- podcli
- check
- http
- localhost:9898/healthz
initialDelaySeconds: 5
timeoutSeconds: 5
readinessProbe:
exec:
command:
- podcli
- check
- http
- localhost:9898/readyz
initialDelaySeconds: 5
timeoutSeconds: 5
resources:
limits:
cpu: 1000m
memory: 128Mi
requests:
cpu: 100m
memory: 32Mi

View File

@@ -0,0 +1,19 @@
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: frontend
namespace: secure
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: frontend
minReplicas: 1
maxReplicas: 4
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 99

View File

@@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: frontend
namespace: secure
spec:
type: ClusterIP
selector:
app: frontend
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
- name: https
port: 443
protocol: TCP
targetPort: https

View File

@@ -25,7 +25,7 @@ spec:
serviceAccountName: webapp
containers:
- name: backend
image: stefanprodan/podinfo:3.2.4
image: ghcr.io/stefanprodan/podinfo:6.0.1
imagePullPolicy: IfNotPresent
ports:
- name: http

View File

@@ -25,7 +25,7 @@ spec:
serviceAccountName: webapp
containers:
- name: frontend
image: stefanprodan/podinfo:3.2.4
image: ghcr.io/stefanprodan/podinfo:6.0.1
imagePullPolicy: IfNotPresent
ports:
- name: http

View File

@@ -1,35 +0,0 @@
# podinfo end-to-end testing
The e2e testing infrastructure is powered by CircleCI and [Kubernetes Kind](https://github.com/kubernetes-sigs/kind).
### CI workflow
* download go modules
* run unit tests
* build container
* install kubectl, Helm v3 and Kubernetes Kind CLIs
* create local Kubernetes cluster with kind
* load podinfo image onto the local cluster
* deploy podinfo with Helm
* set the podinfo image to the locally built one
* run Helm tests
```yaml
jobs:
e2e-kubernetes:
machine: true
steps:
- checkout
- run:
name: Build podinfo container
command: e2e/build.sh
- run:
name: Start Kubernetes Kind cluster
command: e2e/bootstrap.sh
- run:
name: Install podinfo with Helm
command: e2e/install.sh
- run:
name: Run Helm tests
command: e2e/test.sh
```

View File

@@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -o errexit
REPO_ROOT=$(git rev-parse --show-toplevel)
KIND_VERSION=v0.7.0
if [[ "$1" ]]; then
KIND_VERSION=$1
fi
echo ">>> Installing kubectl"
curl -sLO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && \
chmod +x kubectl && \
sudo mv kubectl /usr/local/bin/
echo ">>> Installing kind"
curl -sSLo kind "https://github.com/kubernetes-sigs/kind/releases/download/$KIND_VERSION/kind-linux-amd64"
chmod +x kind
sudo mv kind /usr/local/bin/kind
echo ">>> Creating kind cluster"
kind create cluster --wait 5m
echo ">>> Installing Helm v3"
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -o errexit
docker build -t test/podinfo:latest .

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env bash
set -o errexit
REPO_ROOT=$(git rev-parse --show-toplevel)
echo '>>> Loading image in Kind'
kind load docker-image test/podinfo:latest
echo '>>> Installing'
helm upgrade -i podinfo ${REPO_ROOT}/charts/podinfo --namespace=default
kubectl set image deployment/podinfo podinfo=test/podinfo:latest
kubectl rollout status deployment/podinfo

View File

@@ -1,12 +0,0 @@
#!/usr/bin/env bash
set -o errexit
function finish {
echo '>>> Test logs'
kubectl logs -l app=podinfo || true
}
trap "finish" EXIT SIGINT
echo '>>> Start integration tests'
helm test podinfo

34
go.mod
View File

@@ -1,26 +1,24 @@
module github.com/stefanprodan/podinfo
go 1.14
go 1.16
require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/chzyer/logex v1.1.10 // indirect
github.com/chzyer/readline v0.0.0-20160726135117-62c6fe619375
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fatih/color v1.7.0
github.com/fsnotify/fsnotify v1.4.7
github.com/go-chi/chi v4.0.3+incompatible // indirect
github.com/gorilla/mux v1.7.4
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1
github.com/fatih/color v1.9.0
github.com/fsnotify/fsnotify v1.4.9
github.com/gomodule/redigo v1.8.4
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/go-getter v1.4.1
github.com/prometheus/client_golang v1.5.1
github.com/spf13/cobra v0.0.6
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/prometheus/client_golang v1.11.0
github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.6.2
github.com/swaggo/http-swagger v0.0.0-20190614090009-c2865af9083e
github.com/swaggo/swag v1.6.5
go.uber.org/zap v1.10.0
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
google.golang.org/grpc v1.23.0
github.com/spf13/viper v1.8.1
github.com/swaggo/http-swagger v1.0.0
github.com/swaggo/swag v1.7.0
go.uber.org/zap v1.19.1
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
google.golang.org/grpc v1.38.0
)

727
go.sum
View File

@@ -3,18 +3,44 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1 h1:lRi0CHyU+ytlvylOlFKKq0af6JncuyoRh1J+QJBqQx0=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
@@ -24,330 +50,448 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY=
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20160726135117-62c6fe619375 h1:JVe1zduaiPlSLOuQcU/MqRJkBbWRPsjdW48+20AtJXM=
github.com/chzyer/readline v0.0.0-20160726135117-62c6fe619375/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/go-chi/chi v4.0.3+incompatible h1:gakN3pDJnzZN5jqFV2TEdF66rTfKeITyR8qu6ekICEY=
github.com/go-chi/chi v4.0.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=
github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA=
github.com/go-openapi/spec v0.20.0 h1:HGLc8AJ7ynOxwv0Lq4TsnwLsWMawHAYiJIFzbcML86I=
github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY=
github.com/go-openapi/swag v0.19.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI=
github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg=
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-getter v1.4.1 h1:3A2Mh8smGFcf5M+gmcv898mZdrxpseik45IpcyISLsA=
github.com/hashicorp/go-getter v1.4.1/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=
github.com/swaggo/http-swagger v0.0.0-20190614090009-c2865af9083e h1:m5sYJ43teIUlESuKRFQRRm7kqi6ExiYwVKfoXNuRgHU=
github.com/swaggo/http-swagger v0.0.0-20190614090009-c2865af9083e/go.mod h1:eycbshptIv+tqTMlLEaGC2noPNcetbrcYEelLafrIDI=
github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
github.com/swaggo/swag v1.6.5 h1:2C+t+xyK6p1sujqncYO/VnMvPZcBJjNdKKyxbOdAW8o=
github.com/swaggo/swag v1.6.5/go.mod h1:Y7ZLSS0d0DdxhWGVhQdu+Bu1QhaF5k0RD7FKdiAykeY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
github.com/swaggo/http-swagger v1.0.0 h1:ksYgVBCYmAaxFsGVGojlPROgYfiQQSllETTWMtHJHTo=
github.com/swaggo/http-swagger v1.0.0/go.mod h1:cKIcshBU9yEAnfWv6ZzVKSsEf8h5ozxB8/zHQWyOQ/8=
github.com/swaggo/swag v1.7.0 h1:5bCA/MTLQoIqDXXyHfOpMeDvL9j68OY/udlK4pQoo4E=
github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201207224615-747e23833adb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -356,19 +500,58 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -379,62 +562,190 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468 h1:fTfk6GjmihJbK0mSUFgPPgYpsdmApQ86Mcd4GuKax9U=
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59 h1:QjA/9ArTfVTLfEhClDCG7SGrZkZixxWpwNCDiwJfh88=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0 h1:Dh6fw+p6FyRl5x/FvNswO1ji0lIGzm3KP8Y9VkS9PTE=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -23,7 +23,7 @@ spec:
spec:
containers:
- name: podinfod
image: stefanprodan/podinfo:3.2.4
image: ghcr.io/stefanprodan/podinfo:6.0.1
imagePullPolicy: IfNotPresent
ports:
- name: http

View File

@@ -1,4 +1,4 @@
apiVersion: autoscaling/v2beta1
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: podinfo
@@ -13,6 +13,8 @@ spec:
- type: Resource
resource:
name: cpu
# scale up if usage is above
# 99% of the requested CPU (100m)
targetAverageUtilization: 99
target:
type: Utilization
# scale up if usage is above
# 99% of the requested CPU (100m)
averageUtilization: 99

View File

@@ -1,7 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- hpa.yaml
- deployment.yaml
- service.yaml
commonLabels:
app: podinfo

150
pkg/api/cache.go Normal file
View File

@@ -0,0 +1,150 @@
package api
import (
"io/ioutil"
"net/http"
"time"
"github.com/gomodule/redigo/redis"
"github.com/gorilla/mux"
"go.uber.org/zap"
"github.com/stefanprodan/podinfo/pkg/version"
)
// Cache godoc
// @Summary Save payload in cache
// @Description writes the posted content in cache
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /cache/{key} [post]
// @Success 202
func (s *Server) cacheWriteHandler(w http.ResponseWriter, r *http.Request) {
if s.pool == nil {
s.ErrorResponse(w, r, "cache server is offline", http.StatusBadRequest)
return
}
key := mux.Vars(r)["key"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
s.ErrorResponse(w, r, "reading the request body failed", http.StatusBadRequest)
return
}
conn := s.pool.Get()
defer conn.Close()
_, err = conn.Do("SET", key, string(body))
if err != nil {
s.logger.Warn("cache set failed", zap.Error(err))
s.ErrorResponse(w, r, "cache set failed", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusAccepted)
}
// Cache godoc
// @Summary Delete payload from cache
// @Description deletes the key and its value from cache
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /cache/{key} [delete]
// @Success 202
func (s *Server) cacheDeleteHandler(w http.ResponseWriter, r *http.Request) {
if s.pool == nil {
s.ErrorResponse(w, r, "cache server is offline", http.StatusBadRequest)
return
}
key := mux.Vars(r)["key"]
conn := s.pool.Get()
defer conn.Close()
_, err := conn.Do("DEL", key)
if err != nil {
s.logger.Warn("cache delete failed", zap.Error(err))
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusAccepted)
}
// Cache godoc
// @Summary Get payload from cache
// @Description returns the content from cache if key exists
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /cache/{key} [get]
// @Success 200 {string} string value
func (s *Server) cacheReadHandler(w http.ResponseWriter, r *http.Request) {
if s.pool == nil {
s.ErrorResponse(w, r, "cache server is offline", http.StatusBadRequest)
return
}
key := mux.Vars(r)["key"]
conn := s.pool.Get()
defer conn.Close()
ok, err := redis.Bool(conn.Do("EXISTS", key))
if err != nil || !ok {
s.logger.Warn("cache key not found", zap.String("key", key))
w.WriteHeader(http.StatusNotFound)
return
}
data, err := redis.String(conn.Do("GET", key))
if err != nil {
s.logger.Warn("cache get failed", zap.Error(err))
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(data))
}
func (s *Server) startCachePool(ticker *time.Ticker, stopCh <-chan struct{}) {
if s.config.CacheServer == "" {
return
}
s.pool = &redis.Pool{
MaxIdle: 3,
IdleTimeout: 240 * time.Second,
Dial: func() (redis.Conn, error) {
return redis.Dial("tcp", s.config.CacheServer)
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
}
// set <hostname>=<version> with an expiry time of one minute
setVersion := func() {
conn := s.pool.Get()
if _, err := conn.Do("SET", s.config.Hostname, version.VERSION, "EX", 60); err != nil {
s.logger.Warn("cache server is offline", zap.Error(err), zap.String("server", s.config.CacheServer))
}
_ = conn.Close()
}
// set version on a schedule
go func() {
setVersion()
for {
select {
case <-stopCh:
return
case <-ticker.C:
setVersion()
}
}
}()
}

View File

@@ -1,6 +1,7 @@
package api
import (
"math/rand"
"net/http"
"strconv"
@@ -9,6 +10,39 @@ import (
"github.com/gorilla/mux"
)
type RandomDelayMiddleware struct {
min int
max int
unit string
}
func NewRandomDelayMiddleware(minDelay, maxDelay int, delayUnit string) *RandomDelayMiddleware {
return &RandomDelayMiddleware{
min: minDelay,
max: maxDelay,
unit: delayUnit,
}
}
func (m *RandomDelayMiddleware) Handler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var unit time.Duration
rand.Seed(time.Now().Unix())
switch m.unit {
case "s":
unit = time.Second
case "ms":
unit = time.Millisecond
default:
unit = time.Second
}
delay := rand.Intn(m.max-m.min) + m.min
time.Sleep(time.Duration(delay) * unit)
next.ServeHTTP(w, r)
})
}
// Delay godoc
// @Summary Delay
// @Description waits for the specified period

View File

@@ -1,12 +1,12 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag at
// 2019-08-07 15:52:23.918713 +0300 EEST m=+0.023438272
// This file was generated by swaggo/swag
package docs
import (
"bytes"
"encoding/json"
"strings"
"github.com/alecthomas/template"
"github.com/swaggo/swag"
@@ -16,8 +16,8 @@ var doc = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "Go microservice template for Kubernetes.",
"title": "Podinfo API",
"description": "{{.Description}}",
"title": "{{.Title}}",
"contact": {
"name": "Source Code",
"url": "https://github.com/stefanprodan/podinfo"
@@ -26,10 +26,10 @@ var doc = `{
"name": "MIT License",
"url": "https://github.com/stefanprodan/podinfo/blob/master/LICENSE"
},
"version": "2.0"
"version": "{{.Version}}"
},
"host": "localhost:9898",
"basePath": "/",
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/": {
"get": {
@@ -68,7 +68,6 @@ var doc = `{
"202": {
"description": "Accepted",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -92,13 +91,71 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.RuntimeResponse"
}
}
}
}
},
"/cache/{key}": {
"get": {
"description": "returns the content from cache if key exists",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Get payload from cache",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
},
"post": {
"description": "writes the posted content in cache",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Save payload in cache",
"responses": {
"202": {
"description": ""
}
}
},
"delete": {
"description": "deletes the key and its value from cache",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Delete payload from cache",
"responses": {
"202": {
"description": ""
}
}
}
},
"/chunked/{seconds}": {
"get": {
"description": "uses transfer-encoding type chunked to give a partial response and then waits for the specified period",
@@ -116,7 +173,6 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -140,7 +196,6 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -164,8 +219,10 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ArrayResponse"
"type": "array",
"items": {
"type": "string"
}
}
}
}
@@ -188,8 +245,10 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ArrayResponse"
"type": "array",
"items": {
"type": "string"
}
}
}
}
@@ -333,7 +392,6 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -357,7 +415,6 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -404,7 +461,6 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.TokenResponse"
}
}
@@ -428,7 +484,6 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.TokenValidationResponse"
}
},
@@ -455,7 +510,6 @@ var doc = `{
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -479,7 +533,6 @@ var doc = `{
"202": {
"description": "Accepted",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -488,13 +541,11 @@ var doc = `{
}
},
"definitions": {
"api.ArrayResponse": {
"type": "array",
"items": {}
},
"api.MapResponse": {
"type": "object",
"additionalProperties": {}
"additionalProperties": {
"type": "string"
}
},
"api.RuntimeResponse": {
"type": "object",
@@ -511,6 +562,9 @@ var doc = `{
"hostname": {
"type": "string"
},
"logo": {
"type": "string"
},
"message": {
"type": "string"
},
@@ -566,11 +620,21 @@ type swaggerInfo struct {
}
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = swaggerInfo{Schemes: []string{"http", "https"}}
var SwaggerInfo = swaggerInfo{
Version: "2.0",
Host: "localhost:9898",
BasePath: "/",
Schemes: []string{"http", "https"},
Title: "Podinfo API",
Description: "Go microservice template for Kubernetes.",
}
type s struct{}
func (s *s) ReadDoc() string {
sInfo := SwaggerInfo
sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
t, err := template.New("swagger_info").Funcs(template.FuncMap{
"marshal": func(v interface{}) string {
a, _ := json.Marshal(v)
@@ -582,7 +646,7 @@ func (s *s) ReadDoc() string {
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, SwaggerInfo); err != nil {
if err := t.Execute(&tpl, sInfo); err != nil {
return doc
}

View File

@@ -1,4 +1,8 @@
{
"schemes": [
"http",
"https"
],
"swagger": "2.0",
"info": {
"description": "Go microservice template for Kubernetes.",
@@ -53,7 +57,6 @@
"202": {
"description": "Accepted",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -77,13 +80,71 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.RuntimeResponse"
}
}
}
}
},
"/cache/{key}": {
"get": {
"description": "returns the content from cache if key exists",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Get payload from cache",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
},
"post": {
"description": "writes the posted content in cache",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Save payload in cache",
"responses": {
"202": {
"description": ""
}
}
},
"delete": {
"description": "deletes the key and its value from cache",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Delete payload from cache",
"responses": {
"202": {
"description": ""
}
}
}
},
"/chunked/{seconds}": {
"get": {
"description": "uses transfer-encoding type chunked to give a partial response and then waits for the specified period",
@@ -101,7 +162,6 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -125,7 +185,6 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -149,8 +208,10 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ArrayResponse"
"type": "array",
"items": {
"type": "string"
}
}
}
}
@@ -173,8 +234,10 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ArrayResponse"
"type": "array",
"items": {
"type": "string"
}
}
}
}
@@ -318,7 +381,6 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -342,7 +404,6 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -389,7 +450,6 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.TokenResponse"
}
}
@@ -413,7 +473,6 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.TokenValidationResponse"
}
},
@@ -440,7 +499,6 @@
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -464,7 +522,6 @@
"202": {
"description": "Accepted",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
@@ -473,13 +530,11 @@
}
},
"definitions": {
"api.ArrayResponse": {
"type": "array",
"items": {}
},
"api.MapResponse": {
"type": "object",
"additionalProperties": {}
"additionalProperties": {
"type": "string"
}
},
"api.RuntimeResponse": {
"type": "object",
@@ -496,6 +551,9 @@
"hostname": {
"type": "string"
},
"logo": {
"type": "string"
},
"message": {
"type": "string"
},

View File

@@ -1,10 +1,8 @@
basePath: /
definitions:
api.ArrayResponse:
items: {}
type: array
api.MapResponse:
additionalProperties: {}
additionalProperties:
type: string
type: object
api.RuntimeResponse:
properties:
@@ -16,6 +14,8 @@ definitions:
type: string
hostname:
type: string
logo:
type: string
message:
type: string
num_cpu:
@@ -80,7 +80,6 @@ paths:
description: Accepted
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Echo
tags:
- HTTP API
@@ -96,10 +95,48 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.RuntimeResponse'
type: object
summary: Runtime information
tags:
- HTTP API
/cache/{key}:
delete:
consumes:
- application/json
description: deletes the key and its value from cache
produces:
- application/json
responses:
"202":
description: ""
summary: Delete payload from cache
tags:
- HTTP API
get:
consumes:
- application/json
description: returns the content from cache if key exists
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
summary: Get payload from cache
tags:
- HTTP API
post:
consumes:
- application/json
description: writes the posted content in cache
produces:
- application/json
responses:
"202":
description: ""
summary: Save payload in cache
tags:
- HTTP API
/chunked/{seconds}:
get:
consumes:
@@ -113,7 +150,6 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Chunked transfer encoding
tags:
- HTTP API
@@ -129,7 +165,6 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Delay
tags:
- HTTP API
@@ -144,8 +179,9 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.ArrayResponse'
type: object
items:
type: string
type: array
summary: Environment
tags:
- HTTP API
@@ -160,8 +196,9 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.ArrayResponse'
type: object
items:
type: string
type: array
summary: Headers
tags:
- HTTP API
@@ -257,7 +294,6 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Status code
tags:
- HTTP API
@@ -274,7 +310,6 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Upload file
tags:
- HTTP API
@@ -305,7 +340,6 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.TokenResponse'
type: object
summary: Generate JWT token
tags:
- HTTP API
@@ -321,7 +355,6 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.TokenValidationResponse'
type: object
"401":
description: Unauthorized
schema:
@@ -339,7 +372,6 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Version
tags:
- HTTP API
@@ -355,8 +387,10 @@ paths:
description: Accepted
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Echo over websockets
tags:
- HTTP API
schemes:
- http
- https
swagger: "2.0"

View File

@@ -11,17 +11,6 @@ import (
"go.uber.org/zap"
)
func randomDelayMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
min := 0
max := 5
rand.Seed(time.Now().Unix())
delay := rand.Intn(max-min) + min
time.Sleep(time.Duration(delay) * time.Second)
next.ServeHTTP(w, r)
})
}
func randomErrorMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rand.Seed(time.Now().Unix())

View File

@@ -3,25 +3,25 @@ package api
import (
"context"
"fmt"
"github.com/swaggo/swag"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"net/http"
_ "net/http/pprof"
"os"
"path"
"strings"
"sync/atomic"
"time"
"github.com/gomodule/redigo/redis"
"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/spf13/viper"
_ "github.com/stefanprodan/podinfo/pkg/api/docs"
"github.com/stefanprodan/podinfo/pkg/fscache"
httpSwagger "github.com/swaggo/http-swagger"
"github.com/swaggo/swag"
"go.uber.org/zap"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
// @title Podinfo API
@@ -44,11 +44,6 @@ var (
watcher *fscache.Watcher
)
type FluxConfig struct {
GitUrl string `mapstructure:"git-url"`
GitBranch string `mapstructure:"git-branch"`
}
type Config struct {
HttpClientTimeout time.Duration `mapstructure:"http-client-timeout"`
HttpServerTimeout time.Duration `mapstructure:"http-server-timeout"`
@@ -60,21 +55,30 @@ type Config struct {
UIPath string `mapstructure:"ui-path"`
DataPath string `mapstructure:"data-path"`
ConfigPath string `mapstructure:"config-path"`
CertPath string `mapstructure:"cert-path"`
Host string `mapstructure:"host"`
Port string `mapstructure:"port"`
SecurePort string `mapstructure:"secure-port"`
PortMetrics int `mapstructure:"port-metrics"`
Hostname string `mapstructure:"hostname"`
H2C bool `mapstructure:"h2c"`
RandomDelay bool `mapstructure:"random-delay"`
RandomDelayUnit string `mapstructure:"random-delay-unit"`
RandomDelayMin int `mapstructure:"random-delay-min"`
RandomDelayMax int `mapstructure:"random-delay-max"`
RandomError bool `mapstructure:"random-error"`
Unhealthy bool `mapstructure:"unhealthy"`
Unready bool `mapstructure:"unready"`
JWTSecret string `mapstructure:"jwt-secret"`
CacheServer string `mapstructure:"cache-server"`
}
type Server struct {
router *mux.Router
logger *zap.Logger
config *Config
router *mux.Router
logger *zap.Logger
config *Config
pool *redis.Pool
handler http.Handler
}
func NewServer(config *Config, logger *zap.Logger) (*Server, error) {
@@ -103,8 +107,11 @@ func (s *Server) registerHandlers() {
s.router.HandleFunc("/readyz/disable", s.disableReadyHandler).Methods("POST")
s.router.HandleFunc("/panic", s.panicHandler).Methods("GET")
s.router.HandleFunc("/status/{code:[0-9]+}", s.statusHandler).Methods("GET", "POST", "PUT").Name("status")
s.router.HandleFunc("/store", s.storeWriteHandler).Methods("POST")
s.router.HandleFunc("/store", s.storeWriteHandler).Methods("POST", "PUT")
s.router.HandleFunc("/store/{hash}", s.storeReadHandler).Methods("GET").Name("store")
s.router.HandleFunc("/cache/{key}", s.cacheWriteHandler).Methods("POST", "PUT")
s.router.HandleFunc("/cache/{key}", s.cacheDeleteHandler).Methods("DELETE")
s.router.HandleFunc("/cache/{key}", s.cacheReadHandler).Methods("GET").Name("cache")
s.router.HandleFunc("/configs", s.configReadHandler).Methods("GET")
s.router.HandleFunc("/token", s.tokenGenerateHandler).Methods("POST")
s.router.HandleFunc("/token/validate", s.tokenValidateHandler).Methods("GET")
@@ -135,7 +142,8 @@ func (s *Server) registerMiddlewares() {
s.router.Use(httpLogger.Handler)
s.router.Use(versionMiddleware)
if s.config.RandomDelay {
s.router.Use(randomDelayMiddleware)
randomDelayer := NewRandomDelayMiddleware(s.config.RandomDelayMin, s.config.RandomDelayMax, s.config.RandomDelayUnit)
s.router.Use(randomDelayer.Handler)
}
if s.config.RandomError {
s.router.Use(randomErrorMiddleware)
@@ -148,19 +156,10 @@ func (s *Server) ListenAndServe(stopCh <-chan struct{}) {
s.registerHandlers()
s.registerMiddlewares()
var handler http.Handler
if s.config.H2C {
handler = h2c.NewHandler(s.router, &http2.Server{})
s.handler = h2c.NewHandler(s.router, &http2.Server{})
} else {
handler = s.router
}
srv := &http.Server{
Addr: ":" + s.config.Port,
WriteTimeout: s.config.HttpServerTimeout,
ReadTimeout: s.config.HttpServerTimeout,
IdleTimeout: 2 * s.config.HttpServerTimeout,
Handler: handler,
s.handler = s.router
}
//s.printRoutes()
@@ -176,12 +175,15 @@ func (s *Server) ListenAndServe(stopCh <-chan struct{}) {
}
}
// run server in background
go func() {
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
s.logger.Fatal("HTTP server crashed", zap.Error(err))
}
}()
// start redis connection pool
ticker := time.NewTicker(30 * time.Second)
s.startCachePool(ticker, stopCh)
// create the http server
srv := s.startServer()
// create the secure server
secureSrv := s.startSecureServer()
// signal Kubernetes the server is ready to receive traffic
if !s.config.Unhealthy {
@@ -200,7 +202,12 @@ func (s *Server) ListenAndServe(stopCh <-chan struct{}) {
atomic.StoreInt32(&healthy, 0)
atomic.StoreInt32(&ready, 0)
s.logger.Info("Shutting down HTTP server", zap.Duration("timeout", s.config.HttpServerShutdownTimeout))
// close cache pool
if s.pool != nil {
_ = s.pool.Close()
}
s.logger.Info("Shutting down HTTP/HTTPS server", zap.Duration("timeout", s.config.HttpServerShutdownTimeout))
// wait for Kubernetes readiness probe to remove this instance from the load balancer
// the readiness check interval must be lower than the timeout
@@ -208,12 +215,80 @@ func (s *Server) ListenAndServe(stopCh <-chan struct{}) {
time.Sleep(3 * time.Second)
}
// attempt graceful shutdown
if err := srv.Shutdown(ctx); err != nil {
s.logger.Warn("HTTP server graceful shutdown failed", zap.Error(err))
} else {
s.logger.Info("HTTP server stopped")
// determine if the http server was started
if srv != nil {
if err := srv.Shutdown(ctx); err != nil {
s.logger.Warn("HTTP server graceful shutdown failed", zap.Error(err))
}
}
// determine if the secure server was started
if secureSrv != nil {
if err := secureSrv.Shutdown(ctx); err != nil {
s.logger.Warn("HTTPS server graceful shutdown failed", zap.Error(err))
}
}
}
func (s *Server) startServer() *http.Server {
// determine if the port is specified
if s.config.Port == "0" {
// move on immediately
return nil
}
srv := &http.Server{
Addr: s.config.Host + ":" + s.config.Port,
WriteTimeout: s.config.HttpServerTimeout,
ReadTimeout: s.config.HttpServerTimeout,
IdleTimeout: 2 * s.config.HttpServerTimeout,
Handler: s.handler,
}
// start the server in the background
go func() {
s.logger.Info("Starting HTTP Server.", zap.String("addr", srv.Addr))
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
s.logger.Fatal("HTTP server crashed", zap.Error(err))
}
}()
// return the server and routine
return srv
}
func (s *Server) startSecureServer() *http.Server {
// determine if the port is specified
if s.config.SecurePort == "0" {
// move on immediately
return nil
}
srv := &http.Server{
Addr: s.config.Host + ":" + s.config.SecurePort,
WriteTimeout: s.config.HttpServerTimeout,
ReadTimeout: s.config.HttpServerTimeout,
IdleTimeout: 2 * s.config.HttpServerTimeout,
Handler: s.handler,
}
cert := path.Join(s.config.CertPath, "tls.crt")
key := path.Join(s.config.CertPath, "tls.key")
// start the server in the background
go func() {
s.logger.Info("Starting HTTPS Server.", zap.String("addr", srv.Addr))
if err := srv.ListenAndServeTLS(cert, key); err != http.ErrServerClosed {
s.logger.Fatal("HTTPS server crashed", zap.Error(err))
}
}()
// return the server
return srv
}
func (s *Server) startMetricsServer() {

View File

@@ -8,7 +8,7 @@ import (
"io/ioutil"
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/v4"
"go.uber.org/zap"
)
@@ -39,11 +39,12 @@ func (s *Server) tokenGenerateHandler(w http.ResponseWriter, r *http.Request) {
user = string(body)
}
expiresAt := time.Now().Add(time.Minute * 1)
claims := &jwtCustomClaims{
user,
jwt.StandardClaims{
Issuer: "podinfo",
ExpiresAt: time.Now().Add(time.Minute * 1).Unix(),
ExpiresAt: jwt.At(expiresAt),
},
}
@@ -56,7 +57,7 @@ func (s *Server) tokenGenerateHandler(w http.ResponseWriter, r *http.Request) {
var result = TokenResponse{
Token: t,
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0),
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt.Unix(), 0),
}
s.JSONResponse(w, r, result)
@@ -103,7 +104,7 @@ func (s *Server) tokenValidateHandler(w http.ResponseWriter, r *http.Request) {
} else {
var result = TokenValidationResponse{
TokenName: claims.Name,
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0),
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt.Unix(), 0),
}
s.JSONResponse(w, r, result)
}

36
pkg/api/token_test.go Normal file
View File

@@ -0,0 +1,36 @@
package api
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestTokenHandler(t *testing.T) {
req, err := http.NewRequest("POST", "/token", strings.NewReader("test-user"))
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
srv := NewMockServer()
handler := http.HandlerFunc(srv.tokenGenerateHandler)
handler.ServeHTTP(rr, req)
// Check the status code is what we expect.
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}
var token TokenResponse
if err := json.Unmarshal(rr.Body.Bytes(), &token); err != nil {
t.Fatal(err)
}
if token.Token == "" {
t.Error("handler returned no token")
}
}

View File

@@ -8,6 +8,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/reflection"
)
type Server struct {
@@ -37,6 +38,7 @@ func (s *Server) ListenAndServe() {
srv := grpc.NewServer()
server := health.NewServer()
reflection.Register(srv)
grpc_health_v1.RegisterHealthServer(srv, server)
server.SetServingStatus(s.config.ServiceName, grpc_health_v1.HealthCheckResponse_SERVING)

View File

@@ -1,4 +1,4 @@
package version
var VERSION = "3.2.4"
var VERSION = "6.0.1"
var REVISION = "unknown"

7
test/build.sh Executable file
View File

@@ -0,0 +1,7 @@
#! /usr/bin/env sh
set -e
# build the docker file
GIT_COMMIT=$(git rev-list -1 HEAD) && \
DOCKER_BUILDKIT=1 docker build --tag test/podinfo --build-arg "REVISION=${GIT_COMMIT}" .

27
test/deploy.sh Executable file
View File

@@ -0,0 +1,27 @@
#! /usr/bin/env sh
# install cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.5.3/cert-manager.yaml
# wait for cert manager
kubectl -n cert-manager rollout status deployment/cert-manager --timeout=2m
kubectl -n cert-manager rollout status deployment/cert-manager-webhook --timeout=2m
kubectl -n cert-manager rollout status deployment/cert-manager-cainjector --timeout=2m
# install self-signed certificate
cat << 'EOF' | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: self-signed
spec:
selfSigned: {}
EOF
# install podinfo with tls enabled
helm upgrade --install podinfo ./charts/podinfo \
--set image.repository=test/podinfo \
--set image.tag=latest \
--set tls.enabled=true \
--set certificate.create=true \
--namespace=default

20
test/e2e.sh Executable file
View File

@@ -0,0 +1,20 @@
#! /usr/bin/env sh
set -e
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
# run the build
$SCRIPT_DIR/build.sh
# create the kind cluster
kind create cluster || true
# load the docker image
kind load docker-image test/podinfo:latest
# run the deploy
$SCRIPT_DIR/deploy.sh
# run the tests
$SCRIPT_DIR/test.sh

9
test/test.sh Executable file
View File

@@ -0,0 +1,9 @@
#1 /usr/bin/env sh
set -e
# wait for podinfo
kubectl rollout status deployment/podinfo --timeout=3m
# test podinfo
helm test podinfo