Compare commits

...

64 commits
v0.8.0 ... main

Author SHA1 Message Date
Hector
74265fda0e merge: update-example-systemd-file
* Merge branch 'update-example-systemd-file' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!115
* Merged by: Hector <hector@hjs.dev>
2023-10-27 06:23:20 +00:00
Hector
deabab8363 chore: update example systemd file (!115)
- Update the example systemd file to be more complete
- Added description, documentation link, and some improved service settings

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/115
2023-10-27 06:23:20 +00:00
Hector
581220c654 merge: add-manual-docker-multistep-build
* Merge branch 'add-manual-docker-multistep-build' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!114
* Merged by: Hector <hector@hjs.dev>
2023-10-27 06:01:44 +00:00
Hector
f4d6598308 build: add manual docker multistep build (!114)
- Include two Dockerfiles in the repo: one for Goreleaser and one for manual builds
- Update the Goreleaser config to use it's own Dockerfile
- The Dockerfile for manual builds uses a two step process to build the exporter - this way the base system does not need Go installed

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/114
2023-10-27 06:01:41 +00:00
Hector
31b29021c5 merge: update-dependencies
* Merge branch 'update-dependencies' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!113
* Merged by: Hector <hector@hjs.dev>
2023-10-27 05:34:22 +00:00
Hector
900e05fea8 chore: update dependencies (!113)
- Update project dependencies

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/113
2023-10-27 05:34:21 +00:00
Hector
2b91e58d14
merge: add-healthcheck-file-to-gorelaser-config
* Merge branch 'add-healthcheck-file-to-gorelaser-config' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!112
* Merged by: Hector <hector@hjs.dev>
2023-09-09 09:09:44 +00:00
Hector
b1fa45f76b
chore: add healthcheck file to gorelaser config (!112)
* Add missing goreleaser config to include the healthcheck script in the docker image

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/112
2023-09-09 09:09:44 +00:00
Hector
a2139d9774
merge: add-troubleshooting-section-to-readme
* Merge branch 'add-troubleshooting-section-to-readme' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!111
* Merged by: Hector <hector@hjs.dev>
2023-09-09 06:39:58 +00:00
Hector
283ce50c59
docs: add troubleshooting section to readme (!111)
* Update README with a troubleshooting section
* Describe how to solve the "no such file or directory" error

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/111
2023-09-09 06:39:58 +00:00
Hector
1333fde5ca
merge: add-health-endpoint-to-exporter
* Merge branch 'add-health-endpoint-to-exporter' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!110
* Merged by: Hector <hector@hjs.dev>
2023-09-09 06:15:23 +00:00
Hector
8c67b44052
feat: add health endpoint to exporter (!110)
* Add new `/health` endpoint to check if exporter is running correctly
* Update docker healthcheck to use the new endpoint

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/110
2023-09-09 06:15:23 +00:00
Hector
f92318e864
merge: add-healthcheck-to-docker-image
* Merge branch 'add-healthcheck-to-docker-image' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!109
* Merged by: Hector <hector@hjs.dev>
2023-09-07 21:16:20 +00:00
Hector
9738a20a82
build: add healthcheck to docker image (!109)
* Update docker image to include a healthcheck instruction using a custom script
* Replace docker base image with alpine

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/109
2023-09-07 21:16:20 +00:00
Hector
a4c48ba46f
merge: publish-arm64-based-docker-images
* Merge branch 'publish-arm64-based-docker-images' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!108
* Merged by: Hector <hector@hjs.dev>
2023-09-07 20:29:30 +00:00
Hector
970bbb51d4
ci: publish arm64 based docker images (!108)
* Update `goreleaser` config to publish `arm64` based docker images
* Remove manual image tagging script and replace with `goreleaser` config

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/108
2023-09-07 20:29:30 +00:00
Hector
e80e361068
merge: update-dependencies
* Merge branch 'update-dependencies' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!107
* Merged by: Hector <hector@hjs.dev>
2023-09-07 18:33:25 +00:00
Hector
4a344cf8d0
chore: update dependencies (!107)
* Update project dependencies

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/107
2023-09-07 18:33:24 +00:00
Hector
b1504423f4 merge: update-dependencies
* Merge branch 'update-dependencies' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!106
* Merged by: Hector <hector@hjs.dev>
2023-08-01 13:30:21 +00:00
Hector
ba36fdb24e chore: update dependencies (!106)
* Update dependencies to the latest versions

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/106
2023-08-01 13:30:20 +00:00
Hector
235d34114b merge: chore/exclude-new-merge-messages-from-changelog
* Merge branch 'chore/exclude-new-merge-messages-from-changelog' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!104
* Merged by: Hector <hector@hjs.dev>
2023-07-03 07:18:37 +00:00
Hector
29d03c5a88 chore: exclude new merge messages from changelog (!104)
* Update the goreleaser config to exclude the new merge commit messages

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/104
2023-07-03 07:18:37 +00:00
Hector
a388773836 merge: chore/dependency-updates
* Merge branch 'chore/dependency-updates' into 'main'
* See merge request hectorjsmith/fail2ban-prometheus-exporter!103
* Merged by: Hector <hector@hjs.dev>
2023-07-03 07:03:14 +00:00
Hector
fdd223c96b chore: dependency updates (!103)
* Update dependencies to their latest versions

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/103
2023-07-03 07:03:14 +00:00
Hector
fe039c0787 Merge branch 'docs/add-go-report-card-to-readme' into 'main'
docs: add go report card to readme

See merge request hectorjsmith/fail2ban-prometheus-exporter!102
2023-06-23 20:42:46 +00:00
Hector
426011fe14 docs: add go report card to readme (!102)
* Add a badge with the project's go report card to the project `README` file

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/102
2023-06-23 20:42:46 +00:00
Hector
f877fcb79d Merge branch 'ci/fix-release-step-script' into 'main'
ci: fix release step script

See merge request hectorjsmith/fail2ban-prometheus-exporter!101
2023-06-22 21:52:31 +00:00
Hector
8c67048868 ci: fix release step script (!101)
* Fix the yaml config for the release step to handle a multi-line command

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/101
2023-06-22 21:52:31 +00:00
Hector
0a132c83df Merge branch 'docs/add-dry-run-command-to-readme' into 'main'
docs: add dry run flag to readme

See merge request hectorjsmith/fail2ban-prometheus-exporter!100
2023-06-22 18:37:01 +00:00
Hector
e46da3e7dd docs: add dry run flag to readme (!100)
* Update `README` to include the new `--dry-run` flag

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/100
2023-06-22 18:37:01 +00:00
Hector
18000cf9ef Merge branch 'ci/add-go-vet-to-test-stage' into 'main'
ci: add go vet to test stage

