├── .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 --------------------------------------------------------------------------------