├── .github
└── workflows
│ └── citest.yml
├── .gitignore
├── .golangci.yml
├── Apache-Dashboard.json
├── Changelog
├── LICENSE
├── Makefile
├── README.md
├── apxs.sh
├── buildtools
├── go.mod
├── go.sum
└── tools.go
├── cmd
└── mod_prometheus_status
│ ├── dump.go
│ ├── logger.go
│ ├── module.go
│ ├── prometheus.go
│ └── prometheus_internal_test.go
├── example_apache.conf
├── go.mod
├── go.sum
├── src
├── mod_prometheus_status.c
├── mod_prometheus_status.h
└── mod_prometheus_status_format.c
└── t
├── Makefile
├── common
├── Makefile
├── apache
│ ├── apache.conf
│ ├── build.sh
│ ├── build_watcher.init
│ ├── build_watcher.sh
│ ├── playbook_common.yml
│ └── prometheus.yml
├── docker-compose.yml
├── local_test.sh
└── t
│ └── 01-basic.t
├── testbox_centos8
├── Makefile
├── apache
│ ├── Dockerfile
│ └── playbook.yml
├── docker-compose.yml
└── local_test.sh
└── testbox_ubuntu18.04
├── Makefile
├── apache
├── Dockerfile
└── playbook.yml
├── docker-compose.yml
└── local_test.sh
/.github/workflows/citest.yml:
--------------------------------------------------------------------------------
1 | name: citest
2 | on: [push, pull_request]
3 | jobs:
4 | test:
5 | strategy:
6 | matrix:
7 | go-version: [1.21.x]
8 | os: [ubuntu-latest]
9 | runs-on: ${{ matrix.os }}
10 | steps:
11 | - name: Install Go
12 | uses: actions/setup-go@v2
13 | with:
14 | go-version: ${{ matrix.go-version }}
15 | - uses: actions/checkout@v2
16 | - run: sudo apt-get -y update
17 | - run: sudo apt-get -y install apache2-dev make gcc
18 | - run: make clean
19 | - run: make
20 | - run: make clean
21 | - run: make build
22 | - run: make clean
23 | - run: make updatedeps
24 | if: github.event_name != 'pull_request'
25 | - run: make citest
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.ko
3 | *.slo
4 | *.lib
5 | .libs/
6 | *.a
7 | *.la
8 | *.lo
9 | *.so
10 | *.so.*
11 | *.dylib
12 | .vscode
13 | vendor/
14 | tools/
15 | go.work
16 | go.work.sum
17 | mod_prometheus_status_go.h
18 | *.gz
19 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | linters-settings:
2 | gocyclo:
3 | min-complexity: 15
4 | goconst:
5 | min-len: 8
6 | min-occurrences: 12
7 | lll:
8 | line-length: 120
9 | gocritic:
10 | enabled-tags:
11 | - performance
12 | - style
13 | - experimental
14 | disabled-checks:
15 | - emptyFallthrough
16 | - paramTypeCombine
17 | - unnamedResult
18 | - exitAfterDefer
19 | settings:
20 | rangeValCopy:
21 | sizeThreshold: 32
22 | funlen:
23 | lines: 150
24 | statements: 120
25 | gocognit:
26 | min-complexity: 35
27 | exhaustive:
28 | default-signifies-exhaustive: true
29 | linters:
30 | enable-all: true
31 | fast: false
32 | disable:
33 | - cyclop
34 | - depguard
35 | - dupl
36 | - errcheck
37 | - exhaustruct
38 | - gochecknoglobals
39 | - gochecknoinits
40 | - goconst
41 | - godot
42 | - gofumpt
43 | - lll
44 | - nakedret
45 | - nlreturn
46 | - nonamedreturns
47 | - wsl
48 | # deprecated
49 | - exportloopref
50 | issues:
51 | max-same-issues: 0
52 | max-issues-per-linter: 0
53 | exclude-use-default: true
54 | exclude:
55 | - "`noCopy` is unused"
56 | - 'Magic number: 1\D '
57 | - 'Magic number: 2\D '
58 | - 'Magic number: 64\D '
59 | - 'commentFormatting: put a space between'
60 | - "Function 'registerMetrics' is too long"
61 | - "type assertion must be checked"
62 | - "metric name should not include type"
63 | - "counter metrics should have"
64 | - "variable name 'l' is too short"
65 | - "parameter name 'c' is too short"
66 | exclude-rules:
67 | # Exclude some linters from running on tests files.
68 | - path: _test\.go
69 | linters:
70 | - gomnd
71 |
--------------------------------------------------------------------------------
/Changelog:
--------------------------------------------------------------------------------
1 | Changes
2 |
3 | 0.3.2 Fri Nov 22 14:58:40 CET 2024
4 | - migrate example dashboard to react panels
5 |
6 | 0.3.1 Wed Nov 8 11:07:40 CET 2023
7 | - change log format to match apaches own logs
8 |
9 | 0.3.0 Fri Feb 17 08:56:46 CET 2023
10 | - update dependencies
11 | - bump required golang to 1.19
12 | - fix handling c int (#4)
13 |
14 | 0.2.0 Tue May 3 10:42:58 CEST 2022
15 | - update dependencies
16 |
17 | 0.1.0 Wed Apr 28 09:40:16 CEST 2021
18 | - fix setting PrometheusStatusResponseTimeBuckets
19 |
20 | 0.0.9 Thu Jun 25 14:04:37 CEST 2020
21 | - fix compile error on gcc 10 again
22 |
23 | 0.0.8 Tue Jun 23 22:30:20 CEST 2020
24 | - fix compile error on gcc 10
25 |
26 | 0.0.7 Mon May 18 11:05:59 CEST 2020
27 | - fix issue when initally loaded by reload instead of a fresh apache start
28 | - improve responding to metrics requests during apache reloads
29 | - update example dashboard
30 |
31 | 0.0.6 Sat May 9 18:15:31 CEST 2020
32 | - add vhost to default label names
33 | - hide 'disabled' scoreboard entry, its not an actual slot
34 | - fix permission issue again
35 |
36 | 0.0.5 Sat May 9 15:31:40 CEST 2020
37 | - add process memory/counter/io metrics
38 | - start go metric server in subprocess
39 |
40 | 0.0.4 Mon May 4 10:28:17 CEST 2020
41 | - add example grafana dashboard
42 | - add new apache directives:
43 | - PrometheusStatusTmpFolder
44 | - PrometheusStatusResponseTimeBuckets
45 | - PrometheusStatusResponseSizeBuckets
46 | - fix permission issue with system apaches starting as root
47 |
48 | 0.0.3 Wed Apr 29 10:01:17 CEST 2020
49 | - fixed reload issue on some systems
50 |
51 | 0.0.2 Tue Apr 21 16:53:05 CEST 2020
52 | - add missing includes
53 | - improve build from dist tarball
54 |
55 | 0.0.1 Tue Apr 21 09:43:37 CEST 2020
56 | - initial release
57 | - dynamic labeling
58 | - basic set of metrics
59 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 |
3 | # Makefile for mod_prometheus_status.c
4 |
5 | MAKE:=make
6 | SHELL:=bash
7 | APXS=./apxs.sh
8 | WRAPPER_SOURCE=src/mod_prometheus_status.c src/mod_prometheus_status_format.c
9 | WRAPPER_HEADER=src/mod_prometheus_status.h
10 | GO_SRC_DIR=cmd/mod_prometheus_status
11 | GO_SOURCES=\
12 | $(GO_SRC_DIR)/dump.go\
13 | $(GO_SRC_DIR)/logger.go\
14 | $(GO_SRC_DIR)/prometheus.go\
15 | $(GO_SRC_DIR)/module.go
16 | DISTFILES=\
17 | $(APXS) \
18 | $(GO_SOURCES) \
19 | $(WRAPPER_SOURCE) \
20 | $(WRAPPER_HEADER) \
21 | buildtools/ \
22 | example_apache.conf \
23 | go.* \
24 | vendor \
25 | Apache-Dashboard.json \
26 | LICENSE \
27 | README.md \
28 | Changelog \
29 | Makefile
30 |
31 | GOVERSION:=$(shell \
32 | go version | \
33 | awk -F'go| ' '{ split($$5, a, /\./); printf ("%04d%04d", a[1], a[2]); exit; }' \
34 | )
35 | MINGOVERSION:=00010022
36 | MINGOVERSIONSTR:=1.22
37 | BUILDHASH:=$(shell git rev-parse --short HEAD)
38 | BUILDDATE:=$(shell LC_TIME=C date +%Y-%d-%m_%T)
39 | # see https://github.com/go-modules-by-example/index/blob/master/010_tools/README.md
40 | # and https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
41 | TOOLSFOLDER=$(shell pwd)/tools
42 | export GOBIN := $(TOOLSFOLDER)
43 | export PATH := $(GOBIN):$(PATH)
44 |
45 | GO=go
46 |
47 | VERSION=$(shell grep "define VERSION" $(WRAPPER_HEADER) | cut -d " " -f 3 | tr -d '"')
48 | NAME=$(shell grep "define NAME" $(WRAPPER_HEADER) | cut -d " " -f 3 | tr -d '"')
49 | RELEASENAME=$(NAME)-$(VERSION)-$(shell uname | tr 'A-Z' 'a-z')-$(shell uname -m)
50 | BUILD_TAG=$(shell git rev-parse --short HEAD >/dev/null 2>&1 >/dev/null 2>&1)
51 | ifeq ($(BUILD_TAG),)
52 | BUILD_TAG=$(VERSION)
53 | endif
54 |
55 | .PHONY: vendor
56 |
57 | all: build
58 |
59 | CMDS = $(shell cd ./cmd && ls -1)
60 |
61 | tools: | versioncheck
62 | set -e; for DEP in $(shell grep "_ " buildtools/tools.go | awk '{ print $$2 }' | grep -v go-spew); do \
63 | ( cd buildtools && $(GO) install $$DEP@latest ) ; \
64 | done
65 | set -e; for DEP in $(shell grep "_ " buildtools/tools.go | awk '{ print $$2 }' | grep go-spew); do \
66 | ( cd buildtools && $(GO) install $$DEP ) ; \
67 | done
68 | ( cd buildtools && $(GO) mod tidy )
69 |
70 | updatedeps: versioncheck
71 | $(MAKE) clean
72 | $(MAKE) tools
73 | $(GO) mod download
74 | set -e; for dir in $(shell ls -d1 cmd/*); do \
75 | ( cd ./$$dir && $(GO) mod download ); \
76 | ( cd ./$$dir && GOPROXY=direct $(GO) get -u ); \
77 | ( cd ./$$dir && GOPROXY=direct $(GO) get -t -u ); \
78 | done
79 | $(GO) mod download
80 | $(MAKE) cleandeps
81 |
82 | cleandeps:
83 | set -e; for dir in $(shell ls -d1 cmd/*); do \
84 | ( cd ./$$dir && $(GO) mod tidy ); \
85 | done
86 | $(GO) mod tidy
87 | ( cd buildtools && $(GO) mod tidy )
88 |
89 | vendor: go.work
90 | $(GO) mod download
91 | $(GO) mod tidy
92 | GOWORK=off $(GO) mod vendor
93 |
94 | go.work:
95 | echo "go $(MINGOVERSIONSTR)" > go.work
96 | $(GO) work use . cmd/* buildtools/.
97 |
98 |
99 |
100 | build: mod_prometheus_status.so
101 |
102 | install: mod_prometheus_status.so
103 | @echo "make install is not supported, simply copy mod_prometheus_status.so to your apache folder"
104 | @echo "and add a LoadModule configuration. See the README for an example configuration."
105 |
106 | clean:
107 | rm -rf *.so src/.libs/ src/*.la src/*.lo src/*.slo mod_prometheus_status_go.h
108 | -$(MAKE) -C t clean
109 |
110 | test: citest releasetest
111 | $(MAKE) -C t test
112 |
113 | testbox_centos8:
114 | $(MAKE) -C t testbox_centos8
115 |
116 | update_readme_available_metrics: testbox_centos8
117 | echo '```' > metrics.txt
118 | curl -qs http://localhost:3000/metrics >/dev/null 2>&1 # warm up metrics
119 | curl -qs http://localhost:3000/metrics | grep ^# | grep apache | sort -k 3 >> metrics.txt
120 | echo '```' >> metrics.txt
121 | sed -e '/^\ *\# \(HELP\|TYPE\)/d' -i README.md
122 | sed -zE 's/```\n```/###METRICS###/' -i README.md
123 | sed -e '/###METRICS###/r metrics.txt' -i README.md
124 | sed -e '/###METRICS###/d' -i README.md
125 | rm metrics.txt
126 |
127 | dist:
128 | rm -f $(NAME)-$(VERSION).tar.gz
129 | mv buildtools/tools.go buildtools/tools.go.off
130 | go mod vendor
131 | mv buildtools/tools.go.off buildtools/tools.go
132 | mv cmd/mod_prometheus_status/dump.go .
133 | echo "package main" > cmd/mod_prometheus_status/dump.go
134 | tar --transform 's,^,./$(NAME)-$(VERSION)/,g' -cf $(NAME)-$(VERSION).tar $(DISTFILES)
135 | gzip -9 $(NAME)-$(VERSION).tar
136 | mv dump.go cmd/mod_prometheus_status/dump.go
137 | go mod vendor
138 |
139 | releasetarball: clean build
140 | rm -f $(RELEASENAME).tar.gz
141 | tar --transform 's,^,./$(RELEASENAME)/,g' -cf $(RELEASENAME).tar *.so example_apache.conf README.md LICENSE Changelog
142 | gzip -9 $(RELEASENAME).tar
143 |
144 | release: releasetest
145 | @echo "***************************************"
146 | @echo "release tarballs created:"
147 | @echo " - $(RELEASENAME).tar.gz"
148 | @echo " - $(NAME)-$(VERSION).tar.gz"
149 | @echo "***************************************"
150 |
151 | releasetest: releasetarball dist
152 | tar zxf $(NAME)-$(VERSION).tar.gz
153 | cd $(NAME)-$(VERSION) && make
154 | rm -rf $(NAME)-$(VERSION)
155 |
156 | version:
157 | TIMESTAMP="$(shell LC_TIME=C date)" && \
158 | NEWVERSION=$$(dialog --stdout --inputbox "New Version:" 0 0 "v$(VERSION)") && \
159 | NEWVERSION=$$(echo $$NEWVERSION | sed "s/^v//g"); \
160 | if [ "v$(VERSION)" = "v$$NEWVERSION" -o "x$$NEWVERSION" = "x" ]; then echo "no changes"; exit 1; fi; \
161 | sed -i -e 's/define VERSION ".*"/define VERSION "'$$NEWVERSION'"/g' $(WRAPPER_HEADER); \
162 | sed -i -e 's/next:/'"$$NEWVERSION"' '"$$TIMESTAMP"'/g' README.md;
163 |
164 | versioncheck:
165 | @[ $$( printf '%s\n' $(GOVERSION) $(MINGOVERSION) | sort | head -n 1 ) = $(MINGOVERSION) ] || { \
166 | echo "**** ERROR:"; \
167 | echo "**** build requires at least golang version $(MINGOVERSIONSTR) or higher"; \
168 | echo "**** this is: $$(go version) from $$(type go)"; \
169 | exit 1; \
170 | }
171 |
172 | dump:
173 | if [ $(shell grep -rc Dump $(GO_SRC_DIR)/*.go | grep -v :0 | grep -v dump.go | wc -l) -ne 0 ]; then \
174 | sed -i.bak 's/\/\/ +build.*/\/\/ build with debug functions/' $(GO_SRC_DIR)/dump.go; \
175 | else \
176 | sed -i.bak 's/\/\/ build.*/\/\/ +build ignore/' $(GO_SRC_DIR)/dump.go; \
177 | fi
178 | rm -f $(GO_SRC_DIR)/dump.go.bak
179 |
180 | GOVET=$(GO) vet -all
181 | SRCFOLDER=./cmd/. ./buildtools/.
182 | fmt: tools
183 | set -e; for CMD in $(CMDS); do \
184 | $(GOVET) ./cmd/$$CMD; \
185 | done
186 | gofmt -w -s $(SRCFOLDER)
187 | ./tools/gofumpt -w $(SRCFOLDER)
188 | ./tools/gci write --skip-generated $(SRCFOLDER)
189 | ./tools/goimports -w $(SRCFOLDER)
190 |
191 |
192 | citest:
193 | #
194 | # Checking gofmt errors
195 | #
196 | if [ $$(cd $(GO_SRC_DIR) && gofmt -s -l . | wc -l) -gt 0 ]; then \
197 | echo "found format errors in these files:"; \
198 | cd $(GO_SRC_DIR) && gofmt -s -l .; \
199 | exit 1; \
200 | fi
201 | #
202 | # Checking TODO items
203 | #
204 | if grep -rn TODO: cmd/ src/; then exit 1; fi
205 | #
206 | # Checking remaining debug calls
207 | #
208 | if grep -rn Dump cmd/*.go | grep -v dump.go; then exit 1; fi
209 | #
210 | # Run other subtests
211 | #
212 | $(MAKE) golangci
213 | $(MAKE) fmt
214 | #
215 | # Normal test cases
216 | #
217 | cd $(GO_SRC_DIR) && go test -v
218 | #
219 | # Benchmark tests
220 | #
221 | cd $(GO_SRC_DIR) && go test -v -bench=B\* -run=^$$ . -benchmem
222 | #
223 | # All CI tests successfull
224 | #
225 | go mod tidy
226 |
227 | golangci: tools
228 | #
229 | # golangci combines a few static code analyzer
230 | # See https://github.com/golangci/golangci-lint
231 | #
232 | golangci-lint run $(GO_SRC_DIR)/...
233 |
234 | mod_prometheus_status.so: mod_prometheus_status_go.so $(WRAPPER_SOURCE)
235 | $(APXS) -c -n $@ -I. $(LIBS) $(WRAPPER_SOURCE)
236 | install src/.libs/mod_prometheus_status.so mod_prometheus_status.so
237 |
238 | mod_prometheus_status_go.so: $(GO_SOURCES) dump
239 | go build -buildmode=c-shared -x -ldflags "-s -w -X main.Build=$(BUILD_TAG)" -o mod_prometheus_status_go.so $(GO_SOURCES)
240 | chmod 755 mod_prometheus_status_go.so
241 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mod_prometheus_status
2 |
3 | mod_prometheus_status is a [Prometheus](https://prometheus.io/) white box exporter
4 | for [Apache HTTPD](https://httpd.apache.org/) with metrics similar to mod_status
5 | plus per-request metrics.
6 |
7 | The exporter is a loadable Apache module and serves metrics directly via the
8 | apache webserver. It comes with dynamic and flexible labeling, see the example
9 | configuration below.
10 |
11 | ## How it works
12 |
13 | Since prometheus exporters are usually bound to a single process and the apache
14 | webserver is a multi process daemon, this module starts a metrics collector in
15 | the parent httpd process.
16 |
17 | Upon start of the main collector it creates the prometheus client library registry
18 | based on the `PrometheusStatusLabelNames`. Then it opens a unix socket to
19 | receive the metrics updates from the child workers.
20 |
21 | On each request, the client worker sends its metrics based on
22 | `PrometheusStatusLabelValues`, which utilizes Apaches LogFormat, to the metrics
23 | collector.
24 |
25 | ## Build Requirements
26 |
27 | - gcc compiler to build (4.9 or newer)
28 | - apache header files
29 | - golang >= 1.22
30 | - docker/docker-compose for running tests
31 |
32 | ## Installation
33 |
34 | > **_NOTE:_** Pre-build modules are available at [GitHub](https://github.com/ConSol/apache_mod_prometheus_status/releases)
35 |
36 | Compile the module like this:
37 |
38 | ```bash
39 | make
40 | ```
41 |
42 | Copy **BOTH** .so files to the apache module directory and adjust the example
43 | configuration.
44 |
45 | ## Configuration
46 |
47 | > **_NOTE:_** Loading this module the very first time requires a apache restart. Reload is not sufficient. Because the metrics collector runs in the parent process.
48 |
49 | ### apache.conf
50 |
51 | ```apache
52 |
53 | LoadModule prometheus_status_module .../mod_prometheus_status.so
54 |
55 | PrometheusStatusEnabled On
56 | PrometheusStatusLabelNames vhost;method;status;application
57 | PrometheusStatusLabelValues %v;%m;%s;
58 | PrometheusStatusTmpFolder /tmp
59 | PrometheusStatusResponseTimeBuckets 0.01;0.1;1;10;30
60 | PrometheusStatusResponseSizeBuckets 1000;10000;100000;1000000;10000000;100000000
61 |
62 |
63 | # make collected metrics available at this url
64 | SetHandler prometheus-metrics
65 |
66 |
67 | # optional custom labels for specific locations
68 |
69 | PrometheusStatusLabel %v;%m;%s;application1
70 |
71 |
72 | # disable collecting metrics for some locations
73 |
74 | PrometheusStatusEnabled Off
75 |
76 | ```
77 |
78 | ### Directives
79 |
80 | #### PrometheusStatusEnabled
81 |
82 | Enable or disable collecting metrics. Available on server and directory level.
83 |
84 | Default: On
85 |
86 | #### PrometheusStatusLabelNames
87 |
88 | Set label names separated by semicolon. This is a global setting and can only
89 | be set once on server level since the metrics have to be registered and cannot
90 | be changed later on.
91 |
92 | Default: method;status
93 |
94 | > **_NOTE:_** Be aware of cardinality explosion and do not overuse labels.
95 | Read more at [prometheus.io](https://prometheus.io/docs/practices/naming/#labels) and
96 | [robustperception.io](https://www.robustperception.io/cardinality-is-key)
97 |
98 | #### PrometheusStatusLabelValues
99 |
100 | Set label values separated by semicolon. You can use the apache log format here.
101 | Some high cardinality variables are not implemented.
102 |
103 | Default: %v;%m;%s
104 |
105 | Useful examples are:
106 |
107 | - `%m` - request method: ex.: GET, POST, ...
108 | - `%s` - response code: ex.: 200, 404, 500, ...
109 | - `%v` - canonical ServerName
110 |
111 | See [httpd.apache.org](http://httpd.apache.org/docs/current/mod/mod_log_config.html#formats) for a
112 | full list of available variables.
113 |
114 | #### PrometheusStatusTmpFolder
115 |
116 | Set the folder where the inter process communication socket will be created in.
117 |
118 | Default: /tmp (or system default temporary folder)
119 |
120 | #### PrometheusStatusResponseTimeBuckets
121 |
122 | Set the buckets for the response time histogram.
123 |
124 | Default: 0.01;0.1;1;10;30
125 |
126 | #### PrometheusStatusResponseSizeBuckets
127 |
128 | Set the buckets for the response size histogram.
129 |
130 | Default: 1000;10000;100000;1000000;10000000;100000000
131 |
132 | ## Metrics
133 |
134 | Then you can access the metrics with a URL like:
135 |
136 | http://your_server_name/metrics
137 |
138 | Or whatever you put your `SetHandler prometheus-metrics` to.
139 |
140 | > **_NOTE:_** You may want to protect the /metrics location by password or domain so no one else can look at it.
141 |
142 | So far this modules supports the following metrics:
143 |
144 | ```
145 | # HELP apache_cpu_load CPU Load 1
146 | # TYPE apache_cpu_load gauge
147 | # TYPE apache_process_counter gauge
148 | # HELP apache_process_counter number of apache processes
149 | # TYPE apache_process_total_io_read_bytes gauge
150 | # HELP apache_process_total_io_read_bytes total read bytes over all apache processes
151 | # TYPE apache_process_total_io_write_bytes gauge
152 | # HELP apache_process_total_io_write_bytes total write bytes over all apache processes
153 | # TYPE apache_process_total_open_fd gauge
154 | # HELP apache_process_total_open_fd total open file handles over all apache processes
155 | # TYPE apache_process_total_rss_memory_bytes gauge
156 | # HELP apache_process_total_rss_memory_bytes total rss bytes over all apache processes
157 | # TYPE apache_process_total_threads gauge
158 | # HELP apache_process_total_threads total number of threads over all apache processes
159 | # TYPE apache_process_total_virt_memory_bytes gauge
160 | # HELP apache_process_total_virt_memory_bytes total virt bytes over all apache processes
161 | # TYPE apache_requests_total counter
162 | # HELP apache_requests_total is the total number of http requests
163 | # TYPE apache_response_size_bytes histogram
164 | # HELP apache_response_size_bytes response size histogram
165 | # TYPE apache_response_time_seconds histogram
166 | # HELP apache_response_time_seconds response time histogram
167 | # HELP apache_server_config_generation current config generation
168 | # TYPE apache_server_config_generation gauge
169 | # TYPE apache_server_info counter
170 | # HELP apache_server_info information about the apache version
171 | # HELP apache_server_mpm_generation current mpm generation
172 | # TYPE apache_server_mpm_generation gauge
173 | # HELP apache_server_name contains the server name
174 | # TYPE apache_server_name counter
175 | # TYPE apache_server_uptime_seconds gauge
176 | # HELP apache_server_uptime_seconds server uptime in seconds
177 | # TYPE apache_workers gauge
178 | # HELP apache_workers is the total number of apache workers
179 | # TYPE apache_workers_scoreboard gauge
180 | # HELP apache_workers_scoreboard is the total number of workers from the scoreboard
181 | ```
182 |
183 | ## Contributing
184 |
185 | Pull requests are welcome. For major changes, please open an issue first to discuss
186 | what you would like to change.
187 |
188 | Please make sure to update tests as appropriate.
189 |
190 | ### Development Environment
191 |
192 | There is a test/dev docker box located in `t/testbox` which can be started for
193 | easy testing and development.
194 |
195 | ```bash
196 | make testbox
197 | ```
198 |
199 | This creates a Centos 8 box which builds the module whenever the source file
200 | changes. You can access the module at `http://localhost:3000/metrics`. It might
201 | take a moment to startup.
202 |
203 | You can access the grafana dashboard at
204 | `http://localhost:3001/dashboard/grafana/` and the Prometheus instance at
205 | `http://localhost:3001/dashboard/prometheus/`.
206 |
207 | Run the unit/integration tests like this:
208 |
209 | ```bash
210 | make test
211 | ```
212 |
213 | Cleanup docker machines and test environment by
214 |
215 | ```bash
216 | make clean
217 | ```
218 |
219 | #### Ressources
220 |
221 | Some useful ressources during development:
222 |
223 | - `Apache Module Development` - [httpd.apache.org](https://httpd.apache.org/docs/current/developer/modguide.html)
224 | - `Apache API docs` - [ci.apache.org](https://ci.apache.org/projects/httpd/trunk/doxygen/)
225 |
226 | ## Roadmap
227 |
228 | - [ ] add trimmable path label, ex.: %{p:1:2} which uses 2 directories levels, starting at the first level
229 |
230 | ## Changes
231 |
232 | see Changelog file.
233 |
234 | ## License
235 |
236 | [MIT](https://choosealicense.com/licenses/mit/)
237 |
--------------------------------------------------------------------------------
/apxs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | APXS=`which apxs2 2>/dev/null`
3 | if [ -z "$APXS" ]; then
4 | APXS=`which apxs 2>/dev/null`
5 | fi
6 |
7 | if [ -z "$APXS" ]; then
8 | if [ -f /usr/sbin/apxs2 ]; then
9 | APXS=/usr/sbin/apxs2
10 | elif [ -f /usr/sbin/apxs ]; then
11 | APXS=/usr/sbin/apxs
12 | fi
13 | fi
14 |
15 | $APXS $@
16 |
--------------------------------------------------------------------------------
/buildtools/go.mod:
--------------------------------------------------------------------------------
1 | module tools
2 |
3 | go 1.22
4 |
5 | require (
6 | github.com/daixiang0/gci v0.12.3
7 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
8 | github.com/golangci/golangci-lint v1.56.2
9 | golang.org/x/tools v0.18.0
10 | golang.org/x/vuln v1.0.4
11 | mvdan.cc/gofumpt v0.6.0
12 | )
13 |
14 | require (
15 | 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
16 | 4d63.com/gochecknoglobals v0.2.1 // indirect
17 | github.com/4meepo/tagalign v1.3.3 // indirect
18 | github.com/Abirdcfly/dupword v0.0.14 // indirect
19 | github.com/Antonboom/errname v0.1.12 // indirect
20 | github.com/Antonboom/nilnil v0.1.7 // indirect
21 | github.com/Antonboom/testifylint v1.1.2 // indirect
22 | github.com/BurntSushi/toml v1.3.2 // indirect
23 | github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
24 | github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 // indirect
25 | github.com/Masterminds/semver v1.5.0 // indirect
26 | github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect
27 | github.com/alecthomas/go-check-sumtype v0.1.4 // indirect
28 | github.com/alexkohler/nakedret/v2 v2.0.2 // indirect
29 | github.com/alexkohler/prealloc v1.0.0 // indirect
30 | github.com/alingse/asasalint v0.0.11 // indirect
31 | github.com/ashanbrown/forbidigo v1.6.0 // indirect
32 | github.com/ashanbrown/makezero v1.1.1 // indirect
33 | github.com/beorn7/perks v1.0.1 // indirect
34 | github.com/bkielbasa/cyclop v1.2.1 // indirect
35 | github.com/blizzy78/varnamelen v0.8.0 // indirect
36 | github.com/bombsimon/wsl/v4 v4.2.1 // indirect
37 | github.com/breml/bidichk v0.2.7 // indirect
38 | github.com/breml/errchkjson v0.3.6 // indirect
39 | github.com/butuzov/ireturn v0.3.0 // indirect
40 | github.com/butuzov/mirror v1.1.0 // indirect
41 | github.com/catenacyber/perfsprint v0.7.1 // indirect
42 | github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
43 | github.com/cespare/xxhash/v2 v2.2.0 // indirect
44 | github.com/charithe/durationcheck v0.0.10 // indirect
45 | github.com/chavacava/garif v0.1.0 // indirect
46 | github.com/curioswitch/go-reassign v0.2.0 // indirect
47 | github.com/denis-tingaikin/go-header v0.4.3 // indirect
48 | github.com/esimonov/ifshort v1.0.4 // indirect
49 | github.com/ettle/strcase v0.2.0 // indirect
50 | github.com/fatih/color v1.16.0 // indirect
51 | github.com/fatih/structtag v1.2.0 // indirect
52 | github.com/firefart/nonamedreturns v1.0.4 // indirect
53 | github.com/fsnotify/fsnotify v1.7.0 // indirect
54 | github.com/fzipp/gocyclo v0.6.0 // indirect
55 | github.com/ghostiam/protogetter v0.3.4 // indirect
56 | github.com/go-critic/go-critic v0.11.1 // indirect
57 | github.com/go-toolsmith/astcast v1.1.0 // indirect
58 | github.com/go-toolsmith/astcopy v1.1.0 // indirect
59 | github.com/go-toolsmith/astequal v1.2.0 // indirect
60 | github.com/go-toolsmith/astfmt v1.1.0 // indirect
61 | github.com/go-toolsmith/astp v1.1.0 // indirect
62 | github.com/go-toolsmith/strparse v1.1.0 // indirect
63 | github.com/go-toolsmith/typep v1.1.0 // indirect
64 | github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
65 | github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
66 | github.com/gobwas/glob v0.2.3 // indirect
67 | github.com/gofrs/flock v0.8.1 // indirect
68 | github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect
69 | github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
70 | github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect
71 | github.com/golangci/gofmt v0.0.0-20231019111953-be8c47862aaa // indirect
72 | github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect
73 | github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect
74 | github.com/golangci/misspell v0.4.1 // indirect
75 | github.com/golangci/revgrep v0.5.2 // indirect
76 | github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect
77 | github.com/google/go-cmp v0.6.0 // indirect
78 | github.com/gordonklaus/ineffassign v0.1.0 // indirect
79 | github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
80 | github.com/gostaticanalysis/comment v1.4.2 // indirect
81 | github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect
82 | github.com/gostaticanalysis/nilerr v0.1.1 // indirect
83 | github.com/hashicorp/go-version v1.6.0 // indirect
84 | github.com/hashicorp/hcl v1.0.0 // indirect
85 | github.com/hexops/gotextdiff v1.0.3 // indirect
86 | github.com/inconshreveable/mousetrap v1.1.0 // indirect
87 | github.com/jgautheron/goconst v1.7.0 // indirect
88 | github.com/jingyugao/rowserrcheck v1.1.1 // indirect
89 | github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect
90 | github.com/jjti/go-spancheck v0.5.2 // indirect
91 | github.com/julz/importas v0.1.0 // indirect
92 | github.com/kisielk/errcheck v1.7.0 // indirect
93 | github.com/kisielk/gotool v1.0.0 // indirect
94 | github.com/kkHAIKE/contextcheck v1.1.4 // indirect
95 | github.com/kulti/thelper v0.6.3 // indirect
96 | github.com/kunwardeep/paralleltest v1.0.10 // indirect
97 | github.com/kyoh86/exportloopref v0.1.11 // indirect
98 | github.com/ldez/gomoddirectives v0.2.3 // indirect
99 | github.com/ldez/tagliatelle v0.5.0 // indirect
100 | github.com/leonklingele/grouper v1.1.1 // indirect
101 | github.com/lufeee/execinquery v1.2.1 // indirect
102 | github.com/macabu/inamedparam v0.1.3 // indirect
103 | github.com/magiconair/properties v1.8.7 // indirect
104 | github.com/maratori/testableexamples v1.0.0 // indirect
105 | github.com/maratori/testpackage v1.1.1 // indirect
106 | github.com/matoous/godox v0.0.0-20240105082147-c5b5e0e7c0c0 // indirect
107 | github.com/mattn/go-colorable v0.1.13 // indirect
108 | github.com/mattn/go-isatty v0.0.20 // indirect
109 | github.com/mattn/go-runewidth v0.0.15 // indirect
110 | github.com/mbilski/exhaustivestruct v1.2.0 // indirect
111 | github.com/mgechev/revive v1.3.7 // indirect
112 | github.com/mitchellh/go-homedir v1.1.0 // indirect
113 | github.com/mitchellh/mapstructure v1.5.0 // indirect
114 | github.com/moricho/tparallel v0.3.1 // indirect
115 | github.com/nakabonne/nestif v0.3.1 // indirect
116 | github.com/nishanths/exhaustive v0.12.0 // indirect
117 | github.com/nishanths/predeclared v0.2.2 // indirect
118 | github.com/nunnatsa/ginkgolinter v0.15.2 // indirect
119 | github.com/olekukonko/tablewriter v0.0.5 // indirect
120 | github.com/pelletier/go-toml/v2 v2.1.1 // indirect
121 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
122 | github.com/polyfloyd/go-errorlint v1.4.8 // indirect
123 | github.com/prometheus/client_golang v1.18.0 // indirect
124 | github.com/prometheus/client_model v0.6.0 // indirect
125 | github.com/prometheus/common v0.47.0 // indirect
126 | github.com/prometheus/procfs v0.12.0 // indirect
127 | github.com/quasilyte/go-ruleguard v0.4.0 // indirect
128 | github.com/quasilyte/gogrep v0.5.0 // indirect
129 | github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
130 | github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
131 | github.com/rivo/uniseg v0.4.7 // indirect
132 | github.com/ryancurrah/gomodguard v1.3.0 // indirect
133 | github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
134 | github.com/sagikazarmark/locafero v0.4.0 // indirect
135 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect
136 | github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect
137 | github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
138 | github.com/sashamelentyev/usestdlibvars v1.25.0 // indirect
139 | github.com/securego/gosec/v2 v2.19.0 // indirect
140 | github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
141 | github.com/sirupsen/logrus v1.9.3 // indirect
142 | github.com/sivchari/containedctx v1.0.3 // indirect
143 | github.com/sivchari/nosnakecase v1.7.0 // indirect
144 | github.com/sivchari/tenv v1.7.1 // indirect
145 | github.com/sonatard/noctx v0.0.2 // indirect
146 | github.com/sourcegraph/conc v0.3.0 // indirect
147 | github.com/sourcegraph/go-diff v0.7.0 // indirect
148 | github.com/spf13/afero v1.11.0 // indirect
149 | github.com/spf13/cast v1.6.0 // indirect
150 | github.com/spf13/cobra v1.8.0 // indirect
151 | github.com/spf13/pflag v1.0.5 // indirect
152 | github.com/spf13/viper v1.18.2 // indirect
153 | github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
154 | github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
155 | github.com/stretchr/objx v0.5.1 // indirect
156 | github.com/stretchr/testify v1.8.4 // indirect
157 | github.com/subosito/gotenv v1.6.0 // indirect
158 | github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect
159 | github.com/tdakkota/asciicheck v0.2.0 // indirect
160 | github.com/tetafro/godot v1.4.16 // indirect
161 | github.com/timakin/bodyclose v0.0.0-20240125160201-f835fa56326a // indirect
162 | github.com/timonwong/loggercheck v0.9.4 // indirect
163 | github.com/tomarrell/wrapcheck/v2 v2.8.3 // indirect
164 | github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
165 | github.com/ultraware/funlen v0.1.0 // indirect
166 | github.com/ultraware/whitespace v0.1.0 // indirect
167 | github.com/uudashr/gocognit v1.1.2 // indirect
168 | github.com/xen0n/gosmopolitan v1.2.2 // indirect
169 | github.com/yagipy/maintidx v1.0.0 // indirect
170 | github.com/yeya24/promlinter v0.2.0 // indirect
171 | github.com/ykadowak/zerologlint v0.1.5 // indirect
172 | gitlab.com/bosi/decorder v0.4.1 // indirect
173 | go-simpler.org/musttag v0.8.0 // indirect
174 | go-simpler.org/sloglint v0.4.0 // indirect
175 | go.uber.org/multierr v1.11.0 // indirect
176 | go.uber.org/zap v1.26.0 // indirect
177 | golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect
178 | golang.org/x/exp/typeparams v0.0.0-20240213143201-ec583247a57a // indirect
179 | golang.org/x/mod v0.15.0 // indirect
180 | golang.org/x/sync v0.6.0 // indirect
181 | golang.org/x/sys v0.17.0 // indirect
182 | golang.org/x/text v0.14.0 // indirect
183 | google.golang.org/protobuf v1.33.0 // indirect
184 | gopkg.in/ini.v1 v1.67.0 // indirect
185 | gopkg.in/yaml.v2 v2.4.0 // indirect
186 | gopkg.in/yaml.v3 v3.0.1 // indirect
187 | honnef.co/go/tools v0.4.6 // indirect
188 | mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect
189 | mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
190 | mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 // indirect
191 | )
192 |
--------------------------------------------------------------------------------
/buildtools/go.sum:
--------------------------------------------------------------------------------
1 | 4d63.com/gocheckcompilerdirectives v1.2.1 h1:AHcMYuw56NPjq/2y615IGg2kYkBdTvOaojYCBcRE7MA=
2 | 4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs=
3 | 4d63.com/gochecknoglobals v0.2.1 h1:1eiorGsgHOFOuoOiJDy2psSrQbRdIHrlge0IJIkUgDc=
4 | 4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU=
5 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
6 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
7 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
8 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
9 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
10 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
11 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
12 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
13 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
14 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
15 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
16 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
17 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
18 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
19 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
20 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
37 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
38 | github.com/4meepo/tagalign v1.3.3 h1:ZsOxcwGD/jP4U/aw7qeWu58i7dwYemfy5Y+IF1ACoNw=
39 | github.com/4meepo/tagalign v1.3.3/go.mod h1:Q9c1rYMZJc9dPRkbQPpcBNCLEmY2njbAsXhQOZFE2dE=
40 | github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11Ba8=
41 | github.com/Abirdcfly/dupword v0.0.14/go.mod h1:VKDAbxdY8YbKUByLGg8EETzYSuC4crm9WwI6Y3S0cLI=
42 | github.com/Antonboom/errname v0.1.12 h1:oh9ak2zUtsLp5oaEd/erjB4GPu9w19NyoIskZClDcQY=
43 | github.com/Antonboom/errname v0.1.12/go.mod h1:bK7todrzvlaZoQagP1orKzWXv59X/x0W0Io2XT1Ssro=
44 | github.com/Antonboom/nilnil v0.1.7 h1:ofgL+BA7vlA1K2wNQOsHzLJ2Pw5B5DpWRLdDAVvvTow=
45 | github.com/Antonboom/nilnil v0.1.7/go.mod h1:TP+ScQWVEq0eSIxqU8CbdT5DFWoHp0MbP+KMUO1BKYQ=
46 | github.com/Antonboom/testifylint v1.1.2 h1:IdLRermiLRogxY5AumBL4sP0A+qKHQM/AP1Xd7XOTKc=
47 | github.com/Antonboom/testifylint v1.1.2/go.mod h1:9PFi+vWa8zzl4/B/kqmFJcw85ZUv8ReyBzuQCd30+WI=
48 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
49 | github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
50 | github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
51 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
52 | github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
53 | github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
54 | github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 h1:sATXp1x6/axKxz2Gjxv8MALP0bXaNRfQinEwyfMcx8c=
55 | github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0/go.mod h1:Nl76DrGNJTA1KJ0LePKBw/vznBX1EHbAZX8mwjR82nI=
56 | github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
57 | github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
58 | github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA=
59 | github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ=
60 | github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
61 | github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
62 | github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c=
63 | github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ=
64 | github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
65 | github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
66 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
67 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
68 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
69 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
70 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
71 | github.com/alexkohler/nakedret/v2 v2.0.2 h1:qnXuZNvv3/AxkAb22q/sEsEpcA99YxLFACDtEw9TPxE=
72 | github.com/alexkohler/nakedret/v2 v2.0.2/go.mod h1:2b8Gkk0GsOrqQv/gPWjNLDSKwG8I5moSXG1K4VIBcTQ=
73 | github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw=
74 | github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
75 | github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw=
76 | github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I=
77 | github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY=
78 | github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU=
79 | github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s=
80 | github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI=
81 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
82 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
83 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
84 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
85 | github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY=
86 | github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM=
87 | github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M=
88 | github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
89 | github.com/bombsimon/wsl/v4 v4.2.1 h1:Cxg6u+XDWff75SIFFmNsqnIOgob+Q9hG6y/ioKbRFiM=
90 | github.com/bombsimon/wsl/v4 v4.2.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo=
91 | github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY=
92 | github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ=
93 | github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA=
94 | github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U=
95 | github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0=
96 | github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA=
97 | github.com/butuzov/mirror v1.1.0 h1:ZqX54gBVMXu78QLoiqdwpl2mgmoOJTk7s4p4o+0avZI=
98 | github.com/butuzov/mirror v1.1.0/go.mod h1:8Q0BdQU6rC6WILDiBM60DBfvV78OLJmMmixe7GF45AE=
99 | github.com/catenacyber/perfsprint v0.7.1 h1:PGW5G/Kxn+YrN04cRAZKC+ZuvlVwolYMrIyyTJ/rMmc=
100 | github.com/catenacyber/perfsprint v0.7.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50=
101 | github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
102 | github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
103 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
104 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
105 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
106 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
107 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
108 | github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4=
109 | github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ=
110 | github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
111 | github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
112 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
113 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
114 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
115 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
116 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
117 | github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
118 | github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo=
119 | github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc=
120 | github.com/daixiang0/gci v0.12.3 h1:yOZI7VAxAGPQmkb1eqt5g/11SUlwoat1fSblGLmdiQc=
121 | github.com/daixiang0/gci v0.12.3/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI=
122 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
123 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
124 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
125 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
126 | github.com/denis-tingaikin/go-header v0.4.3 h1:tEaZKAlqql6SKCY++utLmkPLd6K8IBM20Ha7UVm+mtU=
127 | github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c=
128 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
129 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
130 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
131 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
132 | github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStBA=
133 | github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0=
134 | github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
135 | github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A=
136 | github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
137 | github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
138 | github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
139 | github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
140 | github.com/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phmY9sfv40Y=
141 | github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI=
142 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
143 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
144 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
145 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
146 | github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
147 | github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
148 | github.com/ghostiam/protogetter v0.3.4 h1:5SZ+lZSNmNkSbGVSF9hUHhv/b7ELF9Rwchoq7btYo6c=
149 | github.com/ghostiam/protogetter v0.3.4/go.mod h1:A0JgIhs0fgVnotGinjQiKaFVG3waItLJNwPmcMzDnvk=
150 | github.com/go-critic/go-critic v0.11.1 h1:/zBseUSUMytnRqxjlsYNbDDxpu3R2yH8oLXo/FOE8b8=
151 | github.com/go-critic/go-critic v0.11.1/go.mod h1:aZVQR7+gazH6aDEQx4356SD7d8ez8MipYjXbEl5JAKA=
152 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
153 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
154 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
155 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
156 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
157 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
158 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
159 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
160 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
161 | github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
162 | github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
163 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
164 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
165 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
166 | github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8=
167 | github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU=
168 | github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s=
169 | github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw=
170 | github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4=
171 | github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ=
172 | github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw=
173 | github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY=
174 | github.com/go-toolsmith/astfmt v1.1.0 h1:iJVPDPp6/7AaeLJEruMsBUlOYCmvg0MoCfJprsOmcco=
175 | github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4=
176 | github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA=
177 | github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA=
178 | github.com/go-toolsmith/pkgload v1.2.2 h1:0CtmHq/02QhxcF7E9N5LIFcYFsMR5rdovfqTtRKkgIk=
179 | github.com/go-toolsmith/pkgload v1.2.2/go.mod h1:R2hxLNRKuAsiXCo2i5J6ZQPhnPMOVtU+f0arbFPWCus=
180 | github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
181 | github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw=
182 | github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
183 | github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
184 | github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
185 | github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c=
186 | github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
187 | github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U=
188 | github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
189 | github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
190 | github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
191 | github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
192 | github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
193 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
194 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
195 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
196 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
197 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
198 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
199 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
200 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
201 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
202 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
203 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
204 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
205 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
206 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
207 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
208 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
209 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
210 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
211 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
212 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
213 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
214 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
215 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
216 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
217 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
218 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
219 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
220 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
221 | github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
222 | github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
223 | github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
224 | github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
225 | github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6J5HIP8ZtyMdiDscjMLfRBSPuzVVeo=
226 | github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ=
227 | github.com/golangci/gofmt v0.0.0-20231019111953-be8c47862aaa h1:L0Zq43Px2HrLroRKEgfCsQLMJUkjskJBB1kd1Zjcvvc=
228 | github.com/golangci/gofmt v0.0.0-20231019111953-be8c47862aaa/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM=
229 | github.com/golangci/golangci-lint v1.56.2 h1:dgQzlWHgNbCqJjuxRJhFEnHDVrrjuTGQHJ3RIZMpp/o=
230 | github.com/golangci/golangci-lint v1.56.2/go.mod h1:7CfNO675+EY7j84jihO4iAqDQ80s3HCjcc5M6B7SlZQ=
231 | github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
232 | github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
233 | github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
234 | github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
235 | github.com/golangci/misspell v0.4.1 h1:+y73iSicVy2PqyX7kmUefHusENlrP9YwuHZHPLGQj/g=
236 | github.com/golangci/misspell v0.4.1/go.mod h1:9mAN1quEo3DlpbaIKKyEvRxK1pwqR9s/Sea1bJCtlNI=
237 | github.com/golangci/revgrep v0.5.2 h1:EndcWoRhcnfj2NHQ+28hyuXpLMF+dQmCN+YaeeIl4FU=
238 | github.com/golangci/revgrep v0.5.2/go.mod h1:bjAMA+Sh/QUfTDcHzxfyHxr4xKvllVr/0sCv2e7jJHA=
239 | github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
240 | github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
241 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
242 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
243 | github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
244 | github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=
245 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
246 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
247 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
248 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
249 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
250 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
251 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
252 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
253 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
254 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
255 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
256 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
257 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
258 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
259 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
260 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
261 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
262 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
263 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
264 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
265 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
266 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
267 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
268 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
269 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
270 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
271 | github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
272 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
273 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
274 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
275 | github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s=
276 | github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
277 | github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk=
278 | github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc=
279 | github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado=
280 | github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q=
281 | github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM=
282 | github.com/gostaticanalysis/forcetypeassert v0.1.0 h1:6eUflI3DiGusXGK6X7cCcIgVCpZ2CiZ1Q7jl6ZxNV70=
283 | github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak=
284 | github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk=
285 | github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A=
286 | github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M=
287 | github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY=
288 | github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU=
289 | github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
290 | github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
291 | github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
292 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
293 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
294 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
295 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
296 | github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
297 | github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
298 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
299 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
300 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
301 | github.com/jgautheron/goconst v1.7.0 h1:cEqH+YBKLsECnRSd4F4TK5ri8t/aXtt/qoL0Ft252B0=
302 | github.com/jgautheron/goconst v1.7.0/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
303 | github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
304 | github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
305 | github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48=
306 | github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
307 | github.com/jjti/go-spancheck v0.5.2 h1:WXTZG3efY/ji1Vi8mkH+23O3bLeKR6hp3tI3YB7XwKk=
308 | github.com/jjti/go-spancheck v0.5.2/go.mod h1:ARPNI1JRG1V2Rjnd6/2f2NEfghjSVDZGVmruNKlnXU0=
309 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
310 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
311 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
312 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
313 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
314 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
315 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
316 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
317 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
318 | github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY=
319 | github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0=
320 | github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0=
321 | github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ=
322 | github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
323 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
324 | github.com/kkHAIKE/contextcheck v1.1.4 h1:B6zAaLhOEEcjvUgIYEqystmnFk1Oemn8bvJhbt0GMb8=
325 | github.com/kkHAIKE/contextcheck v1.1.4/go.mod h1:1+i/gWqokIa+dm31mqGLZhZJ7Uh44DJGZVmr6QRBNJg=
326 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
327 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
328 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
329 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
330 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
331 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
332 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
333 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
334 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
335 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
336 | github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs=
337 | github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I=
338 | github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs=
339 | github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY=
340 | github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ=
341 | github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA=
342 | github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA=
343 | github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0=
344 | github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo=
345 | github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4=
346 | github.com/leonklingele/grouper v1.1.1 h1:suWXRU57D4/Enn6pXR0QVqqWWrnJ9Osrz+5rjt8ivzU=
347 | github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY=
348 | github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM=
349 | github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM=
350 | github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk=
351 | github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I=
352 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
353 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
354 | github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI=
355 | github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
356 | github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04=
357 | github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc=
358 | github.com/matoous/godox v0.0.0-20240105082147-c5b5e0e7c0c0 h1:Ny7cm4KSWceJLYyI1sm+aFIVDWSGXLcOJ0O0UaS5wdU=
359 | github.com/matoous/godox v0.0.0-20240105082147-c5b5e0e7c0c0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs=
360 | github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
361 | github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
362 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
363 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
364 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
365 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
366 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
367 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
368 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
369 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
370 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
371 | github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo=
372 | github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
373 | github.com/mgechev/revive v1.3.7 h1:502QY0vQGe9KtYJ9FpxMz9rL+Fc/P13CI5POL4uHCcE=
374 | github.com/mgechev/revive v1.3.7/go.mod h1:RJ16jUbF0OWC3co/+XTxmFNgEpUPwnnA0BRllX2aDNA=
375 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
376 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
377 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
378 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
379 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
380 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
381 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
382 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
383 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
384 | github.com/moricho/tparallel v0.3.1 h1:fQKD4U1wRMAYNngDonW5XupoB/ZGJHdpzrWqgyg9krA=
385 | github.com/moricho/tparallel v0.3.1/go.mod h1:leENX2cUv7Sv2qDgdi0D0fCftN8fRC67Bcn8pqzeYNI=
386 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
387 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
388 | github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U=
389 | github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE=
390 | github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg=
391 | github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
392 | github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
393 | github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
394 | github.com/nunnatsa/ginkgolinter v0.15.2 h1:N2ORxUxPU56R9gsfLIlVVvCv/V/VVou5qVI1oBKBNHg=
395 | github.com/nunnatsa/ginkgolinter v0.15.2/go.mod h1:oYxE7dt1vZI8cK2rZOs3RgTaBN2vggkqnENmoJ8kVvc=
396 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
397 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
398 | github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
399 | github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
400 | github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
401 | github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
402 | github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
403 | github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
404 | github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
405 | github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
406 | github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
407 | github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
408 | github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
409 | github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
410 | github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
411 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
412 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
413 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
414 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
415 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
416 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
417 | github.com/polyfloyd/go-errorlint v1.4.8 h1:jiEjKDH33ouFktyez7sckv6pHWif9B7SuS8cutDXFHw=
418 | github.com/polyfloyd/go-errorlint v1.4.8/go.mod h1:NNCxFcFjZcw3xNjVdCchERkEM6Oz7wta2XJVxRftwO4=
419 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
420 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
421 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
422 | github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
423 | github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
424 | github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
425 | github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
426 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
427 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
428 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
429 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
430 | github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
431 | github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
432 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
433 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
434 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
435 | github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
436 | github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k=
437 | github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
438 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
439 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
440 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
441 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
442 | github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
443 | github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
444 | github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
445 | github.com/quasilyte/go-ruleguard v0.4.0 h1:DyM6r+TKL+xbKB4Nm7Afd1IQh9kEUKQs2pboWGKtvQo=
446 | github.com/quasilyte/go-ruleguard v0.4.0/go.mod h1:Eu76Z/R8IXtViWUIHkE3p8gdH3/PKk1eh3YGfaEof10=
447 | github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo=
448 | github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng=
449 | github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU=
450 | github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
451 | github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs=
452 | github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
453 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
454 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
455 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
456 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
457 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
458 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
459 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
460 | github.com/ryancurrah/gomodguard v1.3.0 h1:q15RT/pd6UggBXVBuLps8BXRvl5GPBcwVA7BJHMLuTw=
461 | github.com/ryancurrah/gomodguard v1.3.0/go.mod h1:ggBxb3luypPEzqVtq33ee7YSN35V28XeGnid8dnni50=
462 | github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU=
463 | github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ=
464 | github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
465 | github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
466 | github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
467 | github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
468 | github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc=
469 | github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
470 | github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw=
471 | github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
472 | github.com/sashamelentyev/usestdlibvars v1.25.0 h1:IK8SI2QyFzy/2OD2PYnhy84dpfNo9qADrRt6LH8vSzU=
473 | github.com/sashamelentyev/usestdlibvars v1.25.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
474 | github.com/securego/gosec/v2 v2.19.0 h1:gl5xMkOI0/E6Hxx0XCY2XujA3V7SNSefA8sC+3f1gnk=
475 | github.com/securego/gosec/v2 v2.19.0/go.mod h1:hOkDcHz9J/XIgIlPDXalxjeVYsHxoWUc5zJSHxcB8YM=
476 | github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
477 | github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
478 | github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
479 | github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
480 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
481 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
482 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
483 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
484 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
485 | github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE=
486 | github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4=
487 | github.com/sivchari/nosnakecase v1.7.0 h1:7QkpWIRMe8x25gckkFd2A5Pi6Ymo0qgr4JrhGt95do8=
488 | github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY=
489 | github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak=
490 | github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg=
491 | github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00=
492 | github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo=
493 | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
494 | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
495 | github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
496 | github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
497 | github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
498 | github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
499 | github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
500 | github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
501 | github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
502 | github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
503 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
504 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
505 | github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
506 | github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
507 | github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0=
508 | github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
509 | github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc=
510 | github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I=
511 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
512 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
513 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
514 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
515 | github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0=
516 | github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
517 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
518 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
519 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
520 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
521 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
522 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
523 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
524 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
525 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
526 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
527 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
528 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
529 | github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8=
530 | github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk=
531 | github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM=
532 | github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg=
533 | github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
534 | github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
535 | github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
536 | github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
537 | github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0=
538 | github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
539 | github.com/timakin/bodyclose v0.0.0-20240125160201-f835fa56326a h1:A6uKudFIfAEpoPdaal3aSqGxBzLyU8TqyXImLwo6dIo=
540 | github.com/timakin/bodyclose v0.0.0-20240125160201-f835fa56326a/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460=
541 | github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4=
542 | github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg=
543 | github.com/tomarrell/wrapcheck/v2 v2.8.3 h1:5ov+Cbhlgi7s/a42BprYoxsr73CbdMUTzE3bRDFASUs=
544 | github.com/tomarrell/wrapcheck/v2 v2.8.3/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo=
545 | github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw=
546 | github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
547 | github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI=
548 | github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4=
549 | github.com/ultraware/whitespace v0.1.0 h1:O1HKYoh0kIeqE8sFqZf1o0qbORXUCOQFrlaQyZsczZw=
550 | github.com/ultraware/whitespace v0.1.0/go.mod h1:/se4r3beMFNmewJ4Xmz0nMQ941GJt+qmSHGP9emHYe0=
551 | github.com/uudashr/gocognit v1.1.2 h1:l6BAEKJqQH2UpKAPKdMfZf5kE4W/2xk8pfU1OVLvniI=
552 | github.com/uudashr/gocognit v1.1.2/go.mod h1:aAVdLURqcanke8h3vg35BC++eseDm66Z7KmchI5et4k=
553 | github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU=
554 | github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg=
555 | github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM=
556 | github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk=
557 | github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o=
558 | github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA=
559 | github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw=
560 | github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg=
561 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
562 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
563 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
564 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
565 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
566 | github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
567 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
568 | gitlab.com/bosi/decorder v0.4.1 h1:VdsdfxhstabyhZovHafFw+9eJ6eU0d2CkFNJcZz/NU4=
569 | gitlab.com/bosi/decorder v0.4.1/go.mod h1:jecSqWUew6Yle1pCr2eLWTensJMmsxHsBwt+PVbkAqA=
570 | go-simpler.org/assert v0.7.0 h1:OzWWZqfNxt8cLS+MlUp6Tgk1HjPkmgdKBq9qvy8lZsA=
571 | go-simpler.org/assert v0.7.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28=
572 | go-simpler.org/musttag v0.8.0 h1:DR4UTgetNNhPRNo02rkK1hwDTRzAPotN+ZqYpdtEwWc=
573 | go-simpler.org/musttag v0.8.0/go.mod h1:fiNdCkXt2S6je9Eblma3okjnlva9NT1Eg/WUt19rWu8=
574 | go-simpler.org/sloglint v0.4.0 h1:UVJuUJo63iNQNFEOtZ6o1xAgagVg/giVLLvG9nNLobI=
575 | go-simpler.org/sloglint v0.4.0/go.mod h1:v6zJ++j/thFPhefs2wEXoCKwT10yo5nkBDYRCXyqgNQ=
576 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
577 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
578 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
579 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
580 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
581 | go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
582 | go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
583 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
584 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
585 | go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
586 | go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
587 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
588 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
589 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
590 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
591 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
592 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
593 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
594 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
595 | golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
596 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
597 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
598 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
599 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
600 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
601 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
602 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
603 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
604 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
605 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
606 | golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
607 | golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
608 | golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
609 | golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
610 | golang.org/x/exp/typeparams v0.0.0-20240213143201-ec583247a57a h1:rrd/FiSCWtI24jk057yBSfEfHrzzjXva1VkDNWRXMag=
611 | golang.org/x/exp/typeparams v0.0.0-20240213143201-ec583247a57a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
612 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
613 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
614 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
615 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
616 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
617 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
618 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
619 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
620 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
621 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
622 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
623 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
624 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
625 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
626 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
627 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
628 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
629 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
630 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
631 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
632 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
633 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
634 | golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
635 | golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
636 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
637 | golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
638 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
639 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
640 | golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
641 | golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
642 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
643 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
644 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
645 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
646 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
647 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
648 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
649 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
650 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
651 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
652 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
653 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
654 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
655 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
656 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
657 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
658 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
659 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
660 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
661 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
662 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
663 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
664 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
665 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
666 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
667 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
668 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
669 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
670 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
671 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
672 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
673 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
674 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
675 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
676 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
677 | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
678 | golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
679 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
680 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
681 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
682 | golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
683 | golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
684 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
685 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
686 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
687 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
688 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
689 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
690 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
691 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
692 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
693 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
694 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
695 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
696 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
697 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
698 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
699 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
700 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
701 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
702 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
703 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
704 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
705 | golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
706 | golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
707 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
708 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
709 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
710 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
711 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
712 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
713 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
714 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
715 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
716 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
717 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
718 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
719 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
720 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
721 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
722 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
723 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
724 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
725 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
726 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
727 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
728 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
729 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
730 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
731 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
732 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
733 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
734 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
735 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
736 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
737 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
738 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
739 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
740 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
741 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
742 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
743 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
744 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
745 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
746 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
747 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
748 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
749 | golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
750 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
751 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
752 | golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
753 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
754 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
755 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
756 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
757 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
758 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
759 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
760 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
761 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
762 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
763 | golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
764 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
765 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
766 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
767 | golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
768 | golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
769 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
770 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
771 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
772 | golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
773 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
774 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
775 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
776 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
777 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
778 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
779 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
780 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
781 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
782 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
783 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
784 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
785 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
786 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
787 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
788 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
789 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
790 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
791 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
792 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
793 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
794 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
795 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
796 | golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
797 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
798 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
799 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
800 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
801 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
802 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
803 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
804 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
805 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
806 | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
807 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
808 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
809 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
810 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
811 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
812 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
813 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
814 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
815 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
816 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
817 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
818 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
819 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
820 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
821 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
822 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
823 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
824 | golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
825 | golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
826 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
827 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
828 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
829 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
830 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
831 | golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
832 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
833 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
834 | golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
835 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
836 | golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
837 | golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
838 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
839 | golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
840 | golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
841 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
842 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
843 | golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
844 | golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
845 | golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
846 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
847 | golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
848 | golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
849 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
850 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
851 | golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
852 | golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
853 | golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
854 | golang.org/x/vuln v1.0.4 h1:SP0mPeg2PmGCu03V+61EcQiOjmpri2XijexKdzv8Z1I=
855 | golang.org/x/vuln v1.0.4/go.mod h1:NbJdUQhX8jY++FtuhrXs2Eyx0yePo9pF7nPlIjo9aaQ=
856 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
857 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
858 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
859 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
860 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
861 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
862 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
863 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
864 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
865 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
866 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
867 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
868 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
869 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
870 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
871 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
872 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
873 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
874 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
875 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
876 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
877 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
878 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
879 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
880 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
881 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
882 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
883 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
884 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
885 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
886 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
887 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
888 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
889 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
890 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
891 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
892 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
893 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
894 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
895 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
896 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
897 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
898 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
899 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
900 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
901 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
902 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
903 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
904 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
905 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
906 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
907 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
908 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
909 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
910 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
911 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
912 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
913 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
914 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
915 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
916 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
917 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
918 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
919 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
920 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
921 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
922 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
923 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
924 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
925 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
926 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
927 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
928 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
929 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
930 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
931 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
932 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
933 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
934 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
935 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
936 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
937 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
938 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
939 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
940 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
941 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
942 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
943 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
944 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
945 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
946 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
947 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
948 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
949 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
950 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
951 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
952 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
953 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
954 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
955 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
956 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
957 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
958 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
959 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
960 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
961 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
962 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
963 | honnef.co/go/tools v0.4.6 h1:oFEHCKeID7to/3autwsWfnuv69j3NsfcXbvJKuIcep8=
964 | honnef.co/go/tools v0.4.6/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0=
965 | mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo=
966 | mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA=
967 | mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
968 | mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
969 | mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
970 | mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
971 | mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 h1:zCr3iRRgdk5eIikZNDphGcM6KGVTx3Yu+/Uu9Es254w=
972 | mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14/go.mod h1:ZzZjEpJDOmx8TdVU6umamY3Xy0UAQUI2DHbf05USVbI=
973 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
974 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
975 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
976 |
--------------------------------------------------------------------------------
/buildtools/tools.go:
--------------------------------------------------------------------------------
1 | //go:build tools
2 | // +build tools
3 |
4 | package tools
5 |
6 | /*
7 | * list of modules required to build and run all tests
8 | * see: https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
9 | */
10 |
11 | import (
12 | _ "github.com/daixiang0/gci"
13 | _ "github.com/davecgh/go-spew/spew"
14 | _ "github.com/golangci/golangci-lint/cmd/golangci-lint"
15 | _ "golang.org/x/tools/cmd/goimports"
16 | _ "golang.org/x/vuln/cmd/govulncheck"
17 | _ "mvdan.cc/gofumpt"
18 | )
19 |
--------------------------------------------------------------------------------
/cmd/mod_prometheus_status/dump.go:
--------------------------------------------------------------------------------
1 | //go:build ignore
2 | // +build ignore
3 |
4 | package main
5 |
6 | import (
7 | "fmt"
8 | "os"
9 |
10 | "github.com/davecgh/go-spew/spew"
11 | )
12 |
13 | // Dump displays arbitrary data
14 | func Dump(v interface{}) {
15 | spew.Config.Indent = "\t"
16 | spew.Config.MaxDepth = 3
17 | spew.Config.DisableMethods = true
18 | fmt.Fprint(os.Stderr, spew.Sdump(v))
19 | }
20 |
21 | // SDump returns arbitrary data as string
22 | func SDump(v interface{}) string {
23 | spew.Config.Indent = "\t"
24 | spew.Config.MaxDepth = 3
25 | spew.Config.DisableMethods = true
26 | return spew.Sdump(v)
27 | }
28 |
--------------------------------------------------------------------------------
/cmd/mod_prometheus_status/logger.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 | "runtime"
8 |
9 | "github.com/kdar/factorlog"
10 | )
11 |
12 | const (
13 | // LogFormat sets the log format
14 | LogFormat = `[%{Time "Mon Jan 02 15:04:05.000000 2006"}] [mod_prometheus_status:%{severity}] [pid %{Pid}:tid ???] %{Message}`
15 |
16 | // LogVerbosityDebug sets the debug log level
17 | LogVerbosityDebug = 2
18 |
19 | // LogVerbosityInfo sets the info log level
20 | LogVerbosityInfo = 3
21 |
22 | // LogVerbosityError sets the error log level
23 | LogVerbosityError = 4
24 | )
25 |
26 | // initialize standard logger which will be configured later from the configuration file options.
27 | var logger *factorlog.FactorLog
28 |
29 | // initLogging initializes the logging system.
30 | func initLogging(enableDebug int) {
31 | if logger != nil {
32 | return
33 | }
34 | logger = factorlog.New(os.Stderr, factorlog.NewStdFormatter(LogFormat))
35 |
36 | logger.SetVerbosity(LogVerbosityDebug)
37 | switch enableDebug {
38 | case 1:
39 | logger.SetMinMaxSeverity(factorlog.StringToSeverity("DEBUG"), factorlog.StringToSeverity("PANIC"))
40 | default:
41 | logger.SetMinMaxSeverity(factorlog.StringToSeverity("INFO"), factorlog.StringToSeverity("PANIC"))
42 | }
43 | }
44 |
45 | func logf(lvl int, format string, v ...interface{}) {
46 | msg := fmt.Sprintf(format, v...)
47 | _, file, line, _ := runtime.Caller(2)
48 | logmsg := fmt.Sprintf("[%s:%d] %s", filepath.Base(file), line, msg)
49 | switch lvl {
50 | case LogVerbosityError:
51 | logger.Errorf(logmsg)
52 | case LogVerbosityInfo:
53 | logger.Infof(logmsg)
54 | case LogVerbosityDebug:
55 | logger.Debugf(logmsg)
56 | default:
57 | }
58 | }
59 |
60 | func logErrorf(format string, v ...interface{}) {
61 | logf(LogVerbosityError, format, v...)
62 | }
63 |
64 | func logInfof(format string, v ...interface{}) {
65 | logf(LogVerbosityInfo, format, v...)
66 | }
67 |
68 | func logDebugf(format string, v ...interface{}) {
69 | logf(LogVerbosityDebug, format, v...)
70 | }
71 |
--------------------------------------------------------------------------------
/cmd/mod_prometheus_status/module.go:
--------------------------------------------------------------------------------
1 | // package name: mod_prometheus_status
2 | package main
3 |
4 | /*
5 | #cgo CFLAGS: -I/usr/include/httpd
6 | #cgo CFLAGS: -I/usr/include/apache2
7 | #cgo CFLAGS: -I/usr/include/apr-1
8 | #cgo CFLAGS: -I/usr/include/apr-1.0
9 |
10 | #include
11 | #include
12 |
13 | */
14 | import "C"
15 |
16 | import (
17 | "bufio"
18 | "errors"
19 | "io"
20 | "net"
21 | "os"
22 | "os/signal"
23 | "strings"
24 | "syscall"
25 | "time"
26 | )
27 |
28 | const (
29 | // ServerMetrics have different labels as RequestMetrics and are used on server level
30 | ServerMetrics int = iota
31 |
32 | // RequestMetrics are used to gather request specific metrics
33 | RequestMetrics
34 | )
35 |
36 | // Build contains the current git commit id
37 | // compile passing -ldflags "-X main.Build=" to set the id.
38 | var Build string
39 |
40 | var defaultSocketTimeout = 1
41 |
42 | const (
43 | // SigHupDelayExitSeconds sets the amount of extra seconds till exiting after receiving a SIGHUP
44 | SigHupDelayExitSeconds = 5
45 | )
46 |
47 | //export prometheusStatusInit
48 | func prometheusStatusInit(metricsSocket, serverDesc *C.char, serverHostName, version *C.char, debug, userID, groupID C.int, labelNames *C.char, mpmName *C.char, socketTimeout C.int, timeBuckets, sizeBuckets *C.char) C.int {
49 | defaultSocketTimeout = int(socketTimeout)
50 |
51 | initLogging(int(debug))
52 |
53 | err := registerMetrics(C.GoString(serverDesc), C.GoString(serverHostName), C.GoString(labelNames), C.GoString(mpmName), C.GoString(timeBuckets), C.GoString(sizeBuckets))
54 | if err != nil {
55 | logErrorf("failed to initialize metrics: %s", err.Error())
56 | return C.int(1)
57 | }
58 |
59 | sigs := make(chan os.Signal, 1)
60 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
61 | go func() {
62 | sig := <-sigs
63 | logDebugf("got signal: %d(%s)", sig, sig.String())
64 | // wait a few extra seconds on sighups to answer metrics requests during reloads
65 | if sig == syscall.SIGHUP {
66 | time.Sleep(time.Duration(SigHupDelayExitSeconds) * time.Second)
67 | }
68 | os.Remove(C.GoString(metricsSocket))
69 | os.Exit(0)
70 | }()
71 |
72 | startChannel := make(chan bool)
73 | go startMetricServer(startChannel, C.GoString(metricsSocket), int(userID), int(groupID))
74 | <-startChannel
75 |
76 | logInfof("mod_prometheus_status v%s initialized - socket:%s - uid:%d - gid:%d - build:%s", C.GoString(version), C.GoString(metricsSocket), userID, groupID, Build)
77 | return C.int(0)
78 | }
79 |
80 | func startMetricServer(startChannel chan bool, socketPath string, userID, groupID int) {
81 | logDebugf("InitMetricsCollector: %s (uid: %d, gid: %d)", socketPath, userID, groupID)
82 | l, err := net.Listen("unix", socketPath)
83 | if err != nil {
84 | logErrorf("listen error: %s", err.Error())
85 | startChannel <- false
86 | return
87 | }
88 | defer l.Close()
89 | err = os.Chown(socketPath, userID, groupID)
90 | if err != nil {
91 | logErrorf("cannot chown metricssocket: %s", err.Error())
92 | startChannel <- false
93 | return
94 | }
95 |
96 | logDebugf("listening on metricsSocket: %s", socketPath)
97 | startChannel <- true
98 | for {
99 | conn, err := l.Accept()
100 | if err != nil {
101 | // listener closed
102 | return
103 | }
104 | conn.SetDeadline(time.Now().Add(time.Duration(defaultSocketTimeout) * time.Second))
105 | go metricServer(conn)
106 | }
107 | }
108 |
109 | func metricServer(c net.Conn) {
110 | defer c.Close()
111 |
112 | buf := bufio.NewReader(c)
113 |
114 | for {
115 | line, err := buf.ReadString('\n')
116 | if err != nil {
117 | if errors.Is(err, io.EOF) {
118 | return
119 | }
120 | logErrorf("Reading client error: %s", err.Error())
121 | return
122 | }
123 | line = strings.TrimSpace(line)
124 | if line == "" {
125 | return
126 | }
127 | args := strings.SplitN(line, ":", 2)
128 | switch args[0] {
129 | case "metrics":
130 | _, err = c.Write(metricsGet())
131 | if err != nil {
132 | logErrorf("Writing client error: %s", err.Error())
133 | return
134 | }
135 | return
136 | case "server":
137 | metricsUpdate(ServerMetrics, args[1])
138 | case "request":
139 | metricsUpdate(RequestMetrics, args[1])
140 | default:
141 | logErrorf("unknown metrics update request: %s", args[0])
142 | return
143 | }
144 | }
145 | }
146 |
147 | func main() {}
148 |
--------------------------------------------------------------------------------
/cmd/mod_prometheus_status/prometheus.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "math"
6 | "os"
7 | "strconv"
8 | "strings"
9 | "time"
10 |
11 | "github.com/prometheus/client_golang/prometheus"
12 | "github.com/prometheus/common/expfmt"
13 | "github.com/shirou/gopsutil/process"
14 | )
15 |
16 | var (
17 | registry *prometheus.Registry
18 | collectors = make(map[string]interface{})
19 | labelCount = 0
20 | )
21 |
22 | const (
23 | // ProcUpdateInterval set the minimum update interval in seconds for proc statistics
24 | ProcUpdateInterval int64 = 3
25 | )
26 |
27 | var lastProcUpdate int64
28 |
29 | type procUpdate struct {
30 | Total int
31 | Threads int
32 | OpenFD int
33 | RSS uint64
34 | VMS uint64
35 | ReadBytes uint64
36 | WriteBytes uint64
37 | }
38 |
39 | func registerMetrics(serverDesc, serverName, labelNames, mpmName, timeBuckets, sizeBuckets string) (err error) {
40 | if registry != nil {
41 | return
42 | }
43 | registry = prometheus.NewRegistry()
44 | requestLabels := make([]string, 0)
45 | labelNames = strings.TrimSpace(labelNames)
46 | if labelNames != "" {
47 | requestLabels = strings.Split(labelNames, ";")
48 | }
49 | labelCount = len(requestLabels)
50 |
51 | /* server related metrics */
52 | promServerInfo := prometheus.NewCounterVec(
53 | prometheus.CounterOpts{
54 | Namespace: "apache",
55 | Name: "server_info",
56 | Help: "information about the apache version",
57 | },
58 | []string{"server_description", "mpm"})
59 | registry.MustRegister(promServerInfo)
60 | promServerInfo.WithLabelValues(serverDesc, mpmName).Add(1)
61 | collectors["promServerInfo"] = promServerInfo
62 |
63 | promServerName := prometheus.NewCounterVec(
64 | prometheus.CounterOpts{
65 | Namespace: "apache",
66 | Name: "server_name",
67 | Help: "contains the server name",
68 | },
69 | []string{"server_name"})
70 | registry.MustRegister(promServerName)
71 | promServerName.WithLabelValues(serverName).Add(1)
72 | collectors["promServerName"] = promServerName
73 |
74 | promServerUptime := prometheus.NewGauge(
75 | prometheus.GaugeOpts{
76 | Namespace: "apache",
77 | Name: "server_uptime_seconds",
78 | Help: "server uptime in seconds",
79 | })
80 | registry.MustRegister(promServerUptime)
81 | collectors["promServerUptime"] = promServerUptime
82 |
83 | promCPULoad := prometheus.NewGauge(
84 | prometheus.GaugeOpts{
85 | Namespace: "apache",
86 | Name: "cpu_load",
87 | Help: "CPU Load 1",
88 | })
89 | registry.MustRegister(promCPULoad)
90 | collectors["promCPULoad"] = promCPULoad
91 |
92 | promMPMGeneration := prometheus.NewGauge(
93 | prometheus.GaugeOpts{
94 | Namespace: "apache",
95 | Name: "server_mpm_generation",
96 | Help: "current mpm generation",
97 | })
98 | registry.MustRegister(promMPMGeneration)
99 | collectors["promMPMGeneration"] = promMPMGeneration
100 |
101 | promConfigGeneration := prometheus.NewGauge(
102 | prometheus.GaugeOpts{
103 | Namespace: "apache",
104 | Name: "server_config_generation",
105 | Help: "current config generation",
106 | })
107 | registry.MustRegister(promConfigGeneration)
108 | collectors["promConfigGeneration"] = promConfigGeneration
109 |
110 | promWorkers := prometheus.NewGaugeVec(
111 | prometheus.GaugeOpts{
112 | Namespace: "apache",
113 | Name: "workers",
114 | Help: "is the total number of apache workers",
115 | },
116 | []string{"state"})
117 | registry.MustRegister(promWorkers)
118 | collectors["promWorkers"] = promWorkers
119 | promWorkers.WithLabelValues("ready").Set(0)
120 | promWorkers.WithLabelValues("busy").Set(0)
121 |
122 | promScoreboard := prometheus.NewGaugeVec(
123 | prometheus.GaugeOpts{
124 | Namespace: "apache",
125 | Name: "workers_scoreboard",
126 | Help: "is the total number of workers from the scoreboard",
127 | },
128 | []string{"state"})
129 | registry.MustRegister(promScoreboard)
130 | collectors["promScoreboard"] = promScoreboard
131 |
132 | /* process related metrics */
133 | promProcessCounter := prometheus.NewGauge(
134 | prometheus.GaugeOpts{
135 | Namespace: "apache",
136 | Name: "process_counter",
137 | Help: "number of apache processes",
138 | })
139 | registry.MustRegister(promProcessCounter)
140 | promProcessCounter.Set(0)
141 | collectors["promProcCounter"] = promProcessCounter
142 |
143 | promThreads := prometheus.NewGauge(
144 | prometheus.GaugeOpts{
145 | Namespace: "apache",
146 | Name: "process_total_threads",
147 | Help: "total number of threads over all apache processes",
148 | })
149 | registry.MustRegister(promThreads)
150 | promThreads.Set(0)
151 | collectors["promThreads"] = promThreads
152 |
153 | promMemoryReal := prometheus.NewGauge(
154 | prometheus.GaugeOpts{
155 | Namespace: "apache",
156 | Name: "process_total_rss_memory_bytes",
157 | Help: "total rss bytes over all apache processes",
158 | })
159 | registry.MustRegister(promMemoryReal)
160 | promMemoryReal.Set(0)
161 | collectors["promMemoryReal"] = promMemoryReal
162 |
163 | promMemoryVirt := prometheus.NewGauge(
164 | prometheus.GaugeOpts{
165 | Namespace: "apache",
166 | Name: "process_total_virt_memory_bytes",
167 | Help: "total virt bytes over all apache processes",
168 | })
169 | registry.MustRegister(promMemoryVirt)
170 | promMemoryVirt.Set(0)
171 | collectors["promMemoryVirt"] = promMemoryVirt
172 |
173 | promReadBytes := prometheus.NewGauge(
174 | prometheus.GaugeOpts{
175 | Namespace: "apache",
176 | Name: "process_total_io_read_bytes",
177 | Help: "total read bytes over all apache processes",
178 | })
179 | registry.MustRegister(promReadBytes)
180 | promReadBytes.Set(0)
181 | collectors["promReadBytes"] = promReadBytes
182 |
183 | promWriteBytes := prometheus.NewGauge(
184 | prometheus.GaugeOpts{
185 | Namespace: "apache",
186 | Name: "process_total_io_write_bytes",
187 | Help: "total write bytes over all apache processes",
188 | })
189 | registry.MustRegister(promWriteBytes)
190 | promWriteBytes.Set(0)
191 | collectors["promWriteBytes"] = promWriteBytes
192 |
193 | promOpenFD := prometheus.NewGauge(
194 | prometheus.GaugeOpts{
195 | Namespace: "apache",
196 | Name: "process_total_open_fd",
197 | Help: "total open file handles over all apache processes",
198 | })
199 | registry.MustRegister(promOpenFD)
200 | promOpenFD.Set(0)
201 | collectors["promOpenFD"] = promOpenFD
202 |
203 | /* request related metrics */
204 | promRequests := prometheus.NewCounterVec(
205 | prometheus.CounterOpts{
206 | Namespace: "apache",
207 | Name: "requests_total",
208 | Help: "is the total number of http requests",
209 | },
210 | requestLabels)
211 | registry.MustRegister(promRequests)
212 | collectors["promRequests"] = promRequests
213 |
214 | timeBucketList, err := expandBuckets(timeBuckets)
215 | if err != nil {
216 | return
217 | }
218 | promResponseTime := prometheus.NewHistogramVec(
219 | prometheus.HistogramOpts{
220 | Namespace: "apache",
221 | Name: "response_time_seconds",
222 | Help: "response time histogram",
223 | Buckets: timeBucketList,
224 | },
225 | requestLabels)
226 | registry.MustRegister(promResponseTime)
227 | collectors["promResponseTime"] = promResponseTime
228 |
229 | sizeBucketList, err := expandBuckets(sizeBuckets)
230 | if err != nil {
231 | return
232 | }
233 | promResponseSize := prometheus.NewHistogramVec(
234 | prometheus.HistogramOpts{
235 | Namespace: "apache",
236 | Name: "response_size_bytes",
237 | Help: "response size histogram",
238 | Buckets: sizeBucketList,
239 | },
240 | requestLabels)
241 | registry.MustRegister(promResponseSize)
242 | collectors["promResponseSize"] = promResponseSize
243 | return
244 | }
245 |
246 | func metricsGet() []byte {
247 | now := time.Now().Unix()
248 | if now-lastProcUpdate > ProcUpdateInterval {
249 | lastProcUpdate = now
250 | updateProcMetrics()
251 | }
252 | var buf bytes.Buffer
253 | gathering, err := registry.Gather()
254 | if err != nil {
255 | logErrorf("internal prometheus error: %s", err.Error())
256 | return (buf.Bytes())
257 | }
258 | for _, m := range gathering {
259 | expfmt.MetricFamilyToText(&buf, m)
260 | }
261 | buf.WriteString("\n\n")
262 | return (buf.Bytes())
263 | }
264 |
265 | // updateProcMetrics updates memory statistics for all children with match httpd/apache in its cmdline
266 | func updateProcMetrics() {
267 | stats := &procUpdate{}
268 |
269 | pid := os.Getppid()
270 | if pid == 1 {
271 | pid = os.Getpid()
272 | }
273 | if pid >= math.MaxInt32 || pid <= math.MinInt32 {
274 | return
275 | }
276 | mainProcess, _ := process.NewProcess(int32(pid))
277 | countProcStats(mainProcess, stats)
278 |
279 | collectors["promProcCounter"].(prometheus.Gauge).Set(float64(stats.Total))
280 | collectors["promThreads"].(prometheus.Gauge).Set(float64(stats.Threads))
281 | collectors["promOpenFD"].(prometheus.Gauge).Set(float64(stats.OpenFD))
282 | collectors["promMemoryReal"].(prometheus.Gauge).Set(float64(stats.RSS))
283 | collectors["promMemoryVirt"].(prometheus.Gauge).Set(float64(stats.VMS))
284 | collectors["promReadBytes"].(prometheus.Gauge).Set(float64(stats.ReadBytes))
285 | collectors["promWriteBytes"].(prometheus.Gauge).Set(float64(stats.WriteBytes))
286 | }
287 |
288 | func countProcStats(proc *process.Process, stats *procUpdate) {
289 | cmdLine, err := proc.Cmdline()
290 | if err != nil {
291 | return
292 | }
293 |
294 | // only count apache processes
295 | if !strings.Contains(cmdLine, "apache") && !strings.Contains(cmdLine, "httpd") {
296 | return
297 | }
298 |
299 | memInfo, err := proc.MemoryInfo()
300 | if err != nil {
301 | return
302 | }
303 | ioInfo, err := proc.IOCounters()
304 | if err != nil {
305 | return
306 | }
307 | openFD, err := proc.NumFDs()
308 | if err != nil {
309 | return
310 | }
311 | numThreads, err := proc.NumThreads()
312 | if err != nil {
313 | return
314 | }
315 | stats.Total++
316 | stats.Threads += int(numThreads)
317 | stats.OpenFD += int(openFD)
318 | stats.RSS += memInfo.RSS
319 | stats.VMS += memInfo.VMS
320 | stats.ReadBytes += ioInfo.ReadBytes
321 | stats.WriteBytes += ioInfo.WriteBytes
322 | children, err := proc.Children()
323 | if err != nil {
324 | return
325 | }
326 | for _, child := range children {
327 | countProcStats(child, stats)
328 | }
329 | }
330 |
331 | func metricsUpdate(metricsType int, data string) {
332 | args := strings.Split(data, ";")
333 | name := args[0]
334 | val, _ := strconv.ParseFloat(args[1], 64)
335 | label := args[2:]
336 |
337 | // trim / expand labels to expected size
338 | if metricsType == RequestMetrics {
339 | switch {
340 | case len(label) > labelCount:
341 | label = label[0:labelCount]
342 | case len(label) < labelCount:
343 | label = append(label, make([]string, labelCount-len(label))...)
344 | }
345 | }
346 |
347 | collector, ok := collectors[name]
348 | if !ok {
349 | logErrorf("unknown metric: %s", name)
350 | return
351 | }
352 | switch col := collector.(type) {
353 | case prometheus.Gauge:
354 | col.Set(val)
355 | case prometheus.Counter:
356 | col.Add(val)
357 | case *prometheus.CounterVec:
358 | col.WithLabelValues(label...).Add(val)
359 | case *prometheus.GaugeVec:
360 | col.WithLabelValues(label...).Set(val)
361 | case *prometheus.HistogramVec:
362 | col.WithLabelValues(label...).Observe(val)
363 | default:
364 | logErrorf("unknown type: %T from metric %s", col, data)
365 | }
366 | }
367 |
368 | func expandBuckets(input string) (list []float64, err error) {
369 | for _, s := range strings.Split(input, ";") {
370 | s = strings.TrimSpace(s)
371 | n, fErr := strconv.ParseFloat(s, 64)
372 | if fErr != nil {
373 | err = fErr
374 | return
375 | }
376 | list = append(list, n)
377 | }
378 | return
379 | }
380 |
--------------------------------------------------------------------------------
/cmd/mod_prometheus_status/prometheus_internal_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | "github.com/stretchr/testify/require"
8 | )
9 |
10 | func TestExpandBuckets(t *testing.T) {
11 | t.Parallel()
12 | list := []float64{0.1, 0.5, 1, 10}
13 | res, err := expandBuckets(" 0.1;0.5;1; 10")
14 | require.NoError(t, err)
15 | assert.Equal(t, list, res)
16 | }
17 |
--------------------------------------------------------------------------------
/example_apache.conf:
--------------------------------------------------------------------------------
1 | LoadModule prometheus_status_module /.../mod_prometheus_status.so
2 | PrometheusStatusEnabled On
3 | PrometheusStatusLabelNames method;status
4 | PrometheusStatusLabelValues %m;%s
5 |
6 |
7 | SetHandler prometheus-metrics
8 |
9 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ConSol/apache_mod_prometheus_status
2 |
3 | go 1.22.1
4 |
5 | toolchain go1.22.9
6 |
7 | require (
8 | github.com/kdar/factorlog v0.0.0-20211012144011-6ea75a169038
9 | github.com/prometheus/client_golang v1.20.5
10 | github.com/prometheus/common v0.60.1
11 | github.com/shirou/gopsutil v3.21.11+incompatible
12 | github.com/stretchr/testify v1.9.0
13 | )
14 |
15 | require (
16 | github.com/beorn7/perks v1.0.1 // indirect
17 | github.com/cespare/xxhash/v2 v2.3.0 // indirect
18 | github.com/davecgh/go-spew v1.1.1 // indirect
19 | github.com/go-ole/go-ole v1.3.0 // indirect
20 | github.com/kr/text v0.2.0 // indirect
21 | github.com/mattn/go-colorable v0.1.13 // indirect
22 | github.com/mattn/go-isatty v0.0.20 // indirect
23 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
24 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
25 | github.com/pmezard/go-difflib v1.0.0 // indirect
26 | github.com/prometheus/client_model v0.6.1 // indirect
27 | github.com/prometheus/procfs v0.15.1 // indirect
28 | github.com/tklauser/go-sysconf v0.3.14 // indirect
29 | github.com/tklauser/numcpus v0.9.0 // indirect
30 | github.com/yusufpapurcu/wmi v1.2.4 // indirect
31 | golang.org/x/sys v0.27.0 // indirect
32 | google.golang.org/protobuf v1.35.2 // indirect
33 | gopkg.in/yaml.v3 v3.0.1 // indirect
34 | )
35 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
2 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
3 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
4 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
5 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
9 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
10 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
11 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
12 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
13 | github.com/kdar/factorlog v0.0.0-20211012144011-6ea75a169038 h1:ah2n2FwhELUb5o+KV0zAw8izxYC6UdK6dzjOKr3hfA8=
14 | github.com/kdar/factorlog v0.0.0-20211012144011-6ea75a169038/go.mod h1:vLeQHWaOMUQZ1ytnCskhwI5fCcXA7xxK0QjCngYPqbo=
15 | github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
16 | github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
17 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
18 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
19 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
20 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
21 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
22 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
23 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
24 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
25 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
26 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
27 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
28 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
29 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
30 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
31 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
32 | github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
33 | github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
34 | github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
35 | github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
36 | github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
37 | github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
38 | github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
39 | github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
40 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
41 | github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
42 | github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
43 | github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
44 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
45 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
46 | github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
47 | github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
48 | github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
49 | github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
50 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
51 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
52 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
53 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
54 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
55 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
56 | golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
57 | golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
58 | google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
59 | google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
60 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
61 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
62 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
63 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
64 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
65 |
--------------------------------------------------------------------------------
/src/mod_prometheus_status.c:
--------------------------------------------------------------------------------
1 | /*
2 | ** mod_prometheus_status.c -- Apache sample prometheus_status module
3 | */
4 |
5 | #include "mod_prometheus_status.h"
6 | #include "mod_prometheus_status_go.h"
7 |
8 | extern apr_hash_t *log_hash;
9 | extern unixd_config_rec ap_unixd_config;
10 |
11 | #define SERVER_DISABLED SERVER_NUM_STATUS
12 | #define MOD_STATUS_NUM_STATUS (SERVER_NUM_STATUS+1)
13 | static int status_flags[MOD_STATUS_NUM_STATUS];
14 | static int server_limit, thread_limit, threads_per_child, max_servers;
15 | static apr_proc_t *g_metric_manager = NULL;
16 | static int g_metric_manager_keep_running = TRUE;
17 |
18 | typedef struct {
19 | char context[4096];
20 | /* server level options */
21 | int debug; /* Enable debug logging */
22 | const char *label_names; /* Set custom label names */
23 | const char *time_buckets; /* raw response time buckets */
24 | const char *size_buckets; /* raw response size buckets */
25 | const char *tmp_folder; /* tmp folder for the socket */
26 |
27 | /* directory level options */
28 | int enabled; /* Enable or disable our module */
29 | char label_values[4096]; /* Add custom label values */
30 | apr_array_header_t *label_format; /* parsed label format */
31 | } prometheus_status_config;
32 | static prometheus_status_config config;
33 |
34 | /* Server object for main server as supplied to prometheus_status_init(). */
35 | static server_rec *main_server = NULL;
36 |
37 | int (*prometheusStatusInitFn)() = NULL;
38 | char *metric_socket = NULL;
39 | int metric_socket_fd = 0;
40 |
41 | void *prometheus_status_create_dir_conf(apr_pool_t *pool, char *context);
42 | void *prometheus_status_merge_dir_conf(apr_pool_t *pool, void *BASE, void *ADD);
43 | void *prometheus_status_create_server_conf(apr_pool_t *pool, server_rec *s);
44 | static void prometheus_status_register_hooks(apr_pool_t *p);
45 | const char *prometheus_status_set_debug(cmd_parms *cmd, void *cfg, int val);
46 | static const char *prometheus_status_set_label_names(cmd_parms *cmd, void *cfg, const char *arg);
47 | static const char *prometheus_status_set_tmp_folder(cmd_parms *cmd, void *cfg, const char *arg);
48 | static const char *prometheus_status_set_time_buckets(cmd_parms *cmd, void *cfg, const char *arg);
49 | static const char *prometheus_status_set_size_buckets(cmd_parms *cmd, void *cfg, const char *arg);
50 | const char *prometheus_status_set_enabled(cmd_parms *cmd, void *cfg, int val);
51 | const char *prometheus_status_set_label_values(cmd_parms *cmd, void *cfg, const char *arg);
52 |
53 | /* available configuration directives */
54 | static const command_rec prometheus_status_directives[] = {
55 | /* server level */
56 | AP_INIT_FLAG("PrometheusStatusDebug", prometheus_status_set_debug, NULL, RSRC_CONF, "Set to On to debug output."),
57 | AP_INIT_RAW_ARGS("PrometheusStatusLabelNames", prometheus_status_set_label_names, NULL, RSRC_CONF, "Set a request specific label names from within apache directives."),
58 | AP_INIT_RAW_ARGS("PrometheusStatusTmpFolder", prometheus_status_set_tmp_folder, NULL, RSRC_CONF, "Set folder for communication socket."),
59 | AP_INIT_RAW_ARGS("PrometheusStatusResponseTimeBuckets", prometheus_status_set_time_buckets, NULL, RSRC_CONF, "Set response time histogram buckets."),
60 | AP_INIT_RAW_ARGS("PrometheusStatusResponseSizeBuckets", prometheus_status_set_size_buckets, NULL, RSRC_CONF, "Set response size histogram buckets."),
61 |
62 | /* directory level */
63 | AP_INIT_FLAG("PrometheusStatusEnabled", prometheus_status_set_enabled, NULL, OR_ALL, "Set to Off to disable collecting metrics (for this directory/location)."),
64 | AP_INIT_RAW_ARGS("PrometheusStatusLabelValues", prometheus_status_set_label_values, NULL, OR_ALL, "Set a request label values from within apache directives"),
65 | { NULL }
66 | };
67 |
68 | /* register mod_prometheus_status within the apache */
69 | module AP_MODULE_DECLARE_DATA prometheus_status_module = {
70 | STANDARD20_MODULE_STUFF,
71 | prometheus_status_create_dir_conf, /* create per-dir config structures */
72 | prometheus_status_merge_dir_conf, /* merge per-dir config structures */
73 | prometheus_status_create_server_conf, /* create per-server config structures */
74 | prometheus_status_merge_dir_conf, /* merge per-server config structures */
75 | prometheus_status_directives, /* table of config file commands */
76 | prometheus_status_register_hooks /* register hooks */
77 | };
78 |
79 | /* Handler for the "PrometheusStatusDebug" directive */
80 | const char *prometheus_status_set_debug(cmd_parms *cmd, void *cfg, int val) {
81 | config.debug = val;
82 | return NULL;
83 | }
84 |
85 | /* Handler for the "PrometheusStatusEnabled" directive */
86 | const char *prometheus_status_set_enabled(cmd_parms *cmd, void *cfg, int val) {
87 | prometheus_status_config *conf = (prometheus_status_config *) cfg;
88 | conf->enabled = val;
89 | return NULL;
90 | }
91 |
92 | /* Handler for the "PrometheusStatusLabelNames" directive */
93 | static const char *prometheus_status_set_label_names(cmd_parms *cmd, void *cfg, const char *arg) {
94 | config.label_names = arg;
95 | return NULL;
96 | }
97 |
98 | /* Handler for the "PrometheusStatusTmpFolder" directive */
99 | static const char *prometheus_status_set_tmp_folder(cmd_parms *cmd, void *cfg, const char *arg) {
100 | config.tmp_folder = arg;
101 | return NULL;
102 | }
103 |
104 | /* Handler for the "PrometheusStatusResponseTimeBuckets" directive */
105 | static const char *prometheus_status_set_time_buckets(cmd_parms *cmd, void *cfg, const char *arg) {
106 | config.time_buckets = arg;
107 | return NULL;
108 | }
109 |
110 | /* Handler for the "PrometheusStatusResponseSizeBuckets" directive */
111 | static const char *prometheus_status_set_size_buckets(cmd_parms *cmd, void *cfg, const char *arg) {
112 | config.size_buckets = arg;
113 | return NULL;
114 | }
115 |
116 | /* Handler for the "PrometheusStatusLabelValues" directive */
117 | const char *prometheus_status_set_label_values(cmd_parms *cmd, void *cfg, const char *arg) {
118 | const char *err_string = NULL;
119 | prometheus_status_config *conf = (prometheus_status_config *) cfg;
120 | strcpy(conf->label_values, arg);
121 | conf->label_format = parse_log_string(cmd->pool, conf->label_values, &err_string);
122 | return err_string;
123 | }
124 |
125 | /* open the communication socket */
126 | static int prometheus_status_open_communication_socket() {
127 | struct sockaddr_un addr;
128 | addr.sun_family = AF_UNIX;
129 | // reuse if already open
130 | if(metric_socket_fd != 0) {
131 | return(TRUE);
132 | }
133 | // not yet initialized
134 | if(metric_socket == NULL) {
135 | return(FALSE);
136 | }
137 | strcpy(addr.sun_path, metric_socket);
138 | metric_socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
139 | if(connect(metric_socket_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
140 | logDebugf("failed to open metrics socket: socket:%s fd:%d errno:%d (%s)", metric_socket, metric_socket_fd, errno, strerror(errno));
141 | return(FALSE);
142 | }
143 |
144 | struct timeval timeout;
145 | timeout.tv_sec = DEFAULTSOCKETTIMEOUT;
146 | timeout.tv_usec = 0;
147 |
148 | if(setsockopt(metric_socket_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == -1) {
149 | logErrorf("setsockopt failed: socket:%s fd:%d errno:%d (%s)", metric_socket, metric_socket_fd, errno, strerror(errno));
150 | return(FALSE);
151 | }
152 |
153 | if(setsockopt(metric_socket_fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) == -1) {
154 | logErrorf("setsockopt failed: socket:%s fd:%d errno:%d (%s)", metric_socket, metric_socket_fd, errno, strerror(errno));
155 | return(FALSE);
156 | }
157 |
158 | return(TRUE);
159 | }
160 |
161 | /* close the communication socket */
162 | static int prometheus_status_close_communication_socket() {
163 | if(metric_socket_fd == 0) {
164 | return(TRUE);
165 | }
166 | close(metric_socket_fd);
167 | metric_socket_fd = 0;
168 | return(TRUE);
169 | }
170 |
171 | /* send something over the communication socket */
172 | static int prometheus_status_send_communication_socket(const char *fmt, ...) {
173 | char buffer[4096];
174 | int nbytes;
175 | va_list ap;
176 |
177 | // open socket unless open
178 | if(!prometheus_status_open_communication_socket()) {
179 | return(FALSE);
180 | }
181 |
182 | va_start(ap, fmt);
183 | nbytes = vsnprintf(buffer, 4096, fmt, ap);
184 | va_end(ap);
185 |
186 | if(write(metric_socket_fd, buffer, nbytes) < 0) {
187 | logDebugf("failed to send to metrics collector: socket:%s fd:%d errno:%d (%s)", metric_socket, metric_socket_fd, errno, strerror(errno));
188 | prometheus_status_close_communication_socket();
189 | }
190 | return(TRUE);
191 | }
192 |
193 | /* gather non-request runtime metrics */
194 | static int prometheus_status_monitor() {
195 | int busy = 0;
196 | int ready = 0;
197 | int i, j, res;
198 | worker_score *ws_record;
199 | process_score *ps_record;
200 | ap_generation_t mpm_generation;
201 | apr_interval_time_t uptime;
202 | apr_time_t nowtime;
203 | ap_loadavg_t cpu;
204 |
205 | status_flags[SERVER_DEAD] = 0;
206 | status_flags[SERVER_READY] = 0;
207 | status_flags[SERVER_STARTING] = 0;
208 | status_flags[SERVER_BUSY_READ] = 0;
209 | status_flags[SERVER_BUSY_WRITE] = 0;
210 | status_flags[SERVER_BUSY_KEEPALIVE] = 0;
211 | status_flags[SERVER_BUSY_LOG] = 0;
212 | status_flags[SERVER_BUSY_DNS] = 0;
213 | status_flags[SERVER_CLOSING] = 0;
214 | status_flags[SERVER_GRACEFUL] = 0;
215 | status_flags[SERVER_IDLE_KILL] = 0;
216 | status_flags[SERVER_DISABLED] = 0;
217 |
218 | ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
219 |
220 | nowtime = apr_time_now();
221 | uptime = (apr_uint32_t) apr_time_sec(nowtime - ap_scoreboard_image->global->restart_time);
222 | prometheus_status_send_communication_socket("server:promServerUptime;%ld\n", uptime);
223 |
224 | prometheus_status_send_communication_socket("server:promMPMGeneration;%d\n", mpm_generation);
225 | prometheus_status_send_communication_socket("server:promConfigGeneration;%d\n", ap_state_query(AP_SQ_CONFIG_GEN));
226 |
227 | ap_get_loadavg(&cpu);
228 | prometheus_status_send_communication_socket("server:promCPULoad;%f\n", cpu.loadavg);
229 |
230 | for(i = 0; i < server_limit; ++i) {
231 | ps_record = ap_get_scoreboard_process(i);
232 | for(j = 0; j < thread_limit; ++j) {
233 | ws_record = ap_get_scoreboard_worker_from_indexes(i, j);
234 | res = ws_record->status;
235 |
236 | if ((i >= max_servers || j >= threads_per_child)
237 | && (res == SERVER_DEAD))
238 | status_flags[SERVER_DISABLED]++;
239 | else
240 | status_flags[res]++;
241 |
242 | if(!ps_record->quiescing && ps_record->pid) {
243 | if(res == SERVER_READY) {
244 | if(ps_record->generation == mpm_generation)
245 | ready++;
246 | }
247 | else if (res != SERVER_DEAD &&
248 | res != SERVER_STARTING &&
249 | res != SERVER_IDLE_KILL) {
250 | busy++;
251 | }
252 | }
253 | }
254 | }
255 |
256 | prometheus_status_send_communication_socket("server:promScoreboard;%d;idle\n", status_flags[SERVER_READY]);
257 | prometheus_status_send_communication_socket("server:promScoreboard;%d;startup\n", status_flags[SERVER_STARTING]);
258 | prometheus_status_send_communication_socket("server:promScoreboard;%d;read\n", status_flags[SERVER_BUSY_READ]);
259 | prometheus_status_send_communication_socket("server:promScoreboard;%d;reply\n", status_flags[SERVER_BUSY_WRITE]);
260 | prometheus_status_send_communication_socket("server:promScoreboard;%d;keepalive\n", status_flags[SERVER_BUSY_KEEPALIVE]);
261 | prometheus_status_send_communication_socket("server:promScoreboard;%d;logging\n", status_flags[SERVER_BUSY_LOG]);
262 | prometheus_status_send_communication_socket("server:promScoreboard;%d;closing\n", status_flags[SERVER_CLOSING]);
263 | prometheus_status_send_communication_socket("server:promScoreboard;%d;graceful_stop\n", status_flags[SERVER_GRACEFUL]);
264 | prometheus_status_send_communication_socket("server:promScoreboard;%d;idle_cleanup\n", status_flags[SERVER_IDLE_KILL]);
265 | // disabled slots are not actual worker
266 | //prometheus_status_send_communication_socket("server:promScoreboard;%d;disabled\n", status_flags[SERVER_DISABLED]);
267 |
268 | prometheus_status_send_communication_socket("server:promWorkers;%d;ready\n", ready);
269 | prometheus_status_send_communication_socket("server:promWorkers;%d;busy\n", busy);
270 |
271 | return OK;
272 | }
273 |
274 | /* prometheus_status_handler responds to /metrics requests */
275 | static int prometheus_status_handler(request_rec *r) {
276 | int nbytes;
277 | char buffer[32768];
278 |
279 | // is the module enabled at all?
280 | prometheus_status_config *config = (prometheus_status_config*) ap_get_module_config(r->server->module_config, &prometheus_status_module);
281 | if(config->enabled == 0) {
282 | return(OK);
283 | }
284 |
285 | if(!r->handler || strcmp(r->handler, "prometheus-metrics")) return(DECLINED);
286 | if(r->header_only) {
287 | return(OK);
288 | }
289 |
290 | // update runtime metrics
291 | prometheus_status_monitor();
292 |
293 | ap_set_content_type(r, "text/plain");
294 |
295 | if(!prometheus_status_send_communication_socket("metrics\n")) {
296 | ap_rputs("ERROR: failed fetch metrics\n", r);
297 | logErrorf("failed fetch metrics: socket:%s fd:%d", metric_socket, metric_socket_fd);
298 | return(HTTP_INTERNAL_SERVER_ERROR);
299 | }
300 |
301 | while((nbytes = read(metric_socket_fd, buffer, 32768)) > 0) {
302 | if(nbytes < 0) {
303 | logErrorf("reading metrics failed: socket:%s fd:%d errno:%d (%s)", metric_socket, metric_socket_fd, errno, strerror(errno));
304 | ap_rputs("ERROR: failed fetch metrics\n", r);
305 | return(HTTP_INTERNAL_SERVER_ERROR);
306 | }
307 | if(nbytes == 0) {
308 | break;
309 | }
310 | buffer[nbytes] = 0;
311 | ap_rputs(buffer, r);
312 | // double newline at the end means EOF
313 | if(nbytes > 3 && buffer[nbytes-1] == '\n' && buffer[nbytes-2] == '\n') {
314 | break;
315 | }
316 | }
317 |
318 | prometheus_status_close_communication_socket();
319 |
320 | return(OK);
321 | }
322 |
323 | /* prometheus_status_counter is called on each request to update counter */
324 | static int prometheus_status_counter(request_rec *r) {
325 | apr_time_t now = apr_time_now();
326 | apr_time_t duration = now - r->request_time;
327 |
328 | // is the module enabled at all?
329 | prometheus_status_config *cfg = (prometheus_status_config*) ap_get_module_config(r->per_dir_config, &prometheus_status_module);
330 | if(cfg->enabled == 0) {
331 | return(OK);
332 | }
333 |
334 | const char *label = NULL;
335 | apr_array_header_t *format = cfg->label_format != NULL ? cfg->label_format : config.label_format;
336 | prometheus_status_expand_variables(format, r, &label);
337 |
338 | prometheus_status_send_communication_socket("request:promRequests;1;%s\n", label);
339 | prometheus_status_send_communication_socket("request:promResponseTime;%f;%s\n", (long)duration/(double)APR_USEC_PER_SEC, label);
340 | prometheus_status_send_communication_socket("request:promResponseSize;%d;%s\n", (int)r->bytes_sent, label);
341 | prometheus_status_close_communication_socket();
342 | return(OK);
343 | }
344 |
345 | static apr_status_t prometheus_status_cleanup_handler() {
346 | if(metric_socket != NULL) {
347 | logDebugf("prometheus_status_cleanup_handler");
348 | free(metric_socket);
349 | metric_socket = NULL;
350 | }
351 | g_metric_manager_keep_running = FALSE;
352 | return(OK);
353 | }
354 |
355 | /* prometheus_status_load_gomodule loads/starts the go part */
356 | static void prometheus_status_load_gomodule(apr_pool_t *p, server_rec *s) {
357 | const char* mpm_name = ap_show_mpm();
358 |
359 | // detect go module .so location
360 | void *go_module_handle = NULL;
361 | char origin[PATH_MAX];
362 | apr_os_dso_handle_t *osdso;
363 | apr_os_dso_handle_get((void *)&osdso, prometheus_status_module.dynamic_load_handle);
364 | if(dlinfo(osdso, RTLD_DI_ORIGIN, &origin) != -1) {
365 | char go_so_path[PATH_MAX+100];
366 | snprintf(go_so_path, PATH_MAX+100, "%s/mod_prometheus_status_go.so", origin);
367 |
368 | go_module_handle = dlopen(go_so_path, RTLD_LAZY);
369 | if(!go_module_handle) {
370 | logErrorf("loading %s failed: %s\n", go_so_path, dlerror());
371 | exit(1);
372 | }
373 | }
374 | logDebugf("prometheus_status_load_gomodule gomodule loaded");
375 |
376 | prometheusStatusInitFn = dlsym(go_module_handle, "prometheusStatusInit");
377 |
378 | // run go initializer
379 | int rc = (*prometheusStatusInitFn)(
380 | metric_socket,
381 | ap_get_server_description(),
382 | s->server_hostname,
383 | VERSION,
384 | config.debug,
385 | ap_unixd_config.user_id,
386 | ap_unixd_config.group_id,
387 | config.label_names,
388 | mpm_name,
389 | DEFAULTSOCKETTIMEOUT,
390 | config.time_buckets,
391 | config.size_buckets
392 | );
393 | if(rc != 0) {
394 | logErrorf("mod_prometheus_status initializing failed");
395 | exit(1);
396 | }
397 |
398 | return;
399 | }
400 |
401 | static void prometheus_status_metric_manager_maint(int reason, void *data, apr_wait_t status) {
402 | logDebugf("prometheus_status_metric_manager_maint: %d", reason);
403 | apr_proc_t *proc = data;
404 |
405 | switch (reason) {
406 | case APR_OC_REASON_DEATH:
407 | case APR_OC_REASON_RESTART:
408 | case APR_OC_REASON_LOST:
409 | prometheus_status_cleanup_handler();
410 | apr_proc_other_child_unregister(data);
411 | break;
412 | case APR_OC_REASON_UNREGISTER:
413 | prometheus_status_cleanup_handler();
414 | kill(proc->pid, SIGHUP);
415 | break;
416 | }
417 | }
418 |
419 | static apr_status_t prometheus_status_create_metrics_manager(apr_pool_t * p, server_rec * s) {
420 | apr_status_t rv;
421 |
422 | g_metric_manager = (apr_proc_t *) apr_pcalloc(p, sizeof(*g_metric_manager));
423 | rv = apr_proc_fork(g_metric_manager, p);
424 | if(rv == APR_INCHILD) {
425 | // load all go stuff in a separated sub process
426 | prometheus_status_load_gomodule(p, s);
427 | // wait till process ends...
428 | while(g_metric_manager_keep_running) {
429 | sleep(60);
430 | }
431 |
432 | logDebugf("metrics manager exited");
433 | exit(0);
434 | } else if (rv != APR_INPARENT) {
435 | logErrorf("cannot create metrics manager");
436 | exit(1);
437 | }
438 |
439 | // parent process
440 | apr_pool_note_subprocess(p, g_metric_manager, APR_KILL_ONLY_ONCE);
441 | apr_proc_other_child_register(g_metric_manager, prometheus_status_metric_manager_maint, g_metric_manager, NULL, p);
442 |
443 | // wait till socket exists
444 | struct stat buffer;
445 | int retries = 0;
446 | while(stat(metric_socket, &buffer) != 0) {
447 | usleep(50000);
448 | retries++;
449 | if(retries > 20) {
450 | logErrorf("metrics manager failed to start in time");
451 | break;
452 | }
453 | }
454 |
455 | return APR_SUCCESS;
456 | }
457 |
458 | /* prometheus_status_init creates go metrics manager process */
459 | static int prometheus_status_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) {
460 | /* cache main server */
461 | main_server = s;
462 |
463 | void *data = NULL;
464 | const char *key = "prometheus_status_init";
465 |
466 | // This code is used to prevent double initialization of the module during Apache startup
467 | apr_pool_userdata_get(&data, key, s->process->pool);
468 | if(data == NULL) {
469 | apr_pool_userdata_set((const void *)1, key, apr_pool_cleanup_null, s->process->pool);
470 | return OK;
471 | }
472 |
473 | ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
474 | ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
475 | ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_servers);
476 | ap_mpm_query(AP_MPMQ_MAX_THREADS, &threads_per_child);
477 |
478 | /* work around buggy MPMs */
479 | if (threads_per_child == 0)
480 | threads_per_child = 1;
481 |
482 | prometheus_status_cleanup_handler();
483 | g_metric_manager_keep_running = TRUE;
484 | metric_socket = tempnam(config.tmp_folder, "mtr.");
485 | logDebugf("prometheus_status_init: version %s - using tmp socket %s", VERSION, metric_socket);
486 |
487 | prometheus_status_create_metrics_manager(p, s);
488 |
489 | return OK;
490 | }
491 |
492 | /* prometheus_status_register_hooks registers all required hooks */
493 | static void prometheus_status_register_hooks(apr_pool_t *p) {
494 | const char *err_string = NULL;
495 | // set defaults
496 | config.debug = DEFAULTDEBUG;
497 | config.label_names = DEFAULTLABELNAMES;
498 | config.time_buckets = DEFAULTTIMEBUCKETS;
499 | config.size_buckets = DEFAULTSIZEBUCKETS;
500 | config.tmp_folder = DEFAULTTMPFOLDER;
501 | strcpy(config.label_values, DEFAULTLABELVALUES);
502 |
503 | log_hash = apr_hash_make(p);
504 | prometheus_status_register_all_log_handler(p);
505 | config.label_format = parse_log_string(p, config.label_values, &err_string);
506 | if(err_string != NULL) {
507 | logErrorf("failed to parse label values: %s\n", err_string);
508 | exit(1);
509 | }
510 |
511 | ap_hook_handler(prometheus_status_handler, NULL, NULL, APR_HOOK_MIDDLE);
512 | ap_hook_post_config(prometheus_status_init, NULL, NULL, APR_HOOK_MIDDLE);
513 | ap_hook_log_transaction(prometheus_status_counter, NULL, NULL, APR_HOOK_MIDDLE);
514 | }
515 |
516 | /* Function for creating new configurations for per-directory contexts */
517 | void *prometheus_status_create_dir_conf(apr_pool_t *pool, char *context) {
518 | context = context ? context : "Newly created configuration";
519 |
520 | prometheus_status_config *cfg = apr_pcalloc(pool, sizeof(prometheus_status_config));
521 | if(cfg) {
522 | // Set some default values
523 | strcpy(cfg->context, context);
524 | strcpy(cfg->label_values, "");
525 | cfg->enabled = -1;
526 | }
527 | return cfg;
528 | }
529 |
530 | /* Merging function for configurations */
531 | void *prometheus_status_merge_dir_conf(apr_pool_t *pool, void *BASE, void *ADD) {
532 | prometheus_status_config *base = (prometheus_status_config *) BASE;
533 | prometheus_status_config *add = (prometheus_status_config *) ADD;
534 | prometheus_status_config *conf = (prometheus_status_config *) prometheus_status_create_dir_conf(pool, "Merged configuration");
535 |
536 | conf->enabled = (add->enabled != -1) ? add->enabled : base->enabled;
537 | strcpy(conf->label_values, strlen(add->label_values) ? add->label_values : base->label_values);
538 | conf->label_format = add->label_format != NULL ? add->label_format : base->label_format;
539 | return conf;
540 | }
541 |
542 | /* Function for creating new configurations for per-server contexts */
543 | void *prometheus_status_create_server_conf(apr_pool_t *pool, server_rec *s) {
544 | return(prometheus_status_create_dir_conf(pool, "server config"));
545 | }
546 |
--------------------------------------------------------------------------------
/src/mod_prometheus_status.h:
--------------------------------------------------------------------------------
1 | #define VERSION "0.3.2"
2 | #define NAME "mod_prometheus_status"
3 |
4 | #include "ap_config.h"
5 | #include "apr_lib.h"
6 | #include "apr_strings.h"
7 | #include "httpd.h"
8 | #include "http_core.h"
9 | #include "http_log.h"
10 | #include "http_config.h"
11 | #include "http_protocol.h"
12 | #include "mpm_common.h"
13 | #include "unixd.h"
14 | #include "mod_unixd.h"
15 | #include "mod_log_config.h"
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | #define DEFAULTSOCKETTIMEOUT 3
23 |
24 | #define DEFAULTDEBUG 0
25 | #define DEFAULTTMPFOLDER NULL
26 | #define DEFAULTLABELNAMES "vhost;method;status"
27 | #define DEFAULTLABELVALUES "%v;%m;%s"
28 | #define DEFAULTTIMEBUCKETS "0.01;0.1;1;10;30"
29 | #define DEFAULTSIZEBUCKETS "1000;10000;100000;1000000;10000000;100000000"
30 |
31 | /* global logger */
32 | #define logDebugf(_fmt, ...) if(config.debug > 0) {\
33 | ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, main_server, \
34 | "[%s][%s:%d] "_fmt, NAME, __FILE__, __LINE__, ## __VA_ARGS__); }
35 |
36 | #define logErrorf(_fmt, ...) \
37 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, \
38 | "[%s][%s:%d] "_fmt, NAME, __FILE__, __LINE__, ## __VA_ARGS__);
39 |
40 | apr_array_header_t *parse_log_string(apr_pool_t *p, const char *s, const char **err);
41 | void prometheus_status_expand_variables(apr_array_header_t *format, request_rec *r, const char**output);
42 | int prometheus_status_register_all_log_handler(apr_pool_t *p);
43 |
--------------------------------------------------------------------------------
/src/mod_prometheus_status_format.c:
--------------------------------------------------------------------------------
1 | /*
2 | ** mod_prometheus_status_format.c -- Label format as taken from mod_log_config.c
3 | */
4 |
5 | #include "mod_prometheus_status.h"
6 |
7 | apr_hash_t *log_hash;
8 |
9 | typedef struct {
10 | ap_log_handler_fn_t *func;
11 | char *arg;
12 | int condition_sense;
13 | int want_orig;
14 | apr_array_header_t *conditions;
15 | } log_format_item;
16 |
17 |
18 | static char *pfmt(apr_pool_t *p, int i)
19 | {
20 | if (i <= 0) {
21 | return "-";
22 | }
23 | else {
24 | return apr_itoa(p, i);
25 | }
26 | }
27 |
28 | static const char *constant_item(request_rec *dummy, char *stuff)
29 | {
30 | return stuff;
31 | }
32 |
33 | static const char *log_remote_host(request_rec *r, char *a)
34 | {
35 | return ap_escape_logitem(r->pool, ap_get_remote_host(r->connection,
36 | r->per_dir_config,
37 | REMOTE_NAME, NULL));
38 | }
39 |
40 | static const char *log_remote_address(request_rec *r, char *a)
41 | {
42 | if (a && !strcmp(a, "c")) {
43 | return r->connection->client_ip;
44 | }
45 | else {
46 | return r->useragent_ip;
47 | }
48 | }
49 |
50 | static const char *log_local_address(request_rec *r, char *a)
51 | {
52 | return r->connection->local_ip;
53 | }
54 |
55 | static const char *log_remote_user(request_rec *r, char *a)
56 | {
57 | char *rvalue = r->user;
58 |
59 | if (rvalue == NULL) {
60 | rvalue = "-";
61 | }
62 | else if (strlen(rvalue) == 0) {
63 | rvalue = "\"\"";
64 | }
65 | else {
66 | rvalue = ap_escape_logitem(r->pool, rvalue);
67 | }
68 |
69 | return rvalue;
70 | }
71 |
72 | static const char *log_request_uri(request_rec *r, char *a)
73 | {
74 | return ap_escape_logitem(r->pool, r->uri);
75 | }
76 |
77 | static const char *log_request_method(request_rec *r, char *a)
78 | {
79 | return ap_escape_logitem(r->pool, r->method);
80 | }
81 |
82 | static const char *log_request_protocol(request_rec *r, char *a)
83 | {
84 | return ap_escape_logitem(r->pool, r->protocol);
85 | }
86 |
87 | static const char *log_status(request_rec *r, char *a)
88 | {
89 | return pfmt(r->pool, r->status);
90 | }
91 |
92 | static const char *log_handler(request_rec *r, char *a)
93 | {
94 | return ap_escape_logitem(r->pool, r->handler);
95 | }
96 |
97 | static const char *log_header_in(request_rec *r, char *a)
98 | {
99 | return ap_escape_logitem(r->pool, apr_table_get(r->headers_in, a));
100 | }
101 |
102 | static APR_INLINE char *find_multiple_headers(apr_pool_t *pool,
103 | const apr_table_t *table,
104 | const char *key)
105 | {
106 | const apr_array_header_t *elts;
107 | const apr_table_entry_t *t_elt;
108 | const apr_table_entry_t *t_end;
109 | apr_size_t len;
110 | struct sle {
111 | struct sle *next;
112 | const char *value;
113 | apr_size_t len;
114 | } *result_list, *rp;
115 |
116 | elts = apr_table_elts(table);
117 |
118 | if (!elts->nelts) {
119 | return NULL;
120 | }
121 |
122 | t_elt = (const apr_table_entry_t *)elts->elts;
123 | t_end = t_elt + elts->nelts;
124 | len = 1; /* \0 */
125 | result_list = rp = NULL;
126 |
127 | do {
128 | if (!strcasecmp(t_elt->key, key)) {
129 | if (!result_list) {
130 | result_list = rp = apr_palloc(pool, sizeof(*rp));
131 | }
132 | else {
133 | rp = rp->next = apr_palloc(pool, sizeof(*rp));
134 | len += 2; /* ", " */
135 | }
136 |
137 | rp->next = NULL;
138 | rp->value = t_elt->val;
139 | rp->len = strlen(rp->value);
140 |
141 | len += rp->len;
142 | }
143 | ++t_elt;
144 | } while (t_elt < t_end);
145 |
146 | if (result_list) {
147 | char *result = apr_palloc(pool, len);
148 | char *cp = result;
149 |
150 | rp = result_list;
151 | while (rp) {
152 | if (rp != result_list) {
153 | *cp++ = ',';
154 | *cp++ = ' ';
155 | }
156 | memcpy(cp, rp->value, rp->len);
157 | cp += rp->len;
158 | rp = rp->next;
159 | }
160 | *cp = '\0';
161 |
162 | return result;
163 | }
164 |
165 | return NULL;
166 | }
167 |
168 | static const char *log_header_out(request_rec *r, char *a)
169 | {
170 | const char *cp = NULL;
171 |
172 | if (!strcasecmp(a, "Content-type") && r->content_type) {
173 | cp = ap_field_noparam(r->pool, r->content_type);
174 | }
175 | else if (!strcasecmp(a, "Set-Cookie")) {
176 | cp = find_multiple_headers(r->pool, r->headers_out, a);
177 | }
178 | else {
179 | cp = apr_table_get(r->headers_out, a);
180 | }
181 |
182 | return ap_escape_logitem(r->pool, cp);
183 | }
184 |
185 |
186 | static const char *log_env_var(request_rec *r, char *a)
187 | {
188 | return ap_escape_logitem(r->pool, apr_table_get(r->subprocess_env, a));
189 | }
190 |
191 | static const char *log_cookie(request_rec *r, char *a)
192 | {
193 | const char *cookies_entry;
194 |
195 | /*
196 | * This supports Netscape version 0 cookies while being tolerant to
197 | * some properties of RFC2109/2965 version 1 cookies:
198 | * - case-insensitive match of cookie names
199 | * - white space between the tokens
200 | * It does not support the following version 1 features:
201 | * - quoted strings as cookie values
202 | * - commas to separate cookies
203 | */
204 |
205 | if ((cookies_entry = apr_table_get(r->headers_in, "Cookie"))) {
206 | char *cookie, *last1, *last2;
207 | char *cookies = apr_pstrdup(r->pool, cookies_entry);
208 |
209 | while ((cookie = apr_strtok(cookies, ";", &last1))) {
210 | char *name = apr_strtok(cookie, "=", &last2);
211 | if (name) {
212 | char *value = name + strlen(name) + 1;
213 | apr_collapse_spaces(name, name);
214 |
215 | if (!strcasecmp(name, a)) {
216 | char *last;
217 | value += strspn(value, " \t"); /* Move past leading WS */
218 | last = value + strlen(value) - 1;
219 | while (last >= value && apr_isspace(*last)) {
220 | *last = '\0';
221 | --last;
222 | }
223 |
224 | return ap_escape_logitem(r->pool, value);
225 | }
226 | }
227 | cookies = NULL;
228 | }
229 | }
230 | return NULL;
231 | }
232 |
233 |
234 | /* These next two routines use the canonical name:port so that log
235 | * parsers don't need to duplicate all the vhost parsing crud.
236 | */
237 | static const char *log_virtual_host(request_rec *r, char *a)
238 | {
239 | return ap_escape_logitem(r->pool, r->server->server_hostname);
240 | }
241 |
242 | static const char *log_server_port(request_rec *r, char *a)
243 | {
244 | apr_port_t port;
245 |
246 | if (*a == '\0' || !strcasecmp(a, "canonical")) {
247 | port = r->server->port ? r->server->port : ap_default_port(r);
248 | }
249 | else if (!strcasecmp(a, "remote")) {
250 | port = r->useragent_addr->port;
251 | }
252 | else if (!strcasecmp(a, "local")) {
253 | port = r->connection->local_addr->port;
254 | }
255 | else {
256 | /* bogus format */
257 | return a;
258 | }
259 | return apr_itoa(r->pool, (int)port);
260 | }
261 |
262 | /* This respects the setting of UseCanonicalName so that
263 | * the dynamic mass virtual hosting trick works better.
264 | */
265 | static const char *log_server_name(request_rec *r, char *a)
266 | {
267 | return ap_escape_logitem(r->pool, ap_get_server_name(r));
268 | }
269 |
270 | static const char *log_connection_status(request_rec *r, char *a)
271 | {
272 | if (r->connection->aborted)
273 | return "X";
274 |
275 | if (r->connection->keepalive == AP_CONN_KEEPALIVE &&
276 | (!r->server->keep_alive_max ||
277 | (r->server->keep_alive_max - r->connection->keepalives) > 0)) {
278 | return "+";
279 | }
280 | return "-";
281 | }
282 |
283 | static void prometheus_status_register_log_handler(apr_pool_t *p, char *tag, ap_log_handler_fn_t *handler, int def)
284 | {
285 | ap_log_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
286 | log_struct->func = handler;
287 | log_struct->want_orig_default = def;
288 |
289 | apr_hash_set(log_hash, tag, 1, (const void *)log_struct);
290 | }
291 |
292 | static char *parse_log_misc_string(apr_pool_t *p, log_format_item *it,
293 | const char **sa)
294 | {
295 | const char *s;
296 | char *d;
297 |
298 | it->func = constant_item;
299 | it->conditions = NULL;
300 |
301 | s = *sa;
302 | while (*s && *s != '%') {
303 | s++;
304 | }
305 | /*
306 | * This might allocate a few chars extra if there's a backslash
307 | * escape in the format string.
308 | */
309 | it->arg = apr_palloc(p, s - *sa + 1);
310 |
311 | d = it->arg;
312 | s = *sa;
313 | while (*s && *s != '%') {
314 | if (*s != '\\') {
315 | *d++ = *s++;
316 | }
317 | else {
318 | s++;
319 | switch (*s) {
320 | case '\\':
321 | *d++ = '\\';
322 | s++;
323 | break;
324 | case 'r':
325 | *d++ = '\r';
326 | s++;
327 | break;
328 | case 'n':
329 | *d++ = '\n';
330 | s++;
331 | break;
332 | case 't':
333 | *d++ = '\t';
334 | s++;
335 | break;
336 | default:
337 | /* copy verbatim */
338 | *d++ = '\\';
339 | /*
340 | * Allow the loop to deal with this *s in the normal
341 | * fashion so that it handles end of string etc.
342 | * properly.
343 | */
344 | break;
345 | }
346 | }
347 | }
348 | *d = '\0';
349 |
350 | *sa = s;
351 | return NULL;
352 | }
353 |
354 | static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa)
355 | {
356 | const char *s = *sa;
357 | ap_log_handler *handler;
358 |
359 | if (*s != '%') {
360 | return parse_log_misc_string(p, it, sa);
361 | }
362 |
363 | ++s;
364 | it->condition_sense = 0;
365 | it->conditions = NULL;
366 |
367 | if (*s == '%') {
368 | it->arg = "%";
369 | it->func = constant_item;
370 | *sa = ++s;
371 |
372 | return NULL;
373 | }
374 |
375 | it->want_orig = -1;
376 | it->arg = ""; /* For safety's sake... */
377 |
378 | while (*s) {
379 | int i;
380 |
381 | switch (*s) {
382 | case '!':
383 | ++s;
384 | it->condition_sense = !it->condition_sense;
385 | break;
386 |
387 | case '<':
388 | ++s;
389 | it->want_orig = 1;
390 | break;
391 |
392 | case '>':
393 | ++s;
394 | it->want_orig = 0;
395 | break;
396 |
397 | case ',':
398 | ++s;
399 | break;
400 |
401 | case '{':
402 | ++s;
403 | it->arg = ap_getword(p, &s, '}');
404 | break;
405 |
406 | case '0':
407 | case '1':
408 | case '2':
409 | case '3':
410 | case '4':
411 | case '5':
412 | case '6':
413 | case '7':
414 | case '8':
415 | case '9':
416 | i = *s - '0';
417 | while (apr_isdigit(*++s)) {
418 | i = i * 10 + (*s) - '0';
419 | }
420 | if (!it->conditions) {
421 | it->conditions = apr_array_make(p, 4, sizeof(int));
422 | }
423 | *(int *) apr_array_push(it->conditions) = i;
424 | break;
425 |
426 | default:
427 | handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
428 | if (!handler) {
429 | char dummy[2];
430 |
431 | dummy[0] = s[-1];
432 | dummy[1] = '\0';
433 | return apr_pstrcat(p, "Unrecognized LabelFormat directive %", dummy, NULL);
434 | }
435 | it->func = handler->func;
436 | if (it->want_orig == -1) {
437 | it->want_orig = handler->want_orig_default;
438 | }
439 | *sa = s;
440 | return NULL;
441 | }
442 | }
443 |
444 | return "Ran off end of LabelFormat parsing args to some directive";
445 | }
446 |
447 | apr_array_header_t *parse_log_string(apr_pool_t *p, const char *s, const char **err)
448 | {
449 | apr_array_header_t *a = apr_array_make(p, 30, sizeof(log_format_item));
450 | char *res;
451 |
452 | while (*s) {
453 | if ((res = parse_log_item(p, (log_format_item *) apr_array_push(a), &s))) {
454 | *err = res;
455 | return NULL;
456 | }
457 | }
458 |
459 | return a;
460 | }
461 |
462 | static const char *process_item(request_rec *r, request_rec *orig,
463 | log_format_item *item)
464 | {
465 | const char *cp;
466 |
467 | /* First, see if we need to process this thing at all... */
468 |
469 | if (item->conditions && item->conditions->nelts != 0) {
470 | int i;
471 | int *conds = (int *) item->conditions->elts;
472 | int in_list = 0;
473 |
474 | for (i = 0; i < item->conditions->nelts; ++i) {
475 | if (r->status == conds[i]) {
476 | in_list = 1;
477 | break;
478 | }
479 | }
480 |
481 | if ((item->condition_sense && in_list)
482 | || (!item->condition_sense && !in_list)) {
483 | return "-";
484 | }
485 | }
486 |
487 | /* We do. Do it... */
488 |
489 | cp = (*item->func) (item->want_orig ? orig : r, item->arg);
490 | return cp ? cp : "-";
491 | }
492 |
493 | int prometheus_status_register_all_log_handler(apr_pool_t *p)
494 | {
495 | prometheus_status_register_log_handler(p, "h", log_remote_host, 0);
496 | prometheus_status_register_log_handler(p, "a", log_remote_address, 0 );
497 | prometheus_status_register_log_handler(p, "A", log_local_address, 0 );
498 | prometheus_status_register_log_handler(p, "u", log_remote_user, 0);
499 | prometheus_status_register_log_handler(p, "i", log_header_in, 0);
500 | prometheus_status_register_log_handler(p, "o", log_header_out, 0);
501 | prometheus_status_register_log_handler(p, "e", log_env_var, 0);
502 | prometheus_status_register_log_handler(p, "V", log_server_name, 0);
503 | prometheus_status_register_log_handler(p, "v", log_virtual_host, 0);
504 | prometheus_status_register_log_handler(p, "p", log_server_port, 0);
505 | prometheus_status_register_log_handler(p, "H", log_request_protocol, 0);
506 | prometheus_status_register_log_handler(p, "m", log_request_method, 0);
507 | prometheus_status_register_log_handler(p, "X", log_connection_status, 0);
508 | prometheus_status_register_log_handler(p, "C", log_cookie, 0);
509 | prometheus_status_register_log_handler(p, "U", log_request_uri, 1);
510 | prometheus_status_register_log_handler(p, "s", log_status, 1);
511 | prometheus_status_register_log_handler(p, "R", log_handler, 1);
512 |
513 | return OK;
514 | }
515 |
516 | void prometheus_status_expand_variables(apr_array_header_t *format, request_rec *r, const char**output) {
517 | log_format_item *items;
518 | request_rec *orig;
519 | int i;
520 |
521 | items = (log_format_item *) format->elts;
522 |
523 | orig = r;
524 | while (orig->prev) {
525 | orig = orig->prev;
526 | }
527 | while (r->next) {
528 | r = r->next;
529 | }
530 |
531 | for (i = 0; i < format->nelts; ++i) {
532 | const char *str = process_item(r, orig, &items[i]);
533 | if(*output == NULL) {
534 | *output = str;
535 | } else {
536 | *output = apr_psprintf(r->pool, "%s%s", *output, str);
537 | }
538 | }
539 |
540 | return;
541 | }
--------------------------------------------------------------------------------
/t/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: testbox_centos8
2 |
3 | test:
4 | $(MAKE) -C testbox_centos8 prepare wait_start test
5 | $(MAKE) -C testbox_centos8 clean
6 | $(MAKE) -C testbox_ubuntu18.04 prepare wait_start test
7 | $(MAKE) -C testbox_ubuntu18.04 clean
8 |
9 | clean:
10 | $(MAKE) -C testbox_centos8 clean
11 | $(MAKE) -C testbox_ubuntu18.04 clean
12 |
13 | testbox_centos8:
14 | $(MAKE) -C testbox_centos8 prepare wait_start
15 |
--------------------------------------------------------------------------------
/t/common/Makefile:
--------------------------------------------------------------------------------
1 | SHELL=bash
2 | INTERACTIVE:=$(shell [ -t 0 ] && echo 1)
3 | ifdef INTERACTIVE
4 | DOCKER_COMPOSE_TTY=
5 | else
6 | DOCKER_COMPOSE_TTY=-T
7 | endif
8 |
9 | wait_start:
10 | for x in $$(seq 180); do \
11 | if curl -sq http://localhost:3000 >/dev/null; then break; else sleep 1; fi; \
12 | if [ $$x -eq 180 ]; then echo "startup failed"; exit 1; fi; \
13 | done
14 |
15 | test: wait_start
16 | docker-compose exec $(DOCKER_COMPOSE_TTY) --user root apache /src/t/common/local_test.sh 0
17 |
18 | test_verbose: wait_start
19 | docker-compose exec $(DOCKER_COMPOSE_TTY) --user root apache /src/t/common/local_test.sh 1
20 |
21 | prepare:
22 | docker-compose build
23 | docker-compose up --remove-orphans -d
24 | docker ps
25 | @echo "waiting for testbox to provisioning"
26 | @docker-compose logs -f | while read LOGLINE; do \
27 | echo "$${LOGLINE}"; \
28 | [[ "$${LOGLINE}" == *"Starting Apache web server"* ]] && pkill -P $$$$ docker-compose && exit 0; \
29 | done
30 | @echo "************************************************************************************"
31 | @echo "testbox startup complete:"
32 | @echo "you can access the metrics exporter at http://localhost:3000/metrics"
33 | @echo "you can access the grafana dashboard at http://localhost:3001/dashboard/grafana"
34 | @echo "you can access the prometheus instance at http://localhost:3001/dashboard/prometheus"
35 | @echo "************************************************************************************"
36 | @echo ""
37 |
38 | clean:
39 | docker-compose kill
40 | docker-compose rm -f
41 | -docker network prune -f
42 | -[ $$(docker ps -q | wc -l) -eq 0 ] || docker kill $$(docker ps -q)
43 |
44 | update:
45 | docker-compose pull
46 | for IMG in $$(grep FROM */Dockerfile | awk '{ print $$2 }' | sort -u); do docker pull $$IMG; done
47 |
--------------------------------------------------------------------------------
/t/common/apache/apache.conf:
--------------------------------------------------------------------------------
1 | LoadModule prometheus_status_module /omd/sites/demo/var/tmp/build/mod_prometheus_status.so
2 | PrometheusStatusEnabled On
3 | PrometheusStatusDebug On
4 | PrometheusStatusLabelNames method;status;application
5 | PrometheusStatusLabelValues %m;%s;
6 | #PrometheusStatusTmpFolder /var/tmp
7 |
8 |
9 | SetHandler prometheus-metrics
10 |
11 |
12 |
13 | ServerName apache.test.local
14 | DocumentRoot /
15 |
16 |
17 |
18 | ServerName vhost-a.test.local
19 | DocumentRoot /tmp
20 |
21 |
22 |
23 | ServerName vhost-b.test.local
24 | DocumentRoot /var/tmp
25 |
26 |
27 |
28 | PrometheusStatusLabelValues %m;%s;/test
29 |
30 |
31 |
32 | PrometheusStatusLabelValues %m;%s;/disabled
33 | PrometheusStatusEnabled Off
34 |
35 |
--------------------------------------------------------------------------------
/t/common/apache/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ "x$OMD_SITE" != "xdemo" ]; then
4 | echo "ERROR: this script should be run as demo user only."
5 | exit 3
6 | fi
7 |
8 | echo "building... "
9 | mkdir -p var/tmp/build
10 | rsync -av --exclude=.git/ /src/. var/tmp/build/.
11 | cd var/tmp/build && \
12 | make build && \
13 | echo "build OK" && \
14 | sleep 1 && \
15 | omd restart apache > /dev/null
16 | echo "done."
17 |
--------------------------------------------------------------------------------
/t/common/apache/build_watcher.init:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | ### BEGIN INIT INFO
4 | # Provides: build_watcher
5 | # Required-Start: $local_fs
6 | # Required-Stop: $local_fs
7 | # Should-Start:
8 | # Should-Stop:
9 | # Default-Start: 2 3 4 5
10 | # Default-Stop: 0 1 6
11 | # Short-Description: build mod_prometheus_status module and restart apache on source changes
12 | ### END INIT INFO
13 |
14 | cd $OMD_ROOT
15 |
16 | . ~/.profile
17 |
18 | DAEMON=/src/t/common/apache/build_watcher.sh
19 | NAME=build_watcher
20 | PIDFILE=$OMD_ROOT/tmp/run/build_watcher.lock
21 | LOGFILE=$OMD_ROOT/var/log/build_watcher.log
22 |
23 | case "$1" in
24 | start)
25 | echo -n "Starting $NAME..."
26 | mkdir -p $OMD_ROOT/tmp/run
27 | $DAEMON >> $LOGFILE 2>&1 &
28 | if [ $? -eq 0 ]; then
29 | echo $! > $PIDFILE
30 | echo "OK"
31 | else
32 | rm -f $PIDFILE
33 | echo "failed"
34 | fi
35 | ;;
36 | stop)
37 | echo -n "Stopping $NAME..."
38 | pid=`cat $PIDFILE 2>/dev/null`
39 | if [ -z $pid ]; then
40 | echo ". Not running."
41 | else
42 | kill -TERM $pid
43 | for x in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5; do
44 | echo -n "."
45 | ps -p $pid > /dev/null 2>&1 && sleep 1;
46 | done
47 | rm -f $PIDFILE
48 | ps -p $pid > /dev/null 2>&1;
49 | if [ $? -ne 0 ]; then
50 | echo "OK"
51 | exit 0;
52 | else
53 | echo "failed"
54 | exit 1;
55 | fi
56 | fi
57 | ;;
58 | status)
59 | pid=`cat $PIDFILE 2>/dev/null`
60 | if [ "$pid" != "" ]; then
61 | ps -p $pid > /dev/null 2>&1
62 | if [ $? -eq 0 ]; then
63 | echo "$NAME is running with pid $pid"
64 | exit 0;
65 | fi
66 | fi
67 | echo "$NAME is not running"
68 | exit 1;
69 | ;;
70 | restart)
71 | $0 stop && sleep 1 && $0 start
72 | exit $?
73 | ;;
74 | *)
75 | echo "Usage: $NAME {start|stop|status|restart}"
76 | exit 1
77 | ;;
78 | esac
79 |
80 | exit 0
81 |
--------------------------------------------------------------------------------
/t/common/apache/build_watcher.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ "x$OMD_SITE" != "xdemo" ]; then
4 | echo "ERROR: this script should be run as demo user only."
5 | exit 3
6 | fi
7 |
8 | function finish {
9 | # kill all child procs since inotifywait might be still running
10 | ps -fu $OMD_SITE | grep $$ | grep inotifywait | awk '{ print $2 }' | xargs kill
11 | }
12 | trap finish EXIT
13 |
14 | # keep watching for changes
15 | while inotifywait -q -e close_write /src/src/*.c /src/cmd/mod_prometheus_status/*.go; do
16 | sleep 1
17 | /src/t/common/apache/build.sh
18 | done
19 |
20 |
--------------------------------------------------------------------------------
/t/common/apache/playbook_common.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # apache site
3 | - shell: omd config demo set APACHE_MODE own
4 | - shell: omd config demo set APACHE_TCP_ADDR '0.0.0.0'
5 | - shell: omd config demo set CORE none
6 | - shell: omd config demo set CRONTAB off
7 | - shell: omd config demo set PNP4NAGIOS off
8 | - shell: omd config demo set THRUK_COOKIE_AUTH off
9 | - shell: omd config demo set DEFAULT_GUI none
10 | - name: Remove apache authentication
11 | file:
12 | path: /opt/omd/sites/demo/etc/apache/conf.d/auth.conf
13 | state: absent
14 | - name: enable module in apache
15 | file:
16 | src: /src/t/common/apache/apache.conf
17 | dest: /omd/sites/demo/etc/apache/conf.d/prom.conf
18 | state: link
19 | - shell: sudo su - demo -c "/src/t/common/apache/build.sh"
20 | - name: enable build watcher
21 | file:
22 | src: /src/t/common/apache/build_watcher.init
23 | dest: /omd/sites/demo/etc/init.d/build_watcher
24 | state: link
25 | - name: enable build watcher
26 | file:
27 | src: ../init.d/build_watcher
28 | dest: /omd/sites/demo/etc/rc.d/10-build_watcher
29 | state: link
30 | - shell: sudo su - demo -c "rm -f etc/init-hooks.d/apache-*"
31 |
32 | # create dashboard site
33 | - shell: omd create dashboard
34 | - shell: omd config dashboard set APACHE_MODE own
35 | - shell: omd config dashboard set APACHE_TCP_ADDR '0.0.0.0'
36 | - shell: omd config dashboard set CORE none
37 | - shell: omd config dashboard set CRONTAB off
38 | - shell: omd config dashboard set PNP4NAGIOS off
39 | - shell: omd config dashboard set THRUK_COOKIE_AUTH off
40 | - shell: omd config dashboard set DEFAULT_GUI grafana
41 | - shell: omd config dashboard set PROMETHEUS on
42 | - shell: omd config dashboard set GRAFANA on
43 | - name: Remove apache authentication
44 | file:
45 | path: /opt/omd/sites/dashboard/etc/apache/conf.d/auth.conf
46 | state: absent
47 | - shell: sudo su - dashboard -c "rm -f etc/init-hooks.d/apache-*"
48 | - name: enable prometheus config
49 | file:
50 | src: /src/t/common/apache/prometheus.yml
51 | dest: /omd/sites/dashboard/etc/prometheus/prometheus.d/scrape_configs/static/01-apache.yml
52 | state: link
53 | - name: add apache status dashboard
54 | file:
55 | src: /src/Apache-Dashboard.json
56 | dest: /omd/sites/dashboard/var/grafana/dashboards/Apache-Status.json
57 | state: link
58 | - name: set default user for grafana
59 | lineinfile:
60 | path: /omd/sites/dashboard/etc/apache/conf.d/grafana.conf
61 | regexp: 'RequestHeader set X-WEBAUTH-USER "omdadmin"'
62 | insertbefore: 'RequestHeader unset Authorization'
63 | line: 'RequestHeader set X-WEBAUTH-USER "omdadmin"'
64 | - shell: omd start dashboard
65 |
--------------------------------------------------------------------------------
/t/common/apache/prometheus.yml:
--------------------------------------------------------------------------------
1 | - job_name: 'apache'
2 | scrape_interval: 5s
3 | metrics_path: /metrics
4 | static_configs:
5 | - targets: ['127.0.0.1:5000']
6 |
--------------------------------------------------------------------------------
/t/common/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2.3"
2 |
3 | networks:
4 | test.local:
5 |
6 | services:
7 | apache:
8 | build: apache/
9 | ports:
10 | - "8080:80"
11 | - "8443:443"
12 | - "3000:5000"
13 | - "3001:5001"
14 | volumes:
15 | - ../../:/src:ro
16 | networks:
17 | test.local:
18 | aliases:
19 | - apache.test.local
20 | - vhost-a.test.local
21 | - vhost-b.test.local
22 | hostname: apache
23 | domainname: test.local
24 | # required for strace and gdb
25 | cap_add:
26 | - SYS_PTRACE
27 |
--------------------------------------------------------------------------------
/t/common/local_test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | VERBOSE=0
4 | if [ "x$1" != "x" ]; then
5 | VERBOSE=$1
6 | shift
7 | fi
8 |
9 | TESTS="/src/t/common/t/*.t"
10 | if [ "x$1" != "x" ]; then
11 | TESTS="$*"
12 | shift
13 | fi
14 |
15 | omd stop dashboard >/dev/null 2>&1
16 |
17 | sudo -iu demo \
18 | PERL_DL_NONLAZY=1 \
19 | perl -MExtUtils::Command::MM -e "test_harness($VERBOSE, '/thruk/t', 'lib/')" \
20 | $TESTS
21 | exit $?
22 |
--------------------------------------------------------------------------------
/t/common/t/01-basic.t:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 |
3 | use warnings;
4 | use strict;
5 | use Test::More tests => 52;
6 |
7 | for my $mpm (qw/prefork worker event/) {
8 | my $res = `omd stop apache`;
9 | is($?, 0, "apache stopped");
10 |
11 | set_apache_mpm($mpm);
12 |
13 | $res = `omd start apache`;
14 | is($?, 0, "apache started");
15 |
16 | $res = `curl -qs http://localhost:5000`;
17 | is($?, 0, "curl worked");
18 |
19 | $res = `curl -qs http://localhost:5000/metrics`;
20 | is($?, 0, "curl worked");
21 | like($res, "/apache_requests_total/", "result contains counter");
22 | like($res, "/apache_server_info/", "result contains apache_server_info");
23 | like($res, "/Apache\/2.4/", "result contains apache_server_info");
24 | like($res, "/apache_response_time_seconds.*10\"/", "result contains apache_response_time_seconds");
25 | like($res, "/apache_response_size_bytes.*10000/", "result contains apache_response_size_bytes");
26 | like($res, '/mpm="'.$mpm.'"/', "result contains ".$mpm." mpm");
27 |
28 | $res = `curl -qs http://localhost:5000/test`;
29 | is($?, 0, "curl worked");
30 |
31 | $res = `curl -qs http://localhost:5000/disabled`;
32 | is($?, 0, "curl worked");
33 |
34 | $res = `curl -qs http://localhost:5000/metrics`;
35 | is($?, 0, "curl worked");
36 | like($res, qr(\Qapache_requests_total{application="/test",method="GET",status="404"}\E), "result contains counter with custom label");
37 | unlike($res, qr(\Q/disabled\E), "result does not contain disabled path counter");
38 |
39 | # test reloads
40 | $res = `omd reload apache`;
41 | is($?, 0, "reload worked");
42 |
43 | $res = `curl -qs http://localhost:5000/metrics`;
44 | is($?, 0, "curl worked");
45 | }
46 |
47 | ################################################################################
48 | # switch back
49 | set_apache_mpm("prefork");
50 | my $res = `omd restart apache`;
51 | is($?, 0, "apache restarted");
52 |
53 | ################################################################################
54 | sub set_apache_mpm {
55 | my($mpm) = @_;
56 | `sed -e 's/LoadModule mpm_[a-z]*_module/LoadModule mpm_${mpm}_module/g' -e 's/mod_mpm_[a-z]*.so/mod_mpm_${mpm}.so/g' -i etc/apache/apache.conf`;
57 | }
--------------------------------------------------------------------------------
/t/testbox_centos8/Makefile:
--------------------------------------------------------------------------------
1 | ../common/Makefile
--------------------------------------------------------------------------------
/t/testbox_centos8/apache/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM consol/omd-labs-centos:latest
2 |
3 | COPY playbook.yml /root/ansible_dropin/
4 |
--------------------------------------------------------------------------------
/t/testbox_centos8/apache/playbook.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | tasks:
4 | # machine preparation
5 | - yum:
6 | name:
7 | - perl-Test-Harness
8 | - httpd-devel
9 | - gcc
10 | - rsync
11 | - gdb
12 | - strace
13 | - inotify-tools
14 | - redhat-rpm-config
15 | - golang
16 | state: present
17 | - name: import common tasks
18 | import_tasks: /src/t/common/apache/playbook_common.yml
19 |
--------------------------------------------------------------------------------
/t/testbox_centos8/docker-compose.yml:
--------------------------------------------------------------------------------
1 | ../common/docker-compose.yml
--------------------------------------------------------------------------------
/t/testbox_centos8/local_test.sh:
--------------------------------------------------------------------------------
1 | ../common/local_test.sh
--------------------------------------------------------------------------------
/t/testbox_ubuntu18.04/Makefile:
--------------------------------------------------------------------------------
1 | ../common/Makefile
--------------------------------------------------------------------------------
/t/testbox_ubuntu18.04/apache/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM consol/omd-labs-ubuntu:latest
2 |
3 | COPY playbook.yml /root/ansible_dropin/
4 |
--------------------------------------------------------------------------------
/t/testbox_ubuntu18.04/apache/playbook.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | tasks:
4 |
5 | # machine preparation
6 | - apt:
7 | name:
8 | - software-properties-common
9 | - libtest-harness-perl
10 | - apache2-dev
11 | - gcc
12 | - rsync
13 | - gdb
14 | - strace
15 | - inotify-tools
16 | state: present
17 | - shell: add-apt-repository -y ppa:longsleep/golang-backports
18 | - apt:
19 | name:
20 | - golang-go
21 | state: present
22 | - name: import common tasks
23 | import_tasks: /src/t/common/apache/playbook_common.yml
24 |
--------------------------------------------------------------------------------
/t/testbox_ubuntu18.04/docker-compose.yml:
--------------------------------------------------------------------------------
1 | ../common/docker-compose.yml
--------------------------------------------------------------------------------
/t/testbox_ubuntu18.04/local_test.sh:
--------------------------------------------------------------------------------
1 | ../common/local_test.sh
--------------------------------------------------------------------------------