See merge request hectorjsmith/fail2ban-prometheus-exporter!99
2023-06-22 18:24:15 +00:00
Hector
b6cec83503 ci: add go vet to test stage (!99)
* Add `go vet` to the test stage of the Gitlab CI/CD pipeline
* Fix issues raised by `go vet`

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/99
2023-06-22 18:24:15 +00:00
Hector
8d457da2b9 Merge branch 'feat/add-new-dry-run-mode' into 'main'
feat: add new dry run mode

See merge request hectorjsmith/fail2ban-prometheus-exporter!98
2023-06-22 16:09:36 +00:00
Hector
9c1a10e309 feat: add new dry run mode (!98)
* Add a new *dry-run* mode to exit just before running the server
* This allows testing that the socket is working before starting the server

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/98
2023-06-22 16:09:36 +00:00
Hector
5275d280be Merge branch 'build/disable-cgo-for-makefile-build' into 'main'
build: disable cgo for makefile build

See merge request hectorjsmith/fail2ban-prometheus-exporter!97
2023-06-22 15:22:42 +00:00
Hector
b9ab77d62d build: disable cgo for makefile build (!97)
* Disable `CGO` in the makefile build to avoid dependencies on external C libraries
* This also aligns with the build done for releases and docker images

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/97
2023-06-22 15:22:42 +00:00
Hector
5f879e1921 Merge branch 'feat/print-fail2ban-version-on-startup' into 'main'
feat: print fail2ban version on startup

See merge request hectorjsmith/fail2ban-prometheus-exporter!96
2023-06-22 15:16:12 +00:00
Hector
8e0284d9f0 feat: print fail2ban version on startup (!96)
* Print the version reported by `fail2ban-server` when the exporter starts up
* Logs an error if the connection to the server fails

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/96
2023-06-22 15:16:12 +00:00
Hector
d1b65a2134 Merge branch 'chore/replace-ioutils-with-os-calls' into 'main'
chore: replace ioutil with os calls

See merge request hectorjsmith/fail2ban-prometheus-exporter!95
2023-06-22 14:33:57 +00:00
Hector
dc342e3051 chore: replace ioutil with os calls (!95)
* Replace calls to deprecated `ioutil` functions with the `os` package

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/95
2023-06-22 14:33:56 +00:00
Hector
1508693dd6 Merge branch 'chore/rename-examples-folder' into 'main'
chore: rename examples folder

See merge request hectorjsmith/fail2ban-prometheus-exporter!94
2023-06-22 14:22:31 +00:00
Hector
594d9f26a1 chore: rename examples folder (!94)
* Rename the `examples` folder to `_examples` to distinguish it from source code folders

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/94
2023-06-22 14:22:31 +00:00
Hector
938860d5dc Merge branch 'set-dependency-scanning-config-1' into 'main'
ci: configure dependency scanning

See merge request hectorjsmith/fail2ban-prometheus-exporter!93
2023-06-21 17:50:43 +00:00
Hector
cfa20f6c1d ci: configure dependency scanning (!93)
* Enable dependency scanning CI pipeline

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/93
2023-06-21 17:50:43 +00:00
Hector
211a5015d0 Merge branch 'chore/update-makefile-commands' into 'main'
chore: update makefile commands

See merge request hectorjsmith/fail2ban-prometheus-exporter!92
2023-06-21 15:02:11 +00:00
Hector
56ecc03341 chore: update makefile commands (!92)
* Tweak makefile commands and file formatting
* Remove command and build step for go mod formatting
* Add command to update project dependencies
* Add make command to list all make commands

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/92
2023-06-21 15:02:11 +00:00
Hector
b32bbeb1cd Merge branch 'fix/set-http-server-timeouts' into 'main'
fix: set http server timeouts

See merge request hectorjsmith/fail2ban-prometheus-exporter!91
2023-06-21 11:09:39 +00:00
Hector
37b67643e8 fix: set http server timeouts (!91)
* Set timeout values when configuring the HTTP server to mitigate "Slowloris" vulnerability

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/91
2023-06-21 11:09:39 +00:00
Hector
1eec68b49b Merge branch 'refactor/move-server-setup-code-to-new-package' into 'main'
refactor: move server setup code to new package

See merge request hectorjsmith/fail2ban-prometheus-exporter!90
2023-06-21 10:58:43 +00:00
Hector
11c4b26c0b refactor: move server setup code to new package (!90)
* Move code setting up the HTTP server to it's own package
* This helps clean up the `main` function and make it easier to read
* Rename the `BasicAuthMiddleware` to remove reference to Basic since it can now handle any type of auth type

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/90
2023-06-21 10:58:43 +00:00
Hector
41b05f7e16 Merge branch 'refactor/rewrite-auth-handler-code' into 'main'
refactor: rewrite auth handler code

See merge request hectorjsmith/fail2ban-prometheus-exporter!89
2023-06-21 10:31:33 +00:00
Hector
3cff8ccd64 refactor: rewrite auth handler code (!89)
* Rewrite the code handling basic auth to make it easier to extend for other types of auth.
* The behaviour of the existing code is maintained.
* No changes to how basic auth is configured from a user's perspective.

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/89
2023-06-21 10:31:33 +00:00
Hector
3215fe5f4c Merge branch 'refactor/replace-cli-parser-with-kong' into 'main'
refactor: replace cli parser with kong

See merge request hectorjsmith/fail2ban-prometheus-exporter!88
2023-06-20 20:16:23 +00:00
Hector
43cab7adc2 refactor: replace cli parser with kong (!88)
https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/88
2023-06-20 20:16:23 +00:00
Hector
d584345fc6 Merge branch 'ci/add-sast-step-to-pipeline' into 'main'
ci: add sast step to pipeline

See merge request hectorjsmith/fail2ban-prometheus-exporter!87
2023-06-20 17:24:16 +00:00
Hector
8974395bba ci: add sast step to pipeline (!87)
https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/87
2023-06-20 17:24:16 +00:00
Hector
ad593dbd9b Merge branch 'docs/update-readme-with-latest-changes' into 'main'
docs: update readme with latest changes

See merge request hectorjsmith/fail2ban-prometheus-exporter!86
2023-06-20 16:34:25 +00:00
Hector
f1e69fc4da docs: update readme with latest changes (!86)
Update `README` to reflect recent changes in the project build process.

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/86
2023-06-20 16:34:25 +00:00
Hector
30b92d61da Merge branch 'feat/tag-docker-images-with-extra-labels' into 'main'
feat: tag docker images with extra labels

See merge request hectorjsmith/fail2ban-prometheus-exporter!85
2023-06-20 07:20:11 +00:00
Hector
812f506432 feat: tag docker images with extra labels (!85)
Add a new build step to tag docker images with extra labels:
- `:latest`
- `:<major>`
- `:<major>.<minor>`

This job is only triggered on main release tags.

https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/85
2023-06-20 07:20:11 +00:00
Hector
f29e127f5f Merge branch 'chore/update-go-and-dependencies' into 'main'
chore: update go and dependencies

