Compare commits

...

117 Commits
6.3.6 ... 6.6.2

Author SHA1 Message Date
Stefan Prodan
8d010c498e Merge pull request #357 from stefanprodan/release-6.6.2
Release v6.6.2
2024-04-10 14:04:54 +03:00
Stefan Prodan
8b3079a417 Release v6.6.2
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2024-04-10 14:01:43 +03:00
Stefan Prodan
d879d0f4fb Merge pull request #336 from michaelkebe/remove-localhost
Removed reference to localhost from swagger
2024-04-09 10:14:32 +03:00
Michael Kebe
16191504d1 Removed reference to localhost from swagger
Now it is possible to use the swagger webinterface
running on a host other than localhost e.g. in docker
or kubernetes.

Removed the @host line from pkg/api/server.go and
ran make swagger.

Fixes probably #179
2024-04-09 08:54:38 +02:00
Stefan Prodan
d042732a44 Merge pull request #353 from stefanprodan/release-6.6.1
Release v6.6.1
2024-03-27 15:56:20 +02:00
Stefan Prodan
649864583b Release v6.6.1
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2024-03-27 15:36:51 +02:00
Stefan Prodan
c07eb64558 Merge pull request #352 from stefanprodan/go-1.22
Update dependencies to Go 1.22
2024-03-26 14:23:19 +02:00
Stefan Prodan
44942884c3 Update dependencies to Go 1.22
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2024-03-26 14:19:26 +02:00
Stefan Prodan
d562a2a82a Merge pull request #344 from toyamagu-2021/feat-allow-input-config-and-extraArgs
feat(chart): allow input config and extraArgs
2024-03-26 09:00:43 +02:00
toyamagu2021
d7c1bf015c feat(chart): allow input config and extraArgs
Signed-off-by: toyamagu2021 <toyamagu2021@gmail.com>
2024-03-26 09:28:44 +09:00
Stefan Prodan
cdec0786ef Merge pull request #347 from eltociear/patch-1
Update README.md
2024-03-25 10:14:24 +02:00
Stefan Prodan
e6d611e1e2 Merge pull request #349 from mustafakarci/feature/extraEnv
feature(chart): allow extraEnvs to be parst
2024-03-25 10:14:01 +02:00
Mustafa Karci
36bea810ef feature(chart): allow extraEnvs to be parst 2024-03-20 12:28:40 +01:00
Stefan Prodan
50047dab3a Merge pull request #348 from stefanprodan/dependabot/go_modules/google.golang.org/protobuf-1.33.0
build(deps): bump google.golang.org/protobuf from 1.31.0 to 1.33.0
2024-03-14 10:36:40 +02:00
dependabot[bot]
2b936e6700 build(deps): bump google.golang.org/protobuf from 1.31.0 to 1.33.0
Bumps google.golang.org/protobuf from 1.31.0 to 1.33.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-13 23:03:41 +00:00
Ikko Eltociear Ashimine
55e4e51eba Update README.md
Github -> GitHub
2024-03-06 00:27:17 +09:00
Stefan Prodan
47090ad9e1 Merge pull request #331 from the-technat/master
feat(helm): add topologySpreadConstraints and PDB
2024-02-28 08:58:04 +00:00
Nathanael Liechti
6a0bbda8a5 feat(helm): add topologySpreadConstraints and PDB 2024-02-26 20:54:29 +00:00
Stefan Prodan
357009a863 Sign only GHCR artifacts with Notation
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2024-02-26 14:00:22 +02:00
Stefan Prodan
0f98770296 Split signature actions
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2024-02-26 13:27:46 +02:00
Stefan Prodan
f9032836a6 Merge pull request #343 from stefanprodan/release-6.6.0
Release v6.6.0
2024-02-26 12:17:54 +02:00
Stefan Prodan
5368c3fe10 Release v6.6.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2024-02-26 12:15:05 +02:00
Stefan Prodan
b1207aa9b1 Merge pull request #341 from JasonTheDeveloper/feat/notation
feat(notation): sign artifacts using notation
2024-02-26 11:58:49 +02:00
Jason
c7f9b521fa ci(notation): install 1.1.0
Signed-off-by: Jason <jagoodse@microsoft.com>
2024-02-26 20:40:03 +11:00
Stefan Prodan
24405a5a5d Setup notation signing keys
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2024-02-25 12:12:22 +02:00
Stefan Prodan
5195b158fc Merge pull request #334 from michaelkebe/patch-1
Fixes /store endpoint with the kustomize deployment
2024-02-25 11:31:44 +02:00
Stefan Prodan
532db405f8 Merge pull request #322 from JayKaku/feature/grpc-version-echo-apis
Implement gRPC for `echo` and `version` APIs
2024-02-25 11:30:59 +02:00
JayKaku
2251bee699 Fixed parsing logger in echo_test.go service registeration 2024-02-24 23:44:12 +05:30
JayKaku
8535efccb7 Implemented zap logger in place of log | gprc echo 2024-02-24 23:44:12 +05:30
JayKaku
e008d1f261 Added config, logger for grpc echo api 2024-02-24 23:44:12 +05:30
Jay Kaku
22097353d2 Feature grpc version echo api (#3)
added grpc verion and echo apis

---------

Co-authored-by: Prashant Dwivedi <prashantdwivedi194@gmail.com>
2024-02-24 23:44:12 +05:30
JayKaku
c305843105 restructured api to api/http, api/grpc, pkg http 2024-02-24 23:44:12 +05:30
Jason
0d2c428859 refactor(trustpolicy): jasonthedeveloper -> stefanprodan 2024-02-09 10:59:44 +11:00
Jason
ecaa7cf4d3 ci(release): trigger on tag push 2024-02-09 10:47:11 +11:00
Jason
8447b6985b ci(notation): remove release workflow 2024-02-09 10:42:51 +11:00
Jason
9371d6d153 ci(release): sign artefacts using notation 2024-02-09 10:41:19 +11:00
Jason
20b8c1043c Merge branch 'master' into feat/notation 2024-02-09 10:14:38 +11:00
Michael Kebe
2d80c7a22d added /data to kustomize deployment
Added an emptyDir to the kustomzie deployment, otherwise the /store endpoint does not work for storing files.
2024-01-10 09:44:12 +01:00
Stefan Prodan
dc830d02a6 Enable GitHub Sponsors 2023-12-30 22:18:54 +02:00
Stefan Prodan
badf3271a1 Merge pull request #332 from arunsathiya/master
ci: Use `GITHUB_OUTPUT` instead of set-output
2023-12-29 00:11:06 +02:00
Arun
9f9c2f3245 Use GITHUB_OUTPUT envvar instead of set-output command as the latter is deprecated 2023-12-28 13:21:55 -08:00
Stefan Prodan
33dac1ba40 Merge pull request #329 from stefanprodan/release-6.5.4
Release 6.5.4
2023-12-17 16:44:12 +02:00
Stefan Prodan
1cf8b8aeef Release 6.5.4
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-12-17 16:36:33 +02:00
Stefan Prodan
22fb1c3d34 Merge pull request #328 from stefanprodan/timoni-schemas-update
Update Timoni CUE schemas
2023-12-17 16:34:38 +02:00
Stefan Prodan
adf8157da6 Update Timoni CUE schemas
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-12-17 15:53:22 +02:00
Stefan Prodan
03f8ad0251 Merge pull request #327 from stefanprodan/up-deps
Update Go dependencies
2023-12-17 15:18:23 +02:00
Stefan Prodan
101e371e96 Update Go dependencies
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-12-17 15:14:43 +02:00
Stefan Prodan
53c9f3ad9b Merge pull request #326 from stefanprodan/alpine-3.19
Update Alpine to 3.19
2023-12-17 15:10:30 +02:00
Stefan Prodan
a69f0282fd Update Alpine to 3.19
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-12-17 15:07:34 +02:00
Jason
174d183056 refactor(rename): policy.json -> trustpolicy.json 2023-12-14 16:16:12 +11:00
Jason
7bb64e7567 fix(trustpolicy): small typo in registryScopes 2023-12-08 17:30:47 +11:00
Jason
49a4b31d53 feat(notation): update registry scopes 2023-12-07 18:06:52 +11:00
Jason
c29e013a33 feat(notation): add trust store policy 2023-12-07 16:57:04 +11:00
Jason
45ecda63bb ci(release): don't trigger on tag push 2023-12-07 16:26:01 +11:00
Jason
4868f430c7 ci(release): trigger on tag 2023-12-07 16:15:17 +11:00
Jason
ca9c6bb4f8 ci(release): sign artefacts using notaiton 2023-12-07 16:06:40 +11:00
Jason
86f3b1a57c ci(release): create notation config folder 2023-12-07 15:52:29 +11:00
Jason
b22dd96a54 feat(notation): add signingkey.json config 2023-12-07 15:51:22 +11:00
Jason
5aaf95849e ci(release): setup signing keys for notation 2023-12-07 15:50:30 +11:00
Jason
e197eca420 ci(release): add notation release workflow 2023-12-07 15:46:22 +11:00
Stefan Prodan
73fcdbe4a6 Merge pull request #320 from duxinxiao/patch-1
Add comment on sleep during graceful shutdown
2023-11-15 13:32:44 +02:00
du
074d0f9ff2 add comment on sleep during graceful shutdown 2023-11-15 18:47:52 +08:00
Stefan Prodan
d9bc6301e9 Merge pull request #316 from stefanprodan/release-6.5.3
Release 6.5.3
2023-10-30 14:38:28 +02:00
Stefan Prodan
a0e323e331 Release 6.5.3
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-30 14:27:41 +02:00
Stefan Prodan
1ee349fa17 Merge pull request #315 from stefanprodan/timoni-tests
timoni: Add connectivity test to module
2023-10-30 14:25:12 +02:00
Stefan Prodan
0f526c3cd4 timoni: Run module tests in CI
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-30 14:11:41 +02:00
Stefan Prodan
021c55fed9 timoni: Add connectivity test to module
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-30 14:10:25 +02:00
Stefan Prodan
bb2408d17d Merge pull request #314 from stefanprodan/timoni-vet-module
timoni: Add debug values and vet module in CI
2023-10-30 12:56:48 +02:00
Stefan Prodan
5eb3cafd6a timoni: Vet module in CI
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-30 12:51:28 +02:00
Stefan Prodan
df0f8ba885 timoni: Add debug values
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-30 12:51:03 +02:00
Stefan Prodan
19a59d96f1 timoni: Update module schemas
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-30 12:41:21 +02:00
Stefan Prodan
401461595a Merge pull request #313 from stefanprodan/docs-cosign-verify
docs: Verify podinfo release assets with cosign
2023-10-30 12:32:21 +02:00
Stefan Prodan
bd77584ade docs: Verify podinfo release assets with cosign
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-30 12:28:21 +02:00
Stefan Prodan
87e0dbaa7e Merge pull request #312 from stefanprodan/dependabot/go_modules/google.golang.org/grpc-1.58.3
Bump google.golang.org/grpc from 1.58.2 to 1.58.3
2023-10-26 00:38:00 +03:00
dependabot[bot]
c5494104a1 Bump google.golang.org/grpc from 1.58.2 to 1.58.3
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.58.2 to 1.58.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.58.2...v1.58.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-25 21:27:33 +00:00
Stefan Prodan
74c60a927c Merge pull request #310 from stefanprodan/release-6.5.2
Release 6.5.2
2023-10-12 12:18:47 +03:00
Stefan Prodan
ecdf07c4d5 Release 6.5.2
Fix for CVE-2023-39325 and CVE-2023-38545

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-12 12:11:37 +03:00
Stefan Prodan
ff29c549ff Merge pull request #309 from stefanprodan/dependabot/go_modules/golang.org/x/net-0.17.0
Bump golang.org/x/net from 0.15.0 to 0.17.0
2023-10-12 09:24:03 +03:00
dependabot[bot]
fa75fc0520 Bump golang.org/x/net from 0.15.0 to 0.17.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.15.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.15.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-11 23:14:03 +00:00
Stefan Prodan
0bc496456d Merge pull request #307 from stefanprodan/release-6.5.1
Release 6.5.1
2023-10-02 21:04:32 +03:00
Stefan Prodan
398c543171 Release 6.5.1
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-02 20:58:27 +03:00
Stefan Prodan
a54dc2a9c7 Merge pull request #306 from stefanprodan/update-otel-deps
Update Go dependencies
2023-10-02 20:57:13 +03:00
Stefan Prodan
bfa42afa1f Bump Go to 1.21
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-02 20:46:06 +03:00
Stefan Prodan
590987704e Update open telemetry packages
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-02 20:45:20 +03:00
Stefan Prodan
d561182076 Merge pull request #305 from stefanprodan/timoni-v0.14
Update module to Timoni v0.14 APIs
2023-10-02 20:41:06 +03:00
Stefan Prodan
72bd6faf35 Update module to Timoni v0.14 APIs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-10-02 20:36:58 +03:00
Stefan Prodan
2cbe0fcdff Merge pull request #304 from stefanprodan/release-6.5.0
Release v6.5.0
2023-09-23 12:35:00 +03:00
Stefan Prodan
87e594b109 Release 6.5.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-09-23 12:29:59 +03:00
Stefan Prodan
7ec9e6c84a Merge pull request #303 from stefanprodan/publish-timoni-module
Publish signed Timoni module to GHCR
2023-09-23 12:22:06 +03:00
Stefan Prodan
8183d0d5fc Publish signed Timoni module to GHCR
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-09-23 12:16:48 +03:00
Stefan Prodan
aa27416651 Merge pull request #302 from stefanprodan/module-imps
Timoni module improvements
2023-09-23 11:35:47 +03:00
Stefan Prodan
b0594a85b9 Add e2e tests for Timoni module
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-09-23 11:29:47 +03:00
Stefan Prodan
e816d1b5bc Add UI configuration
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-09-23 10:04:56 +03:00
Stefan Prodan
6316e213d1 Improve monitoring config
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-09-23 09:25:25 +03:00
Stefan Prodan
a1b112f4e1 Mark vendored code
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-09-23 00:43:20 +03:00
Stefan Prodan
1495fd888e Merge pull request #301 from stefanprodan/timoni-module
Add Timoni module
2023-09-23 00:37:48 +03:00
Stefan Prodan
dfc4a6d37e Add Timoni module
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-09-23 00:33:36 +03:00
Stefan Prodan
aaa47e535f Merge pull request #300 from stefanprodan/up-deps
Update dependencies
2023-09-22 23:29:36 +03:00
Stefan Prodan
0278e11a05 Update dependencies
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-09-22 23:24:32 +03:00
Stefan Prodan
12ceae475f Merge pull request #284 from johankok/add-additional-labels-to-ingress
Added additionalLabels option for ingress in helm chart
2023-09-22 23:13:11 +03:00
Stefan Prodan
4892983fd1 Merge pull request #290 from stefanprodan/release-6.4.1
Release v6.4.1
2023-08-10 15:25:19 +03:00
Stefan Prodan
bcf492e92b Release v6.4.1
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-08-10 15:10:01 +03:00
Stefan Prodan
a54550e439 Merge pull request #289 from stefanprodan/go-1.21
Build with Go 1.21
2023-08-10 15:08:37 +03:00
Stefan Prodan
29dd482f49 Build with Go 1.21
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-08-10 15:04:03 +03:00
Stefan Prodan
3a7d4d1544 Merge pull request #285 from stefanprodan/up-deps
Update dependencies
2023-08-10 14:59:18 +03:00
Stefan Prodan
c14b116dea Update dependencies
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-08-07 16:20:42 +03:00
Johan Kok
12c078938d Added additionalLabels option for ingress 2023-07-17 17:50:42 +02:00
Stefan Prodan
dd3869b1a1 Merge pull request #274 from stefanprodan/alpine-3.18
Update base image to Alpine 3.18
2023-06-26 13:02:18 +03:00
Stefan Prodan
45cfe3abc2 Update base image to Alpine 3.18
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-06-26 12:57:54 +03:00
Stefan Prodan
fcf573111b Merge pull request #273 from stefanprodan/release-6.4.0
Release 6.4.0
2023-06-26 12:55:42 +03:00
Stefan Prodan
cadabcc6a5 Release 6.4.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-06-26 12:50:40 +03:00
Stefan Prodan
9dfb676083 Sign release tags with OpenPGP
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-06-26 12:49:33 +03:00
Stefan Prodan
e06a5517da Merge pull request #272 from stefanprodan/deps-up
Update dependencies
2023-06-22 18:51:41 +03:00
Stefan Prodan
fedab0de38 Update dependencies
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2023-06-22 18:36:47 +03:00
Stefan Prodan
7d13025a35 Merge pull request #271 from jjchambl/feature/add_dynamic_paths
feat(echo): add dynamic path support for echo endpoint
2023-06-22 18:30:00 +03:00
Jacob Chambliss
7280e43cbf chore: add pathprefix to enable dynamic paths for echo 2023-06-08 10:14:21 -05:00
Jacob Chambliss
3ef0b4cd09 feat: add dynamic path support for echo endpoint 2023-06-07 16:55:25 -05:00
217 changed files with 29737 additions and 1264 deletions

View File

@@ -1,9 +1,10 @@
# Podinfo signed releases
Podinfo deployment manifests are published to GitHub Container Registry as OCI artifacts
and are signed using [cosign](https://github.com/sigstore/cosign).
Podinfo release assets (container image, Helm chart, Flux artifact, Timoni module)
are published to GitHub Container Registry and are signed with
[Cosign v2](https://github.com/sigstore/cosign) keyless & GitHub Actions OIDC.
## Verify the artifacts with cosign
## Verify podinfo with cosign
Install the [cosign](https://github.com/sigstore/cosign) CLI:
@@ -11,29 +12,50 @@ Install the [cosign](https://github.com/sigstore/cosign) CLI:
brew install sigstore/tap/cosign
```
Verify a podinfo release with cosign CLI:
### Container image
Verify the podinfo container image hosted on GHCR:
```sh
cosign verify -key https://raw.githubusercontent.com/stefanprodan/podinfo/master/cosign/cosign.pub \
ghcr.io/stefanprodan/podinfo-deploy:latest
cosign verify ghcr.io/stefanprodan/podinfo:6.5.0 \
--certificate-identity-regexp="^https://github.com/stefanprodan/podinfo.*$" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
```
## Download the artifacts with crane
Install the [crane](https://github.com/google/go-containerregistry/tree/main/cmd/crane) CLI:
Verify the podinfo container image hosted on Docker Hub:
```sh
brew install crane
cosign verify docker.io/stefanprodan/podinfo:6.5.0 \
--certificate-identity-regexp="^https://github.com/stefanprodan/podinfo.*$" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
```
Download the podinfo deployment manifests with crane CLI:
### Helm chart
```console
$ crane export ghcr.io/stefanprodan/podinfo-deploy:latest -| tar -xf -
Verify the podinfo [Helm](https://helm.sh) chart hosted on GHCR:
$ ls -1
deployment.yaml
hpa.yaml
kustomization.yaml
service.yaml
```sh
cosign verify ghcr.io/stefanprodan/charts/podinfo:6.5.0 \
--certificate-identity-regexp="^https://github.com/stefanprodan/podinfo.*$" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
```
### Flux artifact
Verify the podinfo [Flux](https://fluxcd.io) artifact hosted on GHCR:
```sh
cosign verify ghcr.io/stefanprodan/manifests/podinfo:6.5.0 \
--certificate-identity-regexp="^https://github.com/stefanprodan/podinfo.*$" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
```
### Timoni module
Verify the podinfo [Timoni](https://timoni.sh) module hosted on GHCR:
```sh
cosign verify ghcr.io/stefanprodan/modules/podinfo:6.5.0 \
--certificate-identity-regexp="^https://github.com/stefanprodan/podinfo.*$" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
```

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
timoni/podinfo/cue.mod/** linguist-vendored

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
github: stefanprodan

View File

@@ -3,7 +3,7 @@ name: cve-scan
on:
push:
branches:
- 'master'
- "master"
permissions:
contents: read
@@ -19,7 +19,7 @@ jobs:
run: |
IMAGE=test/podinfo:${GITHUB_SHA}
docker build -t ${IMAGE} .
echo "::set-output name=image::$IMAGE"
echo "image=$IMAGE" >> $GITHUB_OUTPUT
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:

View File

@@ -11,18 +11,13 @@ permissions:
jobs:
kind-helm:
strategy:
matrix:
helm-version:
- v3.11.0
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Kubernetes
uses: helm/kind-action@v1.5.0
uses: helm/kind-action@v1.9.0
with:
version: v0.17.0
cluster_name: kind
- name: Build container image
run: |
@@ -31,7 +26,7 @@ jobs:
- name: Setup Helm
uses: azure/setup-helm@v3
with:
version: ${{ matrix.helm-version }}
version: v3.14.3
- name: Deploy
run: ./test/deploy.sh
- name: Run integration tests
@@ -40,3 +35,44 @@ jobs:
if: failure()
run: |
kubectl logs -l app=podinfo || true
kind-timoni:
runs-on: ubuntu-latest
services:
registry:
image: registry:2
ports:
- 5000:5000
env:
PODINFO_IMAGE_URL: "test/podinfo"
PODINFO_MODULE_URL: "oci://localhost:5000/podinfo"
PODINFO_VERSION: "0.0.0-devel"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Timoni
uses: stefanprodan/timoni/actions/setup@main
- name: Setup Kubernetes
uses: helm/kind-action@v1.9.0
with:
cluster_name: kind
- name: Build container
run: |
docker build -t ${PODINFO_IMAGE_URL}:${PODINFO_VERSION} --build-arg "REVISION=${GITHUB_SHA}" -f Dockerfile.xx .
kind load docker-image ${PODINFO_IMAGE_URL}:${PODINFO_VERSION}
- name: Vet module
run: |
timoni mod vet ./timoni/podinfo --debug
- name: Build module
run: |
timoni mod push ./timoni/podinfo ${PODINFO_MODULE_URL} -v ${PODINFO_VERSION}
- name: Apply bundle
run: |
timoni bundle apply -f ./timoni/bundles/test.podinfo.cue --runtime-from-env
- name: Verify status
run: |
timoni -n podinfo status backend
timoni -n podinfo status frontend
- name: Debug failure
if: failure()
run: |
kubectl -n podinfo get all || true

View File

@@ -6,40 +6,55 @@ on:
- '*'
permissions:
contents: write # needed to write releases
id-token: write # needed for keyless signing
packages: write # needed for ghcr access
contents: read
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write # needed to write releases
id-token: write # needed for keyless signing
packages: write # needed for ghcr access
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: sigstore/cosign-installer@v3
- uses: fluxcd/flux2/action@main
- name: Setup Go
uses: actions/setup-go@v3
- uses: stefanprodan/timoni/actions/setup@main
- name: Setup Notation CLI
uses: notaryproject/notation-action/setup@v1
with:
go-version: 1.20.x
version: "1.1.0"
- name: Setup Notation signing keys
run: |
mkdir -p ~/.config/notation/localkeys/
cp ./.notation/signingkeys.json ~/.config/notation/
cp ./.notation/notation.crt ~/.config/notation/localkeys/
echo "$NOTATION_KEY" > ~/.config/notation/localkeys/notation.key
env:
NOTATION_KEY: ${{ secrets.NOTATION_SIGNING_KEY }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.22.x
- name: Setup Helm
uses: azure/setup-helm@v3
with:
version: v3.10.3
version: v3.14.3
- name: Setup QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
with:
platforms: all
- name: Setup Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.GHCR_TOKEN }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
@@ -55,7 +70,7 @@ jobs:
echo "REVISION=${GITHUB_SHA}" >> $GITHUB_OUTPUT
- name: Generate images meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: |
docker.io/stefanprodan/podinfo
@@ -64,7 +79,7 @@ jobs:
type=raw,value=${{ steps.prep.outputs.VERSION }}
type=raw,value=latest
- name: Publish multi-arch image
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
sbom: true
provenance: true
@@ -77,6 +92,15 @@ jobs:
platforms: linux/amd64,linux/arm/v7,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Publish Timoni module to GHCR
run: |
timoni mod push ./timoni/podinfo oci://ghcr.io/stefanprodan/modules/podinfo \
--sign cosign \
--version ${{ steps.prep.outputs.VERSION }} \
-a 'org.opencontainers.image.source=https://github.com/stefanprodan/podinfo' \
-a 'org.opencontainers.image.licenses=Apache-2.0' \
-a 'org.opencontainers.image.description=A timoni.sh module for deploying Podinfo.' \
-a 'org.opencontainers.image.documentation=https://github.com/stefanprodan/podinfo/blob/main/timoni/podinfo/README.md'
- name: Publish Helm chart to GHCR
run: |
helm package charts/podinfo
@@ -89,7 +113,7 @@ jobs:
--source="${{ github.event.repository.html_url }}" \
--revision="${GITHUB_REF_NAME}/${GITHUB_SHA}"
flux tag artifact oci://ghcr.io/stefanprodan/manifests/podinfo:${{ steps.prep.outputs.VERSION }} --tag latest
- name: Sign OCI artifacts
- name: Sign artifacts with Cosign
env:
COSIGN_EXPERIMENTAL: 1
run: |
@@ -98,7 +122,7 @@ jobs:
cosign sign ghcr.io/stefanprodan/charts/podinfo:${{ steps.prep.outputs.VERSION }} --yes
cosign sign ghcr.io/stefanprodan/manifests/podinfo:${{ steps.prep.outputs.VERSION }} --yes
- name: Publish base image
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
push: true
builder: ${{ steps.buildx.outputs.name }}
@@ -117,7 +141,7 @@ jobs:
--source="${{ github.event.repository.html_url }}" \
--revision="${GITHUB_REF_NAME}/${GITHUB_SHA}"
flux tag artifact oci://ghcr.io/stefanprodan/podinfo-deploy:${{ steps.prep.outputs.VERSION }} --tag latest
- name: Sign config artifact
- name: Sign config artifact with cso
run: |
echo "$COSIGN_KEY" > /tmp/cosign.key
cosign sign -key /tmp/cosign.key ghcr.io/stefanprodan/podinfo-deploy:${{ steps.prep.outputs.VERSION }} --yes
@@ -125,13 +149,20 @@ jobs:
env:
COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
COSIGN_KEY: ${{secrets.COSIGN_KEY}}
- name: Sign artifacts with Notation
run: |
notation sign --signature-format cose ghcr.io/stefanprodan/podinfo:${{ steps.prep.outputs.VERSION }}
notation sign --signature-format cose ghcr.io/stefanprodan/charts/podinfo:${{ steps.prep.outputs.VERSION }}
notation sign --signature-format cose ghcr.io/stefanprodan/manifests/podinfo:${{ steps.prep.outputs.VERSION }}
notation sign --signature-format cose ghcr.io/stefanprodan/podinfo-deploy:${{ steps.prep.outputs.VERSION }}
notation sign --signature-format cose ghcr.io/stefanprodan/podinfo-deploy:latest
- 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@v4
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --release-notes=/tmp/release.txt --skip-validate

View File

@@ -17,17 +17,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Restore Go cache
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: 1.20.x
go-version: 1.22.x
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Setup kubectl
uses: azure/setup-kubectl@v3
with:
@@ -37,9 +34,11 @@ jobs:
- name: Setup Helm
uses: azure/setup-helm@v3
with:
version: v3.10.3
version: v3.14.3
- name: Setup CUE
uses: cue-lang/setup-cue@main
uses: cue-lang/setup-cue@v1.0.0
- name: Setup Timoni
uses: stefanprodan/timoni/actions/setup@main
- name: Run unit tests
run: make test
- name: Validate Helm chart
@@ -49,12 +48,10 @@ jobs:
- name: Validate Kustomize overlay
run: |
kubectl kustomize ./kustomize/ | kubeconform -strict -summary -kubernetes-version ${{ env.KUBERNETES_VERSION }}
- name: Generate CUE definitions
run: make cue-mod
- name: Verify CUE formatting
working-directory: ./cue
working-directory: ./timoni/podinfo
run: |
cue fmt .
cue fmt ./..
status=$(git status . --porcelain)
[[ -z "$status" ]] || {
echo "CUE files are not correctly formatted"
@@ -62,11 +59,11 @@ jobs:
git diff
exit 1
}
- name: Validate CUE
working-directory: ./cue
- name: Validate Timoni module
working-directory: ./timoni/podinfo
run: |
cue vet --all-errors --concrete .
cue gen | kubeconform -strict -summary -skip=ServiceMonitor -kubernetes-version ${{ env.KUBERNETES_VERSION }}
timoni mod lint .
timoni build podinfo . -f test_values.cue | kubeconform -strict -summary -skip=ServiceMonitor -kubernetes-version ${{ env.KUBERNETES_VERSION }}
- name: Check if working tree is dirty
run: |
if [[ $(git diff --stat) != '' ]]; then

3
.gitignore vendored
View File

@@ -23,3 +23,6 @@ bin/
cue/cue.mod/gen/
cue/go.mod
cue/go.sum
.notation/podinfo.csr
.notation/podinfo.key

15
.notation/README.md Normal file
View File

@@ -0,0 +1,15 @@
# Podinfo signed releases
Podinfo release assets such as the Helm chart and the Flux artifact
are published to GitHub Container Registry and are signed with
[Notation](https://github.com/notaryproject/notation).
## Generate signing keys
Generate a new signing key pair:
```sh
openssl genrsa -out podinfo.key 2048
openssl req -new -key podinfo.key -out podinfo.csr -config codesign.cnf
openssl x509 -req -days 1826 -in podinfo.csr -signkey podinfo.key -out notation.crt -extensions v3_req -extfile codesign.cnf
```

18
.notation/codesign.cnf Normal file
View File

@@ -0,0 +1,18 @@
[ req ]
default_bits = 2048
default_keyfile = privatekey.pem
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[ req_distinguished_name ]
C = RO
ST = BU
L = Bucharest
O = Notary
CN = stefanprodan.com
[ v3_req ]
keyUsage = critical,digitalSignature
extendedKeyUsage = critical,codeSigning
#subjectKeyIdentifier = hash

21
.notation/notation.crt Normal file
View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDbDCCAlSgAwIBAgIUP7zhmTw5XTWLcgBGkBEsErMOkz4wDQYJKoZIhvcNAQEL
BQAwWjELMAkGA1UEBhMCUk8xCzAJBgNVBAgMAkJVMRIwEAYDVQQHDAlCdWNoYXJl
c3QxDzANBgNVBAoMBk5vdGFyeTEZMBcGA1UEAwwQc3RlZmFucHJvZGFuLmNvbTAe
Fw0yNDAyMjUxMDAyMzZaFw0yOTAyMjQxMDAyMzZaMFoxCzAJBgNVBAYTAlJPMQsw
CQYDVQQIDAJCVTESMBAGA1UEBwwJQnVjaGFyZXN0MQ8wDQYDVQQKDAZOb3Rhcnkx
GTAXBgNVBAMMEHN0ZWZhbnByb2Rhbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDtH4oPi3SyX/DGv6NdjIvmApvD9eeSgsmHdwpAly8T9D2me+fx
Z+wRNJmq4aq/A1anX+Sg28iwHzV+1WKpsHnjYzDAJSEYP2S8A5H1nGRKUoibdijw
C3QBh5C75rjF/tmZVSX/Vgbf3HJJEsF4WUxWabLxoV2QLo7UlEsQd9+bSeKNMncx
1+E6FdbRCrYo90iobvZJ8K/S2zCWq/JTeHfTnmSEDhx6nMJcaSjvMPn3zyauWcQw
dDpkcaGiJ64fEJRT2OFxXv9u+vDmIMKzo/Wjbd+IzFj6YY4VisK88aU7tmDelnk5
gQB9eu62PFoaVsYJp4VOhblFKvGJpQwbWB9BAgMBAAGjKjAoMA4GA1UdDwEB/wQE
AwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEA
6x+C6hAIbLwMvkNx4K5p7Qe/pLQR0VwQFAw10yr/5KSN+YKFpon6pQ0TebL7qll+
uBGZvtQhN6v+DlnVqB7lvJKd+89isgirkkews5KwuXg7Gv5UPIugH0dXISZU8DMJ
7J4oKREv5HzdFmfsUfNlQcfyVTjKL6UINXfKGdqNNxXxR9b4a1TY2JcmEhzBTHaq
ZqX6HK784a0dB7aHgeFrFwPCCP4M684Hs7CFbk3jo2Ef4ljnB5AyWpe8pwCLMdRt
UjSjL5xJWVQvRU+STQsPr6SvpokPCG4rLQyjgeYYk4CCj5piSxbSUZFavq8v1y7Y
m91USVqfeUX7ZzjDxPHE2A==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,10 @@
{
"default": "stefanprodan.com",
"keys": [
{
"name": "stefanprodan.com",
"keyPath": "/home/runner/.config/notation/localkeys/notation.key",
"certPath": "/home/runner/.config/notation/localkeys/notation.crt"
}
]
}

View File

@@ -0,0 +1,19 @@
{
"version": "1.0",
"trustPolicies": [
{
"name": "stefanprodan.com",
"registryScopes": [
"ghcr.io/stefanprodan/podinfo-deploy",
"ghcr.io/stefanprodan/charts/podinfo"
],
"signatureVerification": {
"level" : "strict"
},
"trustStores": [ "ca:stefanprodan.com" ],
"trustedIdentities": [
"x509.subject: C=RO, ST=BU, L=Bucharest, O=Notary, CN=stefanprodan.com"
]
}
]
}

View File

@@ -1,4 +1,4 @@
FROM golang:1.20-alpine as builder
FROM golang:1.22-alpine as builder
ARG REVISION
@@ -18,7 +18,7 @@ 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.17
FROM alpine:3.19
ARG BUILD_DATE
ARG VERSION

View File

@@ -1,4 +1,4 @@
FROM golang:1.20
FROM golang:1.22
WORKDIR /workspace

View File

@@ -1,5 +1,5 @@
ARG GO_VERSION=1.20
ARG XX_VERSION=1.2.0
ARG GO_VERSION=1.22
ARG XX_VERSION=1.4.0
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
@@ -28,7 +28,7 @@ RUN xx-go build -ldflags "-s -w \
-X github.com/stefanprodan/podinfo/pkg/version.REVISION=${REVISION}" \
-a -o bin/podcli cmd/podcli/*
FROM alpine:3.17
FROM alpine:3.19
ARG BUILD_DATE
ARG VERSION

View File

@@ -24,7 +24,7 @@ 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/podcli ./cmd/podcli/*
tidy:
rm -f go.sum; go mod tidy -compat=1.19
rm -f go.sum; go mod tidy -compat=1.22
vet:
go vet ./...
@@ -82,26 +82,19 @@ version-set:
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/webapp/backend/deployment.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/bases/frontend/deployment.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/bases/backend/deployment.yaml && \
/usr/bin/sed -i '' "s/$$current/$$next/g" cue/main.cue && \
echo "Version $$next set in code, deployment, chart and kustomize"
/usr/bin/sed -i '' "s/$$current/$$next/g" timoni/podinfo/values.cue && \
echo "Version $$next set in code, deployment, module, chart and kustomize"
release:
git tag $(VERSION)
git tag -s -m $(VERSION) $(VERSION)
git push origin $(VERSION)
swagger:
go install github.com/swaggo/swag/cmd/swag@latest
go get github.com/swaggo/swag/gen@latest
go get github.com/swaggo/swag/cmd/swag@latest
cd pkg/api && $$(go env GOPATH)/bin/swag init -g server.go
cd pkg/api/http && $$(go env GOPATH)/bin/swag init -g server.go
.PHONY: cue-mod
cue-mod:
@cd cue && go mod init github.com/stefanprodan/podinfo/cue
@cd cue && go get k8s.io/api/...
@cd cue && cue get go k8s.io/api/...
.PHONY: cue-gen
cue-gen:
@cd cue && cue fmt ./... && cue vet --all-errors --concrete ./...
@cd cue && cue gen
.PHONY: timoni-build
timoni-build:
@timoni build podinfo ./timoni/podinfo -f ./timoni/podinfo/debug_values.cue

View File

@@ -20,9 +20,9 @@ Specifications:
* 12-factor app with viper
* Fault injection (random errors and latency)
* Swagger docs
* CUE, Helm and Kustomize installers
* Timoni, Helm and Kustomize installers
* End-to-End testing with Kubernetes Kind and Helm
* Multi-arch container image with Docker buildx and Github Actions
* Multi-arch container image with Docker buildx and GitHub Actions
* Container image signing with Sigstore cosign
* SBOMs and SLSA Provenance embedded in the container image
* CVE scanning with Trivy
@@ -57,6 +57,8 @@ Web API:
gRPC API:
* `/grpc.health.v1.Health/Check` health checking
* `/grpc.EchoService/Echo` echos the received content
* `/grpc.VersionService/Version` returns podinfo version and Git commit hash
Web UI:
@@ -66,18 +68,23 @@ To access the Swagger UI open `<podinfo-host>/swagger/index.html` in a browser.
### Guides
* [GitOps Progressive Deliver with Flagger, Helm v3 and Linkerd](https://helm.workshop.flagger.dev/intro/)
* [GitOps Progressive Deliver on EKS with Flagger and AppMesh](https://eks.handson.flagger.dev/prerequisites/)
* [Automated canary deployments with Flagger and Istio](https://medium.com/google-cloud/automated-canary-deployments-with-flagger-and-istio-ac747827f9d1)
* [Kubernetes autoscaling with Istio metrics](https://medium.com/google-cloud/kubernetes-autoscaling-with-istio-metrics-76442253a45a)
* [Autoscaling EKS on Fargate with custom metrics](https://aws.amazon.com/blogs/containers/autoscaling-eks-on-fargate-with-custom-metrics/)
* [Managing Helm releases the GitOps way](https://medium.com/google-cloud/managing-helm-releases-the-gitops-way-207a6ac6ff0e)
* [Securing EKS Ingress With Contour And Lets Encrypt The GitOps Way](https://aws.amazon.com/blogs/containers/securing-eks-ingress-contour-lets-encrypt-gitops/)
* [Getting started with Timoni](https://timoni.sh/quickstart/)
* [Getting started with Flux](https://fluxcd.io/flux/get-started/)
* [Progressive Deliver with Flagger and Linkerd](https://docs.flagger.app/tutorials/linkerd-progressive-delivery)
* [Automated canary deployments with Kubernetes Gateway API](https://docs.flagger.app/tutorials/gatewayapi-progressive-delivery)
### Install
To install Podinfo on Kubernetes the minimum required version is **Kubernetes v1.23**.
#### Timoni
Install with [Timoni](https://timoni.sh):
```bash
timoni -n default apply podinfo oci://ghcr.io/stefanprodan/modules/podinfo
```
#### Helm
Install from github.io:

View File

@@ -1,6 +1,6 @@
apiVersion: v1
version: 6.3.6
appVersion: 6.3.6
version: 6.6.2
appVersion: 6.6.2
name: podinfo
engine: gotpl
description: Podinfo Helm chart for Kubernetes

View File

@@ -51,7 +51,7 @@ The command removes all the Kubernetes components associated with the chart and
The following tables lists the configurable parameters of the podinfo chart and their default values.
| Parameter | Default | Description |
|-----------------------------------|------------------------|------------------------------------------------------------------------------------------------------------------------|
| --------------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `replicaCount` | `1` | Desired number of pods |
| `logLevel` | `info` | Log level: `debug`, `info`, `warn`, `error` |
| `backend` | `None` | Echo backend URL |
@@ -79,6 +79,10 @@ The following tables lists the configurable parameters of the podinfo chart and
| `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) |
| `extraEnvs` | `[]` | Extra environment variables for the podinfo container |
| `config.path` | `""` | config file path |
| `config.name` | `""` | config file name |
| `extraArgs` | `[]` | Additional command line arguments to pass to podinfo container |
| `hpa.enabled` | `false` | Enables the Kubernetes HPA |
| `hpa.maxReplicas` | `10` | Maximum amount of pods |
| `hpa.cpu` | `None` | Target CPU usage per pod |
@@ -94,6 +98,7 @@ The following tables lists the configurable parameters of the podinfo chart and
| `serviceMonitor.additionalLabels` | `{}` | Add additional labels to the service monitor |
| `ingress.enabled` | `false` | Enables Ingress |
| `ingress.className ` | `""` | Use ingressClassName |
| `ingress.additionalLabels` | `{}` | Add additional labels to the ingress |
| `ingress.annotations` | `{}` | Ingress annotations |
| `ingress.hosts` | `[]` | Ingress accepted hosts |
| `ingress.tls` | `[]` | Ingress TLS configuration |
@@ -127,4 +132,3 @@ $ helm install my-release podinfo/podinfo -f values.yaml
```
> **Tip**: You can use the default [values.yaml](values.yaml)

View File

@@ -87,6 +87,15 @@ spec:
{{- if .Values.h2c.enabled }}
- --h2c
{{- end }}
{{- with .Values.config.path }}
- --config-path={{ . }}
{{- end }}
{{- with .Values.config.name }}
- --config={{ . }}
{{- end }}
{{- with .Values.extraArgs }}
{{- toYaml . | nindent 12 }}
{{- end }}
env:
{{- if .Values.ui.message }}
- name: PODINFO_UI_MESSAGE
@@ -104,6 +113,9 @@ spec:
- name: PODINFO_BACKEND_URL
value: {{ .Values.backend }}
{{- end }}
{{- if .Values.extraEnvs }}
{{ toYaml .Values.extraEnvs | indent 10 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.service.httpPort | default 9898 }}
@@ -203,3 +215,7 @@ spec:
secret:
secretName: {{ template "podinfo.tlsSecretName" . }}
{{- end }}
{{- with .Values.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -7,6 +7,9 @@ metadata:
name: {{ $fullName }}
labels:
{{- include "podinfo.labels" . | nindent 4 }}
{{- with .Values.ingress.additionalLabels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}

View File

@@ -0,0 +1,14 @@
{{- if and .Values.podDisruptionBudget (gt (int .Values.replicaCount) 1) }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "podinfo.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "podinfo.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "podinfo.selectorLabels" . | nindent 6 }}
{{- toYaml .Values.podDisruptionBudget | nindent 2 }}
{{- end }}

View File

@@ -8,7 +8,7 @@ backends: []
image:
repository: ghcr.io/stefanprodan/podinfo
tag: 6.3.6
tag: 6.6.2
pullPolicy: IfNotPresent
ui:
@@ -41,6 +41,16 @@ service:
h2c:
enabled: false
# config file settings
config:
# config file path
path: ""
# config file name
name: ""
# Additional command line arguments to pass to podinfo container
extraArgs: []
# enable tls on the podinfo service
tls:
enabled: false
@@ -100,6 +110,7 @@ securityContext: {}
ingress:
enabled: false
className: ""
additionalLabels: {}
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
@@ -130,6 +141,14 @@ resources:
cpu: 100m
memory: 64Mi
# Extra environment variables for the podinfo container
extraEnvs: []
# Example on how to configure extraEnvs
# - name: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
# value: "http://otel:4317"
# - name: MULTIPLE_VALUES
# value: TEST
nodeSelector: {}
tolerations: []

View File

@@ -8,7 +8,7 @@ backends: []
image:
repository: ghcr.io/stefanprodan/podinfo
tag: 6.3.6
tag: 6.6.2
pullPolicy: IfNotPresent
ui:
@@ -45,6 +45,16 @@ service:
h2c:
enabled: false
# config file settings
config:
# config file path
path: ""
# config file name
name: ""
# Additional command line arguments to pass to podinfo container
extraArgs: []
# enable tls on the podinfo service
tls:
enabled: false
@@ -104,6 +114,7 @@ securityContext: {}
ingress:
enabled: false
className: ""
additionalLabels: {}
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
@@ -133,6 +144,14 @@ resources:
cpu: 1m
memory: 16Mi
# Extra environment variables for the podinfo container
extraEnvs: []
# Example on how to configure extraEnvs
# - name: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
# value: "http://otel:4317"
# - name: MULTIPLE_VALUES
# value: TEST
nodeSelector: {}
tolerations: []
@@ -141,6 +160,13 @@ affinity: {}
podAnnotations: {}
# https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/
topologySpreadConstraints: []
# Disruption budget will be configured only when the replicaCount is greater than 1
podDisruptionBudget: {}
# maxUnavailable: 1
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
probes:
readiness:

View File

@@ -13,8 +13,8 @@ import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/stefanprodan/podinfo/pkg/api"
"github.com/stefanprodan/podinfo/pkg/grpc"
"github.com/stefanprodan/podinfo/pkg/api/grpc"
"github.com/stefanprodan/podinfo/pkg/api/http"
"github.com/stefanprodan/podinfo/pkg/signals"
"github.com/stefanprodan/podinfo/pkg/version"
go_grpc "google.golang.org/grpc"
@@ -138,11 +138,13 @@ func main() {
var grpcServer *go_grpc.Server
if grpcCfg.Port > 0 {
grpcSrv, _ := grpc.NewServer(&grpcCfg, logger)
//grpcinfoSrv, _ := grpc.NewInfoServer(&grpcCfg)
grpcServer = grpcSrv.ListenAndServe()
}
// load HTTP server config
var srvCfg api.Config
var srvCfg http.Config
if err := viper.Unmarshal(&srvCfg); err != nil {
logger.Panic("config unmarshal failed", zap.Error(err))
}
@@ -155,7 +157,7 @@ func main() {
)
// start HTTP server
srv, _ := api.NewServer(&srvCfg, logger)
srv, _ := http.NewServer(&srvCfg, logger)
httpServer, httpsServer, healthy, ready := srv.ListenAndServe()
// graceful shutdown

View File

@@ -1,60 +0,0 @@
# Podinfo CUE module
This directory contains a [CUE](https://cuelang.org/docs/) module and tooling
for generating podinfo's Kubernetes resources.
The module contains a `podinfo.#Application` definition which takes `podinfo.#Config` as input.
## Prerequisites
Install CUE with:
```shell
brew install cue
```
Generate the Kubernetes API definitions required by this module with:
```shell
go mod init github.com/stefanprodan/podinfo/cue
go get k8s.io/api/...
cue get go k8s.io/api/...
```
## Configuration
Configure the application in `main.cue`:
```cue
app: podinfo.#Application & {
config: {
meta: {
name: "podinfo"
namespace: "default"
}
image: tag: "6.1.3"
resources: requests: {
cpu: "100m"
memory: "16Mi"
}
hpa: {
enabled: true
maxReplicas: 3
}
ingress: {
enabled: true
className: "nginx"
host: "podinfo.example.com"
tls: true
annotations: "cert-manager.io/cluster-issuer": "letsencrypt"
}
serviceMonitor: enabled: true
}
}
```
## Generate the manifests
```shell
cue gen
```

View File

@@ -1 +0,0 @@
module: "github.com/stefanprodan/podinfo/cue"

View File

@@ -1,33 +0,0 @@
package main
import (
podinfo "github.com/stefanprodan/podinfo/cue/podinfo"
)
app: podinfo.#Application & {
config: {
meta: {
name: "podinfo"
namespace: "default"
}
image: tag: "6.3.6"
resources: requests: {
cpu: "100m"
memory: "16Mi"
}
hpa: {
enabled: true
maxReplicas: 3
}
ingress: {
enabled: true
className: "nginx"
host: "podinfo.example.com"
tls: true
annotations: "cert-manager.io/cluster-issuer": "letsencrypt"
}
serviceMonitor: enabled: true
}
}
objects: app.objects

View File

@@ -1,12 +0,0 @@
package main
import (
"tool/cli"
"encoding/yaml"
)
command: gen: {
task: print: cli.Print & {
text: yaml.MarshalStream([ for x in objects {x}])
}
}

View File

@@ -1,26 +0,0 @@
package podinfo
#Application: {
config: #Config
objects: {
service: #Service & {_config: config}
account: #ServiceAccount & {_config: config}
deployment: #Deployment & {
_config: config
_serviceAccount: account.metadata.name
}
}
if config.hpa.enabled == true {
objects: hpa: #HorizontalPodAutoscaler & {_config: config}
}
if config.ingress.enabled == true {
objects: ingress: #Ingress & {_config: config}
}
if config.serviceMonitor.enabled == true {
objects: serviceMonitor: #ServiceMonitor & {_config: config}
}
}

View File

@@ -1,41 +0,0 @@
package podinfo
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1 "k8s.io/api/core/v1"
)
#Config: {
meta: metav1.#ObjectMeta
hpa: #hpaConfig
ingress: #ingressConfig
service: #serviceConfig
serviceMonitor: #serviceMonConfig
image: {
repository: *"ghcr.io/stefanprodan/podinfo" | string
pullPolicy: *"IfNotPresent" | string
tag: string
}
cache?: string & =~"^tcp://"
backends: [...string]
logLevel: *"info" | string
replicas: *1 | int
resources: *{
requests: {
cpu: "1m"
memory: "16Mi"
}
limits: memory: "128Mi"
} | corev1.#ResourceRequirements
selectorLabels: *{"app.kubernetes.io/name": meta.name} | {[ string]: string}
meta: annotations: *{"app.kubernetes.io/version": "\(image.tag)"} | {[ string]: string}
meta: labels: *selectorLabels | {[ string]: string}
securityContext?: corev1.#PodSecurityContext
affinity?: corev1.#Affinity
tolerations?: [ ...corev1.#Toleration]
}

View File

@@ -1,110 +0,0 @@
package podinfo
import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
)
#Deployment: appsv1.#Deployment & {
_config: #Config
_serviceAccount: string
apiVersion: "apps/v1"
kind: "Deployment"
metadata: _config.meta
spec: appsv1.#DeploymentSpec & {
if !_config.hpa.enabled {
replicas: _config.replicas
}
strategy: {
type: "RollingUpdate"
rollingUpdate: maxUnavailable: 1
}
selector: matchLabels: _config.selectorLabels
template: {
metadata: {
labels: _config.selectorLabels
if !_config.serviceMonitor.enabled {
annotations: {
"prometheus.io/scrape": "true"
"prometheus.io/port": "\(_config.service.metricsPort)"
}
}
}
spec: corev1.#PodSpec & {
terminationGracePeriodSeconds: 15
serviceAccountName: _serviceAccount
containers: [
{
name: "podinfo"
image: "\(_config.image.repository):\(_config.image.tag)"
imagePullPolicy: _config.image.pullPolicy
command: [
"./podinfo",
"--port=\(_config.service.httpPort)",
"--port-metrics=\(_config.service.metricsPort)",
"--grpc-port=\(_config.service.grpcPort)",
"--level=\(_config.logLevel)",
if _config.cache != _|_ {
"--cache-server=\(_config.cache)"
},
for b in _config.backends {
"--backend-url=\(b)"
},
]
ports: [
{
name: "http"
containerPort: _config.service.httpPort
protocol: "TCP"
},
{
name: "http-metrics"
containerPort: _config.service.metricsPort
protocol: "TCP"
},
{
name: "grpc"
containerPort: _config.service.grpcPort
protocol: "TCP"
},
]
livenessProbe: {
httpGet: {
path: "/healthz"
port: "http"
}
}
readinessProbe: {
httpGet: {
path: "/readyz"
port: "http"
}
}
volumeMounts: [
{
name: "data"
mountPath: "/data"
},
]
resources: _config.resources
if _config.securityContext != _|_ {
securityContext: _config.securityContext
}
},
]
if _config.affinity != _|_ {
affinity: _config.affinity
}
if _config.tolerations != _|_ {
tolerations: _config.tolerations
}
volumes: [
{
name: "data"
emptyDir: {}
},
]
}
}
}
}

View File

@@ -1,44 +0,0 @@
package podinfo
import (
corev1 "k8s.io/api/core/v1"
)
#serviceConfig: {
type: *"ClusterIP" | string
externalPort: *9898 | int
httpPort: *9898 | int
metricsPort: *9797 | int
grpcPort: *9999 | int
}
#Service: corev1.#Service & {
_config: #Config
apiVersion: "v1"
kind: "Service"
metadata: _config.meta
spec: corev1.#ServiceSpec & {
type: _config.service.type
selector: _config.selectorLabels
ports: [
{
name: "http"
port: _config.service.externalPort
targetPort: "\(name)"
protocol: "TCP"
},
{
name: "http-metrics"
port: _config.service.metricsPort
targetPort: "\(name)"
protocol: "TCP"
},
{
name: "grpc"
port: _config.service.grpcPort
targetPort: "\(name)"
protocol: "TCP"
},
]
}
}

View File

@@ -1,22 +0,0 @@
package podinfo
#serviceMonConfig: {
enabled: *false | bool
interval: *"15s" | string
}
#ServiceMonitor: {
_config: #Config
apiVersion: "monitoring.coreos.com/v1"
kind: "ServiceMonitor"
metadata: _config.meta
spec: {
endpoints: [{
path: "/metrics"
port: "http-metrics"
interval: _config.serviceMonitor.interval
}]
namespaceSelector: matchNames: [_config.meta.namespace]
selector: matchLabels: _config.meta.labels
}
}

View File

@@ -23,7 +23,7 @@ spec:
spec:
containers:
- name: backend
image: ghcr.io/stefanprodan/podinfo:6.3.6
image: ghcr.io/stefanprodan/podinfo:6.6.2
imagePullPolicy: IfNotPresent
ports:
- name: http

View File

@@ -23,7 +23,7 @@ spec:
spec:
containers:
- name: frontend
image: ghcr.io/stefanprodan/podinfo:6.3.6
image: ghcr.io/stefanprodan/podinfo:6.6.2
imagePullPolicy: IfNotPresent
ports:
- name: http

View File

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

View File

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

101
go.mod
View File

@@ -1,40 +1,41 @@
module github.com/stefanprodan/podinfo
go 1.19
go 1.22
require (
github.com/chzyer/readline v1.5.1
github.com/fatih/color v1.15.0
github.com/fsnotify/fsnotify v1.6.0
github.com/fatih/color v1.16.0
github.com/fsnotify/fsnotify v1.7.0
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/gomodule/redigo v1.8.9
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
github.com/prometheus/client_golang v1.15.1
github.com/spf13/cobra v1.7.0
github.com/gomodule/redigo v1.9.2
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.1
github.com/prometheus/client_golang v1.19.0
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.15.0
github.com/spf13/viper v1.18.2
github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/swag v1.16.1
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.41.1
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.41.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.41.1
go.opentelemetry.io/contrib/propagators/aws v1.16.1
go.opentelemetry.io/contrib/propagators/b3 v1.16.1
go.opentelemetry.io/contrib/propagators/jaeger v1.16.1
go.opentelemetry.io/contrib/propagators/ot v1.16.1
go.opentelemetry.io/otel v1.15.1
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.1
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.15.1
go.opentelemetry.io/otel/sdk v1.15.1
go.opentelemetry.io/otel/trace v1.15.1
go.uber.org/zap v1.24.0
golang.org/x/net v0.9.0
google.golang.org/grpc v1.54.0
github.com/swaggo/swag v1.16.3
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.49.0
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.49.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0
go.opentelemetry.io/contrib/propagators/aws v1.24.0
go.opentelemetry.io/contrib/propagators/b3 v1.24.0
go.opentelemetry.io/contrib/propagators/jaeger v1.24.0
go.opentelemetry.io/contrib/propagators/ot v1.24.0
go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
go.opentelemetry.io/otel/sdk v1.24.0
go.opentelemetry.io/otel/trace v1.24.0
go.uber.org/zap v1.27.0
golang.org/x/net v0.22.0
google.golang.org/grpc v1.62.1
google.golang.org/protobuf v1.33.0
)
// Fix CVE-2022-32149
replace golang.org/x/text => golang.org/x/text v0.9.0
replace golang.org/x/text => golang.org/x/text v0.14.0
// Fix CVE-2022-28948
replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1
@@ -44,44 +45,48 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/spec v0.20.6 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.1 // indirect
go.opentelemetry.io/otel/metric v0.38.1 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
github.com/urfave/cli/v2 v2.3.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.7.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/protobuf v1.30.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

663
go.sum
View File

@@ -1,102 +1,37 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.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.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
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.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
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=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/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=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
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/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/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/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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/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.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -111,94 +46,29 @@ github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyr
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
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/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/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/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.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
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/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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s=
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
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-20201218002935-b9804c9f04c2/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/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
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.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
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/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.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
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/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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -212,434 +82,159 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
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.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
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/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
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.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe h1:K8pHPVoTgxFJt1lXuIzzOX7zZhZFldJQK/CgKx9BFIc=
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww=
github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ=
github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg=
github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto=
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/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/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.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.41.1 h1:+dnM18FnwN3FC+0qquugd6cT4csOj4o5OXfWVYkrhys=
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.41.1/go.mod h1:CY62MFODuCVxifusuibZ8c6G1lQlfVhgi59NrKehMxQ=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.41.1 h1:QKNnjTL0PkxqiMbrVRYOKbWM+rgR3bY+e2JwLS/mzLg=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.41.1/go.mod h1:r2Guf+UO/9TSYcqZMnZVUEd7dCcBQuEyWneWuwr3248=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.41.1 h1:pX+lppB8PArapyhS6nBStyQmkaDUPWdQf0UmEGRCQ54=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.41.1/go.mod h1:2FmkXne0k9nkp27LD/m+uoh8dNlstsiCJ7PLc/S72aI=
go.opentelemetry.io/contrib/propagators/aws v1.16.1 h1:Jf017vtD4kDqpK1YKafZLLGYoQX5Qp5yn0NIEUDYmHc=
go.opentelemetry.io/contrib/propagators/aws v1.16.1/go.mod h1:X+DmBEHeFa1F82LtjVsw37nyEZIdeI2tY2ZaDczg+e8=
go.opentelemetry.io/contrib/propagators/b3 v1.16.1 h1:Y9Dk1kR93eSHadRTkqnm+QyQVhHthCcvTkoP/Afh7+4=
go.opentelemetry.io/contrib/propagators/b3 v1.16.1/go.mod h1:IR0G6txqoetQrjjdoDGe+udhFegxnQQd0dOJfFS8Jg0=
go.opentelemetry.io/contrib/propagators/jaeger v1.16.1 h1:mwCNB3kgSxBQ8F5sVBpJSHvrygBy/+NfGOb/RmNCSaU=
go.opentelemetry.io/contrib/propagators/jaeger v1.16.1/go.mod h1:X5lRM5nm5gcI9rlfT97imQqHfrklct+VujWt/+qkpG0=
go.opentelemetry.io/contrib/propagators/ot v1.16.1 h1:3WV1I2H0UiNbisvGU6oqmrxN9Cfyk4yPkxUKiikqYyU=
go.opentelemetry.io/contrib/propagators/ot v1.16.1/go.mod h1:8330C6LX5qEV4x/2k+RIHh7H7Bggs9d9Hs42RmsA4j0=
go.opentelemetry.io/otel v1.15.1 h1:3Iwq3lfRByPaws0f6bU3naAqOR1n5IeDWd9390kWHa8=
go.opentelemetry.io/otel v1.15.1/go.mod h1:mHHGEHVDLal6YrKMmk9LqC4a3sF5g+fHfrttQIB1NTc=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.1 h1:XYDQtNzdb2T4uM1pku2m76eSMDJgqhJ+6KzkqgQBALc=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.1/go.mod h1:uOTV75+LOzV+ODmL8ahRLWkFA3eQcSC2aAsbxIu4duk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.1 h1:tyoeaUh8REKay72DVYsSEBYV18+fGONe+YYPaOxgLoE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.1/go.mod h1:HUSnrjQQ19KX9ECjpQxufsF+3ioD3zISPMlauTPZu2g=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.15.1 h1:pIfoG5IAZFzp9EUlJzdSkpUwpaUAAnD+Ru1nBLTACIQ=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.15.1/go.mod h1:poNKBqF5+nR/6ke2oGTDjHfksrsHDOHXAl2g4+9ONsY=
go.opentelemetry.io/otel/metric v0.38.1 h1:2MM7m6wPw9B8Qv8iHygoAgkbejed59uUR6ezR5T3X2s=
go.opentelemetry.io/otel/metric v0.38.1/go.mod h1:FwqNHD3I/5iX9pfrRGZIlYICrJv0rHEUl2Ln5vdIVnQ=
go.opentelemetry.io/otel/sdk v1.15.1 h1:5FKR+skgpzvhPQHIEfcwMYjCBr14LWzs3uSqKiQzETI=
go.opentelemetry.io/otel/sdk v1.15.1/go.mod h1:8rVtxQfrbmbHKfqzpQkT5EzZMcbMBwTzNAggbEAM0KA=
go.opentelemetry.io/otel/trace v1.15.1 h1:uXLo6iHJEzDfrNC0L0mNjItIp06SyaBQxu5t3xMlngY=
go.opentelemetry.io/otel/trace v1.15.1/go.mod h1:IWdQG/5N1x7f6YUlmdLeJvH9yxtuJAfc4VW5Agv9r/8=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.49.0 h1:h+c4WbSjBBc3j+IsxwB2mWvkm2nDh0SyGLa5Y5+V9cw=
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.49.0/go.mod h1:FObmJ0epY1FcwMR7aq7sRkrCfwwV3d0GBGFfyV5JUBg=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.49.0 h1:RtcvQ4iw3w9NBB5yRwgA4sSa82rfId7n4atVpvKx3bY=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.49.0/go.mod h1:f/PbKbRd4cdUICWell6DmzvVJ7QrmBgFrRHjXmAXbK4=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/contrib/propagators/aws v1.24.0 h1:cuwQmy9nGJi99fbwUfZSygCL3d347ddnSCWRuiVjhJ8=
go.opentelemetry.io/contrib/propagators/aws v1.24.0/go.mod h1:7HbFx8Hiiuce72QONjbOtU+3QU+Scs9VOHZIrdmi1rw=
go.opentelemetry.io/contrib/propagators/b3 v1.24.0 h1:n4xwCdTx3pZqZs2CjS/CUZAs03y3dZcGhC/FepKtEUY=
go.opentelemetry.io/contrib/propagators/b3 v1.24.0/go.mod h1:k5wRxKRU2uXx2F8uNJ4TaonuEO/V7/5xoz7kdsDACT8=
go.opentelemetry.io/contrib/propagators/jaeger v1.24.0 h1:CKtIfwSgDvJmaWsZROcHzONZgmQdMYn9mVYWypOWT5o=
go.opentelemetry.io/contrib/propagators/jaeger v1.24.0/go.mod h1:Q5JA/Cfdy/ta+5VeEhrMJRWGyS6UNRwFbl+yS3W1h5I=
go.opentelemetry.io/contrib/propagators/ot v1.24.0 h1:6lf4HoYefKDOTUSCatwkpzliUYihAvlN0omfpOn5IDs=
go.opentelemetry.io/contrib/propagators/ot v1.24.0/go.mod h1:A406hNQ7A0EWsOFzWI1p53YaYQXe12C9f6wGHUxfh0g=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
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/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/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
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-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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-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-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
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/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-20211104180415-d3ed0bb246c8/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/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-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-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-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
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/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-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-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-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-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/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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-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-20201201161351-ac6f37ff4c2a/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-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
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/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/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/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/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
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/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-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
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.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
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.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
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/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -648,13 +243,5 @@ 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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/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=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=

View File

@@ -23,7 +23,7 @@ spec:
spec:
containers:
- name: podinfod
image: ghcr.io/stefanprodan/podinfo:6.3.6
image: ghcr.io/stefanprodan/podinfo:6.6.2
imagePullPolicy: IfNotPresent
ports:
- name: http
@@ -72,3 +72,9 @@ spec:
requests:
cpu: 100m
memory: 64Mi
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}

View File

@@ -4,4 +4,3 @@ resources:
- hpa.yaml
- deployment.yaml
- service.yaml

View File

@@ -1,34 +0,0 @@
package api
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestEchoHandler(t *testing.T) {
expected := `{"test": true}`
req, err := http.NewRequest("POST", "/api/echo", strings.NewReader(expected))
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
srv := NewMockServer()
handler := http.HandlerFunc(srv.echoHandler)
handler.ServeHTTP(rr, req)
// Check the status code is what we expect.
if status := rr.Code; status != http.StatusAccepted {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusAccepted)
}
// Check the response body is what we expect.
if rr.Body.String() != expected {
t.Fatalf("handler returned unexpected body:\ngot \n%v \nwant \n%s",
rr.Body.String(), expected)
}
}

20
pkg/api/grpc/echo.go Normal file
View File

@@ -0,0 +1,20 @@
package grpc
import (
"context"
"github.com/stefanprodan/podinfo/pkg/api/grpc/echo"
"go.uber.org/zap"
)
type echoServer struct {
echo.UnimplementedEchoServiceServer
config *Config
logger *zap.Logger
}
func (s *echoServer) Echo (ctx context.Context, message *echo.Message) (*echo.Message, error){
s.logger.Info("Received message body from client:", zap.String("input body", message.Body))
return &echo.Message {Body: message.Body}, nil
}

View File

@@ -0,0 +1,146 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v4.24.3
// source: echo/echo.proto
package echo
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Message struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Body string `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
}
func (x *Message) Reset() {
*x = Message{}
if protoimpl.UnsafeEnabled {
mi := &file_echo_echo_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Message) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Message) ProtoMessage() {}
func (x *Message) ProtoReflect() protoreflect.Message {
mi := &file_echo_echo_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Message.ProtoReflect.Descriptor instead.
func (*Message) Descriptor() ([]byte, []int) {
return file_echo_echo_proto_rawDescGZIP(), []int{0}
}
func (x *Message) GetBody() string {
if x != nil {
return x.Body
}
return ""
}
var File_echo_echo_proto protoreflect.FileDescriptor
var file_echo_echo_proto_rawDesc = []byte{
0x0a, 0x0f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x04, 0x65, 0x63, 0x68, 0x6f, 0x22, 0x1d, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x32, 0x35, 0x0a, 0x0b, 0x45, 0x63, 0x68, 0x6f, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x0d, 0x2e,
0x65, 0x63, 0x68, 0x6f, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0d, 0x2e, 0x65,
0x63, 0x68, 0x6f, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a,
0x06, 0x2e, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_echo_echo_proto_rawDescOnce sync.Once
file_echo_echo_proto_rawDescData = file_echo_echo_proto_rawDesc
)
func file_echo_echo_proto_rawDescGZIP() []byte {
file_echo_echo_proto_rawDescOnce.Do(func() {
file_echo_echo_proto_rawDescData = protoimpl.X.CompressGZIP(file_echo_echo_proto_rawDescData)
})
return file_echo_echo_proto_rawDescData
}
var file_echo_echo_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_echo_echo_proto_goTypes = []interface{}{
(*Message)(nil), // 0: echo.Message
}
var file_echo_echo_proto_depIdxs = []int32{
0, // 0: echo.EchoService.Echo:input_type -> echo.Message
0, // 1: echo.EchoService.Echo:output_type -> echo.Message
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_echo_echo_proto_init() }
func file_echo_echo_proto_init() {
if File_echo_echo_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_echo_echo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Message); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_echo_echo_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_echo_echo_proto_goTypes,
DependencyIndexes: file_echo_echo_proto_depIdxs,
MessageInfos: file_echo_echo_proto_msgTypes,
}.Build()
File_echo_echo_proto = out.File
file_echo_echo_proto_rawDesc = nil
file_echo_echo_proto_goTypes = nil
file_echo_echo_proto_depIdxs = nil
}

View File

@@ -0,0 +1,14 @@
syntax = "proto3";
option go_package = "./echo";
package echo;
message Message {
string body = 1;
}
service EchoService {
rpc Echo(Message) returns (Message) {}
}

View File

@@ -0,0 +1,109 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v4.24.3
// source: echo/echo.proto
package echo
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
EchoService_Echo_FullMethodName = "/echo.EchoService/Echo"
)
// EchoServiceClient is the client API for EchoService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type EchoServiceClient interface {
Echo(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error)
}
type echoServiceClient struct {
cc grpc.ClientConnInterface
}
func NewEchoServiceClient(cc grpc.ClientConnInterface) EchoServiceClient {
return &echoServiceClient{cc}
}
func (c *echoServiceClient) Echo(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) {
out := new(Message)
err := c.cc.Invoke(ctx, EchoService_Echo_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// EchoServiceServer is the server API for EchoService service.
// All implementations must embed UnimplementedEchoServiceServer
// for forward compatibility
type EchoServiceServer interface {
Echo(context.Context, *Message) (*Message, error)
mustEmbedUnimplementedEchoServiceServer()
}
// UnimplementedEchoServiceServer must be embedded to have forward compatible implementations.
type UnimplementedEchoServiceServer struct {
}
func (UnimplementedEchoServiceServer) Echo(context.Context, *Message) (*Message, error) {
return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented")
}
func (UnimplementedEchoServiceServer) mustEmbedUnimplementedEchoServiceServer() {}
// UnsafeEchoServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to EchoServiceServer will
// result in compilation errors.
type UnsafeEchoServiceServer interface {
mustEmbedUnimplementedEchoServiceServer()
}
func RegisterEchoServiceServer(s grpc.ServiceRegistrar, srv EchoServiceServer) {
s.RegisterService(&EchoService_ServiceDesc, srv)
}
func _EchoService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Message)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EchoServiceServer).Echo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: EchoService_Echo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).Echo(ctx, req.(*Message))
}
return interceptor(ctx, in, info, handler)
}
// EchoService_ServiceDesc is the grpc.ServiceDesc for EchoService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var EchoService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "echo.EchoService",
HandlerType: (*EchoServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Echo",
Handler: _EchoService_Echo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "echo/echo.proto",
}

70
pkg/api/grpc/echo_test.go Normal file
View File

@@ -0,0 +1,70 @@
package grpc
import (
"context"
"log"
"net"
"regexp"
"testing"
"github.com/stefanprodan/podinfo/pkg/api/grpc/echo"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
"google.golang.org/grpc/test/bufconn"
)
func TestGrpcEcho(t *testing.T) {
// Server initialization
// bufconn => uses in-memory connection instead of system network I/O
lis := bufconn.Listen(1024*1024)
t.Cleanup(func() {
lis.Close()
})
s := NewMockGrpcServer()
srv := grpc.NewServer()
t.Cleanup(func() {
srv.Stop()
})
echo.RegisterEchoServiceServer(srv, &echoServer{config: s.config, logger: s.logger})
go func(){
if err := srv.Serve(lis); err != nil {
log.Fatalf("srv.Serve %v", err)
}
}()
// - Test
dialer := func(context.Context, string) (net.Conn, error){
return lis.Dial()
}
ctx := context.Background()
conn, err := grpc.DialContext(ctx, "", grpc.WithContextDialer(dialer), grpc.WithInsecure())
t.Cleanup(func() {
conn.Close()
})
if err != nil {
t.Fatalf("grpc.DialContext %v", err)
}
client := echo.NewEchoServiceClient(conn)
res , err := client.Echo(context.Background(), &echo.Message{Body:"test123-test"})
// Check the status code is what we expect.
if _, ok := status.FromError(err); !ok {
t.Errorf("Echo returned type %T, want %T", err, status.Error)
}
// Check the response body is what we expect.
expected := ".*body.*test123-test.*"
r := regexp.MustCompile(expected)
if !r.MatchString(res.String()) {
t.Fatalf("Returned unexpected body:\ngot \n%v \nwant \n%s",
res, expected)
}
}

31
pkg/api/grpc/mock_grpc.go Normal file
View File

@@ -0,0 +1,31 @@
package grpc
import (
"go.uber.org/zap"
)
func NewMockGrpcServer() *Server {
config := &Config{
Port: 9999,
// ServerShutdownTimeout: 5 * time.Second,
// HttpServerTimeout: 30 * time.Second,
BackendURL: []string{},
ConfigPath: "/config",
DataPath: "/data",
// HttpClientTimeout: 30 * time.Second,
UIColor: "blue",
UIPath: ".ui",
UIMessage: "Greetings",
Hostname: "localhost",
}
logger, _ := zap.NewDevelopment()
return &Server{
//router: mux.NewRouter(),
logger: logger,
config: config,
//tracer: trace.NewNoopTracerProvider().Tracer("mock"),
}
}

24
pkg/api/grpc/panic.go Normal file
View File

@@ -0,0 +1,24 @@
package grpc
import (
"context"
// "log"
"os"
pb "github.com/stefanprodan/podinfo/pkg/api/grpc/panic"
"go.uber.org/zap"
)
type PanicServer struct {
pb.UnimplementedPanicServiceServer
config *Config
logger *zap.Logger
}
func (s *PanicServer) Panic(ctx context.Context, req *pb.PanicRequest) (*pb.PanicResponse, error) {
s.logger.Info("Panic command received")
os.Exit(225)
return &pb.PanicResponse{}, nil
}

View File

@@ -0,0 +1,189 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.6.1
// source: panic.proto
package panic
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type PanicRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *PanicRequest) Reset() {
*x = PanicRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_panic_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PanicRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PanicRequest) ProtoMessage() {}
func (x *PanicRequest) ProtoReflect() protoreflect.Message {
mi := &file_panic_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PanicRequest.ProtoReflect.Descriptor instead.
func (*PanicRequest) Descriptor() ([]byte, []int) {
return file_panic_proto_rawDescGZIP(), []int{0}
}
type PanicResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *PanicResponse) Reset() {
*x = PanicResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_panic_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PanicResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PanicResponse) ProtoMessage() {}
func (x *PanicResponse) ProtoReflect() protoreflect.Message {
mi := &file_panic_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PanicResponse.ProtoReflect.Descriptor instead.
func (*PanicResponse) Descriptor() ([]byte, []int) {
return file_panic_proto_rawDescGZIP(), []int{1}
}
var File_panic_proto protoreflect.FileDescriptor
var file_panic_proto_rawDesc = []byte{
0x0a, 0x0b, 0x70, 0x61, 0x6e, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70,
0x61, 0x6e, 0x69, 0x63, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x44, 0x0a, 0x0c, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x05, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x12, 0x13,
0x2e, 0x70, 0x61, 0x6e, 0x69, 0x63, 0x2e, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x70, 0x61, 0x6e, 0x69, 0x63, 0x2e, 0x50, 0x61, 0x6e, 0x69,
0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x2e,
0x2f, 0x70, 0x61, 0x6e, 0x69, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_panic_proto_rawDescOnce sync.Once
file_panic_proto_rawDescData = file_panic_proto_rawDesc
)
func file_panic_proto_rawDescGZIP() []byte {
file_panic_proto_rawDescOnce.Do(func() {
file_panic_proto_rawDescData = protoimpl.X.CompressGZIP(file_panic_proto_rawDescData)
})
return file_panic_proto_rawDescData
}
var file_panic_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_panic_proto_goTypes = []interface{}{
(*PanicRequest)(nil), // 0: panic.PanicRequest
(*PanicResponse)(nil), // 1: panic.PanicResponse
}
var file_panic_proto_depIdxs = []int32{
0, // 0: panic.PanicService.Panic:input_type -> panic.PanicRequest
1, // 1: panic.PanicService.Panic:output_type -> panic.PanicResponse
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_panic_proto_init() }
func file_panic_proto_init() {
if File_panic_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_panic_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PanicRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_panic_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PanicResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_panic_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_panic_proto_goTypes,
DependencyIndexes: file_panic_proto_depIdxs,
MessageInfos: file_panic_proto_msgTypes,
}.Build()
File_panic_proto = out.File
file_panic_proto_rawDesc = nil
file_panic_proto_goTypes = nil
file_panic_proto_depIdxs = nil
}

View File

@@ -0,0 +1,17 @@
syntax = "proto3";
option go_package = "./panic";
package panic;
// The greeting service definition.
service PanicService {
rpc Panic (PanicRequest) returns (PanicResponse) {}
}
message PanicRequest {
}
message PanicResponse {
}

View File

@@ -0,0 +1,105 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.6.1
// source: panic.proto
package panic
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// PanicServiceClient is the client API for PanicService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type PanicServiceClient interface {
Panic(ctx context.Context, in *PanicRequest, opts ...grpc.CallOption) (*PanicResponse, error)
}
type panicServiceClient struct {
cc grpc.ClientConnInterface
}
func NewPanicServiceClient(cc grpc.ClientConnInterface) PanicServiceClient {
return &panicServiceClient{cc}
}
func (c *panicServiceClient) Panic(ctx context.Context, in *PanicRequest, opts ...grpc.CallOption) (*PanicResponse, error) {
out := new(PanicResponse)
err := c.cc.Invoke(ctx, "/panic.PanicService/Panic", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// PanicServiceServer is the server API for PanicService service.
// All implementations must embed UnimplementedPanicServiceServer
// for forward compatibility
type PanicServiceServer interface {
Panic(context.Context, *PanicRequest) (*PanicResponse, error)
mustEmbedUnimplementedPanicServiceServer()
}
// UnimplementedPanicServiceServer must be embedded to have forward compatible implementations.
type UnimplementedPanicServiceServer struct {
}
func (UnimplementedPanicServiceServer) Panic(context.Context, *PanicRequest) (*PanicResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Panic not implemented")
}
func (UnimplementedPanicServiceServer) mustEmbedUnimplementedPanicServiceServer() {}
// UnsafePanicServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to PanicServiceServer will
// result in compilation errors.
type UnsafePanicServiceServer interface {
mustEmbedUnimplementedPanicServiceServer()
}
func RegisterPanicServiceServer(s grpc.ServiceRegistrar, srv PanicServiceServer) {
s.RegisterService(&PanicService_ServiceDesc, srv)
}
func _PanicService_Panic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PanicRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PanicServiceServer).Panic(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/panic.PanicService/Panic",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PanicServiceServer).Panic(ctx, req.(*PanicRequest))
}
return interceptor(ctx, in, info, handler)
}
// PanicService_ServiceDesc is the grpc.ServiceDesc for PanicService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var PanicService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "panic.PanicService",
HandlerType: (*PanicServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Panic",
Handler: _PanicService_Panic_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "panic.proto",
}

93
pkg/api/grpc/server.go Normal file
View File

@@ -0,0 +1,93 @@
package grpc
import (
"fmt"
"net"
"github.com/stefanprodan/podinfo/pkg/api/grpc/echo"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/reflection"
"github.com/stefanprodan/podinfo/pkg/api/grpc/panic"
"github.com/stefanprodan/podinfo/pkg/api/grpc/version"
)
type Server struct {
logger *zap.Logger
config *Config
}
type Config struct {
Port int `mapstructure:"grpc-port"`
ServiceName string `mapstructure:"grpc-service-name"`
BackendURL []string `mapstructure:"backend-url"`
UILogo string `mapstructure:"ui-logo"`
UIMessage string `mapstructure:"ui-message"`
UIColor string `mapstructure:"ui-color"`
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"`
}
func NewServer(config *Config, logger *zap.Logger) (*Server, error) {
srv := &Server{
logger: logger,
config: config,
}
return srv, nil
}
func (s *Server) ListenAndServe() *grpc.Server {
listener, err := net.Listen("tcp", fmt.Sprintf(":%v", s.config.Port))
if err != nil {
s.logger.Fatal("failed to listen", zap.Int("port", s.config.Port))
}
srv := grpc.NewServer()
server := health.NewServer()
// Register grpc apis for refection
echo.RegisterEchoServiceServer(srv, &echoServer{config: s.config, logger: s.logger})
version.RegisterVersionServiceServer(srv, &VersionServer{config: s.config, logger: s.logger})
panic.RegisterPanicServiceServer(srv, &PanicServer{config: s.config, logger: s.logger})
reflection.Register(srv)
grpc_health_v1.RegisterHealthServer(srv, server)
server.SetServingStatus(s.config.ServiceName, grpc_health_v1.HealthCheckResponse_SERVING)
go func() {
if err := srv.Serve(listener); err != nil {
s.logger.Fatal("failed to serve", zap.Error(err))
}
}()
return srv
}

21
pkg/api/grpc/version.go Normal file
View File

@@ -0,0 +1,21 @@
package grpc
import (
"context"
pb "github.com/stefanprodan/podinfo/pkg/api/grpc/version"
"github.com/stefanprodan/podinfo/pkg/version"
"go.uber.org/zap"
)
type VersionServer struct {
pb.UnimplementedVersionServiceServer
config *Config
logger *zap.Logger
}
func (s *VersionServer) Version(ctx context.Context, req *pb.VersionRequest) (*pb.VersionResponse, error) {
return &pb.VersionResponse{Version: version.VERSION, Commit: version.REVISION}, nil
}

View File

@@ -0,0 +1,211 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.6.1
// source: version.proto
package version
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type VersionRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *VersionRequest) Reset() {
*x = VersionRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_version_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *VersionRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VersionRequest) ProtoMessage() {}
func (x *VersionRequest) ProtoReflect() protoreflect.Message {
mi := &file_version_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use VersionRequest.ProtoReflect.Descriptor instead.
func (*VersionRequest) Descriptor() ([]byte, []int) {
return file_version_proto_rawDescGZIP(), []int{0}
}
type VersionResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
Commit string `protobuf:"bytes,2,opt,name=commit,proto3" json:"commit,omitempty"`
}
func (x *VersionResponse) Reset() {
*x = VersionResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_version_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *VersionResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VersionResponse) ProtoMessage() {}
func (x *VersionResponse) ProtoReflect() protoreflect.Message {
mi := &file_version_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use VersionResponse.ProtoReflect.Descriptor instead.
func (*VersionResponse) Descriptor() ([]byte, []int) {
return file_version_proto_rawDescGZIP(), []int{1}
}
func (x *VersionResponse) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
func (x *VersionResponse) GetCommit() string {
if x != nil {
return x.Commit
}
return ""
}
var File_version_proto protoreflect.FileDescriptor
var file_version_proto_rawDesc = []byte{
0x0a, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x10, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x0f, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x32,
0x50, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x12, 0x3e, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x2e, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_version_proto_rawDescOnce sync.Once
file_version_proto_rawDescData = file_version_proto_rawDesc
)
func file_version_proto_rawDescGZIP() []byte {
file_version_proto_rawDescOnce.Do(func() {
file_version_proto_rawDescData = protoimpl.X.CompressGZIP(file_version_proto_rawDescData)
})
return file_version_proto_rawDescData
}
var file_version_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_version_proto_goTypes = []interface{}{
(*VersionRequest)(nil), // 0: version.VersionRequest
(*VersionResponse)(nil), // 1: version.VersionResponse
}
var file_version_proto_depIdxs = []int32{
0, // 0: version.VersionService.Version:input_type -> version.VersionRequest
1, // 1: version.VersionService.Version:output_type -> version.VersionResponse
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_version_proto_init() }
func file_version_proto_init() {
if File_version_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_version_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*VersionRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_version_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*VersionResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_version_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_version_proto_goTypes,
DependencyIndexes: file_version_proto_depIdxs,
MessageInfos: file_version_proto_msgTypes,
}.Build()
File_version_proto = out.File
file_version_proto_rawDesc = nil
file_version_proto_goTypes = nil
file_version_proto_depIdxs = nil
}

View File

@@ -0,0 +1,18 @@
syntax = "proto3";
option go_package = "./version";
package version;
service VersionService {
rpc Version (VersionRequest) returns (VersionResponse) {}
}
message VersionRequest {}
message VersionResponse {
string version = 1;
string commit = 2;
}

View File

@@ -0,0 +1,105 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.6.1
// source: version.proto
package version
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// VersionServiceClient is the client API for VersionService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type VersionServiceClient interface {
Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error)
}
type versionServiceClient struct {
cc grpc.ClientConnInterface
}
func NewVersionServiceClient(cc grpc.ClientConnInterface) VersionServiceClient {
return &versionServiceClient{cc}
}
func (c *versionServiceClient) Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error) {
out := new(VersionResponse)
err := c.cc.Invoke(ctx, "/version.VersionService/Version", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// VersionServiceServer is the server API for VersionService service.
// All implementations must embed UnimplementedVersionServiceServer
// for forward compatibility
type VersionServiceServer interface {
Version(context.Context, *VersionRequest) (*VersionResponse, error)
mustEmbedUnimplementedVersionServiceServer()
}
// UnimplementedVersionServiceServer must be embedded to have forward compatible implementations.
type UnimplementedVersionServiceServer struct {
}
func (UnimplementedVersionServiceServer) Version(context.Context, *VersionRequest) (*VersionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Version not implemented")
}
func (UnimplementedVersionServiceServer) mustEmbedUnimplementedVersionServiceServer() {}
// UnsafeVersionServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to VersionServiceServer will
// result in compilation errors.
type UnsafeVersionServiceServer interface {
mustEmbedUnimplementedVersionServiceServer()
}
func RegisterVersionServiceServer(s grpc.ServiceRegistrar, srv VersionServiceServer) {
s.RegisterService(&VersionService_ServiceDesc, srv)
}
func _VersionService_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(VersionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(VersionServiceServer).Version(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/version.VersionService/Version",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(VersionServiceServer).Version(ctx, req.(*VersionRequest))
}
return interceptor(ctx, in, info, handler)
}
// VersionService_ServiceDesc is the grpc.ServiceDesc for VersionService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var VersionService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "version.VersionService",
HandlerType: (*VersionServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Version",
Handler: _VersionService_Version_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "version.proto",
}

View File

@@ -0,0 +1,72 @@
package grpc
import (
"context"
"fmt"
"log"
"net"
"regexp"
"testing"
"github.com/stefanprodan/podinfo/pkg/api/grpc/version"
v "github.com/stefanprodan/podinfo/pkg/version"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
"google.golang.org/grpc/test/bufconn"
)
func TestGrpcVersion(t *testing.T) {
// Server initialization
// bufconn => uses in-memory connection instead of system network I/O
lis := bufconn.Listen(1024*1024)
t.Cleanup(func() {
lis.Close()
})
srv := grpc.NewServer()
t.Cleanup(func() {
srv.Stop()
})
version.RegisterVersionServiceServer(srv, &VersionServer{})
go func(){
if err := srv.Serve(lis); err != nil {
log.Fatalf("srv.Serve %v", err)
}
}()
// - Test
dialer := func(context.Context, string) (net.Conn, error){
return lis.Dial()
}
ctx := context.Background()
conn, err := grpc.DialContext(ctx, "", grpc.WithContextDialer(dialer), grpc.WithInsecure())
t.Cleanup(func() {
conn.Close()
})
if err != nil {
t.Fatalf("grpc.DialContext %v", err)
}
client := version.NewVersionServiceClient(conn)
res , err := client.Version(context.Background(), &version.VersionRequest{})
// Check the status code is what we expect.
if _, ok := status.FromError(err); !ok {
t.Errorf("Version returned type %T, want %T", err, status.Error)
}
// Check the response body is what we expect.
expected := fmt.Sprintf(".*%s.*", v.VERSION)
r := regexp.MustCompile(expected)
if !r.MatchString(res.String()) {
t.Fatalf("Returned unexpected body:\ngot \n%v \nwant \n%s",
res, expected)
}
}

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"math/rand"
@@ -17,7 +17,7 @@ import (
// @Produce json
// @Param seconds path int true "seconds to wait for"
// @Router /chunked/{seconds} [get]
// @Success 200 {object} api.MapResponse
// @Success 200 {object} http.MapResponse
func (s *Server) chunkedHandler(w http.ResponseWriter, r *http.Request) {
_, span := s.tracer.Start(r.Context(), "chunkedHandler")
defer span.End()

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"

View File

@@ -1,4 +1,4 @@
package api
package http
import "net/http"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"math/rand"
@@ -51,7 +51,7 @@ func (m *RandomDelayMiddleware) Handler(next http.Handler) http.Handler {
// @Produce json
// @Param seconds path int true "seconds to wait for"
// @Router /delay/{seconds} [get]
// @Success 200 {object} api.MapResponse
// @Success 200 {object} http.MapResponse
func (s *Server) delayHandler(w http.ResponseWriter, r *http.Request) {
_, span := s.tracer.Start(r.Context(), "delayHandler")
defer span.End()

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"

View File

@@ -1,5 +1,4 @@
// Code generated by swaggo/swag. DO NOT EDIT.
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs
import "github.com/swaggo/swag"
@@ -20,7 +19,6 @@ const docTemplate = `{
},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/": {
@@ -60,7 +58,7 @@ const docTemplate = `{
"202": {
"description": "Accepted",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -83,7 +81,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.RuntimeResponse"
"$ref": "#/definitions/http.RuntimeResponse"
}
}
}
@@ -201,7 +199,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -233,7 +231,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -439,7 +437,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -462,7 +460,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -517,7 +515,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.TokenResponse"
"$ref": "#/definitions/http.TokenResponse"
}
}
}
@@ -540,7 +538,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.TokenValidationResponse"
"$ref": "#/definitions/http.TokenValidationResponse"
}
},
"401": {
@@ -566,7 +564,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -589,7 +587,7 @@ const docTemplate = `{
"202": {
"description": "Accepted",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -597,13 +595,13 @@ const docTemplate = `{
}
},
"definitions": {
"api.MapResponse": {
"http.MapResponse": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"api.RuntimeResponse": {
"http.RuntimeResponse": {
"type": "object",
"properties": {
"color": {
@@ -641,7 +639,7 @@ const docTemplate = `{
}
}
},
"api.TokenResponse": {
"http.TokenResponse": {
"type": "object",
"properties": {
"expires_at": {
@@ -652,7 +650,7 @@ const docTemplate = `{
}
}
},
"api.TokenValidationResponse": {
"http.TokenValidationResponse": {
"type": "object",
"properties": {
"expires_at": {
@@ -669,7 +667,7 @@ const docTemplate = `{
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "2.0",
Host: "localhost:9898",
Host: "",
BasePath: "/",
Schemes: []string{"http", "https"},
Title: "Podinfo API",

View File

@@ -17,7 +17,6 @@
},
"version": "2.0"
},
"host": "localhost:9898",
"basePath": "/",
"paths": {
"/": {
@@ -57,7 +56,7 @@
"202": {
"description": "Accepted",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -80,7 +79,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.RuntimeResponse"
"$ref": "#/definitions/http.RuntimeResponse"
}
}
}
@@ -198,7 +197,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -230,7 +229,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -436,7 +435,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -459,7 +458,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -514,7 +513,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.TokenResponse"
"$ref": "#/definitions/http.TokenResponse"
}
}
}
@@ -537,7 +536,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.TokenValidationResponse"
"$ref": "#/definitions/http.TokenValidationResponse"
}
},
"401": {
@@ -563,7 +562,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -586,7 +585,7 @@
"202": {
"description": "Accepted",
"schema": {
"$ref": "#/definitions/api.MapResponse"
"$ref": "#/definitions/http.MapResponse"
}
}
}
@@ -594,13 +593,13 @@
}
},
"definitions": {
"api.MapResponse": {
"http.MapResponse": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"api.RuntimeResponse": {
"http.RuntimeResponse": {
"type": "object",
"properties": {
"color": {
@@ -638,7 +637,7 @@
}
}
},
"api.TokenResponse": {
"http.TokenResponse": {
"type": "object",
"properties": {
"expires_at": {
@@ -649,7 +648,7 @@
}
}
},
"api.TokenValidationResponse": {
"http.TokenValidationResponse": {
"type": "object",
"properties": {
"expires_at": {

View File

@@ -1,10 +1,10 @@
basePath: /
definitions:
api.MapResponse:
http.MapResponse:
additionalProperties:
type: string
type: object
api.RuntimeResponse:
http.RuntimeResponse:
properties:
color:
type: string
@@ -29,21 +29,20 @@ definitions:
version:
type: string
type: object
api.TokenResponse:
http.TokenResponse:
properties:
expires_at:
type: string
token:
type: string
type: object
api.TokenValidationResponse:
http.TokenValidationResponse:
properties:
expires_at:
type: string
token_name:
type: string
type: object
host: localhost:9898
info:
contact:
name: Source Code
@@ -79,7 +78,7 @@ paths:
"202":
description: Accepted
schema:
$ref: '#/definitions/api.MapResponse'
$ref: '#/definitions/http.MapResponse'
summary: Echo
tags:
- HTTP API
@@ -94,7 +93,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.RuntimeResponse'
$ref: '#/definitions/http.RuntimeResponse'
summary: Runtime information
tags:
- HTTP API
@@ -173,7 +172,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
$ref: '#/definitions/http.MapResponse'
summary: Chunked transfer encoding
tags:
- HTTP API
@@ -194,7 +193,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
$ref: '#/definitions/http.MapResponse'
summary: Delay
tags:
- HTTP API
@@ -330,7 +329,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
$ref: '#/definitions/http.MapResponse'
summary: Status code
tags:
- HTTP API
@@ -346,7 +345,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
$ref: '#/definitions/http.MapResponse'
summary: Upload file
tags:
- HTTP API
@@ -382,7 +381,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.TokenResponse'
$ref: '#/definitions/http.TokenResponse'
summary: Generate JWT token
tags:
- HTTP API
@@ -397,7 +396,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.TokenValidationResponse'
$ref: '#/definitions/http.TokenValidationResponse'
"401":
description: Unauthorized
schema:
@@ -414,7 +413,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
$ref: '#/definitions/http.MapResponse'
summary: Version
tags:
- HTTP API
@@ -429,7 +428,7 @@ paths:
"202":
description: Accepted
schema:
$ref: '#/definitions/api.MapResponse'
$ref: '#/definitions/http.MapResponse'
summary: Echo over websockets
tags:
- HTTP API

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"bytes"
@@ -22,7 +22,7 @@ import (
// @Accept json
// @Produce json
// @Router /api/echo [post]
// @Success 202 {object} api.MapResponse
// @Success 202 {object} http.MapResponse
func (s *Server) echoHandler(w http.ResponseWriter, r *http.Request) {
ctx, span := s.tracer.Start(r.Context(), "echoHandler")
defer span.End()

48
pkg/api/http/echo_test.go Normal file
View File

@@ -0,0 +1,48 @@
package http
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestEchoHandler(t *testing.T) {
cases := []struct {
url string
method string
expected string
}{
{url: "/api/echo", method: "POST", expected: `{"test": true}`},
{url: "/api/echo", method: "PUT", expected: `{"test": true}`},
{url: "/echo", method: "PUT", expected: `{"test": true}`},
{url: "/echo/", method: "POST", expected: `{"test": true}`},
{url: "/echo/test", method: "POST", expected: `{"test": true}`},
{url: "/echo/test/", method: "POST", expected: `{"test": true}`},
{url: "/echo/test/test123-test", method: "POST", expected: `{"test": true}`},
}
srv := NewMockServer()
handler := http.HandlerFunc(srv.echoHandler)
for _, c := range cases {
req, err := http.NewRequest(c.method, c.url, strings.NewReader(c.expected))
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
// Check the status code is what we expect.
if status := rr.Code; status != http.StatusAccepted {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusAccepted)
}
// Check the response body is what we expect.
if rr.Body.String() != c.expected {
t.Fatalf("handler returned unexpected body:\ngot \n%v \nwant \n%s",
rr.Body.String(), c.expected)
}
}
}

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"
@@ -18,7 +18,7 @@ var wsCon = websocket.Upgrader{}
// @Accept json
// @Produce json
// @Router /ws/echo [post]
// @Success 202 {object} api.MapResponse
// @Success 202 {object} http.MapResponse
// Test: go run ./cmd/podcli/* ws localhost:9898/ws/echo
func (s *Server) echoWsHandler(w http.ResponseWriter, r *http.Request) {
c, err := wsCon.Upgrade(w, r, nil)

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"
@@ -13,7 +13,7 @@ import (
// @Accept json
// @Produce json
// @Router /env [get]
// @Success 200 {object} api.ArrayResponse
// @Success 200 {object} http.ArrayResponse
func (s *Server) envHandler(w http.ResponseWriter, r *http.Request) {
_, span := s.tracer.Start(r.Context(), "envHandler")
defer span.End()

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"
@@ -11,7 +11,7 @@ import (
// @Accept json
// @Produce json
// @Router /headers [get]
// @Success 200 {object} api.ArrayResponse
// @Success 200 {object} http.ArrayResponse
func (s *Server) echoHeadersHandler(w http.ResponseWriter, r *http.Request) {
_, span := s.tracer.Start(r.Context(), "echoHeadersHandler")
defer span.End()

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"bytes"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"html/template"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"
@@ -15,7 +15,7 @@ import (
// @Tags HTTP API
// @Accept json
// @Produce json
// @Success 200 {object} api.RuntimeResponse
// @Success 200 {object} http.RuntimeResponse
// @Router /api/info [get]
func (s *Server) infoHandler(w http.ResponseWriter, r *http.Request) {
_, span := s.tracer.Start(r.Context(), "infoHandler")

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"bufio"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"time"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"context"
@@ -14,7 +14,7 @@ import (
"github.com/gomodule/redigo/redis"
"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus/promhttp"
_ "github.com/stefanprodan/podinfo/pkg/api/docs"
_ "github.com/stefanprodan/podinfo/pkg/api/http/docs"
"github.com/stefanprodan/podinfo/pkg/fscache"
httpSwagger "github.com/swaggo/http-swagger"
"github.com/swaggo/swag"
@@ -35,7 +35,6 @@ import (
// @license.name MIT License
// @license.url https://github.com/stefanprodan/podinfo/blob/master/LICENSE
// @host localhost:9898
// @BasePath /
// @schemes http https
@@ -100,7 +99,8 @@ func (s *Server) registerHandlers() {
s.router.HandleFunc("/", s.indexHandler).HeadersRegexp("User-Agent", "^Mozilla.*").Methods("GET")
s.router.HandleFunc("/", s.infoHandler).Methods("GET")
s.router.HandleFunc("/version", s.versionHandler).Methods("GET")
s.router.HandleFunc("/echo", s.echoHandler).Methods("POST")
s.router.HandleFunc("/echo", s.echoHandler)
s.router.PathPrefix("/echo/").HandlerFunc(s.echoHandler)
s.router.HandleFunc("/env", s.envHandler).Methods("GET", "POST")
s.router.HandleFunc("/headers", s.echoHeadersHandler).Methods("GET", "POST")
s.router.HandleFunc("/delay/{wait:[0-9]+}", s.delayHandler).Methods("GET").Name("delay")
@@ -119,7 +119,8 @@ func (s *Server) registerHandlers() {
s.router.HandleFunc("/token", s.tokenGenerateHandler).Methods("POST")
s.router.HandleFunc("/token/validate", s.tokenValidateHandler).Methods("GET")
s.router.HandleFunc("/api/info", s.infoHandler).Methods("GET")
s.router.HandleFunc("/api/echo", s.echoHandler).Methods("POST")
s.router.HandleFunc("/api/echo", s.echoHandler)
s.router.PathPrefix("/api/echo/").HandlerFunc(s.echoHandler)
s.router.HandleFunc("/ws/echo", s.echoWsHandler)
s.router.HandleFunc("/chunked", s.chunkedHandler)
s.router.HandleFunc("/chunked/{wait:[0-9]+}", s.chunkedHandler)

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"
@@ -16,7 +16,7 @@ import (
// @Produce json
// @Param code path int true "status code to return"
// @Router /status/{code} [get]
// @Success 200 {object} api.MapResponse
// @Success 200 {object} http.MapResponse
func (s *Server) statusHandler(w http.ResponseWriter, r *http.Request) {
_, span := s.tracer.Start(r.Context(), "statusHandler")
defer span.End()

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"crypto/sha1"
@@ -19,7 +19,7 @@ import (
// @Accept json
// @Produce json
// @Router /store [post]
// @Success 200 {object} api.MapResponse
// @Success 200 {object} http.MapResponse
func (s *Server) storeWriteHandler(w http.ResponseWriter, r *http.Request) {
_, span := s.tracer.Start(r.Context(), "storeWriteHandler")
defer span.End()

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"fmt"
@@ -23,7 +23,7 @@ type jwtCustomClaims struct {
// @Accept json
// @Produce json
// @Router /token [post]
// @Success 200 {object} api.TokenResponse
// @Success 200 {object} http.TokenResponse
func (s *Server) tokenGenerateHandler(w http.ResponseWriter, r *http.Request) {
_, span := s.tracer.Start(r.Context(), "tokenGenerateHandler")
defer span.End()
@@ -72,7 +72,7 @@ func (s *Server) tokenGenerateHandler(w http.ResponseWriter, r *http.Request) {
// @Accept json
// @Produce json
// @Router /token/validate [post]
// @Success 200 {object} api.TokenValidationResponse
// @Success 200 {object} http.TokenValidationResponse
// @Failure 401 {string} string "Unauthorized"
// Get: JWT=$(curl -s -d 'test' localhost:9898/token | jq -r .token)
// Post: curl -H "Authorization: Bearer ${JWT}" localhost:9898/token/validate

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"encoding/json"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"context"

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"net/http"
@@ -12,7 +12,7 @@ import (
// @Tags HTTP API
// @Produce json
// @Router /version [get]
// @Success 200 {object} api.MapResponse
// @Success 200 {object} http.MapResponse
func (s *Server) versionHandler(w http.ResponseWriter, r *http.Request) {
result := map[string]string{
"version": version.VERSION,

View File

@@ -1,4 +1,4 @@
package api
package http
import (
"fmt"

View File

@@ -1,52 +0,0 @@
package grpc
import (
"fmt"
"net"
"go.uber.org/zap"
"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 {
logger *zap.Logger
config *Config
}
type Config struct {
Port int `mapstructure:"grpc-port"`
ServiceName string `mapstructure:"grpc-service-name"`
}
func NewServer(config *Config, logger *zap.Logger) (*Server, error) {
srv := &Server{
logger: logger,
config: config,
}
return srv, nil
}
func (s *Server) ListenAndServe() *grpc.Server {
listener, err := net.Listen("tcp", fmt.Sprintf(":%v", s.config.Port))
if err != nil {
s.logger.Fatal("failed to listen", zap.Int("port", s.config.Port))
}
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)
go func() {
if err := srv.Serve(listener); err != nil {
s.logger.Fatal("failed to serve", zap.Error(err))
}
}()
return srv
}

View File

@@ -48,7 +48,8 @@ func (s *Shutdown) Graceful(stopCh <-chan struct{}, httpServer *http.Server, htt
s.logger.Info("Shutting down HTTP/HTTPS server", zap.Duration("timeout", s.serverShutdownTimeout))
// wait for Kubernetes readiness probe to remove this instance from the load balancer
// There could be a period where a terminating pod may still receive requests. Implementing a brief wait can mitigate this.
// See: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination
// the readiness check interval must be lower than the timeout
if viper.GetString("level") != "debug" {
time.Sleep(3 * time.Second)

View File

@@ -1,4 +1,4 @@
package version
var VERSION = "6.3.6"
var VERSION = "6.6.2"
var REVISION = "unknown"

View File

@@ -0,0 +1,68 @@
bundle: {
apiVersion: "v1alpha1"
name: "podinfo"
_modURL: "oci://ghcr.io/stefanprodan/modules/podinfo" @timoni(runtime:string:PODINFO_MODULE_URL)
_imgURL: "ghcr.io/stefanprodan/modules/podinfo" @timoni(runtime:string:PODINFO_IMAGE_URL)
_imgTag: "latest" @timoni(runtime:string:PODINFO_VERSION)
instances: {
backend: {
module: url: _modURL
namespace: "podinfo"
values: {
image: {
repository: _imgURL
tag: _imgTag
}
resources: requests: {
cpu: "100m"
memory: "128Mi"
}
autoscaling: {
enabled: true
minReplicas: 1
maxReplicas: 10
cpu: 90
}
test: enabled: true
}
}
frontend: {
module: url: _modURL
namespace: "podinfo"
values: {
image: {
repository: _imgURL
tag: _imgTag
}
ui: backend: "http://backend.podinfo.svc.cluster.local/echo"
replicas: 2
podSecurityContext: {
runAsUser: 100
runAsGroup: 101
fsGroup: 101
}
securityContext: {
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities: drop: ["ALL"]
seccompProfile: type: "RuntimeDefault"
}
ingress: {
enabled: true
className: "nginx"
host: "podinfo.local"
tls: true
annotations: {
"nginx.ingress.kubernetes.io/ssl-redirect": "false"
"nginx.ingress.kubernetes.io/force-ssl-redirect": "false"
"cert-manager.io/cluster-issuer": "self-signed"
}
}
test: enabled: true
}
}
}
}

140
timoni/podinfo/README.md Normal file
View File

@@ -0,0 +1,140 @@
# Podinfo
[Podinfo](https://github.com/stefanprodan/podinfo) is a tiny web application
made with Go that showcases best practices of running microservices in Kubernetes.
## Module Repository
This module is available on GitHub Container Registry at
[ghcr.io/stefanprodan/modules/podinfo](https://github.com/stefanprodan/podinfo/pkgs/container/modules%2Fpodinfo).
## Install
To create an instance using the default values:
```shell
timoni -n default apply podinfo oci://ghcr.io/stefanprodan/modules/podinfo
```
To install a specific module version:
```shell
timoni -n default apply podinfo oci://ghcr.io/stefanprodan/modules/podinfo -v 6.5.0
```
To change the [default configuration](#configuration),
create one or more `values.cue` files and apply them to the instance.
For example, create a file `my-values.cue` with the following content:
```cue
values: {
resources: requests: {
cpu: "100m"
memory: "128Mi"
}
}
```
And apply the values with:
```shell
timoni -n default apply podinfo oci://ghcr.io/stefanprodan/modules/podinfo \
--values ./my-values.cue
```
## Uninstall
To uninstall an instance and delete all its Kubernetes resources:
```shell
timoni -n default delete podinfo
```
## Configuration
### General values
| Key | Type | Default | Description |
|------------------------------|-----------------------------------------|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
| `image: tag:` | `string` | `<latest version>` | Container image tag |
| `image: digest:` | `string` | `""` | Container image digest, takes precedence over `tag` when specified |
| `image: repository:` | `string` | `ghcr.io/stefanprodan/podinfo` | Container image repository |
| `image: pullPolicy:` | `string` | `IfNotPresent` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) |
| `metadata: labels:` | `{[ string]: string}` | `{}` | Common labels for all resources |
| `metadata: annotations:` | `{[ string]: string}` | `{}` | Common annotations for all resources |
| `podAnnotations:` | `{[ string]: string}` | `{}` | Annotations applied to pods |
| `imagePullSecrets:` | `[...corev1.LocalObjectReference]` | `[]` | [Kubernetes image pull secrets](https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) |
| `tolerations:` | `[ ...corev1.#Toleration]` | `[]` | [Kubernetes toleration](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration) |
| `affinity:` | `corev1.#Affinity` | `{}` | [Kubernetes affinity and anti-affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) |
| `resources:` | `corev1.#ResourceRequirements` | `{}` | [Kubernetes resource requests and limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers) |
| `topologySpreadConstraints:` | `[...corev1.#TopologySpreadConstraint]` | `[]` | [Kubernetes pod topology spread constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints) |
| `podSecurityContext:` | `corev1.#PodSecurityContext` | `{}` | [Kubernetes pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context) |
| `securityContext:` | `corev1.#SecurityContext` | `{}` | [Kubernetes container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context) |
| `test: enabled:` | `bool` | `false` | Run end-to-end tests at install and upgrades |
#### Recommended values
Comply with the
restricted [Kubernetes pod security standard](https://kubernetes.io/docs/concepts/security/pod-security-standards/):
```cue
values: {
podSecurityContext: {
runAsUser: 100
runAsGroup: 101
fsGroup: 101
}
securityContext: {
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities: drop: ["ALL"]
seccompProfile: type: "RuntimeDefault"
}
}
```
### Autoscaling values
| Key | Type | Default | Description |
|-----------------------------|----------|---------------|--------------------------------------------------------------------------------------------------------------|
| `replicas:` | `int` | `1` | Number of pods when autoscaling is disabled |
| `autoscaling: enabled:` | `bool` | `false` | Enable [Kubernetes HPA](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) creation |
| `autoscaling: minReplicas:` | `int` | `replicas` | Minimum number of pods |
| `autoscaling: maxReplicas:` | `int` | `minReplicas` | Maximum number of pods |
| `autoscaling: cpu:` | `int` | `99` | CPU average utilization (percentage) |
| `autoscaling: memory:` | `string` | `""` | memory average value (e.g. `1024Mi`) |
### Ingress values
| Key | Type | Default | Description |
|-------------------------|-----------------------|-----------------|--------------------------------------------------------------------------------------------------------|
| `service: port:` | `int` | `80` | Kubernetes Service ClusterIP port |
| `ingress: enabled:` | `bool` | `false` | Enable [Kubernetes Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) creation |
| `ingress: tls:` | `bool` | `false` | Enable TLS (requires cert-manager) |
| `ingress: host:` | `string` | `podinfo.local` | Ingress host |
| `ingress: className:` | `string` | `""` | Ingress class name |
| `ingress: annotations:` | `{[ string]: string}` | `{}` | Annotations applied to ingress |
### Monitoring values
| Key | Type | Default | Description |
|-------------------------|--------|---------|-------------------------------------------------------------------------------|
| `monitoring: enabled:` | `bool` | `false` | Enable [Prometheus ServiceMonitor](https://prometheus-operator.dev/) creation |
| `monitoring: interval:` | `int` | `15` | Prometheus scrape interval in seconds |
### Cashing values
| Key | Type | Default | Description |
|----------------------|----------|---------|---------------------------------------------------------|
| `caching: enabled:` | `bool` | `false` | Enable Redis caching |
| `caching: redisURL:` | `string` | `""` | Redis URL in the format `tcp://:[password]@host[:port]` |
### UI values
| Key | Type | Default | Description |
|----------------|----------|-----------|------------------|
| `ui: color:` | `string` | `#34577c` | Background color |
| `ui: message:` | `string` | `""` | Greeting message |
| `ui: backend:` | `string` | `""` | Backend URL |

Some files were not shown because too many files have changed in this diff Show More