Compare commits

...

38 Commits

Author SHA1 Message Date
940bd0405f New template orient added (#190)
* added orient theme

* added creation test for orient theme

* Added new Orient theme to CHANGELOG.md

* fix: Template fixed a bit

---------

Co-authored-by: Paramtamtam <7326800+tarampampam@users.noreply.github.com>
2023-04-17 15:06:28 +04:00
d40e8879d1 feat: Non-existing pages now return styled 404 status page (with 404 status code) (#189)
* 🎨 style(CHANGELOG.md): add UNRELEASED section
🔧 chore(notfound/handler.go): refactor to use core.RespondWithErrorPage
🔧 chore(http/server.go): pass config, templatePicker, renderer and options to notfoundHandler
🔧 chore(hurl/404.hurl): update test to expect HTML response instead of plain text

* 📝 docs(CHANGELOG.md): fix typo in changed section

* 🔥 chore(CHANGELOG.md): release version 2.22.0
🔥 chore(flags.go): remove unused code for serve command
2023-04-07 14:42:00 +04:00
6a67510bdc build(deps): bump go.uber.org/automaxprocs from 1.5.1 to 1.5.2 (#183) 2023-04-02 02:27:23 +00:00
a79521a37d build(deps): bump github.com/urfave/cli/v2 from 2.24.4 to 2.25.1 (#185) 2023-04-02 02:24:01 +00:00
4667194271 build(deps): bump github.com/fasthttp/router from 1.4.17 to 1.4.18 (#184) 2023-04-02 02:23:55 +00:00
a20852b03a build(deps): bump aquasecurity/trivy-action from 0.9.1 to 0.9.2 (#182) 2023-04-02 02:22:48 +00:00
ec0f1cc0d3 build(deps): bump github.com/valyala/fasthttp from 1.44.0 to 1.45.0 (#186) 2023-04-02 02:19:54 +00:00
49d9650d35 build(deps): bump github.com/fatih/color from 1.14.1 to 1.15.0 (#187) 2023-04-02 02:19:27 +00:00
1b876c45cc chore(deps): update golangci/golangci-lint docker tag to v1.52 (#181)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-18 17:17:13 +04:00
23f52f25e2 Bump aquasecurity/trivy-action from 0.8.0 to 0.9.1 (#177) 2023-03-02 03:53:22 +00:00
16d7d80183 Bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#178) 2023-03-02 03:49:08 +00:00
5389fe00dd Bump github.com/fasthttp/router from 1.4.16 to 1.4.17 (#179) 2023-03-02 03:48:51 +00:00
cd67674976 Bump go.uber.org/goleak from 1.2.0 to 1.2.1 (#180) 2023-03-02 03:48:33 +00:00
36673a49a4 ci: 👷 CI system updated 2023-02-23 22:03:32 +04:00
b84b3ba9f4 chore: 🔧 Changes that do not modify src or test files 2023-02-23 21:50:35 +04:00
5343d8c934 docs(changelog): 📚 Changelog file updated 2023-02-23 21:50:31 +04:00
80891b0b46 ci: 👷 CI system updated 2023-02-23 21:50:20 +04:00
48313685ec chore: Module name changed, deps updated 2023-02-23 21:49:45 +04:00
7dc47c00ac chore(deps): update golangci/golangci-lint docker tag to v1.51 (#174)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-02 17:08:43 +04:00
830b5bb934 chore(deps): update golang docker tag to v1.20 (#173)
* chore(deps): update golang docker tag to v1.20

* updated

* update

* update

* Update .github/workflows/tests.yml

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Paramtamtam <7326800+tarampampam@users.noreply.github.com>
2023-02-02 13:09:29 +04:00
8d43984644 Bump github.com/urfave/cli/v2 from 2.24.1 to 2.24.2 (#168) 2023-02-02 07:25:44 +00:00
1b152c1a80 Bump github.com/fasthttp/router from 1.4.14 to 1.4.16 (#169) 2023-02-02 03:26:30 +00:00
175b9f0cfb Bump go.uber.org/goleak from 1.1.11 to 1.2.0 (#170) 2023-02-02 03:25:57 +00:00
16180be7e2 Bump github.com/valyala/fasthttp from 1.43.0 to 1.44.0 (#171) 2023-02-02 03:22:36 +00:00
11b270ee68 Bump github.com/fatih/color from 1.13.0 to 1.14.1 (#172) 2023-02-02 03:22:15 +00:00
97ab8a4475 Bump docker/build-push-action from 3 to 4 (#167) 2023-02-02 03:20:00 +00:00
da3b864e02 fix: docker-compose server running command 2023-01-29 15:56:58 +04:00
0bd989e493 docs(changelog): file updated 2023-01-29 15:49:28 +04:00
59c4d2022c ci: workflow files have been updated 2023-01-29 15:39:49 +04:00
1ec17caa1d feat: Possibility to use custom env variables in templates (#165) 2023-01-29 15:25:38 +04:00
252618a975 chore: Better CLI (#163) 2023-01-29 14:54:56 +04:00
315c7660d1 Update README.md 2022-12-27 11:41:51 +04:00
6a6809b07f Bump go.uber.org/zap from 1.23.0 to 1.24.0 (#159) 2022-12-02 06:09:55 +00:00
a960b5928e Bump github.com/valyala/fasthttp from 1.41.0 to 1.43.0 (#157) 2022-12-02 06:06:16 +00:00
46d96d7bb4 Bump github.com/prometheus/client_golang from 1.13.0 to 1.14.0 (#160) 2022-12-02 06:02:10 +00:00
69063a9cf7 Bump github.com/fasthttp/router from 1.4.13 to 1.4.14 (#158) 2022-12-02 06:01:28 +00:00
7c8d2f54c7 Update orangeopensource/hurl Docker tag to v1.8.0 (#155)
* Update orangeopensource/hurl Docker tag to v1.8.0

* wip: temporary commit

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Paramtamtam <7326800+tarampampam@users.noreply.github.com>
2022-11-18 23:46:46 +04:00
49aed23f8c Update README.md 2022-11-15 13:06:39 +04:00
56 changed files with 1347 additions and 1391 deletions

View File

@ -1,14 +1,9 @@
.dockerignore
Dockerfile
.github
.git
.gitignore
.editorconfig
.idea
.vscode
test
temp
tmp
LICENSE
Makefile
error-pages
## Ignore everything
*
## Except the following files and directories
!/cmd
!/internal
!/templates
!/error-pages.yml
!/go.*

View File

@ -7,11 +7,8 @@ charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.{yml, yaml, sh, conf, json}]
indent_size = 2
trim_trailing_whitespace = true
[{Makefile, go.mod, *.go}]
indent_style = tab

View File

@ -8,7 +8,7 @@ on:
jobs:
docker-hub-description:
name: Docker Hub Description
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

View File

@ -7,7 +7,7 @@ on:
jobs:
purge-cdn-cache:
name: Purge jsDelivr CDN cache
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: gacts/purge-jsdelivr-cache@v1 # Action page: <https://github.com/gacts/purge-jsdelivr-cache>
with:
@ -19,31 +19,30 @@ jobs:
build:
name: Build for ${{ matrix.os }} (${{ matrix.arch }})
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [linux, darwin] # linux, freebsd, darwin, windows
arch: [amd64] # amd64, 386
steps:
- uses: actions/setup-go@v3
with: {go-version: 1.19}
- uses: actions/checkout@v3
- uses: gacts/github-slug@v1
id: slug
- uses: gacts/setup-go-with-cache@v1
with: {go-version-file: go.mod}
- {uses: gacts/github-slug@v1, id: slug}
- name: Generate builder values
id: values
run: echo "::set-output name=binary-name::error-pages-${{ matrix.os }}-${{ matrix.arch }}"
run: echo "binary-name=error-pages-${{ matrix.os }}-${{ matrix.arch }}" >> $GITHUB_OUTPUT
- name: Build application
env:
GOOS: ${{ matrix.os }}
GOARCH: ${{ matrix.arch }}
CGO_ENABLED: 0
LDFLAGS: -s -w -X github.com/tarampampam/error-pages/internal/version.version=${{ steps.slug.outputs.version }}
LDFLAGS: -s -w -X gh.tarampamp.am/error-pages/internal/version.version=${{ steps.slug.outputs.version }}
run: go build -trimpath -ldflags "$LDFLAGS" -o "./${{ steps.values.outputs.binary-name }}" ./cmd/error-pages/
- name: Upload binary file to release
@ -56,12 +55,11 @@ jobs:
docker-image:
name: Build docker image
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: gacts/github-slug@v1
id: slug
- {uses: gacts/github-slug@v1, id: slug}
- uses: docker/setup-qemu-action@v2 # Action page: <https://github.com/docker/setup-qemu-action>
@ -78,7 +76,7 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v3 # Action page: <https://github.com/docker/build-push-action>
- uses: docker/build-push-action@v4 # Action page: <https://github.com/docker/build-push-action>
with:
context: .
file: Dockerfile
@ -97,11 +95,10 @@ jobs:
demo:
name: Update the demonstration
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
needs: [docker-image]
steps:
- uses: gacts/github-slug@v1
id: slug
- {uses: gacts/github-slug@v1, id: slug}
- name: Take rendered templates from the built docker image
run: |

View File

@ -15,7 +15,7 @@ concurrency:
jobs: # Docs: <https://git.io/JvxXE>
gitleaks:
name: Gitleaks
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: {fetch-depth: 0}
@ -25,20 +25,24 @@ jobs: # Docs: <https://git.io/JvxXE>
golangci-lint:
name: Golang-CI (lint)
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: golangci/golangci-lint-action@v3 # Action page: <https://github.com/golangci/golangci-lint-action>
- uses: gacts/setup-go-with-cache@v1
with: {go-version: 1.19}
- uses: golangci/golangci-lint-action@v3
with: {skip-pkg-cache: true, skip-build-cache: true}
validate-config-file:
name: Validate config file
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with: {node-version: '16'}
- uses: gacts/setup-node-with-cache@v1
with: {node-version: 16}
- name: Install linter
run: npm install -g ajv-cli # Package page: <https://www.npmjs.com/package/ajv-cli>
@ -48,12 +52,12 @@ jobs: # Docs: <https://git.io/JvxXE>
lint-l10n:
name: Lint l10n file(s)
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with: {node-version: '16'}
- uses: gacts/setup-node-with-cache@v1
with: {node-version: 16}
- name: Install eslint
run: npm install -g eslint@v8 # Package page: <https://www.npmjs.com/package/eslint>
@ -64,24 +68,13 @@ jobs: # Docs: <https://git.io/JvxXE>
go-test:
name: Unit tests
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with: {go-version: 1.19}
- uses: actions/checkout@v3
with: {fetch-depth: 2} # Fixes codecov error 'Issue detecting commit SHA'
- name: Go modules Cache # Docs: <https://git.io/JfAKn#go---modules>
uses: actions/cache@v3
id: go-cache
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- if: steps.go-cache.outputs.cache-hit != 'true'
run: go mod download
- uses: gacts/setup-go-with-cache@v1
with: {go-version-file: go.mod}
- name: Run Unit tests
run: go test -race -covermode=atomic -coverprofile /tmp/coverage.txt ./...
@ -94,7 +87,7 @@ jobs: # Docs: <https://git.io/JvxXE>
build:
name: Build for ${{ matrix.os }} (${{ matrix.arch }})
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
@ -102,34 +95,24 @@ jobs: # Docs: <https://git.io/JvxXE>
arch: [amd64] # amd64, 386
needs: [golangci-lint, go-test, validate-config-file]
steps:
- uses: actions/setup-go@v3
with: {go-version: 1.19}
- uses: actions/checkout@v3
- uses: gacts/github-slug@v1
id: slug
- uses: gacts/setup-go-with-cache@v1
with: {go-version-file: go.mod}
- name: Go modules Cache # Docs: <https://git.io/JfAKn#go---modules>
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- run: go mod download
- {uses: gacts/github-slug@v1, id: slug}
- name: Build application
env:
GOOS: ${{ matrix.os }}
GOARCH: ${{ matrix.arch }}
CGO_ENABLED: 0
LDFLAGS: -s -w -X github.com/tarampampam/error-pages/internal/version.version=${{ steps.slug.outputs.branch-name-slug }}@${{ steps.slug.outputs.commit-hash-short }}
LDFLAGS: -s -w -X gh.tarampamp.am/error-pages/internal/version.version=${{ steps.slug.outputs.branch-name-slug }}@${{ steps.slug.outputs.commit-hash-short }}
run: go build -trimpath -ldflags "$LDFLAGS" -o ./error-pages ./cmd/error-pages/
- name: Try to execute
if: matrix.os == 'linux'
run: ./error-pages version && ./error-pages -h
run: ./error-pages --version && ./error-pages -h
- uses: actions/upload-artifact@v3
with:
@ -140,7 +123,7 @@ jobs: # Docs: <https://git.io/JvxXE>
generate:
name: Run templates generator
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v3
@ -155,7 +138,7 @@ jobs: # Docs: <https://git.io/JvxXE>
run: mv ./error-pages ./../error-pages && chmod +x ./../error-pages
- name: Run generator
run: ./error-pages build ./out --verbose --index
run: ./error-pages --verbose build --index ./out
- name: Test files creation
run: |
@ -171,18 +154,18 @@ jobs: # Docs: <https://git.io/JvxXE>
test -f ./out/app-down/404.html
test -f ./out/connection/404.html
test -f ./out/matrix/404.html
test -f ./out/orient/404.html
docker-image:
name: Build docker image
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
needs: [golangci-lint, go-test, validate-config-file]
steps:
- uses: actions/checkout@v3
- uses: gacts/github-slug@v1
id: slug
- {uses: gacts/github-slug@v1, id: slug}
- uses: docker/build-push-action@v3 # Action page: <https://github.com/docker/build-push-action>
- uses: docker/build-push-action@v4 # Action page: <https://github.com/docker/build-push-action>
with:
context: .
file: Dockerfile
@ -200,7 +183,7 @@ jobs: # Docs: <https://git.io/JvxXE>
scan-docker-image:
name: Scan the docker image
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
needs: [docker-image]
steps:
- uses: actions/checkout@v3 # is needed for `upload-sarif` action
@ -210,7 +193,7 @@ jobs: # Docs: <https://git.io/JvxXE>
name: docker-image
path: .artifact
- uses: aquasecurity/trivy-action@0.8.0 # action page: <https://github.com/aquasecurity/trivy-action>
- uses: aquasecurity/trivy-action@0.9.2 # action page: <https://github.com/aquasecurity/trivy-action>
with:
input: .artifact/docker-image.tar
format: sarif
@ -225,7 +208,7 @@ jobs: # Docs: <https://git.io/JvxXE>
poke-docker-image:
name: Run the docker image
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
needs: [docker-image]
timeout-minutes: 2
steps:
@ -247,7 +230,7 @@ jobs: # Docs: <https://git.io/JvxXE>
- name: Wait for container "healthy" state
run: until [[ "`docker inspect -f {{.State.Health.Status}} app`" == "healthy" ]]; do echo "wait 1 sec.."; sleep 1; done
- run: hurl --color --test --fail-at-end --variable host=127.0.0.1 --variable port=8080 --summary ./test/hurl/*.hurl
- run: hurl --color --test --fail-at-end --variable host=127.0.0.1 --variable port=8080 ./test/hurl/*.hurl
- name: Stop the container
if: always()

View File

@ -4,6 +4,46 @@ All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver].
## v2.23.0
### Added
- Template `orient` [#190]
[#190]:https://github.com/tarampampam/error-pages/pull/190
## v2.22.0
### Changed
- Non-existing pages now return styled `404` status page (with `404` status code) [#188]
[#188]:https://github.com/tarampampam/error-pages/issues/188
## v2.21.0
### Changed
- Go updated from `1.19` up to `1.20`
- Go dependencies updated
- Module name changed from `github.com/tarampampam/error-pages` to `gh.tarampamp.am/error-pages`
## v2.20.0
### Changed
- `version` subcommand replaced by `--version` flag [#163]
- `--config-file` flag is not global anymore (use `error-pages (serve|build) --config-file ...` instead of `error-pages --config-file ... (serve|build) ...`) [#163]
- Flags `--verbose`, `--debug` and `--log-json` are deprecated, use `--log-level` and `--log-format` instead [#163]
### Added
- Possibility to use custom env variables in templates [#164], [#165]
[#164]:https://github.com/tarampampam/error-pages/issues/164
[#165]:https://github.com/tarampampam/error-pages/pull/165
[#163]:https://github.com/tarampampam/error-pages/pull/163
## v2.19.0
### Changed

View File

@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1.2
# Image page: <https://hub.docker.com/_/golang>
FROM golang:1.19-alpine as builder
FROM golang:1.20-alpine as builder
# can be passed with any prefix (like `v1.2.3@GITHASH`), e.g.: `docker build --build-arg "APP_VERSION=v1.2.3@GITHASH" .`
ARG APP_VERSION="undefined@docker"
@ -11,12 +11,12 @@ WORKDIR /src
COPY . .
# arguments to pass on each go tool link invocation
ENV LDFLAGS="-s -w -X github.com/tarampampam/error-pages/internal/version.version=$APP_VERSION"
ENV LDFLAGS="-s -w -X gh.tarampamp.am/error-pages/internal/version.version=$APP_VERSION"
RUN set -x \
&& go version \
&& CGO_ENABLED=0 go build -trimpath -ldflags "$LDFLAGS" -o ./error-pages ./cmd/error-pages/ \
&& ./error-pages version \
&& ./error-pages --version \
&& ./error-pages -h
WORKDIR /tmp/rootfs
@ -38,7 +38,7 @@ WORKDIR /tmp/rootfs/opt
# generate static error pages (for usage inside another docker images, for example)
RUN set -x \
&& ./../bin/error-pages --config-file ./error-pages.yml build ./html --verbose --index \
&& ./../bin/error-pages --verbose build --config-file ./error-pages.yml --index ./html \
&& ls -l ./html
# use empty filesystem
@ -72,8 +72,8 @@ ENV LISTEN_PORT="8080" \
DISABLE_L10N="false"
# Docs: <https://docs.docker.com/engine/reference/builder/#healthcheck>
HEALTHCHECK --interval=7s --timeout=2s CMD ["/bin/error-pages", "healthcheck", "--log-json"]
HEALTHCHECK --interval=7s --timeout=2s CMD ["/bin/error-pages", "--log-json", "healthcheck"]
ENTRYPOINT ["/bin/error-pages"]
CMD ["serve", "--log-json"]
CMD ["--log-json", "serve"]

View File

@ -3,7 +3,7 @@
# Makefile readme (en): <https://www.gnu.org/software/make/manual/html_node/index.html#SEC_Contents>
SHELL = /bin/sh
LDFLAGS = "-s -w -X github.com/tarampampam/error-pages/internal/version.version=$(shell git rev-parse HEAD)"
LDFLAGS = "-s -w -X gh.tarampamp.am/error-pages/internal/version.version=$(shell git rev-parse HEAD)"
DC_RUN_ARGS = --rm --user "$(shell id -u):$(shell id -g)"
APP_NAME = $(notdir $(CURDIR))
@ -43,7 +43,7 @@ gotest: ## Run app tests
docker-compose run $(DC_RUN_ARGS) --no-deps app go test -v -race -timeout 10s ./...
int-test: ## Run integration tests (docs: https://hurl.dev/docs/man-page.html#options)
docker-compose run --rm hurl --color --test --fail-at-end --variable host=web --variable port=8080 --summary ./test/hurl/*.hurl
docker-compose run --rm hurl --color --test --fail-at-end --variable host=web --variable port=8080 ./test/hurl/*.hurl
test: lint gotest int-test ## Run app tests and linters

View File

@ -5,8 +5,8 @@
<p align="center">
<a href="#"><img src="https://img.shields.io/github/go-mod/go-version/tarampampam/error-pages?longCache=true&label=&logo=go&logoColor=white&style=flat-square" alt="" /></a>
<a href="https://codecov.io/gh/tarampampam/error-pages"><img src="https://img.shields.io/codecov/c/github/tarampampam/error-pages/master.svg?maxAge=30&label=&logo=codecov&logoColor=white&style=flat-square" alt="" /></a>
<a href="https://github.com/tarampampam/error-pages/actions"><img src="https://img.shields.io/github/workflow/status/tarampampam/error-pages/tests?maxAge=30&label=tests&logo=github&style=flat-square" alt="" /></a>
<a href="https://github.com/tarampampam/error-pages/actions"><img src="https://img.shields.io/github/workflow/status/tarampampam/error-pages/release?maxAge=30&label=release&logo=github&style=flat-square" alt="" /></a>
<a href="https://github.com/tarampampam/error-pages/actions"><img src="https://img.shields.io/github/actions/workflow/status/tarampampam/error-pages/tests.yml?branch=master&maxAge=30&label=tests&logo=github&style=flat-square" alt="" /></a>
<a href="https://github.com/tarampampam/error-pages/actions"><img src="https://img.shields.io/github/actions/workflow/status/tarampampam/error-pages/release.yml?maxAge=30&label=release&logo=github&style=flat-square" alt="" /></a>
<a href="https://hub.docker.com/r/tarampampam/error-pages"><img src="https://img.shields.io/docker/pulls/tarampampam/error-pages.svg?maxAge=30&label=pulls&logo=docker&logoColor=white&style=flat-square" alt="" /></a>
<a href="https://hub.docker.com/r/tarampampam/error-pages"><img src="https://img.shields.io/docker/image-size/tarampampam/error-pages/latest?maxAge=30&label=size&logo=docker&logoColor=white&style=flat-square" alt="" /></a>
<a href="https://github.com/tarampampam/error-pages/blob/master/LICENSE"><img src="https://img.shields.io/github/license/tarampampam/error-pages.svg?maxAge=30&style=flat-square" alt="" /></a>
@ -40,8 +40,6 @@ One day you may want to replace the standard error pages of your HTTP server wit
Download the latest binary file for your os/arch from the [releases page][releases] or use our docker image:
[![image stats](https://dockeri.co/image/tarampampam/error-pages)][docker-hub-tags]
| Registry | Image |
|-----------------------------------|-----------------------------------|
| [Docker Hub][docker-hub] | `tarampampam/error-pages` |
@ -125,6 +123,7 @@ Transfer/sec: 140.23MB
| `app-down` | [![app-down][app-down-screen]][app-down-link] |
| `connection` | [![connection][connection-screen]][connection-link] |
| `matrix` | [![matrix][matrix-screen]][matrix-link] |
| `orient` | [![orient][orient-screen]][orient-link] |
> Note: `noise` template highly uses the CPU, be careful
@ -150,6 +149,8 @@ Transfer/sec: 140.23MB
[connection-link]:https://tarampampam.github.io/error-pages/connection/404.html
[matrix-screen]:https://hsto.org/webt/ng/tf/oi/ngtfoiolvmq6hf15kimcxmhprhk.gif
[matrix-link]:https://tarampampam.github.io/error-pages/matrix/404.html
[orient-screen]:https://hsto.org/webt/pz/eu/v_/pzeuv_lyeqr0xpusa4zfrtgk7sa.png
[orient-link]:https://tarampampam.github.io/error-pages/orient/404.html
## 🦾 Contributors

View File

@ -5,9 +5,14 @@ import (
"path/filepath"
"github.com/fatih/color"
"github.com/tarampampam/error-pages/internal/cli"
"go.uber.org/automaxprocs/maxprocs"
"gh.tarampamp.am/error-pages/internal/cli"
)
// set GOMAXPROCS to match Linux container CPU quota.
var _, _ = maxprocs.Set(maxprocs.Min(1), maxprocs.Logger(func(_ string, _ ...any) {}))
// exitFn is a function for application exiting.
var exitFn = os.Exit //nolint:gochecknoglobals
@ -17,9 +22,7 @@ func main() { exitFn(run()) }
// run this CLI application.
// Exit codes documentation: <https://tldp.org/LDP/abs/html/exitcodes.html>
func run() int {
cmd := cli.NewCommand(filepath.Base(os.Args[0]))
if err := cmd.Execute(); err != nil {
if err := (cli.NewApp(filepath.Base(os.Args[0]))).Run(os.Args); err != nil {
_, _ = color.New(color.FgHiRed, color.Bold).Fprintln(os.Stderr, err.Error())
return 1

View File

@ -6,36 +6,16 @@ import (
"github.com/kami-zh/go-capturer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Main(t *testing.T) {
func Test_MainHelp(t *testing.T) {
os.Args = []string{"", "--help"}
exitFn = func(code int) { assert.Equal(t, 0, code) }
exitFn = func(code int) { require.Equal(t, 0, code) }
output := capturer.CaptureStdout(main)
assert.Contains(t, output, "Usage:")
assert.Contains(t, output, "Available Commands:")
assert.Contains(t, output, "Flags:")
}
func Test_MainWithoutCommands(t *testing.T) {
os.Args = []string{""}
exitFn = func(code int) { assert.Equal(t, 0, code) }
output := capturer.CaptureStdout(main)
assert.Contains(t, output, "Usage:")
assert.Contains(t, output, "Available Commands:")
assert.Contains(t, output, "Flags:")
}
func Test_MainUnknownSubcommand(t *testing.T) {
os.Args = []string{"", "foobar"}
exitFn = func(code int) { assert.Equal(t, 1, code) }
output := capturer.CaptureStderr(main)
assert.Contains(t, output, "unknown command")
assert.Contains(t, output, "foobar")
assert.Contains(t, output, "USAGE:")
assert.Contains(t, output, "COMMANDS:")
assert.Contains(t, output, "GLOBAL OPTIONS:")
}

View File

@ -4,11 +4,12 @@ version: '3.8'
volumes:
tmp-data: {}
golint-go: {}
golint-cache: {}
services:
app: &app-service
image: golang:1.19-buster # Image page: <https://hub.docker.com/_/golang>
image: golang:1.20-buster # Image page: <https://hub.docker.com/_/golang>
working_dir: /src
environment:
HOME: /tmp
@ -18,32 +19,35 @@ services:
- /etc/group:/etc/group:ro
- .:/src:rw
- tmp-data:/tmp:rw
security_opt: [no-new-privileges:true]
web:
<<: *app-service
ports:
- "8080:8080/tcp" # Open <http://127.0.0.1:8080>
command: sh -c "go build -o /tmp/app ./cmd/error-pages && /tmp/app serve"
command: sh -c "go build -buildvcs=false -o /tmp/app ./cmd/error-pages && /tmp/app serve --show-details --proxy-headers=X-Foo,Bar,Baz_blah"
healthcheck:
test: ['CMD', '/tmp/app', 'healthcheck', '--log-json']
test: ['CMD', '/tmp/app', '--log-json', 'healthcheck']
interval: 4s
timeout: 1s
start_period: 5s
retries: 5
golint:
image: golangci/golangci-lint:v1.50-alpine # Image page: <https://hub.docker.com/r/golangci/golangci-lint>
image: golangci/golangci-lint:v1.52-alpine # Image page: <https://hub.docker.com/r/golangci/golangci-lint>
environment:
GOLANGCI_LINT_CACHE: /tmp/golint # <https://github.com/golangci/golangci-lint/blob/v1.42.0/internal/cache/default.go#L68>
volumes:
- .:/src:ro
- golint-go:/go:rw # go dependencies will be downloaded on each run without this
- golint-cache:/tmp/golint:rw
- .:/src:ro
working_dir: /src
command: /bin/true
security_opt: [no-new-privileges:true]
hurl:
image: orangeopensource/hurl:1.7.0
image: orangeopensource/hurl:1.8.0
volumes:
- .:/src:ro
working_dir: /src
depends_on:
web: {condition: service_healthy}
security_opt: [no-new-privileges:true]

View File

@ -15,6 +15,7 @@ templates:
- path: ./templates/app-down.html
- path: ./templates/connection.html
- path: ./templates/matrix.html
- path: ./templates/orient.html
formats:
json:

55
go.mod
View File

@ -1,41 +1,48 @@
module github.com/tarampampam/error-pages
module gh.tarampamp.am/error-pages
go 1.19
go 1.20
require (
github.com/a8m/envsubst v1.3.0
github.com/fasthttp/router v1.4.13
github.com/fatih/color v1.13.0
github.com/a8m/envsubst v1.4.2
github.com/fasthttp/router v1.4.18
github.com/fatih/color v1.15.0
github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.13.0
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/client_model v0.3.0
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.1
github.com/valyala/fasthttp v1.41.0
go.uber.org/zap v1.23.0
github.com/stretchr/testify v1.8.2
github.com/urfave/cli/v2 v2.25.1
github.com/valyala/fasthttp v1.45.0
go.uber.org/automaxprocs v1.5.2
go.uber.org/goleak v1.2.1
go.uber.org/zap v1.24.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.3 // indirect
github.com/kr/text v0.2.0 // 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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect
github.com/prometheus/common v0.40.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/sys v0.6.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)

553
go.sum
View File

@ -1,540 +1,105 @@
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.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/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=
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/a8m/envsubst v1.3.0 h1:GmXKmVssap0YtlU3E230W98RWtWCyIZzjtf1apWWyAg=
github.com/a8m/envsubst v1.3.0/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=
github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fasthttp/router v1.4.13 h1:42M7+7tNO6clb5seb4HhXlBIX1lnNv8DLhiT6jUv75A=
github.com/fasthttp/router v1.4.13/go.mod h1:mVhHMaSQA2Hi1HeuL/ZMuZpsZWk5bya75EpaDr3fO7E=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
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/fasthttp/router v1.4.18 h1:elMnlFq527oZd8MHsuUpO6uLDup1exv8rXPfIjClDHk=
github.com/fasthttp/router v1.4.18/go.mod h1:ZmC20Mn0VgCBbUWFDmnYzFbQYRfdGeKgpkBy0+JioKA=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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/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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.3.0/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/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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d h1:cVtBfNW5XTHiKQe7jDaDBSh/EVM4XLPutLAGboIXuM0=
github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
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.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/prometheus/common v0.40.0 h1:Afz7EVRqGg2Mqqf4JuF9vdvp1pi220m55Pi9T2JnO4Q=
github.com/prometheus/common v0.40.0/go.mod h1:L65ZJPSmfn/UBWLQIHV7dBrKFidB/wPlF1y5TlSt9OE=
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/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/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo=
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
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/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.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.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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli/v2 v2.25.1 h1:zw8dSP7ghX0Gmm8vugrs6q9Ku0wzweqPyshy+syu9Gw=
github.com/urfave/cli/v2 v2.25.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY=
github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
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=
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.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-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-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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/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/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-20181114220301-adae6a3d119a/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/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-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-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
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=
github.com/valyala/fasthttp v1.45.0 h1:zPkkzpIn8tdHZUrVa6PzYd0i5verqiPSkgTd3bSUcpA=
github.com/valyala/fasthttp v1.45.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20201207232520-09787c993a3a/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-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-20190422165155-953cdadca894/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-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
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/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/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/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/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/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-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/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/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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=

View File

@ -8,7 +8,8 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/breaker"
"gh.tarampamp.am/error-pages/internal/breaker"
)
func TestNewOSSignals(t *testing.T) {

View File

@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/checkers"
"gh.tarampamp.am/error-pages/internal/checkers"
)
type httpClientFunc func(*http.Request) (*http.Response, error)

View File

@ -4,7 +4,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/checkers"
"gh.tarampamp.am/error-pages/internal/checkers"
)
func TestLiveChecker_Check(t *testing.T) {

103
internal/cli/app.go Normal file
View File

@ -0,0 +1,103 @@
package cli
import (
"context"
"fmt"
"runtime"
"strings"
"github.com/urfave/cli/v2"
"gh.tarampamp.am/error-pages/internal/checkers"
"gh.tarampamp.am/error-pages/internal/cli/build"
"gh.tarampamp.am/error-pages/internal/cli/healthcheck"
"gh.tarampamp.am/error-pages/internal/cli/serve"
"gh.tarampamp.am/error-pages/internal/env"
"gh.tarampamp.am/error-pages/internal/logger"
"gh.tarampamp.am/error-pages/internal/version"
)
// NewApp creates new console application.
func NewApp(appName string) *cli.App { //nolint:funlen
const (
logLevelFlagName = "log-level"
logFormatFlagName = "log-format"
verboseFlagName = "verbose"
debugFlagName = "debug"
logJSONFlagName = "log-json"
defaultLogLevel = logger.InfoLevel
defaultLogFormat = logger.ConsoleFormat
)
// create "default" logger (will be overwritten later with customized)
var log, _ = logger.New(defaultLogLevel, defaultLogFormat) // error will never occurs
return &cli.App{
Usage: appName,
Before: func(c *cli.Context) (err error) {
_ = log.Sync() // sync previous logger instance
var logLevel, logFormat = defaultLogLevel, defaultLogFormat //nolint:ineffassign
if c.Bool(verboseFlagName) || c.Bool(debugFlagName) {
logLevel = logger.DebugLevel
} else {
// parse logging level
if logLevel, err = logger.ParseLevel(c.String(logLevelFlagName)); err != nil {
return err
}
}
if c.Bool(logJSONFlagName) {
logFormat = logger.JSONFormat
} else {
// parse logging format
if logFormat, err = logger.ParseFormat(c.String(logFormatFlagName)); err != nil {
return err
}
}
configured, err := logger.New(logLevel, logFormat) // create new logger instance
if err != nil {
return err
}
*log = *configured // replace "default" logger with customized
return nil
},
Commands: []*cli.Command{
healthcheck.NewCommand(checkers.NewHealthChecker(context.TODO())),
build.NewCommand(log),
serve.NewCommand(log),
},
Version: fmt.Sprintf("%s (%s)", version.Version(), runtime.Version()),
Flags: []cli.Flag{ // global flags
&cli.BoolFlag{ // kept for backward compatibility
Name: verboseFlagName,
Usage: "verbose output (DEPRECATED FLAG)",
},
&cli.BoolFlag{ // kept for backward compatibility
Name: debugFlagName,
Usage: "debug output (DEPRECATED FLAG)",
},
&cli.BoolFlag{ // kept for backward compatibility
Name: logJSONFlagName,
Usage: "logs in JSON format (DEPRECATED FLAG)",
},
&cli.StringFlag{
Name: logLevelFlagName,
Value: defaultLogLevel.String(),
Usage: "logging level (`" + strings.Join(logger.LevelStrings(), "/") + "`)",
EnvVars: []string{env.LogLevel.String()},
},
&cli.StringFlag{
Name: logFormatFlagName,
Value: defaultLogFormat.String(),
Usage: "logging format (`" + strings.Join(logger.FormatStrings(), "/") + "`)",
EnvVars: []string{env.LogFormat.String()},
},
},
}
}

19
internal/cli/app_test.go Normal file
View File

@ -0,0 +1,19 @@
package cli_test
import (
"testing"
"github.com/stretchr/testify/assert"
"gh.tarampamp.am/error-pages/internal/cli"
)
func TestNewCommand(t *testing.T) {
t.Parallel()
app := cli.NewApp("app")
assert.NotEmpty(t, app.Flags)
assert.NoError(t, app.Run([]string{"", "--log-level", "debug", "--log-format", "json"}))
}

View File

@ -5,60 +5,59 @@ import (
"path"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/tarampampam/error-pages/internal/config"
"github.com/tarampampam/error-pages/internal/tpl"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"gh.tarampamp.am/error-pages/internal/cli/shared"
"gh.tarampamp.am/error-pages/internal/config"
"gh.tarampamp.am/error-pages/internal/tpl"
)
type command struct {
c *cli.Command
}
// NewCommand creates `build` command.
func NewCommand(log *zap.Logger, configFile *string) *cobra.Command {
var (
generateIndex bool
disableL10n bool
cfg *config.Config
func NewCommand(log *zap.Logger) *cli.Command {
var cmd = command{}
const (
generateIndexFlagName = "index"
disableL10nFlagName = "disable-l10n"
)
cmd := &cobra.Command{
Use: "build <output-directory>",
Aliases: []string{"b"},
Short: "Build the error pages",
Args: cobra.ExactArgs(1),
PreRunE: func(*cobra.Command, []string) (err error) {
if configFile == nil {
return errors.New("path to the config file is required for this command")
cmd.c = &cli.Command{
Name: "build",
Aliases: []string{"b"},
Usage: "build <output-directory>",
Description: "Build the error pages",
Action: func(c *cli.Context) error {
cfg, cfgErr := config.FromYamlFile(c.String(shared.ConfigFileFlag.Name))
if cfgErr != nil {
return cfgErr
}
if cfg, err = config.FromYamlFile(*configFile); err != nil {
return err
}
return
},
RunE: func(_ *cobra.Command, args []string) error {
if len(args) != 1 {
if c.Args().Len() != 1 {
return errors.New("wrong arguments count")
}
return run(log, cfg, args[0], generateIndex, disableL10n)
return cmd.Run(log, cfg, c.Args().First(), c.Bool(generateIndexFlagName), c.Bool(disableL10nFlagName))
},
Flags: []cli.Flag{ // global flags
&cli.BoolFlag{
Name: generateIndexFlagName,
Aliases: []string{"i"},
Usage: "generate index page",
},
&cli.BoolFlag{
Name: disableL10nFlagName,
Usage: "disable error pages localization",
},
shared.ConfigFileFlag,
},
}
cmd.Flags().BoolVarP(
&generateIndex,
"index", "i",
false,
"generate index page",
)
cmd.Flags().BoolVarP(
&disableL10n,
"disable-l10n", "",
false,
"disable error pages localization",
)
return cmd
return cmd.c
}
const (
@ -68,14 +67,14 @@ const (
outDirPerm = os.FileMode(0775)
)
func run(log *zap.Logger, cfg *config.Config, outDirectoryPath string, generateIndex, disableL10n bool) error { //nolint:funlen,lll
func (cmd *command) Run(log *zap.Logger, cfg *config.Config, outDirectoryPath string, generateIndex, disableL10n bool) error { //nolint:funlen,lll
if len(cfg.Templates) == 0 {
return errors.New("no loaded templates")
}
log.Info("output directory preparing", zap.String("path", outDirectoryPath))
if err := createDirectory(outDirectoryPath, outDirPerm); err != nil {
if err := cmd.createDirectory(outDirectoryPath, outDirPerm); err != nil {
return errors.Wrap(err, "cannot prepare output directory")
}
@ -86,7 +85,7 @@ func run(log *zap.Logger, cfg *config.Config, outDirectoryPath string, generateI
log.Debug("template processing", zap.String("name", template.Name()))
for _, page := range cfg.Pages {
if err := createDirectory(path.Join(outDirectoryPath, template.Name()), outDirPerm); err != nil {
if err := cmd.createDirectory(path.Join(outDirectoryPath, template.Name()), outDirPerm); err != nil {
return err
}
@ -138,7 +137,7 @@ func run(log *zap.Logger, cfg *config.Config, outDirectoryPath string, generateI
return nil
}
func createDirectory(path string, perm os.FileMode) error {
func (cmd *command) createDirectory(path string, perm os.FileMode) error {
stat, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {

View File

@ -1,7 +1,26 @@
package build_test
import "testing"
import (
"flag"
"testing"
func TestNothing(t *testing.T) {
t.Skip("tests for this package have not been implemented yet")
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
"go.uber.org/goleak"
"go.uber.org/zap"
"gh.tarampamp.am/error-pages/internal/cli/build"
)
func TestNewCommand(t *testing.T) {
defer goleak.VerifyNone(t)
cmd := build.NewCommand(zap.NewNop())
assert.NotEmpty(t, cmd.Flags)
assert.Error(t, cmd.Run(
cli.NewContext(cli.NewApp(), &flag.FlagSet{}, nil),
"",
), "should fail because of missing external services")
}

View File

@ -34,7 +34,7 @@
<footer class="footer">
<div class="container text-center text-muted mt-3 mb-3">
For online documentation and support please refer to the
<a href="https://github.com/tarampampam/error-pages">project repository</a>.
<a href="https://gh.tarampamp.am/error-pages">project repository</a>.
</div>
</footer>
</body>

View File

@ -2,57 +2,35 @@
package healthcheck
import (
"fmt"
"strconv"
"errors"
"math"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/urfave/cli/v2"
"github.com/tarampampam/error-pages/internal/env"
"gh.tarampamp.am/error-pages/internal/cli/shared"
)
type checker interface {
Check(port uint16) error
}
const portFlagName = "port"
// NewCommand creates `healthcheck` command.
func NewCommand(checker checker) *cobra.Command {
var port uint16
cmd := &cobra.Command{
Use: "healthcheck",
func NewCommand(checker checker) *cli.Command {
return &cli.Command{
Name: "healthcheck",
Aliases: []string{"chk", "health", "check"},
Short: "Health checker for the HTTP server. Use case - docker healthcheck",
PreRunE: func(c *cobra.Command, _ []string) (lastErr error) {
c.Flags().VisitAll(func(flag *pflag.Flag) {
// flag was NOT defined using CLI (flags should have maximal priority)
if !flag.Changed && flag.Name == portFlagName {
if envPort, exists := env.ListenPort.Lookup(); exists && envPort != "" {
if p, err := strconv.ParseUint(envPort, 10, 16); err == nil {
port = uint16(p)
} else {
lastErr = fmt.Errorf("wrong TCP port environment variable [%s] value", envPort)
}
}
}
})
Usage: "Health checker for the HTTP server. Use case - docker healthcheck",
Action: func(c *cli.Context) error {
var port = c.Uint(shared.ListenPortFlag.Name)
return lastErr
if port <= 0 || port > math.MaxUint16 {
return errors.New("port value out of range")
}
return checker.Check(uint16(port))
},
RunE: func(*cobra.Command, []string) error {
return checker.Check(port)
Flags: []cli.Flag{
shared.ListenPortFlag,
},
}
cmd.Flags().Uint16VarP(
&port,
portFlagName,
"p",
8080, //nolint:gomnd // must be same as default serve `--port` flag value
fmt.Sprintf("TCP port number [$%s]", env.ListenPort),
)
return cmd
}

View File

@ -2,13 +2,13 @@ package healthcheck_test
import (
"errors"
"os"
"flag"
"testing"
"github.com/kami-zh/go-capturer"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/cli/healthcheck"
"github.com/urfave/cli/v2"
"gh.tarampamp.am/error-pages/internal/cli/healthcheck"
)
type fakeChecker struct{ err error }
@ -18,77 +18,30 @@ func (c *fakeChecker) Check(port uint16) error { return c.err }
func TestProperties(t *testing.T) {
cmd := healthcheck.NewCommand(&fakeChecker{err: nil})
assert.Equal(t, "healthcheck", cmd.Use)
assert.Equal(t, "healthcheck", cmd.Name)
assert.ElementsMatch(t, []string{"chk", "health", "check"}, cmd.Aliases)
assert.NotNil(t, cmd.RunE)
assert.NotNil(t, cmd.Action)
}
func TestCommandRun(t *testing.T) {
cmd := healthcheck.NewCommand(&fakeChecker{err: nil})
cmd.SetArgs([]string{})
output := capturer.CaptureOutput(func() {
assert.NoError(t, cmd.Execute())
})
assert.Empty(t, output)
assert.NoError(t, cmd.Run(cli.NewContext(cli.NewApp(), &flag.FlagSet{}, nil)))
}
func TestCommandRunFailed(t *testing.T) {
cmd := healthcheck.NewCommand(&fakeChecker{err: errors.New("foo err")})
cmd.SetArgs([]string{})
output := capturer.CaptureStderr(func() {
assert.Error(t, cmd.Execute())
})
assert.Contains(t, output, "foo err")
assert.ErrorContains(t, cmd.Run(cli.NewContext(cli.NewApp(), &flag.FlagSet{}, nil)), "foo err")
}
func TestPortFlagWrongArgument(t *testing.T) {
cmd := healthcheck.NewCommand(&fakeChecker{err: nil})
cmd.SetArgs([]string{"-p", "65536"}) // 65535 is max
var executed bool
err := cmd.Run(
cli.NewContext(cli.NewApp(), &flag.FlagSet{}, nil),
"", "-p", "65536",
)
cmd.RunE = func(*cobra.Command, []string) error {
executed = true
return nil
}
output := capturer.CaptureStderr(func() {
assert.Error(t, cmd.Execute())
})
assert.Contains(t, output, "invalid argument")
assert.Contains(t, output, "65536")
assert.Contains(t, output, "value out of range")
assert.False(t, executed)
}
func TestPortFlagWrongEnvValue(t *testing.T) {
cmd := healthcheck.NewCommand(&fakeChecker{err: nil})
cmd.SetArgs([]string{})
assert.NoError(t, os.Setenv("LISTEN_PORT", "65536")) // 65535 is max
defer func() { assert.NoError(t, os.Unsetenv("LISTEN_PORT")) }()
var executed bool
cmd.RunE = func(*cobra.Command, []string) error {
executed = true
return nil
}
output := capturer.CaptureStderr(func() {
assert.Error(t, cmd.Execute())
})
assert.Contains(t, output, "wrong TCP port")
assert.Contains(t, output, "environment variable")
assert.Contains(t, output, "65536")
assert.False(t, executed)
assert.ErrorContains(t, err, "port value out of range")
}

View File

@ -1,93 +0,0 @@
// Package cli contains CLI command handlers.
package cli
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/tarampampam/error-pages/internal/checkers"
buildCmd "github.com/tarampampam/error-pages/internal/cli/build"
healthcheckCmd "github.com/tarampampam/error-pages/internal/cli/healthcheck"
serveCmd "github.com/tarampampam/error-pages/internal/cli/serve"
versionCmd "github.com/tarampampam/error-pages/internal/cli/version"
"github.com/tarampampam/error-pages/internal/env"
"github.com/tarampampam/error-pages/internal/logger"
"github.com/tarampampam/error-pages/internal/version"
)
const configFileFlagName = "config-file"
// NewCommand creates root command.
func NewCommand(appName string) *cobra.Command { //nolint:funlen
var (
configFile string
verbose bool
debug bool
logJSON bool
)
ctx := context.Background() // main CLI context
// create "default" logger (will be overwritten later with customized)
log, err := logger.New(false, false, false)
if err != nil {
panic(err)
}
cmd := &cobra.Command{
Use: appName,
PersistentPreRunE: func(c *cobra.Command, _ []string) error {
_ = log.Sync() // sync previous logger instance
customizedLog, e := logger.New(verbose, debug, logJSON)
if e != nil {
return e
}
*log = *customizedLog // override "default" logger with customized
c.Flags().VisitAll(func(flag *pflag.Flag) {
// flag was NOT defined using CLI (flags should have maximal priority)
if !flag.Changed && flag.Name == configFileFlagName {
if envConfigFile, exists := env.ConfigFilePath.Lookup(); exists && envConfigFile != "" {
configFile = envConfigFile
}
}
})
return nil
},
PersistentPostRun: func(*cobra.Command, []string) {
// error ignoring reasons:
// - <https://github.com/uber-go/zap/issues/772>
// - <https://github.com/uber-go/zap/issues/328>
_ = log.Sync()
},
SilenceErrors: true,
SilenceUsage: true,
CompletionOptions: cobra.CompletionOptions{
DisableDefaultCmd: true,
},
}
cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
cmd.PersistentFlags().BoolVarP(&debug, "debug", "", false, "debug output")
cmd.PersistentFlags().BoolVarP(&logJSON, "log-json", "", false, "logs in JSON format")
cmd.PersistentFlags().StringVarP(
&configFile,
configFileFlagName, "c",
"./error-pages.yml",
fmt.Sprintf("path to the config file [$%s]", env.ConfigFilePath),
)
cmd.AddCommand(
versionCmd.NewCommand(version.Version()),
healthcheckCmd.NewCommand(checkers.NewHealthChecker(ctx)),
buildCmd.NewCommand(log, &configFile),
serveCmd.NewCommand(ctx, log, &configFile),
)
return cmd
}

View File

@ -1,84 +0,0 @@
package cli_test
import (
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/cli"
)
func TestSubcommands(t *testing.T) {
cmd := cli.NewCommand("unit test")
cases := []struct {
giveName string
}{
{giveName: "build"},
{giveName: "version"},
{giveName: "healthcheck"},
{giveName: "serve"},
}
// get all existing subcommands and put into the map
subcommands := make(map[string]*cobra.Command)
for _, sub := range cmd.Commands() {
subcommands[sub.Name()] = sub
}
for _, tt := range cases {
tt := tt
t.Run(tt.giveName, func(t *testing.T) {
if _, exists := subcommands[tt.giveName]; !exists {
assert.Failf(t, "command not found", "command [%s] was not found", tt.giveName)
}
})
}
}
func TestFlags(t *testing.T) {
cmd := cli.NewCommand("unit test")
cases := []struct {
giveName string
wantShorthand string
wantDefault string
}{
{giveName: "verbose", wantShorthand: "v", wantDefault: "false"},
{giveName: "debug", wantShorthand: "", wantDefault: "false"},
{giveName: "log-json", wantShorthand: "", wantDefault: "false"},
{giveName: "config-file", wantShorthand: "c", wantDefault: "./error-pages.yml"},
}
for _, tt := range cases {
tt := tt
t.Run(tt.giveName, func(t *testing.T) {
flag := cmd.Flag(tt.giveName)
if flag == nil {
assert.Failf(t, "flag not found", "flag [%s] was not found", tt.giveName)
return
}
assert.Equal(t, tt.wantShorthand, flag.Shorthand)
assert.Equal(t, tt.wantDefault, flag.DefValue)
})
}
}
func TestExecuting(t *testing.T) {
cmd := cli.NewCommand("unit test")
cmd.SetArgs([]string{})
var executed bool
if cmd.Run == nil { // override "Run" property for test (if it was not set)
cmd.Run = func(cmd *cobra.Command, args []string) {
executed = true
}
}
assert.NoError(t, cmd.Execute())
assert.True(t, executed)
}

View File

@ -3,53 +3,164 @@ package serve
import (
"context"
"errors"
"fmt"
"net"
"os"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/tarampampam/error-pages/internal/breaker"
"github.com/tarampampam/error-pages/internal/config"
appHttp "github.com/tarampampam/error-pages/internal/http"
"github.com/tarampampam/error-pages/internal/pick"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"gh.tarampamp.am/error-pages/internal/breaker"
"gh.tarampamp.am/error-pages/internal/cli/shared"
"gh.tarampamp.am/error-pages/internal/config"
"gh.tarampamp.am/error-pages/internal/env"
appHttp "gh.tarampamp.am/error-pages/internal/http"
"gh.tarampamp.am/error-pages/internal/options"
"gh.tarampamp.am/error-pages/internal/pick"
)
type command struct {
c *cli.Command
}
const (
templateNameFlagName = "template-name"
defaultErrorPageFlagName = "default-error-page"
defaultHTTPCodeFlagName = "default-http-code"
showDetailsFlagName = "show-details"
proxyHTTPHeadersFlagName = "proxy-headers"
disableL10nFlagName = "disable-l10n"
)
const (
useRandomTemplate = "random"
useRandomTemplateOnEachRequest = "i-said-random"
useRandomTemplateDaily = "random-daily"
useRandomTemplateHourly = "random-hourly"
)
// NewCommand creates `serve` command.
func NewCommand(ctx context.Context, log *zap.Logger, configFile *string) *cobra.Command {
var (
f flags
cfg *config.Config
)
func NewCommand(log *zap.Logger) *cli.Command { //nolint:funlen
var cmd = command{}
cmd := &cobra.Command{
Use: "serve",
cmd.c = &cli.Command{
Name: "serve",
Aliases: []string{"s", "server"},
Short: "Start HTTP server",
PreRunE: func(cmd *cobra.Command, _ []string) (err error) {
if configFile == nil {
Usage: "Start HTTP server",
Action: func(c *cli.Context) error {
var cfg *config.Config
if configPath := c.String(shared.ConfigFileFlag.Name); configPath == "" { // load config from file
return errors.New("path to the config file is required for this command")
}
if err = f.OverrideUsingEnv(cmd.Flags()); err != nil {
} else if loadedCfg, err := config.FromYamlFile(c.String(shared.ConfigFileFlag.Name)); err != nil {
return err
} else {
cfg = loadedCfg
}
if cfg, err = config.FromYamlFile(*configFile); err != nil {
return err
var (
ip = c.String(shared.ListenAddrFlag.Name)
port = uint16(c.Uint(shared.ListenPortFlag.Name))
o options.ErrorPage
)
if net.ParseIP(ip) == nil {
return fmt.Errorf("wrong IP address [%s] for listening", ip)
}
return f.Validate()
{ // fill options
o.Template.Name = c.String(templateNameFlagName)
o.L10n.Disabled = c.Bool(disableL10nFlagName)
o.Default.PageCode = c.String(defaultErrorPageFlagName)
o.Default.HTTPCode = uint16(c.Uint(defaultHTTPCodeFlagName))
o.ShowDetails = c.Bool(showDetailsFlagName)
if headers := c.String(proxyHTTPHeadersFlagName); headers != "" { //nolint:nestif
var m = make(map[string]struct{})
// make unique and ignore empty strings
for _, header := range strings.Split(headers, ",") {
if h := strings.TrimSpace(header); h != "" {
if strings.ContainsRune(h, ' ') {
return fmt.Errorf("whitespaces in the HTTP headers for proxying [%s] are not allowed", header)
}
if _, ok := m[h]; !ok {
m[h] = struct{}{}
}
}
}
// convert map into slice
o.ProxyHTTPHeaders = make([]string, 0, len(m))
for h := range m {
o.ProxyHTTPHeaders = append(o.ProxyHTTPHeaders, h)
}
}
}
if o.Default.HTTPCode > 599 { //nolint:gomnd
return fmt.Errorf("wrong default HTTP response code [%d]", o.Default.HTTPCode)
}
return cmd.Run(c.Context, log, cfg, ip, port, o)
},
Flags: []cli.Flag{
shared.ConfigFileFlag,
shared.ListenPortFlag,
shared.ListenAddrFlag,
&cli.StringFlag{
Name: templateNameFlagName,
Aliases: []string{"t"},
Usage: fmt.Sprintf(
"template name (set \"%s\" to use a randomized or \"%s\" to use a randomized template on "+
"each request or \"%s/%s\" daily/hourly randomized)",
useRandomTemplate,
useRandomTemplateOnEachRequest,
useRandomTemplateDaily,
useRandomTemplateHourly,
),
EnvVars: []string{env.TemplateName.String()},
},
&cli.StringFlag{
Name: defaultErrorPageFlagName,
Value: "404",
Usage: "default error page",
EnvVars: []string{env.DefaultErrorPage.String()},
},
&cli.UintFlag{
Name: defaultHTTPCodeFlagName,
Value: 404, //nolint:gomnd
Usage: "default HTTP response code",
EnvVars: []string{env.DefaultHTTPCode.String()},
},
&cli.BoolFlag{
Name: showDetailsFlagName,
Usage: "show request details in response",
EnvVars: []string{env.ShowDetails.String()},
},
&cli.StringFlag{
Name: proxyHTTPHeadersFlagName,
Usage: "proxy HTTP request headers list (comma-separated)",
EnvVars: []string{env.ProxyHTTPHeaders.String()},
},
&cli.BoolFlag{
Name: disableL10nFlagName,
Usage: "disable error pages localization",
EnvVars: []string{env.DisableL10n.String()},
},
},
RunE: func(*cobra.Command, []string) error { return run(ctx, log, cfg, f) },
}
f.Init(cmd.Flags())
return cmd
return cmd.c
}
// run current command.
func run(parentCtx context.Context, log *zap.Logger, cfg *config.Config, f flags) error { //nolint:funlen
// Run current command.
func (cmd *command) Run( //nolint:funlen
parentCtx context.Context, log *zap.Logger, cfg *config.Config, ip string, port uint16, opt options.ErrorPage,
) error {
var (
ctx, cancel = context.WithCancel(parentCtx) // serve context creation
oss = breaker.NewOSSignals(ctx) // OS signals listener
@ -70,8 +181,6 @@ func run(parentCtx context.Context, log *zap.Logger, cfg *config.Config, f flags
var (
templateNames = cfg.TemplateNames()
picker interface{ Pick() string }
opt = f.ToOptions()
)
switch opt.Template.Name {
@ -124,8 +233,8 @@ func run(parentCtx context.Context, log *zap.Logger, cfg *config.Config, f flags
defer close(errCh)
log.Info("Server starting",
zap.String("addr", f.Listen.IP),
zap.Uint16("port", f.Listen.Port),
zap.String("addr", ip),
zap.Uint16("port", port),
zap.String("default error page", opt.Default.PageCode),
zap.Uint16("default HTTP response code", opt.Default.HTTPCode),
zap.Strings("proxy headers", opt.ProxyHTTPHeaders),
@ -133,7 +242,7 @@ func run(parentCtx context.Context, log *zap.Logger, cfg *config.Config, f flags
zap.Bool("localization disabled", opt.L10n.Disabled),
)
if err := server.Start(f.Listen.IP, f.Listen.Port); err != nil {
if err := server.Start(ip, port); err != nil {
errCh <- err
}
}(startingErrCh)

View File

@ -1,236 +0,0 @@
package serve
import (
"fmt"
"net"
"sort"
"strconv"
"strings"
"github.com/spf13/pflag"
"github.com/tarampampam/error-pages/internal/env"
"github.com/tarampampam/error-pages/internal/options"
)
type flags struct {
Listen struct {
IP string
Port uint16
}
template struct {
name string
}
l10n struct {
disabled bool
}
defaultErrorPage string
defaultHTTPCode uint16
showDetails bool
proxyHTTPHeaders string // comma-separated
}
const (
listenFlagName = "listen"
portFlagName = "port"
templateNameFlagName = "template-name"
defaultErrorPageFlagName = "default-error-page"
defaultHTTPCodeFlagName = "default-http-code"
showDetailsFlagName = "show-details"
proxyHTTPHeadersFlagName = "proxy-headers"
disableL10nFlagName = "disable-l10n"
)
const (
useRandomTemplate = "random"
useRandomTemplateOnEachRequest = "i-said-random"
useRandomTemplateDaily = "random-daily"
useRandomTemplateHourly = "random-hourly"
)
func (f *flags) Init(flagSet *pflag.FlagSet) {
flagSet.StringVarP(
&f.Listen.IP,
listenFlagName, "l",
"0.0.0.0",
fmt.Sprintf("IP address to Listen on [$%s]", env.ListenAddr),
)
flagSet.Uint16VarP(
&f.Listen.Port,
portFlagName, "p",
8080, //nolint:gomnd // must be same as default healthcheck `--port` flag value
fmt.Sprintf("TCP prt number [$%s]", env.ListenPort),
)
flagSet.StringVarP(
&f.template.name,
templateNameFlagName, "t",
"",
fmt.Sprintf(
"template name (set \"%s\" to use a randomized or \"%s\" to use a randomized template on each request "+
"or \"%s/%s\" daily/hourly randomized) [$%s]",
useRandomTemplate,
useRandomTemplateOnEachRequest,
useRandomTemplateDaily,
useRandomTemplateHourly,
env.TemplateName,
),
)
flagSet.StringVarP(
&f.defaultErrorPage,
defaultErrorPageFlagName, "",
"404",
fmt.Sprintf("default error page [$%s]", env.DefaultErrorPage),
)
flagSet.Uint16VarP(
&f.defaultHTTPCode,
defaultHTTPCodeFlagName, "",
404, //nolint:gomnd
fmt.Sprintf("default HTTP response code [$%s]", env.DefaultHTTPCode),
)
flagSet.BoolVarP(
&f.showDetails,
showDetailsFlagName, "",
false,
fmt.Sprintf("show request details in response [$%s]", env.ShowDetails),
)
flagSet.StringVarP(
&f.proxyHTTPHeaders,
proxyHTTPHeadersFlagName, "",
"",
fmt.Sprintf("proxy HTTP request headers list (comma-separated) [$%s]", env.ProxyHTTPHeaders),
)
flagSet.BoolVarP(
&f.l10n.disabled,
disableL10nFlagName, "",
false,
fmt.Sprintf("disable error pages localization [$%s]", env.DisableL10n),
)
}
func (f *flags) OverrideUsingEnv(flagSet *pflag.FlagSet) (lastErr error) { //nolint:gocognit,gocyclo
flagSet.VisitAll(func(flag *pflag.Flag) {
// flag was NOT defined using CLI (flags should have maximal priority)
if !flag.Changed { //nolint:nestif
switch flag.Name {
case listenFlagName:
if envVar, exists := env.ListenAddr.Lookup(); exists {
f.Listen.IP = strings.TrimSpace(envVar)
}
case portFlagName:
if envVar, exists := env.ListenPort.Lookup(); exists {
if p, err := strconv.ParseUint(envVar, 10, 16); err == nil {
f.Listen.Port = uint16(p)
} else {
lastErr = fmt.Errorf("wrong TCP port environment variable [%s] value", envVar)
}
}
case templateNameFlagName:
if envVar, exists := env.TemplateName.Lookup(); exists {
f.template.name = strings.TrimSpace(envVar)
}
case defaultErrorPageFlagName:
if envVar, exists := env.DefaultErrorPage.Lookup(); exists {
f.defaultErrorPage = strings.TrimSpace(envVar)
}
case defaultHTTPCodeFlagName:
if envVar, exists := env.DefaultHTTPCode.Lookup(); exists {
if code, err := strconv.ParseUint(envVar, 10, 16); err == nil {
f.defaultHTTPCode = uint16(code)
} else {
lastErr = fmt.Errorf("wrong default HTTP response code environment variable [%s] value", envVar)
}
}
case showDetailsFlagName:
if envVar, exists := env.ShowDetails.Lookup(); exists {
if b, err := strconv.ParseBool(envVar); err == nil {
f.showDetails = b
}
}
case proxyHTTPHeadersFlagName:
if envVar, exists := env.ProxyHTTPHeaders.Lookup(); exists {
f.proxyHTTPHeaders = strings.TrimSpace(envVar)
}
case disableL10nFlagName:
if envVar, exists := env.DisableL10n.Lookup(); exists {
if b, err := strconv.ParseBool(envVar); err == nil {
f.l10n.disabled = b
}
}
}
}
})
return lastErr
}
func (f *flags) Validate() error {
if net.ParseIP(f.Listen.IP) == nil {
return fmt.Errorf("wrong IP address [%s] for listening", f.Listen.IP)
}
if f.defaultHTTPCode > 599 { //nolint:gomnd
return fmt.Errorf("wrong default HTTP response code [%d]", f.defaultHTTPCode)
}
if strings.ContainsRune(f.proxyHTTPHeaders, ' ') {
return fmt.Errorf("whitespaces in the HTTP headers for proxying [%s] are not allowed", f.proxyHTTPHeaders)
}
return nil
}
// headersToProxy converts a comma-separated string with headers list into strings slice (with a sorting and without
// duplicates).
func (f *flags) headersToProxy() []string {
var raw = strings.Split(f.proxyHTTPHeaders, ",")
if len(raw) == 0 {
return []string{}
} else if len(raw) == 1 {
if h := strings.TrimSpace(raw[0]); h != "" {
return []string{h}
} else {
return []string{}
}
}
var m = make(map[string]struct{}, len(raw))
// make unique and ignore empty strings
for _, h := range raw {
if h = strings.TrimSpace(h); h != "" {
if _, ok := m[h]; !ok {
m[h] = struct{}{}
}
}
}
// convert map into slice
var headers = make([]string, 0, len(m))
for h := range m {
headers = append(headers, h)
}
// make sort
sort.Strings(headers)
return headers
}
func (f *flags) ToOptions() (o options.ErrorPage) {
o.Default.PageCode = f.defaultErrorPage
o.Default.HTTPCode = f.defaultHTTPCode
o.L10n.Disabled = f.l10n.disabled
o.Template.Name = f.template.name
o.ShowDetails = f.showDetails
o.ProxyHTTPHeaders = f.headersToProxy()
return o
}

View File

@ -0,0 +1,31 @@
package shared
import (
"github.com/urfave/cli/v2"
"gh.tarampamp.am/error-pages/internal/env"
)
var ConfigFileFlag = &cli.StringFlag{ //nolint:gochecknoglobals
Name: "config-file",
Aliases: []string{"c"},
Usage: "path to the config file (yaml)",
Value: "./error-pages.yml",
EnvVars: []string{env.ConfigFilePath.String()},
}
var ListenAddrFlag = &cli.StringFlag{ //nolint:gochecknoglobals
Name: "listen",
Aliases: []string{"l"},
Usage: "IP address to Listen on",
Value: "0.0.0.0",
EnvVars: []string{env.ListenAddr.String()},
}
var ListenPortFlag = &cli.UintFlag{ //nolint:gochecknoglobals
Name: "port",
Aliases: []string{"p"},
Usage: "TCP port number",
Value: 8080, //nolint:gomnd
EnvVars: []string{env.ListenPort.String()},
}

View File

@ -1,24 +0,0 @@
// Package version contains CLI `version` command implementation.
package version
import (
"fmt"
"os"
"runtime"
"github.com/spf13/cobra"
)
// NewCommand creates `version` command.
func NewCommand(ver string) *cobra.Command {
return &cobra.Command{
Use: "version",
Aliases: []string{"v", "ver"},
Short: "Display application version",
RunE: func(*cobra.Command, []string) (err error) {
_, err = fmt.Fprintf(os.Stdout, "app version:\t%s (%s)\n", ver, runtime.Version())
return
},
}
}

View File

@ -1,30 +0,0 @@
package version_test
import (
"runtime"
"testing"
"github.com/kami-zh/go-capturer"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/cli/version"
)
func TestProperties(t *testing.T) {
cmd := version.NewCommand("")
assert.Equal(t, "version", cmd.Use)
assert.ElementsMatch(t, []string{"v", "ver"}, cmd.Aliases)
assert.NotNil(t, cmd.RunE)
}
func TestCommandRun(t *testing.T) {
cmd := version.NewCommand("1.2.3@foobar")
cmd.SetArgs([]string{})
output := capturer.CaptureStdout(func() {
assert.NoError(t, cmd.Execute())
})
assert.Contains(t, output, "1.2.3@foobar")
assert.Contains(t, output, runtime.Version())
}

View File

@ -5,7 +5,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/config"
"gh.tarampamp.am/error-pages/internal/config"
)
func TestFromYaml(t *testing.T) {

3
internal/env/env.go vendored
View File

@ -6,6 +6,9 @@ import "os"
type envVariable string
const (
LogLevel envVariable = "LOG_LEVEL" // logging level
LogFormat envVariable = "LOG_FORMAT" // logging format (json|console)
ListenAddr envVariable = "LISTEN_ADDR" // IP address for listening
ListenPort envVariable = "LISTEN_PORT" // port number for listening
TemplateName envVariable = "TEMPLATE_NAME" // template name

View File

@ -3,10 +3,11 @@ package core
import (
"strconv"
"github.com/tarampampam/error-pages/internal/config"
"github.com/tarampampam/error-pages/internal/options"
"github.com/tarampampam/error-pages/internal/tpl"
"github.com/valyala/fasthttp"
"gh.tarampamp.am/error-pages/internal/config"
"gh.tarampamp.am/error-pages/internal/options"
"gh.tarampamp.am/error-pages/internal/tpl"
)
type templatePicker interface {

View File

@ -4,8 +4,9 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/http/core"
"github.com/valyala/fasthttp"
"gh.tarampamp.am/error-pages/internal/http/core"
)
func TestClientWantFormat(t *testing.T) {

View File

@ -1,11 +1,12 @@
package errorpage
import (
"github.com/tarampampam/error-pages/internal/config"
"github.com/tarampampam/error-pages/internal/http/core"
"github.com/tarampampam/error-pages/internal/options"
"github.com/tarampampam/error-pages/internal/tpl"
"github.com/valyala/fasthttp"
"gh.tarampamp.am/error-pages/internal/config"
"gh.tarampamp.am/error-pages/internal/http/core"
"gh.tarampamp.am/error-pages/internal/options"
"gh.tarampamp.am/error-pages/internal/tpl"
)
type (

View File

@ -3,11 +3,12 @@ package index
import (
"strconv"
"github.com/tarampampam/error-pages/internal/config"
"github.com/tarampampam/error-pages/internal/http/core"
"github.com/tarampampam/error-pages/internal/options"
"github.com/tarampampam/error-pages/internal/tpl"
"github.com/valyala/fasthttp"
"gh.tarampamp.am/error-pages/internal/config"
"gh.tarampamp.am/error-pages/internal/http/core"
"gh.tarampamp.am/error-pages/internal/options"
"gh.tarampamp.am/error-pages/internal/tpl"
)
type (

View File

@ -2,13 +2,27 @@ package notfound
import (
"github.com/valyala/fasthttp"
"gh.tarampamp.am/error-pages/internal/config"
"gh.tarampamp.am/error-pages/internal/http/core"
"gh.tarampamp.am/error-pages/internal/options"
"gh.tarampamp.am/error-pages/internal/tpl"
)
type (
templatePicker interface {
// Pick the template name for responding.
Pick() string
}
renderer interface {
Render(content []byte, props tpl.Properties) ([]byte, error)
}
)
// NewHandler creates handler missing requests handling.
func NewHandler() fasthttp.RequestHandler {
func NewHandler(cfg *config.Config, p templatePicker, rdr renderer, opt options.ErrorPage) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("text/plain; charset=utf-8")
ctx.SetStatusCode(fasthttp.StatusNotFound)
_, _ = ctx.WriteString("Wrong request URL. Error pages are available at the following URLs: /{code}.html")
core.RespondWithErrorPage(ctx, cfg, p, rdr, "404", fasthttp.StatusNotFound, opt)
}
}

View File

@ -5,21 +5,22 @@ import (
"time"
"github.com/fasthttp/router"
"github.com/tarampampam/error-pages/internal/checkers"
"github.com/tarampampam/error-pages/internal/config"
"github.com/tarampampam/error-pages/internal/http/common"
errorpageHandler "github.com/tarampampam/error-pages/internal/http/handlers/errorpage"
healthzHandler "github.com/tarampampam/error-pages/internal/http/handlers/healthz"
indexHandler "github.com/tarampampam/error-pages/internal/http/handlers/index"
metricsHandler "github.com/tarampampam/error-pages/internal/http/handlers/metrics"
notfoundHandler "github.com/tarampampam/error-pages/internal/http/handlers/notfound"
versionHandler "github.com/tarampampam/error-pages/internal/http/handlers/version"
"github.com/tarampampam/error-pages/internal/metrics"
"github.com/tarampampam/error-pages/internal/options"
"github.com/tarampampam/error-pages/internal/tpl"
"github.com/tarampampam/error-pages/internal/version"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
"gh.tarampamp.am/error-pages/internal/checkers"
"gh.tarampamp.am/error-pages/internal/config"
"gh.tarampamp.am/error-pages/internal/http/common"
errorpageHandler "gh.tarampamp.am/error-pages/internal/http/handlers/errorpage"
healthzHandler "gh.tarampamp.am/error-pages/internal/http/handlers/healthz"
indexHandler "gh.tarampamp.am/error-pages/internal/http/handlers/index"
metricsHandler "gh.tarampamp.am/error-pages/internal/http/handlers/metrics"
notfoundHandler "gh.tarampamp.am/error-pages/internal/http/handlers/notfound"
versionHandler "gh.tarampamp.am/error-pages/internal/http/handlers/version"
"gh.tarampamp.am/error-pages/internal/metrics"
"gh.tarampamp.am/error-pages/internal/options"
"gh.tarampamp.am/error-pages/internal/tpl"
"gh.tarampamp.am/error-pages/internal/version"
)
type Server struct {
@ -87,7 +88,7 @@ func (s *Server) Register(cfg *config.Config, templatePicker templatePicker, opt
s.router.GET("/metrics", metricsHandler.NewHandler(reg))
s.router.NotFound = notfoundHandler.NewHandler()
s.router.NotFound = notfoundHandler.NewHandler(cfg, templatePicker, s.rdr, opt)
return nil
}

68
internal/logger/format.go Normal file
View File

@ -0,0 +1,68 @@
package logger
import (
"fmt"
"strings"
)
// A Format is a logging format.
type Format uint8
const (
ConsoleFormat Format = iota // useful for console output (for humans)
JSONFormat // useful for logging aggregation systems (for robots)
)
// String returns a lower-case ASCII representation of the log format.
func (f Format) String() string {
switch f {
case ConsoleFormat:
return "console"
case JSONFormat:
return "json"
}
return fmt.Sprintf("format(%d)", f)
}
// Formats returns a slice of all logging formats.
func Formats() []Format {
return []Format{ConsoleFormat, JSONFormat}
}
// FormatStrings returns a slice of all logging formats as strings.
func FormatStrings() []string {
var (
formats = Formats()
result = make([]string, len(formats))
)
for i := range formats {
result[i] = formats[i].String()
}
return result
}
// ParseFormat parses a format (case is ignored) based on the ASCII representation of the log format.
// If the provided ASCII representation is invalid an error is returned.
//
// This is particularly useful when dealing with text input to configure log formats.
func ParseFormat[T string | []byte](text T) (Format, error) {
var format string
if s, ok := any(text).(string); ok {
format = s
} else {
format = string(any(text).([]byte))
}
switch strings.ToLower(format) {
case "console", "": // make the zero value useful
return ConsoleFormat, nil
case "json":
return JSONFormat, nil
}
return Format(0), fmt.Errorf("unrecognized logging format: %q", text)
}

View File

@ -0,0 +1,62 @@
package logger_test
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
"gh.tarampamp.am/error-pages/internal/logger"
)
func TestFormat_String(t *testing.T) {
for name, tt := range map[string]struct {
giveFormat logger.Format
wantString string
}{
"json": {giveFormat: logger.JSONFormat, wantString: "json"},
"console": {giveFormat: logger.ConsoleFormat, wantString: "console"},
"<unknown>": {giveFormat: logger.Format(255), wantString: "format(255)"},
} {
t.Run(name, func(t *testing.T) {
require.Equal(t, tt.wantString, tt.giveFormat.String())
})
}
}
func TestParseFormat(t *testing.T) {
for name, tt := range map[string]struct {
giveBytes []byte
giveString string
wantFormat logger.Format
wantError error
}{
"<empty value>": {giveBytes: []byte(""), wantFormat: logger.ConsoleFormat},
"<empty value> (string)": {giveString: "", wantFormat: logger.ConsoleFormat},
"console": {giveBytes: []byte("console"), wantFormat: logger.ConsoleFormat},
"console (string)": {giveString: "console", wantFormat: logger.ConsoleFormat},
"json": {giveBytes: []byte("json"), wantFormat: logger.JSONFormat},
"json (string)": {giveString: "json", wantFormat: logger.JSONFormat},
"foobar": {giveBytes: []byte("foobar"), wantError: errors.New("unrecognized logging format: \"foobar\"")}, //nolint:lll
} {
t.Run(name, func(t *testing.T) {
var (
f logger.Format
err error
)
if tt.giveString != "" {
f, err = logger.ParseFormat(tt.giveString)
} else {
f, err = logger.ParseFormat(tt.giveBytes)
}
if tt.wantError == nil {
require.NoError(t, err)
require.Equal(t, tt.wantFormat, f)
} else {
require.EqualError(t, err, tt.wantError.Error())
}
})
}
}

83
internal/logger/level.go Normal file
View File

@ -0,0 +1,83 @@
package logger
import (
"fmt"
"strings"
)
// A Level is a logging level.
type Level int8
const (
DebugLevel Level = iota - 1
InfoLevel // default level (zero-value)
WarnLevel
ErrorLevel
FatalLevel
)
// String returns a lower-case ASCII representation of the log level.
func (l Level) String() string {
switch l {
case DebugLevel:
return "debug"
case InfoLevel:
return "info"
case WarnLevel:
return "warn"
case ErrorLevel:
return "error"
case FatalLevel:
return "fatal"
}
return fmt.Sprintf("level(%d)", l)
}
// Levels returns a slice of all logging levels.
func Levels() []Level {
return []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, FatalLevel}
}
// LevelStrings returns a slice of all logging levels as strings.
func LevelStrings() []string {
var (
levels = Levels()
result = make([]string, len(levels))
)
for i := range levels {
result[i] = levels[i].String()
}
return result
}
// ParseLevel parses a level (case is ignored) based on the ASCII representation of the log level.
// If the provided ASCII representation is invalid an error is returned.
//
// This is particularly useful when dealing with text input to configure log levels.
func ParseLevel[T string | []byte](text T) (Level, error) {
var lvl string
if s, ok := any(text).(string); ok {
lvl = s
} else {
lvl = string(any(text).([]byte))
}
switch strings.ToLower(lvl) {
case "debug", "verbose", "trace":
return DebugLevel, nil
case "info", "": // make the zero value useful
return InfoLevel, nil
case "warn":
return WarnLevel, nil
case "error":
return ErrorLevel, nil
case "fatal":
return FatalLevel, nil
}
return Level(0), fmt.Errorf("unrecognized logging level: %q", text)
}

View File

@ -0,0 +1,84 @@
package logger_test
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
"gh.tarampamp.am/error-pages/internal/logger"
)
func TestLevel_String(t *testing.T) {
for name, tt := range map[string]struct {
giveLevel logger.Level
wantString string
}{
"debug": {giveLevel: logger.DebugLevel, wantString: "debug"},
"info": {giveLevel: logger.InfoLevel, wantString: "info"},
"warn": {giveLevel: logger.WarnLevel, wantString: "warn"},
"error": {giveLevel: logger.ErrorLevel, wantString: "error"},
"fatal": {giveLevel: logger.FatalLevel, wantString: "fatal"},
"<unknown>": {giveLevel: logger.Level(127), wantString: "level(127)"},
} {
t.Run(name, func(t *testing.T) {
require.Equal(t, tt.wantString, tt.giveLevel.String())
})
}
}
func TestParseLevel(t *testing.T) {
for name, tt := range map[string]struct {
giveBytes []byte
giveString string
wantLevel logger.Level
wantError error
}{
"<empty value>": {giveBytes: []byte(""), wantLevel: logger.InfoLevel},
"<empty value> (string)": {giveString: "", wantLevel: logger.InfoLevel},
"trace": {giveBytes: []byte("debug"), wantLevel: logger.DebugLevel},
"verbose": {giveBytes: []byte("debug"), wantLevel: logger.DebugLevel},
"debug": {giveBytes: []byte("debug"), wantLevel: logger.DebugLevel},
"debug (string)": {giveString: "debug", wantLevel: logger.DebugLevel},
"info": {giveBytes: []byte("info"), wantLevel: logger.InfoLevel},
"warn": {giveBytes: []byte("warn"), wantLevel: logger.WarnLevel},
"error": {giveBytes: []byte("error"), wantLevel: logger.ErrorLevel},
"fatal": {giveBytes: []byte("fatal"), wantLevel: logger.FatalLevel},
"fatal (string)": {giveString: "fatal", wantLevel: logger.FatalLevel},
"foobar": {giveBytes: []byte("foobar"), wantError: errors.New("unrecognized logging level: \"foobar\"")}, //nolint:lll
} {
t.Run(name, func(t *testing.T) {
var (
l logger.Level
err error
)
if tt.giveString != "" {
l, err = logger.ParseLevel(tt.giveString)
} else {
l, err = logger.ParseLevel(tt.giveBytes)
}
if tt.wantError == nil {
require.NoError(t, err)
require.Equal(t, tt.wantLevel, l)
} else {
require.EqualError(t, err, tt.wantError.Error())
}
})
}
}
func TestLevels(t *testing.T) {
require.Equal(t, []logger.Level{
logger.DebugLevel,
logger.InfoLevel,
logger.WarnLevel,
logger.ErrorLevel,
logger.FatalLevel,
}, logger.Levels())
}
func TestLevelStrings(t *testing.T) {
require.Equal(t, []string{"debug", "info", "warn", "error", "fatal"}, logger.LevelStrings())
}

View File

@ -2,20 +2,27 @@
package logger
import (
"errors"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// New creates new "zap" logger with little customization.
func New(verbose, debug, logJSON bool) (*zap.Logger, error) {
// New creates new "zap" logger with a small customization.
func New(l Level, f Format) (*zap.Logger, error) {
var config zap.Config
if logJSON {
config = zap.NewProductionConfig()
} else {
switch f {
case ConsoleFormat:
config = zap.NewDevelopmentConfig()
config.EncoderConfig.EncodeLevel = zapcore.LowercaseColorLevelEncoder
config.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("15:04:05")
case JSONFormat:
config = zap.NewProductionConfig() // json encoder is used by default
default:
return nil, errors.New("unsupported logging format")
}
// default configuration for all encoders
@ -24,15 +31,31 @@ func New(verbose, debug, logJSON bool) (*zap.Logger, error) {
config.DisableStacktrace = true
config.DisableCaller = true
if debug {
// enable additional features for debugging
if l <= DebugLevel {
config.Development = true
config.DisableStacktrace = false
config.DisableCaller = false
}
if verbose || debug {
config.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
var zapLvl zapcore.Level
switch l { // convert level to zap.Level
case DebugLevel:
zapLvl = zap.DebugLevel
case InfoLevel:
zapLvl = zap.InfoLevel
case WarnLevel:
zapLvl = zap.WarnLevel
case ErrorLevel:
zapLvl = zap.ErrorLevel
case FatalLevel:
zapLvl = zap.FatalLevel
default:
return nil, errors.New("unsupported logging level")
}
config.Level = zap.NewAtomicLevelAt(zapLvl)
return config.Build()
}

View File

@ -8,64 +8,51 @@ import (
"github.com/kami-zh/go-capturer"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/logger"
"github.com/stretchr/testify/require"
"gh.tarampamp.am/error-pages/internal/logger"
)
func TestNewNotVerboseDebugJSON(t *testing.T) {
func TestNewDebugLevelConsoleFormat(t *testing.T) {
output := capturer.CaptureStderr(func() {
log, err := logger.New(false, false, false)
assert.NoError(t, err)
log, err := logger.New(logger.DebugLevel, logger.ConsoleFormat)
require.NoError(t, err)
log.Info("inf msg")
log.Debug("dbg msg")
log.Info("inf msg")
log.Error("err msg")
})
assert.Contains(t, output, time.Now().Format("15:04:05"))
assert.Regexp(t, `\t.+info.+\tinf msg`, output)
assert.NotContains(t, output, "dbg msg")
assert.Contains(t, output, "err msg")
}
func TestNewVerboseNotDebugJSON(t *testing.T) {
output := capturer.CaptureStderr(func() {
log, err := logger.New(true, false, false)
assert.NoError(t, err)
log.Info("inf msg")
log.Debug("dbg msg")
log.Error("err msg")
})
assert.Contains(t, output, time.Now().Format("15:04:05"))
assert.Regexp(t, `\t.+info.+\tinf msg`, output)
assert.Contains(t, output, "dbg msg")
assert.Contains(t, output, "err msg")
}
func TestNewVerboseDebugNotJSON(t *testing.T) {
output := capturer.CaptureStderr(func() {
log, err := logger.New(true, true, false)
assert.NoError(t, err)
log.Info("inf msg")
log.Debug("dbg msg")
log.Error("err msg")
})
assert.Contains(t, output, time.Now().Format("15:04:05"))
assert.Regexp(t, `\t.+info.+\t.+logger_test\.go:\d+\tinf msg`, output)
assert.Contains(t, output, "dbg msg")
assert.Contains(t, output, "err msg")
}
func TestNewNotVerboseDebugButJSON(t *testing.T) {
func TestNewErrorLevelConsoleFormat(t *testing.T) {
output := capturer.CaptureStderr(func() {
log, err := logger.New(false, false, true)
assert.NoError(t, err)
log, err := logger.New(logger.ErrorLevel, logger.ConsoleFormat)
require.NoError(t, err)
log.Info("inf msg")
log.Debug("dbg msg")
log.Info("inf msg")
log.Error("err msg")
})
assert.NotContains(t, output, "inf msg")
assert.NotContains(t, output, "dbg msg")
assert.Contains(t, output, "err msg")
}
func TestNewWarnLevelJSONFormat(t *testing.T) {
output := capturer.CaptureStderr(func() {
log, err := logger.New(logger.WarnLevel, logger.JSONFormat)
require.NoError(t, err)
log.Debug("dbg msg")
log.Info("inf msg")
log.Warn("warn msg")
log.Error("err msg")
})
@ -75,6 +62,14 @@ func TestNewNotVerboseDebugButJSON(t *testing.T) {
lines := strings.Split(strings.Trim(output, "\n"), "\n")
assert.JSONEq(t, `{"level":"info","ts":0.1,"msg":"inf msg"}`, lines[0])
assert.JSONEq(t, `{"level":"warn","ts":0.1,"msg":"warn msg"}`, lines[0])
assert.JSONEq(t, `{"level":"error","ts":0.1,"msg":"err msg"}`, lines[1])
}
func TestNewErrors(t *testing.T) {
_, err := logger.New(logger.Level(127), logger.ConsoleFormat)
require.EqualError(t, err, "unsupported logging level")
_, err = logger.New(logger.WarnLevel, logger.Format(255))
require.EqualError(t, err, "unsupported logging format")
}

View File

@ -8,7 +8,8 @@ import (
"github.com/prometheus/client_golang/prometheus/testutil"
dto "github.com/prometheus/client_model/go"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/metrics"
"gh.tarampamp.am/error-pages/internal/metrics"
)
func TestMetrics_Register(t *testing.T) {

View File

@ -5,7 +5,8 @@ import (
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/metrics"
"gh.tarampamp.am/error-pages/internal/metrics"
)
func TestNewRegistry(t *testing.T) {

View File

@ -4,7 +4,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/pick"
"gh.tarampamp.am/error-pages/internal/pick"
)
func TestPicker_NextIndex_First(t *testing.T) {

View File

@ -5,7 +5,8 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/pick"
"gh.tarampamp.am/error-pages/internal/pick"
)
func TestStringsSlice_Pick(t *testing.T) {

View File

@ -4,7 +4,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/tpl"
"gh.tarampamp.am/error-pages/internal/tpl"
)
func TestHashBytes(t *testing.T) {

View File

@ -4,7 +4,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/tpl"
"gh.tarampamp.am/error-pages/internal/tpl"
)
func TestProperties_Replaces(t *testing.T) {

View File

@ -10,7 +10,8 @@ import (
"time"
"github.com/pkg/errors"
"github.com/tarampampam/error-pages/internal/version"
"gh.tarampamp.am/error-pages/internal/version"
)
// These functions are always allowed in the templates.
@ -30,6 +31,7 @@ var tplFnMap = template.FuncMap{ //nolint:gochecknoglobals
return 0
},
"env": os.Getenv,
}
var ErrClosed = errors.New("closed")

View File

@ -2,18 +2,25 @@ package tpl_test
import (
"math/rand"
"os"
"strconv"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tarampampam/error-pages/internal/tpl"
"github.com/stretchr/testify/require"
"gh.tarampamp.am/error-pages/internal/tpl"
)
func Test_Render(t *testing.T) {
renderer := tpl.NewTemplateRenderer()
defer func() { _ = renderer.Close() }()
require.NoError(t, os.Setenv("TEST_ENV_VAR", "unit-test"))
defer func() { require.NoError(t, os.Unsetenv("TEST_ENV_VAR")) }()
for name, tt := range map[string]struct {
giveContent string
giveProps tpl.Properties
@ -67,6 +74,11 @@ func Test_Render(t *testing.T) {
giveProps: tpl.Properties{L10nDisabled: true},
wantContent: "Y",
},
"env": {
giveContent: `{{ env "TEST_ENV_VAR" }}`,
wantContent: "unit-test",
},
} {
t.Run(name, func(t *testing.T) {
content, err := renderer.Render([]byte(tt.giveContent), tt.giveProps)

279
templates/orient.html Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,5 +3,5 @@ GET http://{{ host }}:{{ port }}/not-found
HTTP/* 404
[Asserts]
header "Content-Type" contains "text/plain"
body contains "Wrong request URL"
header "Content-Type" contains "text/html"
body contains "The server can not find the requested page"