See merge request hectorjsmith/fail2ban-prometheus-exporter!84
2023-06-19 21:59:42 +00:00
Hector
d7f9b5c4ab chore: update go and dependencies
Update Go version to 1.20
Update dependencies to latest versions
2023-06-19 21:59:42 +00:00
Hector
56cc0b7901 Merge branch 'fix/disable-cgo-in-build' into 'main'
fix: disable cgo in goreleaser build

See merge request hectorjsmith/fail2ban-prometheus-exporter!83
2023-06-19 21:27:09 +00:00
Hector
53a1f12f39 fix: disable cgo in goreleaser build 2023-06-19 21:27:09 +00:00
32 changed files with 692 additions and 918 deletions

View file

@ -6,11 +6,12 @@ stages:
.go_template:
image: golang:latest
dependencies:
extends: .go_template
sast:
stage: test
script:
- make check/dependencies
include:
- template: Security/SAST.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml
format:
extends: .go_template
@ -18,6 +19,13 @@ format:
script:
- make check/fmt
vet:
extends: .go_template
stage: test
allow_failure: true
script:
- make vet
test:
extends: .go_template
stage: test
@ -47,8 +55,6 @@ release:
rules:
- if: $CI_COMMIT_TAG =~ /^v.*$/
script: |
# GITLAB_TOKEN is needed to create GitLab releases.
# DOCKER_* are needed to push Docker images.
docker run --rm --privileged \
-v $PWD:/go/src/gitlab.com/hectorjsmith/fail2ban-prometheus-exporter \
-w /go/src/gitlab.com/hectorjsmith/fail2ban-prometheus-exporter \

View file

@ -1,6 +1,6 @@
project_name: fail2ban_exporter
builds:
-
- env: [CGO_ENABLED=0]
binary: fail2ban_exporter
goos:
- linux
@ -15,9 +15,18 @@ builds:
- "7"
dockers:
- image_templates: ["registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Version }}"]
dockerfile: Dockerfile
- image_templates:
- "registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Major }}-amd64"
- "registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Major }}.{{ .Minor }}-amd64"
- 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Version }}-amd64'
- "registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:latest-amd64"
extra_files:
- health
use: buildx
dockerfile: Dockerfile.goreleaser
build_flag_templates:
- "--pull"
- "--platform=linux/amd64"
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter
@ -26,6 +35,48 @@ dockers:
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=MIT
- image_templates:
- "registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Major }}-arm64"
- "registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Major }}.{{ .Minor }}-arm64"
- 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Version }}-arm64'
- "registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:latest-arm64"
extra_files:
- health
use: buildx
dockerfile: Dockerfile.goreleaser
build_flag_templates:
- "--pull"
- "--platform=linux/arm64"
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter
- --label=org.opencontainers.image.source=https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=MIT
goarch: arm64
docker_manifests:
- name_template: 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Major }}'
image_templates:
- 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Major }}-amd64'
- 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Major }}-arm64'
- name_template: 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Major }}.{{ .Minor }}'
image_templates:
- 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Major }}.{{ .Minor }}-amd64'
- 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Major }}.{{ .Minor }}-arm64'
- name_template: 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Version }}'
image_templates:
- 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Version }}-amd64'
- 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:{{ .Version }}-arm64'
- name_template: 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:latest'
image_templates:
- 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:latest-amd64'
- 'registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:latest-arm64'
changelog:
groups:
@ -43,3 +94,4 @@ changelog:
filters:
exclude:
- "^Merge"
- "^merge"

View file

@ -1,10 +1,30 @@
FROM debian:buster-slim
FROM golang:1.20-buster AS build
# Create build workspace folder
WORKDIR /workspace
ADD . /workspace
# Install updates and build tools
RUN apt update --yes && \
apt install --yes build-essential
# Build the actual binary
RUN make build
# -- -- -- -- -- --
# Set up image to run the tool
FROM alpine
# Create main app folder to run from
WORKDIR /app
# Copy compiled binary to release image
# (must build the binary before running docker build)
COPY fail2ban_exporter /app/fail2ban_exporter
# Copy built binary from build image
COPY --from=build /workspace/fail2ban_exporter /app
# Setup a healthcheck
COPY health /app/health
RUN apk add curl
HEALTHCHECK --interval=10s --timeout=4s --retries=3 CMD /app/health
ENTRYPOINT ["/app/fail2ban_exporter"]

15
Dockerfile.goreleaser Normal file
View file

@ -0,0 +1,15 @@
FROM alpine
# Create main app folder to run from
WORKDIR /app
# Copy compiled binary to release image
# (must build the binary before running docker build)
COPY fail2ban_exporter /app/fail2ban_exporter
# Setup a healthcheck
COPY health /app/health
RUN apk add curl
HEALTHCHECK --interval=10s --timeout=4s --retries=3 CMD /app/health
ENTRYPOINT ["/app/fail2ban_exporter"]

View file

@ -1,23 +1,45 @@
.PHONY: download test fmt check/dependencies check/fmt build build/docker
# List make commands
.PHONY: ls
ls:
cat Makefile | grep "^[a-zA-Z#].*" | cut -d ":" -f 1 | sed s';#;\n#;'g
# Download dependencies
.PHONY: download
download:
go mod download
# Update project dependencies
.PHONY: update
update:
go get -u
go mod download
go mod tidy
# Run project tests
.PHONY: test
test: download
go test ./... -v -race
# Look for "suspicious constructs" in source code
.PHONY: vet
vet: download
go vet ./...
# Format code
.PHONY: fmt
fmt: download
go mod tidy
go fmt ./...
check/dependencies: download
go mod tidy -v
git diff-index --quiet HEAD
# Check for unformatted go code
.PHONY: check/fmt
check/fmt: download
test -z $(shell gofmt -l .)
# Build project
.PHONY: build
build:
go build \
CGO_ENABLED=0 go build \
-ldflags "\
-X main.version=${shell git describe --tags} \
-X main.commit=${shell git rev-parse HEAD} \
@ -27,5 +49,7 @@ build:
-o fail2ban_exporter \
exporter.go
build/docker: build
# Build project docker container
.PHONY: build/docker
build/docker:
docker build -t fail2ban-prometheus-exporter .

View file

@ -1,6 +1,7 @@
# Fail2Ban Prometheus Exporter
[![Pipeline](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/badges/main/pipeline.svg)](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter)
[![Go Report Card](https://goreportcard.com/badge/gitlab.com/hectorjsmith/fail2ban-prometheus-exporter)](https://goreportcard.com/report/gitlab.com/hectorjsmith/fail2ban-prometheus-exporter)
Collect metrics from a running fail2ban instance.
@ -10,6 +11,7 @@ Collect metrics from a running fail2ban instance.
3. Configuration
4. Building from source
5. Textfile metrics
6. Troubleshooting
## 1. Quick Start
@ -22,7 +24,7 @@ The following command will start collecting metrics from the `/var/run/fail2ban/
```
$ fail2ban_exporter --collector.f2b.socket=/var/run/fail2ban/fail2ban.sock --web.listen-address=":9191"
2022/02/20 09:54:06 fail2ban exporter version 0.5.0
2022/02/20 09:54:06 fail2ban exporter version 0.8.1
2022/02/20 09:54:06 starting server at :9191
2022/02/20 09:54:06 reading metrics from fail2ban socket: /var/run/fail2ban/fail2ban.sock
2022/02/20 09:54:06 metrics available at '/metrics'
@ -31,6 +33,9 @@ $ fail2ban_exporter --collector.f2b.socket=/var/run/fail2ban/fail2ban.sock --web
Binary files for each release can be found on the [releases](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/releases) page.
There is also an [example systemd service file](/_examples/systemd/fail2ban_exporter.service) included in the repository.
This is a starting point to run the exporter as a service.
### 1.2. Docker
**Docker run**
@ -55,7 +60,7 @@ services:
- "9191:9191"
```
Use the `:latest` tag to get the latest stable release. Or use the `:nightly` tag for the latest (unstable) version.
Use the `:latest` tag to get the latest stable release.
See the [registry page](https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/container_registry) for all available tags.
**NOTE:** While it is possible to mount the `fail2ban.sock` file directly, it is recommended to mount the parent folder instead.
@ -100,7 +105,7 @@ Status for the jail: sshd
### 2.1. Grafana
The metrics exported by this tool are compatible with Prometheus and Grafana.
A sample grafana dashboard can be found in the [grafana.json](/examples/grafana/dashboard.json) file.
A sample grafana dashboard can be found in the [grafana.json](/_examples/grafana/dashboard.json) file.
Just import the contents of this file into a new Grafana dashboard to get started.
The dashboard supports displaying data from multiple exporters. Use the `instance` dashboard variable to select which ones to display.
@ -114,23 +119,31 @@ There are no configuration files.
**CLI flags**
```
usage: exporter [<flags>]
🚀 Collect prometheus metrics from a running Fail2Ban instance
Flags:
-h, --help Show context-sensitive help (also try --help-long and --help-man).
-v, --version show version info and exit
--collector.f2b.socket="/var/run/fail2ban/fail2ban.sock"
path to the fail2ban server socket
--collector.textfile.directory=""
directory to read text files with metrics from
--web.listen-address=":9191"
address to use for the metrics server
--web.basic-auth.username=""
username to use to protect endpoints with basic auth
--web.basic-auth.password=""
password to use to protect endpoints with basic auth
--collector.f2b.exit-on-socket-connection-error
when set to true the exporter will immediately exit on a fail2ban socket connection error
-h, --help Show context-sensitive help.
-v, --version Show version info and exit
--dry-run Attempt to connect to the fail2ban socket then exit
before starting the server
--web.listen-address=":9191" Address to use for the metrics server
($F2B_WEB_LISTEN_ADDRESS)
--collector.f2b.socket="/var/run/fail2ban/fail2ban.sock"
Path to the fail2ban server socket
($F2B_COLLECTOR_SOCKET)
--collector.f2b.exit-on-socket-connection-error
When set to true the exporter will immediately
exit on a fail2ban socket connection error
($F2B_EXIT_ON_SOCKET_CONN_ERROR)
--collector.textfile.directory=STRING
Directory to read text files with metrics from
($F2B_COLLECTOR_TEXT_PATH)
--web.basic-auth.username=STRING
Username to use to protect endpoints with basic auth
($F2B_WEB_BASICAUTH_USER)
--web.basic-auth.password=STRING
Password to use to protect endpoints with basic auth
($F2B_WEB_BASICAUTH_PASS)
```
**Environment variables**
@ -149,12 +162,13 @@ If both are specified, the CLI flag takes precedence.
## 4. Building from source
The simplest way to build the project is to run the `build/snapshot` make command.
This will use `goreleaser` to build out binaries and archives for the project.
Binaries are stored in the `dist/` folder.
Building from source has the following dependencies:
- Go v1.20
- Make
Alternatively, `go mod download` and `go build` can be used from the `src/` folder to build out the project.
This will download dependencies and build the project.
From there, simply run `make build`
This will download the necessary dependencies and build a `fail2ban_exporter` binary in the root of the project.
## 5. Textfile metrics
@ -191,3 +205,35 @@ docker run -d \
-p "9191:9191" \
registry.gitlab.com/hectorjsmith/fail2ban-prometheus-exporter:latest
```
## 6. Troubleshooting
### 6.1. "no such file or directory"
```
error opening socket: dial unix /var/run/fail2ban/fail2ban.sock: connect: no such file or directory
```
There are a couple of potential causes for the error above.
**File not found**
The first is that the file does not exist, so first check that the file path shown in the error actually exists on the system running the exporter.
The fail2ban server may be storing the socket file in another location on your machine.
If you are using docker, make sure the correct host folder was mounted to the correct location.
If the file is not in the expected location, you can run the exporter with the corresponding CLI flag or environment variable to use a different file path.
**Permissions**
If the file does exist, the likely cause are file permissions.
By default, the fail2ban server runs as the `root` user and the socket file can only be accessed by the same user.
If you are running the exporter as a non-root user, it will not be able to open the socket file to read/write commands to the server, leading to the error above.
In this case there are a few solutions:
1. Run the exporter as the same user as fail2ban (usually `root`)
2. Update the fail2ban server config to run as a non-root user, then run the exporter as the same user
3. Update the socket file permissions to be less restrictive
I would recommend option `1.` since it is the simplest. Option `2.` is a bit more complex, check the [fail2ban server documentation](https://coderwall.com/p/haj28a/running-rootless-fail2ban-on-debian) for more details. And option `3.` is just a temporary fix. The socket file gets re-created each time the fail2ban server is restarted and the original permissions will be restored, so you will need to update the permissions every time the server restarts.

View file

@ -0,0 +1,19 @@
[Unit]
Description=Fail2ban metric exporter for Prometheus
Documentation=https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/blob/main/README.md
Requires=network-online.target
After=network-online.target
[Service]
ExecStart=/usr/sbin/fail2ban_exporter
Restart=on-failure
RestartSec=5s
NoNewPrivileges=true
# Currently need to run the exporter as root to ensure it has read/write access to the
# fail2ban socket file.
User=root
Group=root
[Install]
WantedBy=multi-user.target

29
auth/basic.go Normal file
View file

@ -0,0 +1,29 @@
package auth
import (
"fmt"
"net/http"
)
func NewBasicAuthProvider(username, password string) AuthProvider {
return &basicAuthProvider{
hashedAuth: encodeBasicAuth(username, password),
}
}
type basicAuthProvider struct {
hashedAuth string
}
func (p *basicAuthProvider) IsAllowed(request *http.Request) bool {
username, password, ok := request.BasicAuth()
if !ok {
return false
}
requestAuth := encodeBasicAuth(username, password)
return p.hashedAuth == requestAuth
}
func encodeBasicAuth(username, password string) string {
return HashString(fmt.Sprintf("%s:%s", username, password))
}

53
auth/basic_test.go Normal file
View file

@ -0,0 +1,53 @@
package auth
import (
"net/http"
"net/http/httptest"
"testing"
)
func Test_GIVEN_BasicAuthSet_WHEN_CallingIsAllowedWithCorrectCreds_THEN_TrueReturned(t *testing.T) {
// assemble
username := "u1"
password := HashString("abc")
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
request.SetBasicAuth(username, password)
provider := NewBasicAuthProvider(username, password)
// act
result := provider.IsAllowed(request)
// assert
if !result {
t.Errorf("expected request to be allowed, but failed")
}
}
func Test_GIVEN_BasicAuthSet_WHEN_CallingIsAllowedWithoutCreds_THEN_FalseReturned(t *testing.T) {
// assemble
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
provider := NewBasicAuthProvider("u1", "p1")
// act
result := provider.IsAllowed(request)
// assert
if result {
t.Errorf("expected request to be denied, but was allowed")
}
}
func Test_GIVEN_BasicAuthSet_WHEN_CallingIsAllowedWithWrongCreds_THEN_FalseReturned(t *testing.T) {
// assemble
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
request.SetBasicAuth("wrong", "pw")
provider := NewBasicAuthProvider("u1", "p1")
// act
result := provider.IsAllowed(request)
// assert
if result {
t.Errorf("expected request to be denied, but was allowed")
}
}

14
auth/empty.go Normal file
View file

@ -0,0 +1,14 @@
package auth
import "net/http"
func NewEmptyAuthProvider() AuthProvider {
return &emptyAuthProvider{}
}
type emptyAuthProvider struct {
}
func (p *emptyAuthProvider) IsAllowed(request *http.Request) bool {
return true
}

36
auth/empty_test.go Normal file
View file

@ -0,0 +1,36 @@
package auth
import (
"net/http"
"net/http/httptest"
"testing"
)
func Test_GIVEN_EmptyAuth_WHEN_CallingIsAllowedWithoutAuth_THEN_TrueReturned(t *testing.T) {
// assemble
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
provider := NewEmptyAuthProvider()
// act
response := provider.IsAllowed(request)
// assert
if !response {
t.Errorf("expected request to be allowed, but failed")
}
}
func Test_GIVEN_EmptyAuth_WHEN_CallingIsAllowedWithAuth_THEN_TrueReturned(t *testing.T) {
// assemble
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
request.SetBasicAuth("user", "pass")
provider := NewEmptyAuthProvider()
// act
response := provider.IsAllowed(request)
// assert
if !response {
t.Errorf("expected request to be allowed, but failed")
}
}

View file

@ -5,7 +5,7 @@ import (
"encoding/hex"
)
func Hash(data []byte) []byte {
func hash(data []byte) []byte {
if len(data) == 0 {
return []byte{}
}
@ -14,5 +14,5 @@ func Hash(data []byte) []byte {
}
func HashString(data string) string {
return hex.EncodeToString(Hash([]byte(data)))
return hex.EncodeToString(hash([]byte(data)))
}

View file

@ -1,31 +0,0 @@
package auth
import (
"net/http"
)
type BasicAuthProvider interface {
Enabled() bool
DoesBasicAuthMatch(username, password string) bool
}
func BasicAuthMiddleware(handlerFunc http.HandlerFunc, basicAuthProvider BasicAuthProvider) http.HandlerFunc {
if basicAuthProvider.Enabled() {
return func(w http.ResponseWriter, r *http.Request) {
if doesBasicAuthMatch(r, basicAuthProvider) {
handlerFunc.ServeHTTP(w, r)
} else {
w.WriteHeader(http.StatusUnauthorized)
}
}
}
return handlerFunc
}
func doesBasicAuthMatch(r *http.Request, basicAuthProvider BasicAuthProvider) bool {
rawUsername, rawPassword, ok := r.BasicAuth()
if ok {
return basicAuthProvider.DoesBasicAuthMatch(rawUsername, rawPassword)
}
return false
}

View file

@ -1,58 +0,0 @@
package auth
import (
"net/http"
"net/http/httptest"
"testing"
)
type testAuthProvider struct {
enabled bool
match bool
}
func (p testAuthProvider) Enabled() bool {
return p.enabled
}
func (p testAuthProvider) DoesBasicAuthMatch(username, password string) bool {
return p.match
}
func newTestRequest() *http.Request {
return httptest.NewRequest(http.MethodGet, "http://example.com", nil)
}
func executeBasicAuthMiddlewareTest(t *testing.T, authEnabled bool, authMatches bool, expectedCode int, expectedCallCount int) {
callCount := 0
testHandler := func(w http.ResponseWriter, r *http.Request) {
callCount++
}
handler := BasicAuthMiddleware(testHandler, testAuthProvider{enabled: authEnabled, match: authMatches})
recorder := httptest.NewRecorder()
request := newTestRequest()
if authEnabled {
request.SetBasicAuth("test", "test")
}
handler.ServeHTTP(recorder, request)
if recorder.Code != expectedCode {
t.Errorf("statusCode = %v, want %v", recorder.Code, expectedCode)
}
if callCount != expectedCallCount {
t.Errorf("callCount = %v, want %v", callCount, expectedCallCount)
}
}
func Test_GIVEN_DisabledBasicAuth_WHEN_MethodCalled_THEN_RequestProcessed(t *testing.T) {
executeBasicAuthMiddlewareTest(t, false, false, http.StatusOK, 1)
}
func Test_GIVEN_EnabledBasicAuth_WHEN_MethodCalledWithCorrectCredentials_THEN_RequestProcessed(t *testing.T) {
executeBasicAuthMiddlewareTest(t, true, true, http.StatusOK, 1)
}
func Test_GIVEN_EnabledBasicAuth_WHEN_MethodCalledWithIncorrectCredentials_THEN_RequestRejected(t *testing.T) {
executeBasicAuthMiddlewareTest(t, true, false, http.StatusUnauthorized, 0)
}

9
auth/provider.go Normal file
View file

@ -0,0 +1,9 @@
package auth
import (
"net/http"
)
type AuthProvider interface {
IsAllowed(*http.Request) bool
}

View file

@ -1,25 +0,0 @@
package cfg
import "gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/auth"
type hashedBasicAuth struct {
username string
password string
}
func newHashedBasicAuth(rawUsername, rawPassword string) *hashedBasicAuth {
return &hashedBasicAuth{
username: auth.HashString(rawUsername),
password: auth.HashString(rawPassword),
}
}
func (p *hashedBasicAuth) Enabled() bool {
return len(p.username) > 0 && len(p.password) > 0
}
func (p *hashedBasicAuth) DoesBasicAuthMatch(rawUsername, rawPassword string) bool {
username := auth.HashString(rawUsername)
password := auth.HashString(rawPassword)
return username == p.username && password == p.password
}

View file

@ -1,60 +0,0 @@
package cfg
import "testing"
func Test_hashedBasicAuth_DoesBasicAuthMatch(t *testing.T) {
type args struct {
username string
password string
}
type fields struct {
username string
password string
}
tests := []struct {
name string
fields fields
args args
want bool
}{
{"Happy test #1", fields{username: "1234", password: "1234"}, args{username: "1234", password: "1234"}, true},
{"Happy test #2", fields{username: "test", password: "1234"}, args{username: "test", password: "1234"}, true},
{"Happy test #3", fields{username: "TEST", password: "1234"}, args{username: "TEST", password: "1234"}, true},
{"Non match #1", fields{username: "test", password: "1234"}, args{username: "1234", password: "1234"}, false},
{"Non match #2", fields{username: "1234", password: "test"}, args{username: "1234", password: "1234"}, false},
{"Non match #3", fields{username: "1234", password: "test"}, args{username: "1234", password: "TEST"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
basicAuth := newHashedBasicAuth(tt.fields.username, tt.fields.password)
if got := basicAuth.DoesBasicAuthMatch(tt.args.username, tt.args.password); got != tt.want {
t.Errorf("DoesBasicAuthMatch() = %v, want %v", got, tt.want)
}
})
}
}
func Test_hashedBasicAuth_Enabled(t *testing.T) {
type fields struct {
username string
password string
}
tests := []struct {
name string
fields fields
want bool
}{
{"Both blank", fields{username: "", password: ""}, false},
{"Single blank #1", fields{username: "test", password: ""}, false},
{"Single blank #1", fields{username: "", password: "test"}, false},
{"Both populated", fields{username: "test", password: "test"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
basicAuth := newHashedBasicAuth(tt.fields.username, tt.fields.password)
if got := basicAuth.Enabled(); got != tt.want {
t.Errorf("Enabled() = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -2,108 +2,83 @@ package cfg
import (
"fmt"
"github.com/alecthomas/kingpin/v2"
"log"
"os"
"github.com/alecthomas/kong"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/auth"
)
const (
socketEnvName = "F2B_COLLECTOR_SOCKET"
fileCollectorPathEnvName = "F2B_COLLECTOR_TEXT_PATH"
addressEnvName = "F2B_WEB_LISTEN_ADDRESS"
basicAuthUserEnvName = "F2B_WEB_BASICAUTH_USER"
basicAuthPassEnvName = "F2B_WEB_BASICAUTH_PASS"
exitOnSocketConnErrorEnvName = "F2B_EXIT_ON_SOCKET_CONN_ERROR"
)
type AppSettings struct {
VersionMode bool
MetricsAddress string
Fail2BanSocketPath string
FileCollectorPath string
BasicAuthProvider *hashedBasicAuth
ExitOnSocketConnError bool
}
func init() {
kingpin.HelpFlag.Short('h')
var cliStruct struct {
VersionMode bool `name:"version" short:"v" help:"Show version info and exit"`
DryRunMode bool `name:"dry-run" help:"Attempt to connect to the fail2ban socket then exit before starting the server"`
ServerAddress string `name:"web.listen-address" env:"F2B_WEB_LISTEN_ADDRESS" help:"Address to use for the metrics server" default:"${default_address}"`
F2bSocketPath string `name:"collector.f2b.socket" env:"F2B_COLLECTOR_SOCKET" help:"Path to the fail2ban server socket" default:"${default_socket}"`
ExitOnSocketError bool `name:"collector.f2b.exit-on-socket-connection-error" env:"F2B_EXIT_ON_SOCKET_CONN_ERROR" help:"When set to true the exporter will immediately exit on a fail2ban socket connection error"`
TextFileExporterPath string `name:"collector.textfile.directory" env:"F2B_COLLECTOR_TEXT_PATH" help:"Directory to read text files with metrics from"`
BasicAuthUser string `name:"web.basic-auth.username" env:"F2B_WEB_BASICAUTH_USER" help:"Username to use to protect endpoints with basic auth"`
BasicAuthPass string `name:"web.basic-auth.password" env:"F2B_WEB_BASICAUTH_PASS" help:"Password to use to protect endpoints with basic auth"`
}
func Parse() *AppSettings {
settings := &AppSettings{}
readParamsFromCli(settings)
settings.validateFlags()
ctx := kong.Parse(
&cliStruct,
kong.Vars{
"default_socket": "/var/run/fail2ban/fail2ban.sock",
"default_address": ":9191",
},
kong.Name("fail2ban_exporter"),
kong.Description("🚀 Export prometheus metrics from a running Fail2Ban instance"),
kong.UsageOnError(),
)
validateFlags(ctx)
settings := &AppSettings{
VersionMode: cliStruct.VersionMode,
DryRunMode: cliStruct.DryRunMode,
MetricsAddress: cliStruct.ServerAddress,
Fail2BanSocketPath: cliStruct.F2bSocketPath,
FileCollectorPath: cliStruct.TextFileExporterPath,
ExitOnSocketConnError: cliStruct.ExitOnSocketError,
AuthProvider: createAuthProvider(),
}
return settings
}
func readParamsFromCli(settings *AppSettings) {
versionMode := kingpin.
Flag("version", "show version info and exit").
Short('v').
Default("false").
Bool()
socketPath := kingpin.
Flag("collector.f2b.socket", "path to the fail2ban server socket").
Default("/var/run/fail2ban/fail2ban.sock").
Envar(socketEnvName).
String()
fileCollectorPath := kingpin.
Flag("collector.textfile.directory", "directory to read text files with metrics from").
Default("").
Envar(fileCollectorPathEnvName).
String()
address := kingpin.
Flag("web.listen-address", "address to use for the metrics server").
Default(":9191").
Envar(addressEnvName).
String()
rawBasicAuthUsername := kingpin.
Flag("web.basic-auth.username", "username to use to protect endpoints with basic auth").
Default("").
Envar(basicAuthUserEnvName).
String()
rawBasicAuthPassword := kingpin.
Flag("web.basic-auth.password", "password to use to protect endpoints with basic auth").
Default("").
Envar(basicAuthPassEnvName).
String()
rawExitOnSocketConnError := kingpin.
Flag("collector.f2b.exit-on-socket-connection-error", "when set to true the exporter will immediately exit on a fail2ban socket connection error").
Default("false").
Envar(exitOnSocketConnErrorEnvName).
Bool()
func createAuthProvider() auth.AuthProvider {
username := cliStruct.BasicAuthUser
password := cliStruct.BasicAuthPass
kingpin.Parse()
settings.VersionMode = *versionMode
settings.MetricsAddress = *address
settings.Fail2BanSocketPath = *socketPath
settings.FileCollectorPath = *fileCollectorPath
settings.setBasicAuthValues(*rawBasicAuthUsername, *rawBasicAuthPassword)
settings.ExitOnSocketConnError = *rawExitOnSocketConnError
if len(username) == 0 && len(password) == 0 {
return auth.NewEmptyAuthProvider()
}
log.Print("basic auth enabled")
return auth.NewBasicAuthProvider(username, password)
}
func (settings *AppSettings) setBasicAuthValues(rawUsername, rawPassword string) {
settings.BasicAuthProvider = newHashedBasicAuth(rawUsername, rawPassword)
}
func (settings *AppSettings) validateFlags() {
func validateFlags(cliCtx *kong.Context) {
var flagsValid = true
if !settings.VersionMode {
if settings.Fail2BanSocketPath == "" {
fmt.Println("error: fail2ban socket path must not be blank")
var messages = []string{}
if !cliStruct.VersionMode {
if cliStruct.F2bSocketPath == "" {
messages = append(messages, "error: fail2ban socket path must not be blank")
flagsValid = false
}
if settings.MetricsAddress == "" {
fmt.Println("error: invalid server address, must not be blank")
if cliStruct.ServerAddress == "" {
messages = append(messages, "error: invalid server address, must not be blank")
flagsValid = false
}
if (len(settings.BasicAuthProvider.username) > 0) != (len(settings.BasicAuthProvider.password) > 0) {
fmt.Println("error: to enable basic auth both the username and the password must be provided")
if (len(cliStruct.BasicAuthUser) > 0) != (len(cliStruct.BasicAuthPass) > 0) {
messages = append(messages, "error: to enable basic auth both the username and the password must be provided")
flagsValid = false
}
}
if !flagsValid {
kingpin.Usage()
cliCtx.PrintUsage(false)
fmt.Println()
for i := 0; i < len(messages); i++ {
fmt.Println(messages[i])
}
os.Exit(1)
}
}

13
cfg/settings.go Normal file
View file

@ -0,0 +1,13 @@
package cfg
import "gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/auth"
type AppSettings struct {
VersionMode bool
DryRunMode bool
MetricsAddress string
Fail2BanSocketPath string
FileCollectorPath string
AuthProvider auth.AuthProvider
ExitOnSocketConnError bool
}

View file

@ -1,11 +1,12 @@
package f2b
import (
"log"
"os"
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/cfg"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/socket"
"log"
"os"
)
type Collector struct {
@ -18,7 +19,8 @@ type Collector struct {
}
func NewExporter(appSettings *cfg.AppSettings, exporterVersion string) *Collector {
log.Printf("reading metrics from fail2ban socket: %s", appSettings.Fail2BanSocketPath)
log.Printf("reading fail2ban metrics from socket file: %s", appSettings.Fail2BanSocketPath)
printFail2BanServerVersion(appSettings.Fail2BanSocketPath)
return &Collector{
socketPath: appSettings.Fail2BanSocketPath,
exporterVersion: exporterVersion,
@ -58,3 +60,33 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) {
}
c.collectErrorCountMetric(ch)
}
func (c *Collector) IsHealthy() bool {
s, err := socket.ConnectToSocket(c.socketPath)
if err != nil {
log.Printf("error opening socket: %v", err)
c.socketConnectionErrorCount++
return false
}
pingSuccess, err := s.Ping()
if err != nil {
log.Printf("error pinging fail2ban server: %v", err)
c.socketRequestErrorCount++
return false
}
return pingSuccess
}
func printFail2BanServerVersion(socketPath string) {
s, err := socket.ConnectToSocket(socketPath)
if err != nil {
log.Printf("error connecting to socket: %v", err)
} else {
version, err := s.GetServerVersion()
if err != nil {
log.Printf("error interacting with socket: %v", err)
} else {
log.Printf("successfully connected to fail2ban socket! fail2ban version: %s", version)
}
}
}

View file

@ -1,11 +1,12 @@
package textfile
import (
"github.com/prometheus/client_golang/prometheus"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"github.com/prometheus/client_golang/prometheus"
)
const namespace = "textfile"
@ -19,7 +20,7 @@ var (
)
func (c *Collector) collectFileContents() {
files, err := ioutil.ReadDir(c.folderPath)
files, err := os.ReadDir(c.folderPath)
if err != nil {
log.Printf("error reading directory '%s': %v", c.folderPath, err)
return
@ -36,7 +37,7 @@ func (c *Collector) collectFileContents() {
}
fullPath := filepath.Join(c.folderPath, fileName)
content, err := ioutil.ReadFile(fullPath)
content, err := os.ReadFile(fullPath)
if err != nil {
c.appendErrorForPath(fileName)
log.Printf("error reading contents of file '%s': %v", fileName, err)

View file

@ -1,9 +0,0 @@
[Unit]
Description=Fail2Ban Exporter
[Service]
User=fail2ban_exporter
ExecStart=/usr/sbin/fail2ban_exporter
[Install]
WantedBy=multi-user.target

View file

@ -2,21 +2,16 @@ package main
import (
"fmt"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/auth"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/cfg"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/f2b"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/textfile"
"log"
"net/http"
"os"
"os/signal"
"syscall"
)
const (
metricsPath = "/metrics"
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/cfg"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/f2b"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/textfile"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/server"
)
var (
@ -31,66 +26,34 @@ func printAppVersion() {
fmt.Printf(" build date: %s\r\n commit hash: %s\r\n built by: %s\r\n", date, commit, builtBy)
}
func rootHtmlHandler(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(
`<html>
<head><title>Fail2Ban Exporter</title></head>
<body>
<h1>Fail2Ban Exporter</h1>
<p><a href="` + metricsPath + `">Metrics</a></p>
</body>
</html>`))
if err != nil {
log.Printf("error handling root url: %v", err)
w.WriteHeader(http.StatusInternalServerError)
}
}
func metricHandler(w http.ResponseWriter, r *http.Request, collector *textfile.Collector) {
promhttp.Handler().ServeHTTP(w, r)
collector.WriteTextFileMetrics(w, r)
}
func main() {
appSettings := cfg.Parse()
if appSettings.VersionMode {
printAppVersion()
} else {
handleGracefulShutdown()
log.Printf("fail2ban exporter version %s", version)
log.Printf("starting server at %s", appSettings.MetricsAddress)
return
}
f2bCollector := f2b.NewExporter(appSettings, version)
prometheus.MustRegister(f2bCollector)
handleGracefulShutdown()
log.Printf("fail2ban exporter version %s", version)
log.Printf("starting server at %s", appSettings.MetricsAddress)
textFileCollector := textfile.NewCollector(appSettings)
prometheus.MustRegister(textFileCollector)
f2bCollector := f2b.NewExporter(appSettings, version)
prometheus.MustRegister(f2bCollector)
http.HandleFunc("/", auth.BasicAuthMiddleware(rootHtmlHandler, appSettings.BasicAuthProvider))
http.HandleFunc(metricsPath, auth.BasicAuthMiddleware(
func(w http.ResponseWriter, r *http.Request) {
metricHandler(w, r, textFileCollector)
},
appSettings.BasicAuthProvider,
))
log.Printf("metrics available at '%s'", metricsPath)
if appSettings.BasicAuthProvider.Enabled() {
log.Printf("basic auth enabled")
}
svrErr := make(chan error)
go func() {
svrErr <- http.ListenAndServe(appSettings.MetricsAddress, nil)
}()
log.Print("ready")
textFileCollector := textfile.NewCollector(appSettings)
prometheus.MustRegister(textFileCollector)
if !appSettings.DryRunMode {
svrErr := server.StartServer(appSettings, f2bCollector, textFileCollector)
err := <-svrErr
log.Fatal(err)
} else {
log.Print("running in dry-run mode - exiting")
}
}
func handleGracefulShutdown() {
var signals = make(chan os.Signal)
var signals = make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGTERM)
signal.Notify(signals, syscall.SIGINT)

19
go.mod
View file

@ -1,12 +1,21 @@
module gitlab.com/hectorjsmith/fail2ban-prometheus-exporter
go 1.15
go 1.20
require (
github.com/alecthomas/kingpin/v2 v2.3.2
github.com/alecthomas/kong v0.8.1
github.com/kisielk/og-rek v1.2.0
github.com/nlpodyssey/gopickle v0.2.0
github.com/prometheus/client_golang v1.15.1
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/prometheus/client_golang v1.17.0
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
golang.org/x/sys v0.13.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)

560
go.sum
View file

@ -1,555 +1,33 @@
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/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
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/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU=
github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
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/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
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/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0=
github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY=
github.com/alecthomas/kong v0.8.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/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/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/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-kit/log v0.2.1/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/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/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/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/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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/kisielk/og-rek v1.2.0 h1:CTvDIin+YnetsSQAYbe+QNAxXU3B50C5hseEz8xEoJw=
github.com/kisielk/og-rek v1.2.0/go.mod h1:6ihsOSzSAxR/65S3Bn9zNihoEqRquhDQZ2c6I2+MG3c=
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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
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/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/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/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/nlpodyssey/gopickle v0.2.0 h1:4naD2DVylYJupQLbCQFdwo6yiXEmPyp+0xf5MVlrBDY=
github.com/nlpodyssey/gopickle v0.2.0/go.mod h1:YIUwjJ2O7+vnBsxUN+MHAAI3N+adqEGiw+nDpwW95bY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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/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.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_model v0.0.0-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/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
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/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
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/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/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/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.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/xhit/go-str2duration v1.2.0 h1:BcV5u025cITWxEQKGWr1URRzrcXtu7uk8+luz3Yuhwc=
github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
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/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-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-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-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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
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/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-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/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/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=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.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/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-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.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
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/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=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=

8
health Executable file
View file

@ -0,0 +1,8 @@
#!/bin/sh
port=9191
if [ ! -z $F2B_WEB_LISTEN_ADDRESS ]; then
port=`echo $F2B_WEB_LISTEN_ADDRESS | cut -d ":" -f 2 -`
fi
curl --fail localhost:$port/health || exit 1

17
server/auth.go Normal file
View file

@ -0,0 +1,17 @@
package server
import (
"net/http"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/auth"
)
func AuthMiddleware(handlerFunc http.HandlerFunc, authProvider auth.AuthProvider) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if authProvider.IsAllowed(r) {
handlerFunc.ServeHTTP(w, r)
} else {
w.WriteHeader(http.StatusUnauthorized)
}
}
}

46
server/auth_test.go Normal file
View file

@ -0,0 +1,46 @@
package server
import (
"net/http"
"net/http/httptest"
"testing"
)
type testAuthProvider struct {
match bool
}
func (p testAuthProvider) IsAllowed(request *http.Request) bool {
return p.match
}
func newTestRequest() *http.Request {
return httptest.NewRequest(http.MethodGet, "http://example.com", nil)
}
func executeAuthMiddlewareTest(t *testing.T, authMatches bool, expectedCode int, expectedCallCount int) {
callCount := 0
testHandler := func(w http.ResponseWriter, r *http.Request) {
callCount++
}
handler := AuthMiddleware(testHandler, testAuthProvider{match: authMatches})
recorder := httptest.NewRecorder()
request := newTestRequest()
handler.ServeHTTP(recorder, request)
if recorder.Code != expectedCode {
t.Errorf("statusCode = %v, want %v", recorder.Code, expectedCode)
}
if callCount != expectedCallCount {
t.Errorf("callCount = %v, want %v", callCount, expectedCallCount)
}
}
func Test_GIVEN_MatchingBasicAuth_WHEN_MethodCalled_THEN_RequestProcessed(t *testing.T) {
executeAuthMiddlewareTest(t, true, http.StatusOK, 1)
}
func Test_GIVEN_NonMatchingBasicAuth_WHEN_MethodCalled_THEN_RequestRejected(t *testing.T) {
executeAuthMiddlewareTest(t, false, http.StatusUnauthorized, 0)
}

44
server/handler.go Normal file
View file

@ -0,0 +1,44 @@
package server
import (
"log"
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/f2b"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/textfile"
)
const (
metricsPath = "/metrics"
)
func rootHtmlHandler(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(
`<html>
<head><title>Fail2Ban Exporter</title></head>
<body>
<h1>Fail2Ban Exporter</h1>
<p><a href="` + metricsPath + `">Metrics</a></p>
</body>
</html>`))
if err != nil {
log.Printf("error handling root url: %v", err)
w.WriteHeader(http.StatusInternalServerError)
}
}
func metricHandler(w http.ResponseWriter, r *http.Request, collector *textfile.Collector) {
promhttp.Handler().ServeHTTP(w, r)
collector.WriteTextFileMetrics(w, r)
}
func healthHandler(w http.ResponseWriter, r *http.Request, collector *f2b.Collector) {
if collector.IsHealthy() {
w.WriteHeader(http.StatusOK)
w.Write([]byte("{\"healthy\":true}"))
} else {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("{\"healthy\":false}"))
}
}

48
server/server.go Normal file
View file

@ -0,0 +1,48 @@
package server
import (
"log"
"net/http"
"time"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/cfg"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/f2b"
"gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/textfile"
)
func StartServer(
appSettings *cfg.AppSettings,
f2bCollector *f2b.Collector,
textFileCollector *textfile.Collector,
) chan error {
http.HandleFunc("/", AuthMiddleware(
rootHtmlHandler,
appSettings.AuthProvider,
))
http.HandleFunc(metricsPath, AuthMiddleware(
func(w http.ResponseWriter, r *http.Request) {
metricHandler(w, r, textFileCollector)
},
appSettings.AuthProvider,
))
http.HandleFunc("/health",
func(w http.ResponseWriter, r *http.Request) {
healthHandler(w, r, f2bCollector)
},
)
log.Printf("metrics available at '%s'", metricsPath)
svrErr := make(chan error)
go func() {
httpServer := &http.Server{
Addr: appSettings.MetricsAddress,
ReadHeaderTimeout: 10 * time.Second,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 30 * time.Second,
}
svrErr <- httpServer.ListenAndServe()
}()
log.Print("ready")
return svrErr
}