pax_global_header00006660000000000000000000000064141340623230014510gustar00rootroot0000000000000052 comment=2af6d036253eee1a9a08c6ddf6be6d67537bcdff golang-github-prometheus-common-0.32.1/000077500000000000000000000000001413406232300200015ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/.circleci/000077500000000000000000000000001413406232300216345ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/.circleci/config.yml000066400000000000000000000026141413406232300236270ustar00rootroot00000000000000--- version: 2.1 orbs: go: circleci/go@0.2.0 jobs: test: parameters: go_version: type: string run_style: type: boolean default: false use_gomod_cache: type: boolean default: true docker: - image: circleci/golang:<< parameters.go_version >> working_directory: /go/src/github.com/prometheus/common steps: - checkout - when: condition: << parameters.use_gomod_cache >> steps: - go/load-cache: key: v1-go<< parameters.go_version >> - run: make test - run: cd sigv4 && make test - when: condition: << parameters.run_style >> steps: - run: make style - run: cd sigv4 && make style - when: condition: << parameters.use_gomod_cache >> steps: - go/save-cache: key: v1-go<< parameters.go_version >> - store_test_results: path: test-results workflows: version: 2 tests: jobs: # Supported Go versions are synced with github.com/prometheus/client_golang. - test: name: go-1-13 go_version: "1.13" - test: name: go-1-14 go_version: "1.14" - test: name: go-1-15 go_version: "1.15" - test: name: go-1-16 go_version: "1.16" # Style is only checked against the latest supported Go version. run_style: true golang-github-prometheus-common-0.32.1/.github/000077500000000000000000000000001413406232300213415ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/.github/workflows/000077500000000000000000000000001413406232300233765ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/.github/workflows/golangci-lint.yml000066400000000000000000000011161413406232300266470ustar00rootroot00000000000000name: golangci-lint on: push: paths: - "go.sum" - "go.mod" - "**.go" - "scripts/errcheck_excludes.txt" - ".github/workflows/golangci-lint.yml" pull_request: paths: - "go.sum" - "go.mod" - "**.go" - "scripts/errcheck_excludes.txt" - ".github/workflows/golangci-lint.yml" jobs: golangci: name: lint runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 - name: Lint uses: golangci/golangci-lint-action@v2 with: version: v1.42.0 golang-github-prometheus-common-0.32.1/.gitignore000066400000000000000000000000071413406232300217660ustar00rootroot00000000000000vendor/golang-github-prometheus-common-0.32.1/CODE_OF_CONDUCT.md000066400000000000000000000002331413406232300225760ustar00rootroot00000000000000## Prometheus Community Code of Conduct Prometheus follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). golang-github-prometheus-common-0.32.1/CONTRIBUTING.md000066400000000000000000000015461413406232300222400ustar00rootroot00000000000000# Contributing Prometheus uses GitHub to manage reviews of pull requests. * If you have a trivial fix or improvement, go ahead and create a pull request, addressing (with `@...`) the maintainer of this repository (see [MAINTAINERS.md](MAINTAINERS.md)) in the description of the pull request. * If you plan to do something more involved, first discuss your ideas on our [mailing list](https://groups.google.com/forum/?fromgroups#!forum/prometheus-developers). This will avoid unnecessary work and surely give you and us a good deal of inspiration. * Relevant coding style guidelines are the [Go Code Review Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) and the _Formatting and style_ section of Peter Bourgon's [Go: Best Practices for Production Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). golang-github-prometheus-common-0.32.1/LICENSE000066400000000000000000000261351413406232300210150ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-prometheus-common-0.32.1/MAINTAINERS.md000066400000000000000000000000741413406232300220760ustar00rootroot00000000000000* Julien Pivotto @roidelapluie golang-github-prometheus-common-0.32.1/Makefile000066400000000000000000000013641413406232300214450ustar00rootroot00000000000000# Copyright 2018 The Prometheus Authors # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. include Makefile.common .PHONY: test test:: deps check_license unused common-test ifeq (,$(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(7|8|9|10)\.')) test:: lint endif golang-github-prometheus-common-0.32.1/Makefile.common000066400000000000000000000244501413406232300227350ustar00rootroot00000000000000# Copyright 2018 The Prometheus Authors # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # A common Makefile that includes rules to be reused in different prometheus projects. # !!! Open PRs only against the prometheus/prometheus/Makefile.common repository! # Example usage : # Create the main Makefile in the root project directory. # include Makefile.common # customTarget: # @echo ">> Running customTarget" # # Ensure GOBIN is not set during build so that promu is installed to the correct path unexport GOBIN GO ?= go GOFMT ?= $(GO)fmt FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH))) GOOPTS ?= GOHOSTOS ?= $(shell $(GO) env GOHOSTOS) GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH) GO_VERSION ?= $(shell $(GO) version) GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION)) PRE_GO_111 ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.') GOVENDOR := GO111MODULE := ifeq (, $(PRE_GO_111)) ifneq (,$(wildcard go.mod)) # Enforce Go modules support just in case the directory is inside GOPATH (and for Travis CI). GO111MODULE := on ifneq (,$(wildcard vendor)) # Always use the local vendor/ directory to satisfy the dependencies. GOOPTS := $(GOOPTS) -mod=vendor endif endif else ifneq (,$(wildcard go.mod)) ifneq (,$(wildcard vendor)) $(warning This repository requires Go >= 1.11 because of Go modules) $(warning Some recipes may not work as expected as the current Go runtime is '$(GO_VERSION_NUMBER)') endif else # This repository isn't using Go modules (yet). GOVENDOR := $(FIRST_GOPATH)/bin/govendor endif endif PROMU := $(FIRST_GOPATH)/bin/promu pkgs = ./... ifeq (arm, $(GOHOSTARCH)) GOHOSTARM ?= $(shell GOARM= $(GO) env GOARM) GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)v$(GOHOSTARM) else GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH) endif GOTEST := $(GO) test GOTEST_DIR := ifneq ($(CIRCLE_JOB),) ifneq ($(shell which gotestsum),) GOTEST_DIR := test-results GOTEST := gotestsum --junitfile $(GOTEST_DIR)/unit-tests.xml -- endif endif PROMU_VERSION ?= 0.12.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= GOLANGCI_LINT_VERSION ?= v1.42.0 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386)) # If we're in CI and there is an Actions file, that means the linter # is being run in Actions, so we don't need to run it here. ifeq (,$(CIRCLE_JOB)) GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint else ifeq (,$(wildcard .github/workflows/golangci-lint.yml)) GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint endif endif endif PREFIX ?= $(shell pwd) BIN_DIR ?= $(shell pwd) DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD)) DOCKERFILE_PATH ?= ./Dockerfile DOCKERBUILD_CONTEXT ?= ./ DOCKER_REPO ?= prom DOCKER_ARCHS ?= amd64 BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS)) PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS)) TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS)) ifeq ($(GOHOSTARCH),amd64) ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows)) # Only supported on amd64 test-flags := -race endif endif # This rule is used to forward a target like "build" to "common-build". This # allows a new "build" target to be defined in a Makefile which includes this # one and override "common-build" without override warnings. %: common-% ; .PHONY: common-all common-all: precheck style check_license lint yamllint unused build test .PHONY: common-style common-style: @echo ">> checking code style" @fmtRes=$$($(GOFMT) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \ if [ -n "$${fmtRes}" ]; then \ echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \ echo "Please ensure you are using $$($(GO) version) for formatting code."; \ exit 1; \ fi .PHONY: common-check_license common-check_license: @echo ">> checking license header" @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \ awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \ done); \ if [ -n "$${licRes}" ]; then \ echo "license header checking failed:"; echo "$${licRes}"; \ exit 1; \ fi .PHONY: common-deps common-deps: @echo ">> getting dependencies" ifdef GO111MODULE GO111MODULE=$(GO111MODULE) $(GO) mod download else $(GO) get $(GOOPTS) -t ./... endif .PHONY: update-go-deps update-go-deps: @echo ">> updating Go dependencies" @for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \ $(GO) get $$m; \ done GO111MODULE=$(GO111MODULE) $(GO) mod tidy ifneq (,$(wildcard vendor)) GO111MODULE=$(GO111MODULE) $(GO) mod vendor endif .PHONY: common-test-short common-test-short: $(GOTEST_DIR) @echo ">> running short tests" GO111MODULE=$(GO111MODULE) $(GOTEST) -short $(GOOPTS) $(pkgs) .PHONY: common-test common-test: $(GOTEST_DIR) @echo ">> running all tests" GO111MODULE=$(GO111MODULE) $(GOTEST) $(test-flags) $(GOOPTS) $(pkgs) $(GOTEST_DIR): @mkdir -p $@ .PHONY: common-format common-format: @echo ">> formatting code" GO111MODULE=$(GO111MODULE) $(GO) fmt $(pkgs) .PHONY: common-vet common-vet: @echo ">> vetting code" GO111MODULE=$(GO111MODULE) $(GO) vet $(GOOPTS) $(pkgs) .PHONY: common-lint common-lint: $(GOLANGCI_LINT) ifdef GOLANGCI_LINT @echo ">> running golangci-lint" ifdef GO111MODULE # 'go list' needs to be executed before staticcheck to prepopulate the modules cache. # Otherwise staticcheck might fail randomly for some reason not yet explained. GO111MODULE=$(GO111MODULE) $(GO) list -e -compiled -test=true -export=false -deps=true -find=false -tags= -- ./... > /dev/null GO111MODULE=$(GO111MODULE) $(GOLANGCI_LINT) run $(GOLANGCI_LINT_OPTS) $(pkgs) else $(GOLANGCI_LINT) run $(pkgs) endif endif .PHONY: common-yamllint common-yamllint: @echo ">> running yamllint on all YAML files in the repository" ifeq (, $(shell which yamllint)) @echo "yamllint not installed so skipping" else yamllint . endif # For backward-compatibility. .PHONY: common-staticcheck common-staticcheck: lint .PHONY: common-unused common-unused: $(GOVENDOR) ifdef GOVENDOR @echo ">> running check for unused packages" @$(GOVENDOR) list +unused | grep . && exit 1 || echo 'No unused packages' else ifdef GO111MODULE @echo ">> running check for unused/missing packages in go.mod" GO111MODULE=$(GO111MODULE) $(GO) mod tidy ifeq (,$(wildcard vendor)) @git diff --exit-code -- go.sum go.mod else @echo ">> running check for unused packages in vendor/" GO111MODULE=$(GO111MODULE) $(GO) mod vendor @git diff --exit-code -- go.sum go.mod vendor/ endif endif endif .PHONY: common-build common-build: promu @echo ">> building binaries" GO111MODULE=$(GO111MODULE) $(PROMU) build --prefix $(PREFIX) $(PROMU_BINARIES) .PHONY: common-tarball common-tarball: promu @echo ">> building release tarball" $(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR) .PHONY: common-docker $(BUILD_DOCKER_ARCHS) common-docker: $(BUILD_DOCKER_ARCHS) $(BUILD_DOCKER_ARCHS): common-docker-%: docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" \ -f $(DOCKERFILE_PATH) \ --build-arg ARCH="$*" \ --build-arg OS="linux" \ $(DOCKERBUILD_CONTEXT) .PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS) common-docker-publish: $(PUBLISH_DOCKER_ARCHS) $(PUBLISH_DOCKER_ARCHS): common-docker-publish-%: docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION))) .PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS) common-docker-tag-latest: $(TAG_DOCKER_ARCHS) $(TAG_DOCKER_ARCHS): common-docker-tag-latest-%: docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest" docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)" .PHONY: common-docker-manifest common-docker-manifest: DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(DOCKER_IMAGE_TAG)) DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" .PHONY: promu promu: $(PROMU) $(PROMU): $(eval PROMU_TMP := $(shell mktemp -d)) curl -s -L $(PROMU_URL) | tar -xvzf - -C $(PROMU_TMP) mkdir -p $(FIRST_GOPATH)/bin cp $(PROMU_TMP)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu rm -r $(PROMU_TMP) .PHONY: proto proto: @echo ">> generating code from proto files" @./scripts/genproto.sh ifdef GOLANGCI_LINT $(GOLANGCI_LINT): mkdir -p $(FIRST_GOPATH)/bin curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/$(GOLANGCI_LINT_VERSION)/install.sh \ | sed -e '/install -d/d' \ | sh -s -- -b $(FIRST_GOPATH)/bin $(GOLANGCI_LINT_VERSION) endif ifdef GOVENDOR .PHONY: $(GOVENDOR) $(GOVENDOR): GOOS= GOARCH= $(GO) get -u github.com/kardianos/govendor endif .PHONY: precheck precheck:: define PRECHECK_COMMAND_template = precheck:: $(1)_precheck PRECHECK_COMMAND_$(1) ?= $(1) $$(strip $$(PRECHECK_OPTIONS_$(1))) .PHONY: $(1)_precheck $(1)_precheck: @if ! $$(PRECHECK_COMMAND_$(1)) 1>/dev/null 2>&1; then \ echo "Execution of '$$(PRECHECK_COMMAND_$(1))' command failed. Is $(1) installed?"; \ exit 1; \ fi endef golang-github-prometheus-common-0.32.1/NOTICE000066400000000000000000000002621413406232300207050ustar00rootroot00000000000000Common libraries shared by Prometheus Go components. Copyright 2015 The Prometheus Authors This product includes software developed at SoundCloud Ltd. (http://soundcloud.com/). golang-github-prometheus-common-0.32.1/README.md000066400000000000000000000013301413406232300212550ustar00rootroot00000000000000# Common ![circleci](https://circleci.com/gh/prometheus/common/tree/main.svg?style=shield) This repository contains Go libraries that are shared across Prometheus components and libraries. They are considered internal to Prometheus, without any stability guarantees for external usage. * **config**: Common configuration structures * **expfmt**: Decoding and encoding for the exposition format * **model**: Shared data structures * **promlog**: A logging wrapper around [go-kit/log](https://github.com/go-kit/kit/tree/master/log) * **route**: A routing wrapper around [httprouter](https://github.com/julienschmidt/httprouter) using `context.Context` * **server**: Common servers * **version**: Version information and metrics golang-github-prometheus-common-0.32.1/SECURITY.md000066400000000000000000000002521413406232300215710ustar00rootroot00000000000000# Reporting a security issue The Prometheus security policy, including how to report vulnerabilities, can be found here: https://prometheus.io/docs/operating/security/ golang-github-prometheus-common-0.32.1/config/000077500000000000000000000000001413406232300212465ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/config/config.go000066400000000000000000000037271413406232300230530ustar00rootroot00000000000000// Copyright 2016 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // This package no longer handles safe yaml parsing. In order to // ensure correct yaml unmarshalling, use "yaml.UnmarshalStrict()". package config import ( "encoding/json" "path/filepath" ) const secretToken = "" // Secret special type for storing secrets. type Secret string // MarshalYAML implements the yaml.Marshaler interface for Secrets. func (s Secret) MarshalYAML() (interface{}, error) { if s != "" { return secretToken, nil } return nil, nil } //UnmarshalYAML implements the yaml.Unmarshaler interface for Secrets. func (s *Secret) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain Secret return unmarshal((*plain)(s)) } // MarshalJSON implements the json.Marshaler interface for Secret. func (s Secret) MarshalJSON() ([]byte, error) { if len(s) == 0 { return json.Marshal("") } return json.Marshal(secretToken) } // DirectorySetter is a config type that contains file paths that may // be relative to the file containing the config. type DirectorySetter interface { // SetDirectory joins any relative file paths with dir. // Any paths that are empty or absolute remain unchanged. SetDirectory(dir string) } // JoinDir joins dir and path if path is relative. // If path is empty or absolute, it is returned unchanged. func JoinDir(dir, path string) string { if path == "" || filepath.IsAbs(path) { return path } return filepath.Join(dir, path) } golang-github-prometheus-common-0.32.1/config/config_test.go000066400000000000000000000024071413406232300241040ustar00rootroot00000000000000// Copyright 2021 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +build go1.8 package config import ( "encoding/json" "testing" ) func TestJSONMarshalSecret(t *testing.T) { type tmp struct { S Secret } for _, tc := range []struct { desc string data tmp expected string }{ { desc: "inhabited", // u003c -> "<" // u003e -> ">" data: tmp{"test"}, expected: "{\"S\":\"\\u003csecret\\u003e\"}", }, { desc: "empty", data: tmp{}, expected: "{\"S\":\"\"}", }, } { t.Run(tc.desc, func(t *testing.T) { c, err := json.Marshal(tc.data) if err != nil { t.Fatal(err) } if tc.expected != string(c) { t.Fatalf("Secret not marshaled correctly, got '%s'", string(c)) } }) } } golang-github-prometheus-common-0.32.1/config/http_config.go000066400000000000000000000652361413406232300241150ustar00rootroot00000000000000// Copyright 2016 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +build go1.8 package config import ( "bytes" "context" "crypto/sha256" "crypto/tls" "crypto/x509" "encoding/json" "fmt" "io/ioutil" "net" "net/http" "net/url" "os" "strings" "sync" "time" "github.com/mwitkow/go-conntrack" "golang.org/x/net/http2" "golang.org/x/oauth2" "golang.org/x/oauth2/clientcredentials" "gopkg.in/yaml.v2" ) // DefaultHTTPClientConfig is the default HTTP client configuration. var DefaultHTTPClientConfig = HTTPClientConfig{ FollowRedirects: true, } // defaultHTTPClientOptions holds the default HTTP client options. var defaultHTTPClientOptions = httpClientOptions{ keepAlivesEnabled: true, http2Enabled: true, // 5 minutes is typically above the maximum sane scrape interval. So we can // use keepalive for all configurations. idleConnTimeout: 5 * time.Minute, } type closeIdler interface { CloseIdleConnections() } // BasicAuth contains basic HTTP authentication credentials. type BasicAuth struct { Username string `yaml:"username" json:"username"` Password Secret `yaml:"password,omitempty" json:"password,omitempty"` PasswordFile string `yaml:"password_file,omitempty" json:"password_file,omitempty"` } // SetDirectory joins any relative file paths with dir. func (a *BasicAuth) SetDirectory(dir string) { if a == nil { return } a.PasswordFile = JoinDir(dir, a.PasswordFile) } // Authorization contains HTTP authorization credentials. type Authorization struct { Type string `yaml:"type,omitempty" json:"type,omitempty"` Credentials Secret `yaml:"credentials,omitempty" json:"credentials,omitempty"` CredentialsFile string `yaml:"credentials_file,omitempty" json:"credentials_file,omitempty"` } // SetDirectory joins any relative file paths with dir. func (a *Authorization) SetDirectory(dir string) { if a == nil { return } a.CredentialsFile = JoinDir(dir, a.CredentialsFile) } // URL is a custom URL type that allows validation at configuration load time. type URL struct { *url.URL } // UnmarshalYAML implements the yaml.Unmarshaler interface for URLs. func (u *URL) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string if err := unmarshal(&s); err != nil { return err } urlp, err := url.Parse(s) if err != nil { return err } u.URL = urlp return nil } // MarshalYAML implements the yaml.Marshaler interface for URLs. func (u URL) MarshalYAML() (interface{}, error) { if u.URL != nil { return u.Redacted(), nil } return nil, nil } // Redacted returns the URL but replaces any password with "xxxxx". func (u URL) Redacted() string { if u.URL == nil { return "" } ru := *u.URL if _, ok := ru.User.Password(); ok { // We can not use secretToken because it would be escaped. ru.User = url.UserPassword(ru.User.Username(), "xxxxx") } return ru.String() } // UnmarshalJSON implements the json.Marshaler interface for URL. func (u *URL) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } urlp, err := url.Parse(s) if err != nil { return err } u.URL = urlp return nil } // MarshalJSON implements the json.Marshaler interface for URL. func (u URL) MarshalJSON() ([]byte, error) { if u.URL != nil { return json.Marshal(u.URL.String()) } return []byte("null"), nil } // OAuth2 is the oauth2 client configuration. type OAuth2 struct { ClientID string `yaml:"client_id" json:"client_id"` ClientSecret Secret `yaml:"client_secret" json:"client_secret"` ClientSecretFile string `yaml:"client_secret_file" json:"client_secret_file"` Scopes []string `yaml:"scopes,omitempty" json:"scopes,omitempty"` TokenURL string `yaml:"token_url" json:"token_url"` EndpointParams map[string]string `yaml:"endpoint_params,omitempty" json:"endpoint_params,omitempty"` // TLSConfig is used to connect to the token URL. TLSConfig TLSConfig `yaml:"tls_config,omitempty"` } // SetDirectory joins any relative file paths with dir. func (a *OAuth2) SetDirectory(dir string) { if a == nil { return } a.ClientSecretFile = JoinDir(dir, a.ClientSecretFile) a.TLSConfig.SetDirectory(dir) } // HTTPClientConfig configures an HTTP client. type HTTPClientConfig struct { // The HTTP basic authentication credentials for the targets. BasicAuth *BasicAuth `yaml:"basic_auth,omitempty" json:"basic_auth,omitempty"` // The HTTP authorization credentials for the targets. Authorization *Authorization `yaml:"authorization,omitempty" json:"authorization,omitempty"` // The OAuth2 client credentials used to fetch a token for the targets. OAuth2 *OAuth2 `yaml:"oauth2,omitempty" json:"oauth2,omitempty"` // The bearer token for the targets. Deprecated in favour of // Authorization.Credentials. BearerToken Secret `yaml:"bearer_token,omitempty" json:"bearer_token,omitempty"` // The bearer token file for the targets. Deprecated in favour of // Authorization.CredentialsFile. BearerTokenFile string `yaml:"bearer_token_file,omitempty" json:"bearer_token_file,omitempty"` // HTTP proxy server to use to connect to the targets. ProxyURL URL `yaml:"proxy_url,omitempty" json:"proxy_url,omitempty"` // TLSConfig to use to connect to the targets. TLSConfig TLSConfig `yaml:"tls_config,omitempty" json:"tls_config,omitempty"` // FollowRedirects specifies whether the client should follow HTTP 3xx redirects. // The omitempty flag is not set, because it would be hidden from the // marshalled configuration when set to false. FollowRedirects bool `yaml:"follow_redirects" json:"follow_redirects"` } // SetDirectory joins any relative file paths with dir. func (c *HTTPClientConfig) SetDirectory(dir string) { if c == nil { return } c.TLSConfig.SetDirectory(dir) c.BasicAuth.SetDirectory(dir) c.Authorization.SetDirectory(dir) c.OAuth2.SetDirectory(dir) c.BearerTokenFile = JoinDir(dir, c.BearerTokenFile) } // Validate validates the HTTPClientConfig to check only one of BearerToken, // BasicAuth and BearerTokenFile is configured. func (c *HTTPClientConfig) Validate() error { // Backwards compatibility with the bearer_token field. if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 { return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured") } if (c.BasicAuth != nil || c.OAuth2 != nil) && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) { return fmt.Errorf("at most one of basic_auth, oauth2, bearer_token & bearer_token_file must be configured") } if c.BasicAuth != nil && (string(c.BasicAuth.Password) != "" && c.BasicAuth.PasswordFile != "") { return fmt.Errorf("at most one of basic_auth password & password_file must be configured") } if c.Authorization != nil { if len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0 { return fmt.Errorf("authorization is not compatible with bearer_token & bearer_token_file") } if string(c.Authorization.Credentials) != "" && c.Authorization.CredentialsFile != "" { return fmt.Errorf("at most one of authorization credentials & credentials_file must be configured") } c.Authorization.Type = strings.TrimSpace(c.Authorization.Type) if len(c.Authorization.Type) == 0 { c.Authorization.Type = "Bearer" } if strings.ToLower(c.Authorization.Type) == "basic" { return fmt.Errorf(`authorization type cannot be set to "basic", use "basic_auth" instead`) } if c.BasicAuth != nil || c.OAuth2 != nil { return fmt.Errorf("at most one of basic_auth, oauth2 & authorization must be configured") } } else { if len(c.BearerToken) > 0 { c.Authorization = &Authorization{Credentials: c.BearerToken} c.Authorization.Type = "Bearer" c.BearerToken = "" } if len(c.BearerTokenFile) > 0 { c.Authorization = &Authorization{CredentialsFile: c.BearerTokenFile} c.Authorization.Type = "Bearer" c.BearerTokenFile = "" } } if c.OAuth2 != nil { if c.BasicAuth != nil { return fmt.Errorf("at most one of basic_auth, oauth2 & authorization must be configured") } if len(c.OAuth2.ClientID) == 0 { return fmt.Errorf("oauth2 client_id must be configured") } if len(c.OAuth2.ClientSecret) == 0 && len(c.OAuth2.ClientSecretFile) == 0 { return fmt.Errorf("either oauth2 client_secret or client_secret_file must be configured") } if len(c.OAuth2.TokenURL) == 0 { return fmt.Errorf("oauth2 token_url must be configured") } if len(c.OAuth2.ClientSecret) > 0 && len(c.OAuth2.ClientSecretFile) > 0 { return fmt.Errorf("at most one of oauth2 client_secret & client_secret_file must be configured") } } return nil } // UnmarshalYAML implements the yaml.Unmarshaler interface func (c *HTTPClientConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain HTTPClientConfig *c = DefaultHTTPClientConfig if err := unmarshal((*plain)(c)); err != nil { return err } return c.Validate() } // UnmarshalJSON implements the json.Marshaler interface for URL. func (c *HTTPClientConfig) UnmarshalJSON(data []byte) error { type plain HTTPClientConfig *c = DefaultHTTPClientConfig if err := json.Unmarshal(data, (*plain)(c)); err != nil { return err } return c.Validate() } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (a *BasicAuth) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain BasicAuth return unmarshal((*plain)(a)) } // DialContextFunc defines the signature of the DialContext() function implemented // by net.Dialer. type DialContextFunc func(context.Context, string, string) (net.Conn, error) type httpClientOptions struct { dialContextFunc DialContextFunc keepAlivesEnabled bool http2Enabled bool idleConnTimeout time.Duration } // HTTPClientOption defines an option that can be applied to the HTTP client. type HTTPClientOption func(options *httpClientOptions) // WithDialContextFunc allows you to override func gets used for the actual dialing. The default is `net.Dialer.DialContext`. func WithDialContextFunc(fn DialContextFunc) HTTPClientOption { return func(opts *httpClientOptions) { opts.dialContextFunc = fn } } // WithKeepAlivesDisabled allows to disable HTTP keepalive. func WithKeepAlivesDisabled() HTTPClientOption { return func(opts *httpClientOptions) { opts.keepAlivesEnabled = false } } // WithHTTP2Disabled allows to disable HTTP2. func WithHTTP2Disabled() HTTPClientOption { return func(opts *httpClientOptions) { opts.http2Enabled = false } } // WithIdleConnTimeout allows setting the idle connection timeout. func WithIdleConnTimeout(timeout time.Duration) HTTPClientOption { return func(opts *httpClientOptions) { opts.idleConnTimeout = timeout } } // NewClient returns a http.Client using the specified http.RoundTripper. func newClient(rt http.RoundTripper) *http.Client { return &http.Client{Transport: rt} } // NewClientFromConfig returns a new HTTP client configured for the // given config.HTTPClientConfig and config.HTTPClientOption. // The name is used as go-conntrack metric label. func NewClientFromConfig(cfg HTTPClientConfig, name string, optFuncs ...HTTPClientOption) (*http.Client, error) { rt, err := NewRoundTripperFromConfig(cfg, name, optFuncs...) if err != nil { return nil, err } client := newClient(rt) if !cfg.FollowRedirects { client.CheckRedirect = func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse } } return client, nil } // NewRoundTripperFromConfig returns a new HTTP RoundTripper configured for the // given config.HTTPClientConfig and config.HTTPClientOption. // The name is used as go-conntrack metric label. func NewRoundTripperFromConfig(cfg HTTPClientConfig, name string, optFuncs ...HTTPClientOption) (http.RoundTripper, error) { opts := defaultHTTPClientOptions for _, f := range optFuncs { f(&opts) } var dialContext func(ctx context.Context, network, addr string) (net.Conn, error) if opts.dialContextFunc != nil { dialContext = conntrack.NewDialContextFunc( conntrack.DialWithDialContextFunc((func(context.Context, string, string) (net.Conn, error))(opts.dialContextFunc)), conntrack.DialWithTracing(), conntrack.DialWithName(name)) } else { dialContext = conntrack.NewDialContextFunc( conntrack.DialWithTracing(), conntrack.DialWithName(name)) } newRT := func(tlsConfig *tls.Config) (http.RoundTripper, error) { // The only timeout we care about is the configured scrape timeout. // It is applied on request. So we leave out any timings here. var rt http.RoundTripper = &http.Transport{ Proxy: http.ProxyURL(cfg.ProxyURL.URL), MaxIdleConns: 20000, MaxIdleConnsPerHost: 1000, // see https://github.com/golang/go/issues/13801 DisableKeepAlives: !opts.keepAlivesEnabled, TLSClientConfig: tlsConfig, DisableCompression: true, IdleConnTimeout: opts.idleConnTimeout, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, DialContext: dialContext, } if opts.http2Enabled && os.Getenv("PROMETHEUS_COMMON_DISABLE_HTTP2") == "" { // HTTP/2 support is golang had many problematic cornercases where // dead connections would be kept and used in connection pools. // https://github.com/golang/go/issues/32388 // https://github.com/golang/go/issues/39337 // https://github.com/golang/go/issues/39750 // Do not enable HTTP2 if the environment variable // PROMETHEUS_COMMON_DISABLE_HTTP2 is set to a non-empty value. // This allows users to easily disable HTTP2 in case they run into // issues again, but will be removed once we are confident that // things work as expected. http2t, err := http2.ConfigureTransports(rt.(*http.Transport)) if err != nil { return nil, err } http2t.ReadIdleTimeout = time.Minute } // If a authorization_credentials is provided, create a round tripper that will set the // Authorization header correctly on each request. if cfg.Authorization != nil && len(cfg.Authorization.Credentials) > 0 { rt = NewAuthorizationCredentialsRoundTripper(cfg.Authorization.Type, cfg.Authorization.Credentials, rt) } else if cfg.Authorization != nil && len(cfg.Authorization.CredentialsFile) > 0 { rt = NewAuthorizationCredentialsFileRoundTripper(cfg.Authorization.Type, cfg.Authorization.CredentialsFile, rt) } // Backwards compatibility, be nice with importers who would not have // called Validate(). if len(cfg.BearerToken) > 0 { rt = NewAuthorizationCredentialsRoundTripper("Bearer", cfg.BearerToken, rt) } else if len(cfg.BearerTokenFile) > 0 { rt = NewAuthorizationCredentialsFileRoundTripper("Bearer", cfg.BearerTokenFile, rt) } if cfg.BasicAuth != nil { rt = NewBasicAuthRoundTripper(cfg.BasicAuth.Username, cfg.BasicAuth.Password, cfg.BasicAuth.PasswordFile, rt) } if cfg.OAuth2 != nil { rt = NewOAuth2RoundTripper(cfg.OAuth2, rt) } // Return a new configured RoundTripper. return rt, nil } tlsConfig, err := NewTLSConfig(&cfg.TLSConfig) if err != nil { return nil, err } if len(cfg.TLSConfig.CAFile) == 0 { // No need for a RoundTripper that reloads the CA file automatically. return newRT(tlsConfig) } return NewTLSRoundTripper(tlsConfig, cfg.TLSConfig.CAFile, newRT) } type authorizationCredentialsRoundTripper struct { authType string authCredentials Secret rt http.RoundTripper } // NewAuthorizationCredentialsRoundTripper adds the provided credentials to a // request unless the authorization header has already been set. func NewAuthorizationCredentialsRoundTripper(authType string, authCredentials Secret, rt http.RoundTripper) http.RoundTripper { return &authorizationCredentialsRoundTripper{authType, authCredentials, rt} } func (rt *authorizationCredentialsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if len(req.Header.Get("Authorization")) == 0 { req = cloneRequest(req) req.Header.Set("Authorization", fmt.Sprintf("%s %s", rt.authType, string(rt.authCredentials))) } return rt.rt.RoundTrip(req) } func (rt *authorizationCredentialsRoundTripper) CloseIdleConnections() { if ci, ok := rt.rt.(closeIdler); ok { ci.CloseIdleConnections() } } type authorizationCredentialsFileRoundTripper struct { authType string authCredentialsFile string rt http.RoundTripper } // NewAuthorizationCredentialsFileRoundTripper adds the authorization // credentials read from the provided file to a request unless the authorization // header has already been set. This file is read for every request. func NewAuthorizationCredentialsFileRoundTripper(authType, authCredentialsFile string, rt http.RoundTripper) http.RoundTripper { return &authorizationCredentialsFileRoundTripper{authType, authCredentialsFile, rt} } func (rt *authorizationCredentialsFileRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if len(req.Header.Get("Authorization")) == 0 { b, err := ioutil.ReadFile(rt.authCredentialsFile) if err != nil { return nil, fmt.Errorf("unable to read authorization credentials file %s: %s", rt.authCredentialsFile, err) } authCredentials := strings.TrimSpace(string(b)) req = cloneRequest(req) req.Header.Set("Authorization", fmt.Sprintf("%s %s", rt.authType, authCredentials)) } return rt.rt.RoundTrip(req) } func (rt *authorizationCredentialsFileRoundTripper) CloseIdleConnections() { if ci, ok := rt.rt.(closeIdler); ok { ci.CloseIdleConnections() } } type basicAuthRoundTripper struct { username string password Secret passwordFile string rt http.RoundTripper } // NewBasicAuthRoundTripper will apply a BASIC auth authorization header to a request unless it has // already been set. func NewBasicAuthRoundTripper(username string, password Secret, passwordFile string, rt http.RoundTripper) http.RoundTripper { return &basicAuthRoundTripper{username, password, passwordFile, rt} } func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if len(req.Header.Get("Authorization")) != 0 { return rt.rt.RoundTrip(req) } req = cloneRequest(req) if rt.passwordFile != "" { bs, err := ioutil.ReadFile(rt.passwordFile) if err != nil { return nil, fmt.Errorf("unable to read basic auth password file %s: %s", rt.passwordFile, err) } req.SetBasicAuth(rt.username, strings.TrimSpace(string(bs))) } else { req.SetBasicAuth(rt.username, strings.TrimSpace(string(rt.password))) } return rt.rt.RoundTrip(req) } func (rt *basicAuthRoundTripper) CloseIdleConnections() { if ci, ok := rt.rt.(closeIdler); ok { ci.CloseIdleConnections() } } type oauth2RoundTripper struct { config *OAuth2 rt http.RoundTripper next http.RoundTripper secret string mtx sync.RWMutex } func NewOAuth2RoundTripper(config *OAuth2, next http.RoundTripper) http.RoundTripper { return &oauth2RoundTripper{ config: config, next: next, } } func (rt *oauth2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { var ( secret string changed bool ) if rt.config.ClientSecretFile != "" { data, err := ioutil.ReadFile(rt.config.ClientSecretFile) if err != nil { return nil, fmt.Errorf("unable to read oauth2 client secret file %s: %s", rt.config.ClientSecretFile, err) } secret = strings.TrimSpace(string(data)) rt.mtx.RLock() changed = secret != rt.secret rt.mtx.RUnlock() } if changed || rt.rt == nil { if rt.config.ClientSecret != "" { secret = string(rt.config.ClientSecret) } config := &clientcredentials.Config{ ClientID: rt.config.ClientID, ClientSecret: secret, Scopes: rt.config.Scopes, TokenURL: rt.config.TokenURL, EndpointParams: mapToValues(rt.config.EndpointParams), } tlsConfig, err := NewTLSConfig(&rt.config.TLSConfig) if err != nil { return nil, err } var t http.RoundTripper if len(rt.config.TLSConfig.CAFile) == 0 { t = &http.Transport{TLSClientConfig: tlsConfig} } else { t, err = NewTLSRoundTripper(tlsConfig, rt.config.TLSConfig.CAFile, func(tls *tls.Config) (http.RoundTripper, error) { return &http.Transport{TLSClientConfig: tls}, nil }) if err != nil { return nil, err } } ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{Transport: t}) tokenSource := config.TokenSource(ctx) rt.mtx.Lock() rt.secret = secret rt.rt = &oauth2.Transport{ Base: rt.next, Source: tokenSource, } rt.mtx.Unlock() } rt.mtx.RLock() currentRT := rt.rt rt.mtx.RUnlock() return currentRT.RoundTrip(req) } func (rt *oauth2RoundTripper) CloseIdleConnections() { // OAuth2 RT does not support CloseIdleConnections() but the next RT might. if ci, ok := rt.next.(closeIdler); ok { ci.CloseIdleConnections() } } func mapToValues(m map[string]string) url.Values { v := url.Values{} for name, value := range m { v.Set(name, value) } return v } // cloneRequest returns a clone of the provided *http.Request. // The clone is a shallow copy of the struct and its Header map. func cloneRequest(r *http.Request) *http.Request { // Shallow copy of the struct. r2 := new(http.Request) *r2 = *r // Deep copy of the Header. r2.Header = make(http.Header) for k, s := range r.Header { r2.Header[k] = s } return r2 } // NewTLSConfig creates a new tls.Config from the given TLSConfig. func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) { tlsConfig := &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify} // If a CA cert is provided then let's read it in so we can validate the // scrape target's certificate properly. if len(cfg.CAFile) > 0 { b, err := readCAFile(cfg.CAFile) if err != nil { return nil, err } if !updateRootCA(tlsConfig, b) { return nil, fmt.Errorf("unable to use specified CA cert %s", cfg.CAFile) } } if len(cfg.ServerName) > 0 { tlsConfig.ServerName = cfg.ServerName } // If a client cert & key is provided then configure TLS config accordingly. if len(cfg.CertFile) > 0 && len(cfg.KeyFile) == 0 { return nil, fmt.Errorf("client cert file %q specified without client key file", cfg.CertFile) } else if len(cfg.KeyFile) > 0 && len(cfg.CertFile) == 0 { return nil, fmt.Errorf("client key file %q specified without client cert file", cfg.KeyFile) } else if len(cfg.CertFile) > 0 && len(cfg.KeyFile) > 0 { // Verify that client cert and key are valid. if _, err := cfg.getClientCertificate(nil); err != nil { return nil, err } tlsConfig.GetClientCertificate = cfg.getClientCertificate } return tlsConfig, nil } // TLSConfig configures the options for TLS connections. type TLSConfig struct { // The CA cert to use for the targets. CAFile string `yaml:"ca_file,omitempty" json:"ca_file,omitempty"` // The client cert file for the targets. CertFile string `yaml:"cert_file,omitempty" json:"cert_file,omitempty"` // The client key file for the targets. KeyFile string `yaml:"key_file,omitempty" json:"key_file,omitempty"` // Used to verify the hostname for the targets. ServerName string `yaml:"server_name,omitempty" json:"server_name,omitempty"` // Disable target certificate validation. InsecureSkipVerify bool `yaml:"insecure_skip_verify" json:"insecure_skip_verify"` } // SetDirectory joins any relative file paths with dir. func (c *TLSConfig) SetDirectory(dir string) { if c == nil { return } c.CAFile = JoinDir(dir, c.CAFile) c.CertFile = JoinDir(dir, c.CertFile) c.KeyFile = JoinDir(dir, c.KeyFile) } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain TLSConfig return unmarshal((*plain)(c)) } // getClientCertificate reads the pair of client cert and key from disk and returns a tls.Certificate. func (c *TLSConfig) getClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) { cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile) if err != nil { return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", c.CertFile, c.KeyFile, err) } return &cert, nil } // readCAFile reads the CA cert file from disk. func readCAFile(f string) ([]byte, error) { data, err := ioutil.ReadFile(f) if err != nil { return nil, fmt.Errorf("unable to load specified CA cert %s: %s", f, err) } return data, nil } // updateRootCA parses the given byte slice as a series of PEM encoded certificates and updates tls.Config.RootCAs. func updateRootCA(cfg *tls.Config, b []byte) bool { caCertPool := x509.NewCertPool() if !caCertPool.AppendCertsFromPEM(b) { return false } cfg.RootCAs = caCertPool return true } // tlsRoundTripper is a RoundTripper that updates automatically its TLS // configuration whenever the content of the CA file changes. type tlsRoundTripper struct { caFile string // newRT returns a new RoundTripper. newRT func(*tls.Config) (http.RoundTripper, error) mtx sync.RWMutex rt http.RoundTripper hashCAFile []byte tlsConfig *tls.Config } func NewTLSRoundTripper( cfg *tls.Config, caFile string, newRT func(*tls.Config) (http.RoundTripper, error), ) (http.RoundTripper, error) { t := &tlsRoundTripper{ caFile: caFile, newRT: newRT, tlsConfig: cfg, } rt, err := t.newRT(t.tlsConfig) if err != nil { return nil, err } t.rt = rt _, t.hashCAFile, err = t.getCAWithHash() if err != nil { return nil, err } return t, nil } func (t *tlsRoundTripper) getCAWithHash() ([]byte, []byte, error) { b, err := readCAFile(t.caFile) if err != nil { return nil, nil, err } h := sha256.Sum256(b) return b, h[:], nil } // RoundTrip implements the http.RoundTrip interface. func (t *tlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { b, h, err := t.getCAWithHash() if err != nil { return nil, err } t.mtx.RLock() equal := bytes.Equal(h[:], t.hashCAFile) rt := t.rt t.mtx.RUnlock() if equal { // The CA cert hasn't changed, use the existing RoundTripper. return rt.RoundTrip(req) } // Create a new RoundTripper. tlsConfig := t.tlsConfig.Clone() if !updateRootCA(tlsConfig, b) { return nil, fmt.Errorf("unable to use specified CA cert %s", t.caFile) } rt, err = t.newRT(tlsConfig) if err != nil { return nil, err } t.CloseIdleConnections() t.mtx.Lock() t.rt = rt t.hashCAFile = h[:] t.mtx.Unlock() return rt.RoundTrip(req) } func (t *tlsRoundTripper) CloseIdleConnections() { t.mtx.RLock() defer t.mtx.RUnlock() if ci, ok := t.rt.(closeIdler); ok { ci.CloseIdleConnections() } } func (c HTTPClientConfig) String() string { b, err := yaml.Marshal(c) if err != nil { return fmt.Sprintf("", err) } return string(b) } golang-github-prometheus-common-0.32.1/config/http_config_test.go000066400000000000000000001244311413406232300251450ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +build go1.8 package config import ( "context" "crypto/tls" "crypto/x509" "encoding/json" "errors" "fmt" "io/ioutil" "net" "net/http" "net/http/httptest" "net/url" "os" "path/filepath" "reflect" "strconv" "strings" "sync" "sync/atomic" "testing" "time" yaml "gopkg.in/yaml.v2" ) const ( TLSCAChainPath = "testdata/tls-ca-chain.pem" ServerCertificatePath = "testdata/server.crt" ServerKeyPath = "testdata/server.key" ClientCertificatePath = "testdata/client.crt" ClientKeyNoPassPath = "testdata/client-no-pass.key" InvalidCA = "testdata/client-no-pass.key" WrongClientCertPath = "testdata/self-signed-client.crt" WrongClientKeyPath = "testdata/self-signed-client.key" EmptyFile = "testdata/empty" MissingCA = "missing/ca.crt" MissingCert = "missing/cert.crt" MissingKey = "missing/secret.key" ExpectedMessage = "I'm here to serve you!!!" ExpectedError = "expected error" AuthorizationCredentials = "theanswertothegreatquestionoflifetheuniverseandeverythingisfortytwo" AuthorizationCredentialsFile = "testdata/bearer.token" AuthorizationType = "APIKEY" BearerToken = AuthorizationCredentials BearerTokenFile = AuthorizationCredentialsFile MissingBearerTokenFile = "missing/bearer.token" ExpectedBearer = "Bearer " + BearerToken ExpectedAuthenticationCredentials = AuthorizationType + " " + BearerToken ExpectedUsername = "arthurdent" ExpectedPassword = "42" ExpectedAccessToken = "12345" ) var invalidHTTPClientConfigs = []struct { httpClientConfigFile string errMsg string }{ { httpClientConfigFile: "testdata/http.conf.bearer-token-and-file-set.bad.yml", errMsg: "at most one of bearer_token & bearer_token_file must be configured", }, { httpClientConfigFile: "testdata/http.conf.empty.bad.yml", errMsg: "at most one of basic_auth, oauth2, bearer_token & bearer_token_file must be configured", }, { httpClientConfigFile: "testdata/http.conf.basic-auth.too-much.bad.yaml", errMsg: "at most one of basic_auth password & password_file must be configured", }, { httpClientConfigFile: "testdata/http.conf.mix-bearer-and-creds.bad.yaml", errMsg: "authorization is not compatible with bearer_token & bearer_token_file", }, { httpClientConfigFile: "testdata/http.conf.auth-creds-and-file-set.too-much.bad.yaml", errMsg: "at most one of authorization credentials & credentials_file must be configured", }, { httpClientConfigFile: "testdata/http.conf.basic-auth-and-auth-creds.too-much.bad.yaml", errMsg: "at most one of basic_auth, oauth2 & authorization must be configured", }, { httpClientConfigFile: "testdata/http.conf.basic-auth-and-oauth2.too-much.bad.yaml", errMsg: "at most one of basic_auth, oauth2 & authorization must be configured", }, { httpClientConfigFile: "testdata/http.conf.auth-creds-no-basic.bad.yaml", errMsg: `authorization type cannot be set to "basic", use "basic_auth" instead`, }, { httpClientConfigFile: "testdata/http.conf.oauth2-secret-and-file-set.bad.yml", errMsg: "at most one of oauth2 client_secret & client_secret_file must be configured", }, { httpClientConfigFile: "testdata/http.conf.oauth2-no-client-id.bad.yaml", errMsg: "oauth2 client_id must be configured", }, { httpClientConfigFile: "testdata/http.conf.oauth2-no-client-secret.bad.yaml", errMsg: "either oauth2 client_secret or client_secret_file must be configured", }, { httpClientConfigFile: "testdata/http.conf.oauth2-no-token-url.bad.yaml", errMsg: "oauth2 token_url must be configured", }, } func newTestServer(handler func(w http.ResponseWriter, r *http.Request)) (*httptest.Server, error) { testServer := httptest.NewUnstartedServer(http.HandlerFunc(handler)) tlsCAChain, err := ioutil.ReadFile(TLSCAChainPath) if err != nil { return nil, fmt.Errorf("Can't read %s", TLSCAChainPath) } serverCertificate, err := tls.LoadX509KeyPair(ServerCertificatePath, ServerKeyPath) if err != nil { return nil, fmt.Errorf("Can't load X509 key pair %s - %s", ServerCertificatePath, ServerKeyPath) } rootCAs := x509.NewCertPool() rootCAs.AppendCertsFromPEM(tlsCAChain) testServer.TLS = &tls.Config{ Certificates: make([]tls.Certificate, 1), RootCAs: rootCAs, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: rootCAs} testServer.TLS.Certificates[0] = serverCertificate testServer.StartTLS() return testServer, nil } func TestNewClientFromConfig(t *testing.T) { var newClientValidConfig = []struct { clientConfig HTTPClientConfig handler func(w http.ResponseWriter, r *http.Request) }{ { clientConfig: HTTPClientConfig{ TLSConfig: TLSConfig{ CAFile: "", CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: true}, }, handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, ExpectedMessage) }, }, { clientConfig: HTTPClientConfig{ TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, ExpectedMessage) }, }, { clientConfig: HTTPClientConfig{ BearerToken: BearerToken, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { bearer := r.Header.Get("Authorization") if bearer != ExpectedBearer { fmt.Fprintf(w, "The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", ExpectedBearer, bearer) } else { fmt.Fprint(w, ExpectedMessage) } }, }, { clientConfig: HTTPClientConfig{ BearerTokenFile: BearerTokenFile, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { bearer := r.Header.Get("Authorization") if bearer != ExpectedBearer { fmt.Fprintf(w, "The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", ExpectedBearer, bearer) } else { fmt.Fprint(w, ExpectedMessage) } }, }, { clientConfig: HTTPClientConfig{ Authorization: &Authorization{Credentials: BearerToken}, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { bearer := r.Header.Get("Authorization") if bearer != ExpectedBearer { fmt.Fprintf(w, "The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", ExpectedBearer, bearer) } else { fmt.Fprint(w, ExpectedMessage) } }, }, { clientConfig: HTTPClientConfig{ Authorization: &Authorization{CredentialsFile: AuthorizationCredentialsFile, Type: AuthorizationType}, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { bearer := r.Header.Get("Authorization") if bearer != ExpectedAuthenticationCredentials { fmt.Fprintf(w, "The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", ExpectedAuthenticationCredentials, bearer) } else { fmt.Fprint(w, ExpectedMessage) } }, }, { clientConfig: HTTPClientConfig{ Authorization: &Authorization{ Credentials: AuthorizationCredentials, Type: AuthorizationType, }, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { bearer := r.Header.Get("Authorization") if bearer != ExpectedAuthenticationCredentials { fmt.Fprintf(w, "The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", ExpectedAuthenticationCredentials, bearer) } else { fmt.Fprint(w, ExpectedMessage) } }, }, { clientConfig: HTTPClientConfig{ Authorization: &Authorization{ CredentialsFile: BearerTokenFile, }, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { bearer := r.Header.Get("Authorization") if bearer != ExpectedBearer { fmt.Fprintf(w, "The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", ExpectedBearer, bearer) } else { fmt.Fprint(w, ExpectedMessage) } }, }, { clientConfig: HTTPClientConfig{ BasicAuth: &BasicAuth{ Username: ExpectedUsername, Password: ExpectedPassword, }, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() if !ok { fmt.Fprintf(w, "The Authorization header wasn't set") } else if ExpectedUsername != username { fmt.Fprintf(w, "The expected username (%s) differs from the obtained username (%s).", ExpectedUsername, username) } else if ExpectedPassword != password { fmt.Fprintf(w, "The expected password (%s) differs from the obtained password (%s).", ExpectedPassword, password) } else { fmt.Fprint(w, ExpectedMessage) } }, }, { clientConfig: HTTPClientConfig{ FollowRedirects: true, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/redirected": fmt.Fprint(w, ExpectedMessage) default: w.Header().Set("Location", "/redirected") w.WriteHeader(http.StatusFound) fmt.Fprint(w, "It should follow the redirect.") } }, }, { clientConfig: HTTPClientConfig{ FollowRedirects: false, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/redirected": fmt.Fprint(w, "The redirection was followed.") default: w.Header().Set("Location", "/redirected") w.WriteHeader(http.StatusFound) fmt.Fprint(w, ExpectedMessage) } }, }, { clientConfig: HTTPClientConfig{ OAuth2: &OAuth2{ ClientID: "ExpectedUsername", ClientSecret: "ExpectedPassword", TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, }, handler: func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/token": res, _ := json.Marshal(oauth2TestServerResponse{ AccessToken: ExpectedAccessToken, TokenType: "Bearer", }) w.Header().Add("Content-Type", "application/json") _, _ = w.Write(res) default: authorization := r.Header.Get("Authorization") if authorization != "Bearer "+ExpectedAccessToken { fmt.Fprintf(w, "Expected Authorization header %q, got %q", "Bearer "+ExpectedAccessToken, authorization) } else { fmt.Fprint(w, ExpectedMessage) } } }, }, } for _, validConfig := range newClientValidConfig { testServer, err := newTestServer(validConfig.handler) if err != nil { t.Fatal(err.Error()) } defer testServer.Close() if validConfig.clientConfig.OAuth2 != nil { // We don't have access to the test server's URL when configuring the test cases, // so it has to be specified here. validConfig.clientConfig.OAuth2.TokenURL = testServer.URL + "/token" } err = validConfig.clientConfig.Validate() if err != nil { t.Fatal(err.Error()) } client, err := NewClientFromConfig(validConfig.clientConfig, "test") if err != nil { t.Errorf("Can't create a client from this config: %+v", validConfig.clientConfig) continue } response, err := client.Get(testServer.URL) if err != nil { t.Errorf("Can't connect to the test server using this config: %+v: %v", validConfig.clientConfig, err) continue } message, err := ioutil.ReadAll(response.Body) response.Body.Close() if err != nil { t.Errorf("Can't read the server response body using this config: %+v", validConfig.clientConfig) continue } trimMessage := strings.TrimSpace(string(message)) if ExpectedMessage != trimMessage { t.Errorf("The expected message (%s) differs from the obtained message (%s) using this config: %+v", ExpectedMessage, trimMessage, validConfig.clientConfig) } } } func TestNewClientFromInvalidConfig(t *testing.T) { var newClientInvalidConfig = []struct { clientConfig HTTPClientConfig errorMsg string }{ { clientConfig: HTTPClientConfig{ TLSConfig: TLSConfig{ CAFile: MissingCA, InsecureSkipVerify: true}, }, errorMsg: fmt.Sprintf("unable to load specified CA cert %s:", MissingCA), }, { clientConfig: HTTPClientConfig{ TLSConfig: TLSConfig{ CAFile: InvalidCA, InsecureSkipVerify: true}, }, errorMsg: fmt.Sprintf("unable to use specified CA cert %s", InvalidCA), }, } for _, invalidConfig := range newClientInvalidConfig { client, err := NewClientFromConfig(invalidConfig.clientConfig, "test") if client != nil { t.Errorf("A client instance was returned instead of nil using this config: %+v", invalidConfig.clientConfig) } if err == nil { t.Errorf("No error was returned using this config: %+v", invalidConfig.clientConfig) } if !strings.Contains(err.Error(), invalidConfig.errorMsg) { t.Errorf("Expected error %q does not contain %q", err.Error(), invalidConfig.errorMsg) } } } func TestCustomDialContextFunc(t *testing.T) { dialFn := func(_ context.Context, _, _ string) (net.Conn, error) { return nil, errors.New(ExpectedError) } cfg := HTTPClientConfig{} client, err := NewClientFromConfig(cfg, "test", WithDialContextFunc(dialFn)) if err != nil { t.Fatalf("Can't create a client from this config: %+v", cfg) } _, err = client.Get("http://localhost") if err == nil || !strings.Contains(err.Error(), ExpectedError) { t.Errorf("Expected error %q but got %q", ExpectedError, err) } } func TestCustomIdleConnTimeout(t *testing.T) { timeout := time.Second * 5 cfg := HTTPClientConfig{} rt, err := NewRoundTripperFromConfig(cfg, "test", WithIdleConnTimeout(timeout)) if err != nil { t.Fatalf("Can't create a round-tripper from this config: %+v", cfg) } transport, ok := rt.(*http.Transport) if !ok { t.Fatalf("Unexpected transport: %+v", transport) } if transport.IdleConnTimeout != timeout { t.Fatalf("Unexpected idle connection timeout: %+v", timeout) } } func TestMissingBearerAuthFile(t *testing.T) { cfg := HTTPClientConfig{ BearerTokenFile: MissingBearerTokenFile, TLSConfig: TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, } handler := func(w http.ResponseWriter, r *http.Request) { bearer := r.Header.Get("Authorization") if bearer != ExpectedBearer { fmt.Fprintf(w, "The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", ExpectedBearer, bearer) } else { fmt.Fprint(w, ExpectedMessage) } } testServer, err := newTestServer(handler) if err != nil { t.Fatal(err.Error()) } defer testServer.Close() client, err := NewClientFromConfig(cfg, "test") if err != nil { t.Fatal(err) } _, err = client.Get(testServer.URL) if err == nil { t.Fatal("No error is returned here") } if !strings.Contains(err.Error(), "unable to read authorization credentials file missing/bearer.token: open missing/bearer.token: no such file or directory") { t.Fatal("wrong error message being returned") } } func TestBearerAuthRoundTripper(t *testing.T) { const ( newBearerToken = "goodbyeandthankyouforthefish" ) fakeRoundTripper := NewRoundTripCheckRequest(func(req *http.Request) { bearer := req.Header.Get("Authorization") if bearer != ExpectedBearer { t.Errorf("The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", ExpectedBearer, bearer) } }, nil, nil) // Normal flow. bearerAuthRoundTripper := NewAuthorizationCredentialsRoundTripper("Bearer", BearerToken, fakeRoundTripper) request, _ := http.NewRequest("GET", "/hitchhiker", nil) request.Header.Set("User-Agent", "Douglas Adams mind") _, err := bearerAuthRoundTripper.RoundTrip(request) if err != nil { t.Errorf("unexpected error while executing RoundTrip: %s", err.Error()) } // Should honor already Authorization header set. bearerAuthRoundTripperShouldNotModifyExistingAuthorization := NewAuthorizationCredentialsRoundTripper("Bearer", newBearerToken, fakeRoundTripper) request, _ = http.NewRequest("GET", "/hitchhiker", nil) request.Header.Set("Authorization", ExpectedBearer) _, err = bearerAuthRoundTripperShouldNotModifyExistingAuthorization.RoundTrip(request) if err != nil { t.Errorf("unexpected error while executing RoundTrip: %s", err.Error()) } } func TestBearerAuthFileRoundTripper(t *testing.T) { fakeRoundTripper := NewRoundTripCheckRequest(func(req *http.Request) { bearer := req.Header.Get("Authorization") if bearer != ExpectedBearer { t.Errorf("The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", ExpectedBearer, bearer) } }, nil, nil) // Normal flow. bearerAuthRoundTripper := NewAuthorizationCredentialsFileRoundTripper("Bearer", BearerTokenFile, fakeRoundTripper) request, _ := http.NewRequest("GET", "/hitchhiker", nil) request.Header.Set("User-Agent", "Douglas Adams mind") _, err := bearerAuthRoundTripper.RoundTrip(request) if err != nil { t.Errorf("unexpected error while executing RoundTrip: %s", err.Error()) } // Should honor already Authorization header set. bearerAuthRoundTripperShouldNotModifyExistingAuthorization := NewAuthorizationCredentialsFileRoundTripper("Bearer", MissingBearerTokenFile, fakeRoundTripper) request, _ = http.NewRequest("GET", "/hitchhiker", nil) request.Header.Set("Authorization", ExpectedBearer) _, err = bearerAuthRoundTripperShouldNotModifyExistingAuthorization.RoundTrip(request) if err != nil { t.Errorf("unexpected error while executing RoundTrip: %s", err.Error()) } } func TestTLSConfig(t *testing.T) { configTLSConfig := TLSConfig{ CAFile: TLSCAChainPath, CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "localhost", InsecureSkipVerify: false} tlsCAChain, err := ioutil.ReadFile(TLSCAChainPath) if err != nil { t.Fatalf("Can't read the CA certificate chain (%s)", TLSCAChainPath) } rootCAs := x509.NewCertPool() rootCAs.AppendCertsFromPEM(tlsCAChain) expectedTLSConfig := &tls.Config{ RootCAs: rootCAs, ServerName: configTLSConfig.ServerName, InsecureSkipVerify: configTLSConfig.InsecureSkipVerify} tlsConfig, err := NewTLSConfig(&configTLSConfig) if err != nil { t.Fatalf("Can't create a new TLS Config from a configuration (%s).", err) } clientCertificate, err := tls.LoadX509KeyPair(ClientCertificatePath, ClientKeyNoPassPath) if err != nil { t.Fatalf("Can't load the client key pair ('%s' and '%s'). Reason: %s", ClientCertificatePath, ClientKeyNoPassPath, err) } cert, err := tlsConfig.GetClientCertificate(nil) if err != nil { t.Fatalf("unexpected error returned by tlsConfig.GetClientCertificate(): %s", err) } if !reflect.DeepEqual(cert, &clientCertificate) { t.Fatalf("Unexpected client certificate result: \n\n%+v\n expected\n\n%+v", cert, clientCertificate) } // tlsConfig.rootCAs.LazyCerts contains functions getCert() in go 1.16, which are // never equal. Compare the Subjects instead. if !reflect.DeepEqual(tlsConfig.RootCAs.Subjects(), expectedTLSConfig.RootCAs.Subjects()) { t.Fatalf("Unexpected RootCAs result: \n\n%+v\n expected\n\n%+v", tlsConfig.RootCAs.Subjects(), expectedTLSConfig.RootCAs.Subjects()) } tlsConfig.RootCAs = nil expectedTLSConfig.RootCAs = nil // Non-nil functions are never equal. tlsConfig.GetClientCertificate = nil if !reflect.DeepEqual(tlsConfig, expectedTLSConfig) { t.Fatalf("Unexpected TLS Config result: \n\n%+v\n expected\n\n%+v", tlsConfig, expectedTLSConfig) } } func TestTLSConfigEmpty(t *testing.T) { configTLSConfig := TLSConfig{ InsecureSkipVerify: true, } expectedTLSConfig := &tls.Config{ InsecureSkipVerify: configTLSConfig.InsecureSkipVerify, } tlsConfig, err := NewTLSConfig(&configTLSConfig) if err != nil { t.Fatalf("Can't create a new TLS Config from a configuration (%s).", err) } if !reflect.DeepEqual(tlsConfig, expectedTLSConfig) { t.Fatalf("Unexpected TLS Config result: \n\n%+v\n expected\n\n%+v", tlsConfig, expectedTLSConfig) } } func TestTLSConfigInvalidCA(t *testing.T) { var invalidTLSConfig = []struct { configTLSConfig TLSConfig errorMessage string }{ { configTLSConfig: TLSConfig{ CAFile: MissingCA, CertFile: "", KeyFile: "", ServerName: "", InsecureSkipVerify: false}, errorMessage: fmt.Sprintf("unable to load specified CA cert %s:", MissingCA), }, { configTLSConfig: TLSConfig{ CAFile: "", CertFile: MissingCert, KeyFile: ClientKeyNoPassPath, ServerName: "", InsecureSkipVerify: false}, errorMessage: fmt.Sprintf("unable to use specified client cert (%s) & key (%s):", MissingCert, ClientKeyNoPassPath), }, { configTLSConfig: TLSConfig{ CAFile: "", CertFile: ClientCertificatePath, KeyFile: MissingKey, ServerName: "", InsecureSkipVerify: false}, errorMessage: fmt.Sprintf("unable to use specified client cert (%s) & key (%s):", ClientCertificatePath, MissingKey), }, } for _, anInvalididTLSConfig := range invalidTLSConfig { tlsConfig, err := NewTLSConfig(&anInvalididTLSConfig.configTLSConfig) if tlsConfig != nil && err == nil { t.Errorf("The TLS Config could be created even with this %+v", anInvalididTLSConfig.configTLSConfig) continue } if !strings.Contains(err.Error(), anInvalididTLSConfig.errorMessage) { t.Errorf("The expected error should contain %s, but got %s", anInvalididTLSConfig.errorMessage, err) } } } func TestBasicAuthNoPassword(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.basic-auth.no-password.yaml") if err != nil { t.Fatalf("Error loading HTTP client config: %v", err) } client, err := NewClientFromConfig(*cfg, "test") if err != nil { t.Fatalf("Error creating HTTP Client: %v", err) } rt, ok := client.Transport.(*basicAuthRoundTripper) if !ok { t.Fatalf("Error casting to basic auth transport, %v", client.Transport) } if rt.username != "user" { t.Errorf("Bad HTTP client username: %s", rt.username) } if string(rt.password) != "" { t.Errorf("Expected empty HTTP client password: %s", rt.password) } if string(rt.passwordFile) != "" { t.Errorf("Expected empty HTTP client passwordFile: %s", rt.passwordFile) } } func TestBasicAuthNoUsername(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.basic-auth.no-username.yaml") if err != nil { t.Fatalf("Error loading HTTP client config: %v", err) } client, err := NewClientFromConfig(*cfg, "test") if err != nil { t.Fatalf("Error creating HTTP Client: %v", err) } rt, ok := client.Transport.(*basicAuthRoundTripper) if !ok { t.Fatalf("Error casting to basic auth transport, %v", client.Transport) } if rt.username != "" { t.Errorf("Got unexpected username: %s", rt.username) } if string(rt.password) != "secret" { t.Errorf("Unexpected HTTP client password: %s", string(rt.password)) } if string(rt.passwordFile) != "" { t.Errorf("Expected empty HTTP client passwordFile: %s", rt.passwordFile) } } func TestBasicAuthPasswordFile(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.basic-auth.good.yaml") if err != nil { t.Fatalf("Error loading HTTP client config: %v", err) } client, err := NewClientFromConfig(*cfg, "test") if err != nil { t.Fatalf("Error creating HTTP Client: %v", err) } rt, ok := client.Transport.(*basicAuthRoundTripper) if !ok { t.Fatalf("Error casting to basic auth transport, %v", client.Transport) } if rt.username != "user" { t.Errorf("Bad HTTP client username: %s", rt.username) } if string(rt.password) != "" { t.Errorf("Bad HTTP client password: %s", rt.password) } if string(rt.passwordFile) != "testdata/basic-auth-password" { t.Errorf("Bad HTTP client passwordFile: %s", rt.passwordFile) } } func getCertificateBlobs(t *testing.T) map[string][]byte { files := []string{ TLSCAChainPath, ClientCertificatePath, ClientKeyNoPassPath, ServerCertificatePath, ServerKeyPath, WrongClientCertPath, WrongClientKeyPath, EmptyFile, } bs := make(map[string][]byte, len(files)+1) for _, f := range files { b, err := ioutil.ReadFile(f) if err != nil { t.Fatal(err) } bs[f] = b } return bs } func writeCertificate(bs map[string][]byte, src string, dst string) { b, ok := bs[src] if !ok { panic(fmt.Sprintf("Couldn't find %q in bs", src)) } if err := ioutil.WriteFile(dst, b, 0664); err != nil { panic(err) } } func TestTLSRoundTripper(t *testing.T) { bs := getCertificateBlobs(t) tmpDir, err := ioutil.TempDir("", "tlsroundtripper") if err != nil { t.Fatal("Failed to create tmp dir", err) } defer os.RemoveAll(tmpDir) ca, cert, key := filepath.Join(tmpDir, "ca"), filepath.Join(tmpDir, "cert"), filepath.Join(tmpDir, "key") handler := func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, ExpectedMessage) } testServer, err := newTestServer(handler) if err != nil { t.Fatal(err.Error()) } defer testServer.Close() testCases := []struct { ca string cert string key string errMsg string }{ { // Valid certs. ca: TLSCAChainPath, cert: ClientCertificatePath, key: ClientKeyNoPassPath, }, { // CA not matching. ca: ClientCertificatePath, cert: ClientCertificatePath, key: ClientKeyNoPassPath, errMsg: "certificate signed by unknown authority", }, { // Invalid client cert+key. ca: TLSCAChainPath, cert: WrongClientCertPath, key: WrongClientKeyPath, errMsg: "remote error: tls", }, { // CA file empty ca: EmptyFile, cert: ClientCertificatePath, key: ClientKeyNoPassPath, errMsg: "unable to use specified CA cert", }, { // cert file empty ca: TLSCAChainPath, cert: EmptyFile, key: ClientKeyNoPassPath, errMsg: "failed to find any PEM data in certificate input", }, { // key file empty ca: TLSCAChainPath, cert: ClientCertificatePath, key: EmptyFile, errMsg: "failed to find any PEM data in key input", }, { // Valid certs again. ca: TLSCAChainPath, cert: ClientCertificatePath, key: ClientKeyNoPassPath, }, } cfg := HTTPClientConfig{ TLSConfig: TLSConfig{ CAFile: ca, CertFile: cert, KeyFile: key, InsecureSkipVerify: false}, } var c *http.Client for i, tc := range testCases { tc := tc t.Run(strconv.Itoa(i), func(t *testing.T) { writeCertificate(bs, tc.ca, ca) writeCertificate(bs, tc.cert, cert) writeCertificate(bs, tc.key, key) if c == nil { c, err = NewClientFromConfig(cfg, "test") if err != nil { t.Fatalf("Error creating HTTP Client: %v", err) } } req, err := http.NewRequest(http.MethodGet, testServer.URL, nil) if err != nil { t.Fatalf("Error creating HTTP request: %v", err) } r, err := c.Do(req) if len(tc.errMsg) > 0 { if err == nil { r.Body.Close() t.Fatalf("Could connect to the test server.") } if !strings.Contains(err.Error(), tc.errMsg) { t.Fatalf("Expected error message to contain %q, got %q", tc.errMsg, err) } return } if err != nil { t.Fatalf("Can't connect to the test server") } b, err := ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { t.Errorf("Can't read the server response body") } got := strings.TrimSpace(string(b)) if ExpectedMessage != got { t.Errorf("The expected message %q differs from the obtained message %q", ExpectedMessage, got) } }) } } func TestTLSRoundTripperRaces(t *testing.T) { bs := getCertificateBlobs(t) tmpDir, err := ioutil.TempDir("", "tlsroundtripper") if err != nil { t.Fatal("Failed to create tmp dir", err) } defer os.RemoveAll(tmpDir) ca, cert, key := filepath.Join(tmpDir, "ca"), filepath.Join(tmpDir, "cert"), filepath.Join(tmpDir, "key") handler := func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, ExpectedMessage) } testServer, err := newTestServer(handler) if err != nil { t.Fatal(err.Error()) } defer testServer.Close() cfg := HTTPClientConfig{ TLSConfig: TLSConfig{ CAFile: ca, CertFile: cert, KeyFile: key, InsecureSkipVerify: false}, } var c *http.Client writeCertificate(bs, TLSCAChainPath, ca) writeCertificate(bs, ClientCertificatePath, cert) writeCertificate(bs, ClientKeyNoPassPath, key) c, err = NewClientFromConfig(cfg, "test") if err != nil { t.Fatalf("Error creating HTTP Client: %v", err) } var wg sync.WaitGroup ch := make(chan struct{}) var total, ok int64 // Spawn 10 Go routines polling the server concurrently. for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() for { select { case <-ch: return default: atomic.AddInt64(&total, 1) r, err := c.Get(testServer.URL) if err == nil { r.Body.Close() atomic.AddInt64(&ok, 1) } } } }() } // Change the CA file every 10ms for 1 second. wg.Add(1) go func() { defer wg.Done() i := 0 for { tick := time.NewTicker(10 * time.Millisecond) <-tick.C if i%2 == 0 { writeCertificate(bs, ClientCertificatePath, ca) } else { writeCertificate(bs, TLSCAChainPath, ca) } i++ if i > 100 { close(ch) return } } }() wg.Wait() if ok == total { t.Fatalf("Expecting some requests to fail but got %d/%d successful requests", ok, total) } } func TestHideHTTPClientConfigSecrets(t *testing.T) { c, _, err := LoadHTTPConfigFile("testdata/http.conf.good.yml") if err != nil { t.Errorf("Error parsing %s: %s", "testdata/http.conf.good.yml", err) } // String method must not reveal authentication credentials. s := c.String() if strings.Contains(s, "mysecret") { t.Fatal("http client config's String method reveals authentication credentials.") } } func TestDefaultFollowRedirect(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.good.yml") if err != nil { t.Errorf("Error loading HTTP client config: %v", err) } if !cfg.FollowRedirects { t.Errorf("follow_redirects should be true") } } func TestValidateHTTPConfig(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.good.yml") if err != nil { t.Errorf("Error loading HTTP client config: %v", err) } err = cfg.Validate() if err != nil { t.Fatalf("Error validating %s: %s", "testdata/http.conf.good.yml", err) } } func TestInvalidHTTPConfigs(t *testing.T) { for _, ee := range invalidHTTPClientConfigs { _, _, err := LoadHTTPConfigFile(ee.httpClientConfigFile) if err == nil { t.Error("Expected error with config but got none") continue } if !strings.Contains(err.Error(), ee.errMsg) { t.Errorf("Expected error for invalid HTTP client configuration to contain %q but got: %s", ee.errMsg, err) } } } // LoadHTTPConfig parses the YAML input s into a HTTPClientConfig. func LoadHTTPConfig(s string) (*HTTPClientConfig, error) { cfg := &HTTPClientConfig{} err := yaml.UnmarshalStrict([]byte(s), cfg) if err != nil { return nil, err } return cfg, nil } // LoadHTTPConfigFile parses the given YAML file into a HTTPClientConfig. func LoadHTTPConfigFile(filename string) (*HTTPClientConfig, []byte, error) { content, err := ioutil.ReadFile(filename) if err != nil { return nil, nil, err } cfg, err := LoadHTTPConfig(string(content)) if err != nil { return nil, nil, err } return cfg, content, nil } type roundTrip struct { theResponse *http.Response theError error } func (rt *roundTrip) RoundTrip(r *http.Request) (*http.Response, error) { return rt.theResponse, rt.theError } type roundTripCheckRequest struct { checkRequest func(*http.Request) roundTrip } func (rt *roundTripCheckRequest) RoundTrip(r *http.Request) (*http.Response, error) { rt.checkRequest(r) return rt.theResponse, rt.theError } // NewRoundTripCheckRequest creates a new instance of a type that implements http.RoundTripper, // which before returning theResponse and theError, executes checkRequest against a http.Request. func NewRoundTripCheckRequest(checkRequest func(*http.Request), theResponse *http.Response, theError error) http.RoundTripper { return &roundTripCheckRequest{ checkRequest: checkRequest, roundTrip: roundTrip{ theResponse: theResponse, theError: theError}} } type oauth2TestServerResponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` } func TestOAuth2(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { res, _ := json.Marshal(oauth2TestServerResponse{ AccessToken: "12345", TokenType: "Bearer", }) w.Header().Add("Content-Type", "application/json") _, _ = w.Write(res) })) defer ts.Close() var yamlConfig = fmt.Sprintf(` client_id: 1 client_secret: 2 scopes: - A - B token_url: %s endpoint_params: hi: hello `, ts.URL) expectedConfig := OAuth2{ ClientID: "1", ClientSecret: "2", Scopes: []string{"A", "B"}, EndpointParams: map[string]string{"hi": "hello"}, TokenURL: ts.URL, } var unmarshalledConfig OAuth2 err := yaml.Unmarshal([]byte(yamlConfig), &unmarshalledConfig) if err != nil { t.Fatalf("Expected no error unmarshalling yaml, got %v", err) } if !reflect.DeepEqual(unmarshalledConfig, expectedConfig) { t.Fatalf("Got unmarshalled config %v, expected %v", unmarshalledConfig, expectedConfig) } rt := NewOAuth2RoundTripper(&expectedConfig, http.DefaultTransport) client := http.Client{ Transport: rt, } resp, _ := client.Get(ts.URL) authorization := resp.Request.Header.Get("Authorization") if authorization != "Bearer 12345" { t.Fatalf("Expected authorization header to be 'Bearer 12345', got '%s'", authorization) } } func TestOAuth2WithFile(t *testing.T) { var expectedAuth *string var previousAuth string tokenTS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { auth := r.Header.Get("Authorization") if auth != *expectedAuth { t.Fatalf("bad auth, expected %s, got %s", *expectedAuth, auth) } if auth == previousAuth { t.Fatal("token endpoint called twice") } previousAuth = auth res, _ := json.Marshal(oauth2TestServerResponse{ AccessToken: "12345", TokenType: "Bearer", }) w.Header().Add("Content-Type", "application/json") _, _ = w.Write(res) })) defer tokenTS.Close() ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { auth := r.Header.Get("Authorization") if auth != "Bearer 12345" { t.Fatalf("bad auth, expected %s, got %s", "Bearer 12345", auth) } fmt.Fprintln(w, "Hello, client") })) defer ts.Close() secretFile, err := ioutil.TempFile("", "oauth2_secret") if err != nil { t.Fatal(err) } defer os.Remove(secretFile.Name()) var yamlConfig = fmt.Sprintf(` client_id: 1 client_secret_file: %s scopes: - A - B token_url: %s endpoint_params: hi: hello `, secretFile.Name(), tokenTS.URL) expectedConfig := OAuth2{ ClientID: "1", ClientSecretFile: secretFile.Name(), Scopes: []string{"A", "B"}, EndpointParams: map[string]string{"hi": "hello"}, TokenURL: tokenTS.URL, } var unmarshalledConfig OAuth2 err = yaml.Unmarshal([]byte(yamlConfig), &unmarshalledConfig) if err != nil { t.Fatalf("Expected no error unmarshalling yaml, got %v", err) } if !reflect.DeepEqual(unmarshalledConfig, expectedConfig) { t.Fatalf("Got unmarshalled config %v, expected %v", unmarshalledConfig, expectedConfig) } rt := NewOAuth2RoundTripper(&expectedConfig, http.DefaultTransport) client := http.Client{ Transport: rt, } tk := "Basic MToxMjM0NTY=" expectedAuth = &tk if _, err := secretFile.Write([]byte("123456")); err != nil { t.Fatal(err) } resp, err := client.Get(ts.URL) if err != nil { t.Fatal(err) } authorization := resp.Request.Header.Get("Authorization") if authorization != "Bearer 12345" { t.Fatalf("Expected authorization header to be 'Bearer 12345', got '%s'", authorization) } // Making a second request with the same file content should not re-call the token API. resp, err = client.Get(ts.URL) if err != nil { t.Fatal(err) } tk = "Basic MToxMjM0NTY3" expectedAuth = &tk if _, err := secretFile.Write([]byte("7")); err != nil { t.Fatal(err) } _, err = client.Get(ts.URL) if err != nil { t.Fatal(err) } // Making a second request with the same file content should not re-call the token API. _, err = client.Get(ts.URL) if err != nil { t.Fatal(err) } authorization = resp.Request.Header.Get("Authorization") if authorization != "Bearer 12345" { t.Fatalf("Expected authorization header to be 'Bearer 12345', got '%s'", authorization) } } func TestMarshalURL(t *testing.T) { urlp, err := url.Parse("http://example.com/") if err != nil { t.Fatal(err) } u := &URL{urlp} c, err := json.Marshal(u) if err != nil { t.Fatal(err) } if string(c) != "\"http://example.com/\"" { t.Fatalf("URL not properly marshaled in JSON got '%s'", string(c)) } c, err = yaml.Marshal(u) if err != nil { t.Fatal(err) } if string(c) != "http://example.com/\n" { t.Fatalf("URL not properly marshaled in YAML got '%s'", string(c)) } } func TestMarshalURLWrapperWithNilValue(t *testing.T) { u := &URL{} c, err := json.Marshal(u) if err != nil { t.Fatal(err) } if string(c) != "null" { t.Fatalf("URL with nil value not properly marshaled into JSON, got %q", c) } c, err = yaml.Marshal(u) if err != nil { t.Fatal(err) } if string(c) != "null\n" { t.Fatalf("URL with nil value not properly marshaled into JSON, got %q", c) } } func TestUnmarshalNullURL(t *testing.T) { b := []byte(`null`) { var u URL err := json.Unmarshal(b, &u) if err != nil { t.Fatal(err) } if !isEmptyNonNilURL(u.URL) { t.Fatalf("`null` literal not properly unmarshaled from JSON as URL, got %#v", u.URL) } } { var u URL err := yaml.Unmarshal(b, &u) if err != nil { t.Fatal(err) } if u.URL != nil { // UnmarshalYAML is not called when parsing null literal. t.Fatalf("`null` literal not properly unmarshaled from YAML as URL, got %#v", u.URL) } } } func TestUnmarshalEmptyURL(t *testing.T) { b := []byte(`""`) { var u URL err := json.Unmarshal(b, &u) if err != nil { t.Fatal(err) } if !isEmptyNonNilURL(u.URL) { t.Fatalf("empty string not properly unmarshaled from JSON as URL, got %#v", u.URL) } } { var u URL err := yaml.Unmarshal(b, &u) if err != nil { t.Fatal(err) } if !isEmptyNonNilURL(u.URL) { t.Fatalf("empty string not properly unmarshaled from YAML as URL, got %#v", u.URL) } } } // checks if u equals to &url.URL{} func isEmptyNonNilURL(u *url.URL) bool { return u != nil && *u == url.URL{} } func TestUnmarshalURL(t *testing.T) { b := []byte(`"http://example.com/a b"`) var u URL err := json.Unmarshal(b, &u) if err != nil { t.Fatal(err) } if u.String() != "http://example.com/a%20b" { t.Fatalf("URL not properly unmarshaled in JSON, got '%s'", u.String()) } err = yaml.Unmarshal(b, &u) if err != nil { t.Fatal(err) } if u.String() != "http://example.com/a%20b" { t.Fatalf("URL not properly unmarshaled in YAML, got '%s'", u.String()) } } func TestMarshalURLWithSecret(t *testing.T) { var u URL err := yaml.Unmarshal([]byte("http://foo:bar@example.com"), &u) if err != nil { t.Fatal(err) } b, err := yaml.Marshal(u) if err != nil { t.Fatal(err) } if strings.TrimSpace(string(b)) != "http://foo:xxxxx@example.com" { t.Fatalf("URL not properly marshaled in YAML, got '%s'", string(b)) } } golang-github-prometheus-common-0.32.1/config/testdata/000077500000000000000000000000001413406232300230575ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/config/testdata/basic-auth-password000066400000000000000000000000071413406232300266570ustar00rootroot00000000000000foobar golang-github-prometheus-common-0.32.1/config/testdata/bearer.token000066400000000000000000000001051413406232300253550ustar00rootroot00000000000000theanswertothegreatquestionoflifetheuniverseandeverythingisfortytwo golang-github-prometheus-common-0.32.1/config/testdata/client-no-pass.key000066400000000000000000000032541413406232300264310ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC307b8Il9zajKw mkOih8sfYI+O9gSTvvyQN7Bh+Bu6lLN+XhtRxt+ZqOHfqo30EuPmdScMrqregqup VPGKgfkXVP3hF5rYdWqZx4XOKdyxbaarZupkAv2gtVNEBSmVSj8urt5WZOJVnF7Q GmhCAHpx34L5CCPYDXJBd5ExLwGIByKxQNugor7dJx8ehmVkGKto01GWjgY+sPYp lV9KxvD49ygXYQ6VAqgt/V2EG/PMmT0/jUtmM2tYDFztPkSISJg0vB/f9zHlYIdD GjkBjngekAij77T93xEuouox25UtXmg6ApqvDVEiBxZmN5Dt70HBsQ+IftENEUoY 8jhrImwBAgMBAAECggEBAJNlgjK3SPvdKlnqx9KZuagmH9YMs+zX1eG5lYdojqtT snzf7l3q7b1i6gIS2pHbV7uhMjd8EmwqMIStJKPfxaAMuSj0aWeo9lnp3wNJE7l8 54hGFCkvMLjcy7Adx5L6HqFK++IgME9e+7M3iWNqyMNn6bfO7Ba/6V5PBi9+tmaf nZWqgY2Kf8A2iNnm9RvmiwQ42nsjVsKcXzGdBmFTp69ar/QWtk1dWDajUVw/NctM cs+IypPjZiAE3CgyyiLKzG9CWCjkfMEd14uxFE73q2SAG6RWYSnv1M3WOupAF0rP ll/NMXaMjLlq2q3B9v2ZAaojbbWlHLDdEpE/jwXkkwECgYEA5iWN7SGH8ZE6wDfO EYuTQKpqYt1WbCQxv77leuGcm1KlFYfV8LsB/9xiocVtGm7N126zuwfgzfkIZWQD KrpoFUkz1jUg+kHCqf4FO8hzR0By3hbdTImJQILtC/K3fHJtexFKiW82mb40lgYc +Mk6Nb5CmL6VCX5u8MNBvD8WaLECgYEAzHofIneLLLqF2f2uVzF743CdgP1h0fPI BS3akp56/8qzQWNW+natJRxiTh2R8gdvB+P/UtEZR8E+FbSzZ4dIRrxIi44ew0Cr sROaP4LkaZFflKS/fD8S1M7yZQhussRoRWH0BDvM0hsu6UTGlESHX73b7js4AHpB 2q4frJMTDFECgYBr2f2Aus3yLpTRr1Uqc7Y1/6aLXh4531xQ9yyjQUcaosgqJtXj Uj/Fn4m5NcPDN1nPM1mWtEJtQ97jZNL3GxPbpcpc/9jMbjTDZP8e3Pjo0xMBcMWU MH/Zc4GSr9O8xgL4QUokzbFQqwoJpCO/ks1skhSzb9x37oAe4+HSTd46gQKBgQCk +9hJSCl8kpdTl5Nm+R9cGU6MeGXIMKnwO9pDOSpHX7cZCF1yw/Tan7dWDhfnMEZP GJC3ss1yDyLYArBK1WXk5SCnsalyo6ikvQtVOXixEUIMvo1eY8n++WetS4t+JGl5 qhponBOcZ6CHSR3tHgoYnyloZFHAWOTv3FTkOttAsQKBgQCzWSO2TA4v/vIKIrSV Lf2cI51imcy/JCsYUU+o66VQ6QdIJlfamuAKaKYAwfJtHtZOzAgrh09JV3qEEtN5 duBdXiuygAz8eHbqSoSe5FYgImI0BREDq8Zm3ArgUhv6S9aBeg/mS1W/5ZfmV2cT 0MdlE8vUtcbDkmKpi7CaklzMNw== -----END PRIVATE KEY----- golang-github-prometheus-common-0.32.1/config/testdata/client.crt000066400000000000000000000116461413406232300250570ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 3 (0x3) Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus TLS CA Validity Not Before: Apr 5 08:10:12 2019 GMT Not After : Mar 26 08:10:12 2059 GMT Subject: C=US, O=Prometheus, CN=Client Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:b7:d3:b6:fc:22:5f:73:6a:32:b0:9a:43:a2:87: cb:1f:60:8f:8e:f6:04:93:be:fc:90:37:b0:61:f8: 1b:ba:94:b3:7e:5e:1b:51:c6:df:99:a8:e1:df:aa: 8d:f4:12:e3:e6:75:27:0c:ae:aa:de:82:ab:a9:54: f1:8a:81:f9:17:54:fd:e1:17:9a:d8:75:6a:99:c7: 85:ce:29:dc:b1:6d:a6:ab:66:ea:64:02:fd:a0:b5: 53:44:05:29:95:4a:3f:2e:ae:de:56:64:e2:55:9c: 5e:d0:1a:68:42:00:7a:71:df:82:f9:08:23:d8:0d: 72:41:77:91:31:2f:01:88:07:22:b1:40:db:a0:a2: be:dd:27:1f:1e:86:65:64:18:ab:68:d3:51:96:8e: 06:3e:b0:f6:29:95:5f:4a:c6:f0:f8:f7:28:17:61: 0e:95:02:a8:2d:fd:5d:84:1b:f3:cc:99:3d:3f:8d: 4b:66:33:6b:58:0c:5c:ed:3e:44:88:48:98:34:bc: 1f:df:f7:31:e5:60:87:43:1a:39:01:8e:78:1e:90: 08:a3:ef:b4:fd:df:11:2e:a2:ea:31:db:95:2d:5e: 68:3a:02:9a:af:0d:51:22:07:16:66:37:90:ed:ef: 41:c1:b1:0f:88:7e:d1:0d:11:4a:18:f2:38:6b:22: 6c:01 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical Digital Signature X509v3 Basic Constraints: CA:FALSE X509v3 Extended Key Usage: TLS Web Client Authentication X509v3 Subject Key Identifier: 3A:46:D1:C5:8C:42:60:AC:EF:0C:DD:4B:55:1E:F0:D7:5C:76:C3:33 X509v3 Authority Key Identifier: keyid:4D:02:BF:71:95:6A:AA:58:C5:9C:B8:83:67:5E:64:16:99:E1:2A:9E Authority Information Access: CA Issuers - URI:http://example.com/ca/tls-ca.cer X509v3 CRL Distribution Points: Full Name: URI:http://example.com/ca/tls-ca.crl X509v3 Subject Alternative Name: email:client@prometheus.example.com Signature Algorithm: sha1WithRSAEncryption 73:fc:87:f2:cf:e3:b1:df:2f:f7:bf:f9:74:dc:0b:f0:7f:95: ef:77:ba:6a:7d:c6:c5:f3:d9:d6:c7:eb:f8:a8:30:d3:90:d5: a5:0c:32:33:95:85:a2:05:6e:78:a7:07:a5:e0:cf:f4:65:ef: d2:6d:86:66:2a:7f:13:78:2f:90:dd:9d:a4:34:d4:8f:df:41: 1b:0f:17:99:99:06:2d:26:86:e2:58:3e:84:ca:13:9e:00:ca: 82:07:63:e7:6c:df:e9:47:d6:b3:f7:51:1a:31:f4:3d:79:95: e7:ea:bf:40:84:48:09:23:ba:31:b1:67:cd:05:50:ec:e6:0a: d8:2b:7d:7d:73:7a:8a:5f:f7:72:28:57:9f:15:2d:b1:4e:a1: 3c:06:53:60:6e:b2:f9:04:08:81:3a:f2:ba:5d:7e:ac:93:f7: 3b:1a:de:07:6e:14:a2:0b:e2:28:6a:50:2d:d8:9b:3c:25:e2: 82:6b:90:7e:45:7b:dd:3a:7a:8e:71:99:a7:e8:88:5f:06:71: 5b:3f:18:85:70:f9:eb:c7:26:43:2b:49:8f:17:90:aa:ba:86: 8a:52:63:83:9f:9d:5d:79:53:af:6d:1a:7e:47:0d:ea:3f:33: 18:c0:5f:90:d0:c5:04:8b:e3:4a:45:3d:a6:8c:c3:d1:47:1c: 45:70:a4:75 -----BEGIN CERTIFICATE----- MIIEKjCCAxKgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBpMQswCQYDVQQGEwJVUzET MBEGA1UECgwKUHJvbWV0aGV1czEpMCcGA1UECwwgUHJvbWV0aGV1cyBDZXJ0aWZp Y2F0ZSBBdXRob3JpdHkxGjAYBgNVBAMMEVByb21ldGhldXMgVExTIENBMCAXDTE5 MDQwNTA4MTAxMloYDzIwNTkwMzI2MDgxMDEyWjAzMQswCQYDVQQGEwJVUzETMBEG A1UECgwKUHJvbWV0aGV1czEPMA0GA1UEAwwGQ2xpZW50MIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAt9O2/CJfc2oysJpDoofLH2CPjvYEk778kDewYfgb upSzfl4bUcbfmajh36qN9BLj5nUnDK6q3oKrqVTxioH5F1T94Rea2HVqmceFzinc sW2mq2bqZAL9oLVTRAUplUo/Lq7eVmTiVZxe0BpoQgB6cd+C+Qgj2A1yQXeRMS8B iAcisUDboKK+3ScfHoZlZBiraNNRlo4GPrD2KZVfSsbw+PcoF2EOlQKoLf1dhBvz zJk9P41LZjNrWAxc7T5EiEiYNLwf3/cx5WCHQxo5AY54HpAIo++0/d8RLqLqMduV LV5oOgKarw1RIgcWZjeQ7e9BwbEPiH7RDRFKGPI4ayJsAQIDAQABo4IBDzCCAQsw DgYDVR0PAQH/BAQDAgeAMAkGA1UdEwQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwIw HQYDVR0OBBYEFDpG0cWMQmCs7wzdS1Ue8NdcdsMzMB8GA1UdIwQYMBaAFE0Cv3GV aqpYxZy4g2deZBaZ4SqeMDwGCCsGAQUFBwEBBDAwLjAsBggrBgEFBQcwAoYgaHR0 cDovL2V4YW1wbGUuY29tL2NhL3Rscy1jYS5jZXIwMQYDVR0fBCowKDAmoCSgIoYg aHR0cDovL2V4YW1wbGUuY29tL2NhL3Rscy1jYS5jcmwwKAYDVR0RBCEwH4EdY2xp ZW50QHByb21ldGhldXMuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAHP8 h/LP47HfL/e/+XTcC/B/le93ump9xsXz2dbH6/ioMNOQ1aUMMjOVhaIFbninB6Xg z/Rl79JthmYqfxN4L5DdnaQ01I/fQRsPF5mZBi0mhuJYPoTKE54AyoIHY+ds3+lH 1rP3URox9D15lefqv0CESAkjujGxZ80FUOzmCtgrfX1zeopf93IoV58VLbFOoTwG U2BusvkECIE68rpdfqyT9zsa3gduFKIL4ihqUC3Ymzwl4oJrkH5Fe906eo5xmafo iF8GcVs/GIVw+evHJkMrSY8XkKq6hopSY4OfnV15U69tGn5HDeo/MxjAX5DQxQSL 40pFPaaMw9FHHEVwpHU= -----END CERTIFICATE----- golang-github-prometheus-common-0.32.1/config/testdata/empty000066400000000000000000000000001413406232300241260ustar00rootroot00000000000000http.conf.auth-creds-and-file-set.too-much.bad.yaml000066400000000000000000000001061413406232300343470ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/config/testdataauthorization: credentials: bearertoken credentials_file: key.txt golang-github-prometheus-common-0.32.1/config/testdata/http.conf.auth-creds-no-basic.bad.yaml000066400000000000000000000000351413406232300321200ustar00rootroot00000000000000authorization: type: Basic http.conf.basic-auth-and-auth-creds.too-much.bad.yaml000066400000000000000000000001171413406232300346610ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/config/testdatabasic_auth: username: user password: foo authorization: credentials: foo http.conf.basic-auth-and-oauth2.too-much.bad.yaml000066400000000000000000000001331413406232300340220ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/config/testdatabasic_auth: username: user password: foo oauth2: client_id: foo client_secret: bar golang-github-prometheus-common-0.32.1/config/testdata/http.conf.basic-auth.good.yaml000066400000000000000000000001131413406232300306070ustar00rootroot00000000000000basic_auth: username: user password_file: testdata/basic-auth-password golang-github-prometheus-common-0.32.1/config/testdata/http.conf.basic-auth.no-password.yaml000066400000000000000000000000351413406232300321360ustar00rootroot00000000000000basic_auth: username: user golang-github-prometheus-common-0.32.1/config/testdata/http.conf.basic-auth.no-username.yaml000066400000000000000000000000371413406232300321150ustar00rootroot00000000000000basic_auth: password: secret golang-github-prometheus-common-0.32.1/config/testdata/http.conf.basic-auth.too-much.bad.yaml000066400000000000000000000001331413406232300321410ustar00rootroot00000000000000basic_auth: username: user password: foo password_file: testdata/basic-auth-password golang-github-prometheus-common-0.32.1/config/testdata/http.conf.bearer-token-and-file-set.bad.yml000066400000000000000000000001471413406232300330570ustar00rootroot00000000000000basic_auth: username: username password: "mysecret" bearer_token: mysecret bearer_token_file: file golang-github-prometheus-common-0.32.1/config/testdata/http.conf.empty.bad.yml000066400000000000000000000001161413406232300273650ustar00rootroot00000000000000basic_auth: username: username password: mysecret bearer_token_file: file golang-github-prometheus-common-0.32.1/config/testdata/http.conf.good.yml000066400000000000000000000001301413406232300264260ustar00rootroot00000000000000basic_auth: username: username password: "mysecret" proxy_url: "http://remote.host" golang-github-prometheus-common-0.32.1/config/testdata/http.conf.invalid-bearer-token-file.bad.yml000066400000000000000000000000301413406232300331410ustar00rootroot00000000000000bearer_token_file: file golang-github-prometheus-common-0.32.1/config/testdata/http.conf.mix-bearer-and-creds.bad.yaml000066400000000000000000000000601413406232300322570ustar00rootroot00000000000000authorization: type: APIKEY bearer_token: foo golang-github-prometheus-common-0.32.1/config/testdata/http.conf.oauth2-no-client-id.bad.yaml000066400000000000000000000000771413406232300320600ustar00rootroot00000000000000oauth2: client_secret: "mysecret" token_url: "http://auth" golang-github-prometheus-common-0.32.1/config/testdata/http.conf.oauth2-no-client-secret.bad.yaml000066400000000000000000000000751413406232300327470ustar00rootroot00000000000000oauth2: client_id: "myclientid" token_url: "http://auth" golang-github-prometheus-common-0.32.1/config/testdata/http.conf.oauth2-no-token-url.bad.yaml000066400000000000000000000000761413406232300321270ustar00rootroot00000000000000oauth2: client_id: "myclientid" client_secret: "mysecret" golang-github-prometheus-common-0.32.1/config/testdata/http.conf.oauth2-secret-and-file-set.bad.yml000066400000000000000000000001701413406232300331620ustar00rootroot00000000000000oauth2: client_id: "myclient" client_secret: "mysecret" client_secret_file: "mysecret" token_url: "http://auth" golang-github-prometheus-common-0.32.1/config/testdata/self-signed-client.crt000066400000000000000000000157751413406232300272640ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 0e:47:ce:db:33:a0:10:93:9b:b1:ac:66:7c:16:2d:89:d0:b7:ea:1d Signature Algorithm: sha256WithRSAEncryption Issuer: C = US, ST = Denial, L = Springfield, O = Dis, CN = www.example.com Validity Not Before: Mar 1 16:51:42 2019 GMT Not After : Jul 17 16:51:42 2046 GMT Subject: C = US, ST = Denial, L = Springfield, O = Dis, CN = www.example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (4096 bit) Modulus: 00:ce:c6:ab:fd:9c:d2:da:55:f9:3d:5f:c0:0d:1a: a6:1c:d1:7f:01:f4:0d:9c:ce:85:8b:01:8f:06:73: 0a:b6:92:e1:6e:63:7d:e4:83:ca:c0:11:67:70:d9: 89:0c:a9:62:0a:c3:cc:00:53:6f:b6:1b:0b:e1:eb: 62:00:e8:ed:14:16:c6:29:45:0c:ee:25:40:21:10: c2:3d:9a:3b:5c:27:54:bb:e4:9c:f6:e3:b4:dc:f1: 0e:ba:c5:6f:60:94:45:b8:8d:f6:a4:1a:b4:fa:82: 7b:5a:55:a6:11:c1:d4:e6:41:dc:c7:41:8e:db:46: 6b:a2:0a:c1:13:96:47:12:4b:27:2e:d5:45:d4:51: c9:b6:28:f8:0d:24:44:42:12:b8:b4:cd:ab:4a:67: ba:8c:ff:34:92:38:b4:e5:4a:53:fe:33:72:55:df: 27:d9:70:0f:47:cc:7c:d5:b2:52:bf:80:c0:a7:15: b0:25:c8:d9:a1:41:e2:ee:e9:f5:0f:9f:27:ea:7c: dc:ec:19:48:73:74:48:47:13:59:ea:89:e0:61:50: 08:95:fc:32:9d:73:21:8e:b2:75:95:41:62:0c:61: c7:b9:59:e2:51:a2:4f:bd:74:1b:0d:26:3c:c8:a6: 1a:cb:db:10:cc:33:dd:2a:0b:38:55:60:85:f8:25: 74:1f:0d:26:4e:db:2d:03:12:d5:85:00:cf:51:01: 95:94:c8:85:cc:0e:5a:05:aa:3e:7a:34:e2:17:8b: 3b:c5:21:a2:da:56:0a:ed:de:6c:2c:40:10:85:25: 5d:df:39:e9:45:0e:10:82:bf:34:5c:64:52:35:4b: aa:1a:56:37:ab:1f:7f:b5:07:5f:8a:22:45:4d:96: 21:6c:a2:eb:47:39:bf:38:de:b5:4c:99:af:bf:de: f8:7c:54:8b:40:2e:1f:80:1b:97:6a:fe:2c:05:6a: 1b:9c:cb:a1:1c:f9:9e:36:ef:d9:a2:1d:d4:61:d0: 6d:d1:b6:00:f8:e7:7f:74:f8:c0:81:95:7d:68:dc: f3:93:7d:49:33:99:15:d5:49:d6:6d:69:82:c1:9f: f2:3e:c2:db:0b:b1:e6:7c:e5:98:f4:9f:01:7d:57: ac:36:78:15:a9:54:6f:e6:3e:52:54:68:a3:bc:8f: 99:3f:02:02:1f:d2:21:b1:39:70:61:4c:2f:71:e5: 27:d3:d0:75:46:d7:5e:78:ee:82:a5:bd:6d:12:2d: 0b:40:92:61:c0:9e:8c:71:be:d1:bb:4f:23:fe:4e: f2:79:a0:bd:60:f8:62:e4:9a:5b:1d:e0:a7:99:bd: 32:b2:29:7b:ca:8c:6b:1a:80:c8:6f:b3:aa:a0:9e: 1b:03:ab Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: D3:CB:AC:FB:69:9C:D3:14:67:44:9F:FA:0F:B9:02:60:64:95:4E:17 X509v3 Authority Key Identifier: keyid:D3:CB:AC:FB:69:9C:D3:14:67:44:9F:FA:0F:B9:02:60:64:95:4E:17 X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: sha256WithRSAEncryption 57:d6:69:ed:9e:05:ea:4d:64:3b:88:98:26:6c:00:6e:e7:b7: cb:ff:48:a2:c1:50:03:39:28:46:94:c0:19:7d:ff:10:7b:11: 6e:88:6d:fe:d8:62:3a:ce:28:33:64:86:85:0f:9f:bf:13:23: 48:11:b0:86:fa:7a:1d:6b:8a:e7:8c:76:fb:1b:a8:a9:d5:b3: b8:f0:b4:08:27:a4:91:14:1a:e3:1c:11:83:39:2c:20:f1:19: 21:35:9e:af:69:eb:52:ec:eb:c8:63:e2:bd:76:46:c5:4b:0c: c2:f7:b9:c3:2a:db:31:4a:b9:ea:a5:04:c4:e7:b6:cf:fc:7c: 8b:8a:88:39:ad:f9:06:e1:c6:63:47:6c:47:5c:e9:0b:24:b5: c1:eb:5d:67:ee:07:ac:42:5b:d4:cb:00:eb:ec:c5:2f:3a:d0: 76:f1:2a:9c:b9:44:3e:ed:71:40:02:4d:68:b5:b4:09:de:4d: ba:1c:87:86:2d:3c:b7:2c:e5:87:aa:ff:e2:5e:ad:0b:8c:bb: 39:9a:13:26:e3:c4:34:00:48:06:14:8f:ec:4b:cb:e7:be:80: bd:c7:6c:b0:75:88:4e:cd:b7:b1:7e:bf:92:85:c7:a0:45:4f: 73:ba:a7:27:86:8f:12:cd:35:f7:8c:34:3f:66:1a:7f:53:1d: 21:8c:90:22:ff:e7:d9:95:aa:15:c2:28:d0:c5:9b:6c:61:e9: 15:ff:63:9f:8e:d8:b4:a2:d5:06:38:1a:cc:5f:89:2a:23:70: a3:32:22:cd:00:20:c7:65:60:17:5e:8a:cc:dc:96:08:38:a5: 7d:65:46:79:79:02:11:04:4b:86:9d:f3:b3:2c:c6:2d:18:b4: 31:e1:86:aa:4c:0c:93:c3:fb:7a:5a:63:c2:6f:68:d3:86:2c: 6d:cd:ab:6d:41:d2:36:32:c1:52:25:d0:68:bc:ac:ca:f3:41: f6:5a:46:83:15:bd:e6:aa:3b:dc:6b:44:1f:6c:02:e9:ed:b5: 91:28:8d:af:6f:27:1b:71:83:61:a8:8e:15:36:01:92:42:32: 61:62:43:04:31:f7:f3:f3:c9:c0:93:19:c9:dd:4d:51:3b:64: 3b:06:90:4f:93:22:15:6e:8b:5f:2e:4e:11:a7:b9:a3:f2:fe: 45:c9:ea:4b:58:57:95:b3:77:29:9f:7d:bc:1d:a2:3d:5a:38: b3:72:b2:c7:8b:12:a9:39:4f:4f:2e:bb:7e:ce:91:bb:82:c0: 67:37:79:f6:9c:75:3b:39:6c:82:ac:6a:06:09:70:99:10:76: a4:38:46:50:7d:8e:d0:24:fb:dd:32:8f:40:00:d9:d1:50:20: 69:bd:86:b9:9e:89:23:60 -----BEGIN CERTIFICATE----- MIIFmTCCA4GgAwIBAgIUDkfO2zOgEJObsaxmfBYtidC36h0wDQYJKoZIhvcNAQEL BQAwXDELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkRlbmlhbDEUMBIGA1UEBwwLU3By aW5nZmllbGQxDDAKBgNVBAoMA0RpczEYMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29t MB4XDTE5MDMwMTE2NTE0MloXDTQ2MDcxNzE2NTE0MlowXDELMAkGA1UEBhMCVVMx DzANBgNVBAgMBkRlbmlhbDEUMBIGA1UEBwwLU3ByaW5nZmllbGQxDDAKBgNVBAoM A0RpczEYMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEF AAOCAg8AMIICCgKCAgEAzsar/ZzS2lX5PV/ADRqmHNF/AfQNnM6FiwGPBnMKtpLh bmN95IPKwBFncNmJDKliCsPMAFNvthsL4etiAOjtFBbGKUUM7iVAIRDCPZo7XCdU u+Sc9uO03PEOusVvYJRFuI32pBq0+oJ7WlWmEcHU5kHcx0GO20ZrogrBE5ZHEksn LtVF1FHJtij4DSREQhK4tM2rSme6jP80kji05UpT/jNyVd8n2XAPR8x81bJSv4DA pxWwJcjZoUHi7un1D58n6nzc7BlIc3RIRxNZ6ongYVAIlfwynXMhjrJ1lUFiDGHH uVniUaJPvXQbDSY8yKYay9sQzDPdKgs4VWCF+CV0Hw0mTtstAxLVhQDPUQGVlMiF zA5aBao+ejTiF4s7xSGi2lYK7d5sLEAQhSVd3znpRQ4Qgr80XGRSNUuqGlY3qx9/ tQdfiiJFTZYhbKLrRzm/ON61TJmvv974fFSLQC4fgBuXav4sBWobnMuhHPmeNu/Z oh3UYdBt0bYA+Od/dPjAgZV9aNzzk31JM5kV1UnWbWmCwZ/yPsLbC7HmfOWY9J8B fVesNngVqVRv5j5SVGijvI+ZPwICH9IhsTlwYUwvceUn09B1RtdeeO6Cpb1tEi0L QJJhwJ6Mcb7Ru08j/k7yeaC9YPhi5JpbHeCnmb0ysil7yoxrGoDIb7OqoJ4bA6sC AwEAAaNTMFEwHQYDVR0OBBYEFNPLrPtpnNMUZ0Sf+g+5AmBklU4XMB8GA1UdIwQY MBaAFNPLrPtpnNMUZ0Sf+g+5AmBklU4XMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI hvcNAQELBQADggIBAFfWae2eBepNZDuImCZsAG7nt8v/SKLBUAM5KEaUwBl9/xB7 EW6Ibf7YYjrOKDNkhoUPn78TI0gRsIb6eh1riueMdvsbqKnVs7jwtAgnpJEUGuMc EYM5LCDxGSE1nq9p61Ls68hj4r12RsVLDML3ucMq2zFKueqlBMTnts/8fIuKiDmt +QbhxmNHbEdc6QsktcHrXWfuB6xCW9TLAOvsxS860HbxKpy5RD7tcUACTWi1tAne Tboch4YtPLcs5Yeq/+JerQuMuzmaEybjxDQASAYUj+xLy+e+gL3HbLB1iE7Nt7F+ v5KFx6BFT3O6pyeGjxLNNfeMND9mGn9THSGMkCL/59mVqhXCKNDFm2xh6RX/Y5+O 2LSi1QY4GsxfiSojcKMyIs0AIMdlYBdeiszclgg4pX1lRnl5AhEES4ad87Msxi0Y tDHhhqpMDJPD+3paY8JvaNOGLG3Nq21B0jYywVIl0Gi8rMrzQfZaRoMVveaqO9xr RB9sAunttZEoja9vJxtxg2GojhU2AZJCMmFiQwQx9/PzycCTGcndTVE7ZDsGkE+T IhVui18uThGnuaPy/kXJ6ktYV5Wzdymffbwdoj1aOLNysseLEqk5T08uu37OkbuC wGc3efacdTs5bIKsagYJcJkQdqQ4RlB9jtAk+90yj0AA2dFQIGm9hrmeiSNg -----END CERTIFICATE----- golang-github-prometheus-common-0.32.1/config/testdata/self-signed-client.key000066400000000000000000000063141413406232300272510ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIJRQIBADANBgkqhkiG9w0BAQEFAASCCS8wggkrAgEAAoICAQDOxqv9nNLaVfk9 X8ANGqYc0X8B9A2czoWLAY8Gcwq2kuFuY33kg8rAEWdw2YkMqWIKw8wAU2+2Gwvh 62IA6O0UFsYpRQzuJUAhEMI9mjtcJ1S75Jz247Tc8Q66xW9glEW4jfakGrT6gnta VaYRwdTmQdzHQY7bRmuiCsETlkcSSycu1UXUUcm2KPgNJERCEri0zatKZ7qM/zSS OLTlSlP+M3JV3yfZcA9HzHzVslK/gMCnFbAlyNmhQeLu6fUPnyfqfNzsGUhzdEhH E1nqieBhUAiV/DKdcyGOsnWVQWIMYce5WeJRok+9dBsNJjzIphrL2xDMM90qCzhV YIX4JXQfDSZO2y0DEtWFAM9RAZWUyIXMDloFqj56NOIXizvFIaLaVgrt3mwsQBCF JV3fOelFDhCCvzRcZFI1S6oaVjerH3+1B1+KIkVNliFsoutHOb843rVMma+/3vh8 VItALh+AG5dq/iwFahucy6Ec+Z4279miHdRh0G3RtgD45390+MCBlX1o3POTfUkz mRXVSdZtaYLBn/I+wtsLseZ85Zj0nwF9V6w2eBWpVG/mPlJUaKO8j5k/AgIf0iGx OXBhTC9x5SfT0HVG11547oKlvW0SLQtAkmHAnoxxvtG7TyP+TvJ5oL1g+GLkmlsd 4KeZvTKyKXvKjGsagMhvs6qgnhsDqwIDAQABAoICAQCJTCnPkF4BU6zXL8jZ6qP5 5rEqnt6bDBZoInTRl3m5mPXO0ok5PrlVpzjEGe2CVsYe17uRS9WVWYgeTqkYaZFi EW0q4gqf5mQakIIpXUuk+QiuajI/TRs+yWE6avZ1bn6M+NaYSJN680DszooiqE2x RnJObB1rQ+scAYAKfXJbl0NBOaPQQy5oofNy5m3cYYn7o8Tk9tNL4/kITlbvGNeE pqx4kGBpZJsA1areSjXfqqJBT4lSzXaUOKdydC6gXNGoRZh7vJ36629ConrF3R77 /qR00qzZFyVlFuI0ZOGxzwtK63/3LIs+BOYhaQ5bPM/2JFOXA6kKzcBuEFVkW5oq APoST7hk1mVdMKDigaT5pmuB8JB9RC0w/oR3OXONImKYPf3fBUSU1hw6YyVZFA6c 6SKik3g/sWl0BZvqCJgU3v3qTLhVPXtiDj97g9pWdyfJBduE8Ft89OHljNbY2HBd hyW+/XSjodWW1CRr4v1DNXjg880VOWzueptROviEwFkpxi6oKFBXWegWMW5kR03d 21XlzrB20XckTjK5c8jQ5lQG49CnX8MyYMfj6f0HNCbIghbKfMvO7fWY1sD7wAlL DlLr5MLxal9Wm0Jx56DQ6ZgnSCU0ms2L0RT9IVESGWC1am9/FjMvmK+zdvS3uFgb HzwxN+7XD+4klO7H2GQFIQKCAQEA/pKzCLvJLyX8/bu4U5J9Ndf/V1N8YW++IOdl MZZw/QPZPJhg23Iw/9kGOPL0W1BqxFwaC6UWuR9YXLS+/GfGUlaeLbeGvMs3w3FH W9RjCwLMnBu2JwUqJqSqc9dkQor0up8sa7sYOPqOrHupIFBxx/tV5o24BJ0xz2RH eN8VdT/XejW2CY4UX9LGk0l8iPySGRx5d9MACrHwqmCMhTqiWAob7r3D+DQxqd4r 4q/lZ8ItKTzvrebHotBQcdMeIqIlQWG/chVKynxtB04zNOXwwtSxOKPsN1EysBsC vklZ3FeYFipHKsmKX/COWDjnyKmG/iRVjZ/O5vZ0rsQl8iujbwKCAQEAz+9i0Wod xrqX9Gd30JVANy5rz74wfvBy03J2T1KZmMxPhtVUloWU93952CiUpD2Xb6nwa00V LxYfXlt2YrfV+2I3YP6TC8VXiX7uQ8i6tg2JAY40mrbuYoO3P1gfgdJ909TjLhrL aNg+nCyJDePdeKbX0yMf4ukHBNbvSH65fkp1cl4uU1Wvb4tGNcyYcX1q953JP1ue PwgysbuXz/chpHmw8pH/GSZ5FAxGvHwkBmA0BYhDcpETFfKfm2NEDO5xa/4GTHNi o+d5/fotJmihY5IpyVlSai8Kox9mYUin6ntbFkCvK+x6m7859N1lPG0BJVJTD+Cx AXI6QQDyl+kVhQKCAQEAxXfd0GR5xkzdVaSLcqgq391Qf9iOnrYi8TsMz842jsyx ccNxPkfxokQiA4LR8RML/ozC102Ttr2NuTuq+fc1ayEtSaEWrtOjycLQ63Zv7Vaa iG0melYTQC5y2bC2YLeQ5kIaHubd/zS7/yddJWfBGrLnCxPbLhkRTiInHqdM6co/ xthrADZpr3q79fwG0eu5GClyP3Q4kBM+76o81guJamlNCX/Bx4IVFAL2X7y5YibJ CTfvYyGksbKM8/4jXhIQfArqif/iJ/ckS4ppRhsnCroZTio5TR97BgettRUI01ZO 7sKUuafj4k+i2uQpRwnZYMGma1kPETETiY01MgiPmQKCAQEAyQcnAk8VeovrXN6r d3zUGIVItg+p0w+j88k1mHrDBHaCbFjS7rM20hDsO48AJclmHw6s4RAk6uD4csD6 M3aH6gGKiLuWbkrb1pJgyCfIWzm6u0ZAlVNGJPgysYsA6wIVpDatbGV7QmHOJi7o UgV6mKq0/et3aGjh4EvsCqp5qx9RbMChCPBOLAj6WAj1WMNoJvzlE9v/ofDLEgnL O8QxQlJkQB/mAOqxJDC6Mn/SVFet86tJifm3+gAXTqMpp1bfUQjGDiN/ufaQenrk K738SceFnqQ8iWvxXMN+t48GyCt6ZIkk0dJOt0SpQ5LHzSOVd/+fTjps5nkI2M+R ukweAQKCAQEA3dmHRAqs0gjvJ2gthayT0G7s8s6oObxfKYpRLw8Q8s+JxwZRVr0O aTt1kYn2eXIdO12zLBspRiX+1tmbpD3hEoO+NPplvNsfwzbPtDYofYT1bD8J19JV seFbdHlxNGBHaesjNLIsbTRPokATLtvhyQFNhS2SBV4OLiu3GzfSgGBMaPoSDnNN +OHZ/0aunQkpOF90/LzFrhMYttXhkMSgXGyg4kZkg93HLVGOvz3/WIcaEh8Merqc +pzLRW+nhJin0lDW8RfvAPOZlL6nTTUWZc6cr2yyJFxzw4AqvGhvCnD5Px9mPNpP XM9QqgBE9ayYiJyup/gvGszbv/43ZOuHPg== -----END PRIVATE KEY----- golang-github-prometheus-common-0.32.1/config/testdata/server.crt000066400000000000000000000120251413406232300250770ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus TLS CA Validity Not Before: Apr 5 08:06:57 2019 GMT Not After : Mar 26 08:06:57 2059 GMT Subject: C=US, O=Prometheus, CN=prometheus.example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:bd:6c:b6:7f:d1:2f:be:e4:41:eb:5d:ff:50:78: 03:2b:76:03:da:01:48:20:13:90:66:c9:ce:6e:06: e5:fa:2d:0d:c0:b0:46:28:44:10:a0:61:79:87:a2: 98:4c:29:fa:f9:bb:0f:44:c7:90:5c:5c:55:60:cd: 45:da:b8:e4:dd:28:72:c8:8b:a1:3e:4b:00:09:82: b0:2c:dc:d6:17:c9:02:f4:cd:26:c7:11:28:f3:77: b5:97:c2:76:c2:e0:07:d7:34:5b:e0:ed:1a:59:a5: b4:b7:16:09:3d:35:bd:d9:03:07:9d:7c:3b:f0:63: bd:5e:02:99:cf:32:e1:ac:4c:7a:3e:4c:b2:8e:98: 68:07:4f:59:dc:0d:bf:cc:83:04:5c:d8:90:f0:73: da:2b:08:17:c4:36:a7:d8:94:3d:b6:c0:af:29:0a: d3:19:5f:eb:7d:cc:4d:05:56:11:0a:ee:b1:f3:d7: c9:5a:3c:8c:57:16:91:51:14:f8:20:4e:0f:29:9e: 04:21:e6:f1:e4:e8:44:af:d7:25:92:08:64:fc:2c: 1c:2e:4f:71:53:91:53:1d:e5:f9:7b:52:0f:21:da: 5c:dd:19:68:96:ca:70:6a:f1:c4:0d:07:af:f8:65: 13:92:e9:ef:65:b3:89:86:fd:c0:74:5c:a4:6b:49: 62:c5 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Basic Constraints: CA:FALSE X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication X509v3 Subject Key Identifier: 00:61:01:AD:25:44:8A:EF:E1:2C:EC:83:5A:3A:3B:EA:A0:BD:E1:45 X509v3 Authority Key Identifier: keyid:4D:02:BF:71:95:6A:AA:58:C5:9C:B8:83:67:5E:64:16:99:E1:2A:9E Authority Information Access: CA Issuers - URI:http://example.com/ca/tls-ca.cer X509v3 CRL Distribution Points: Full Name: URI:http://example.com/ca/tls-ca.crl X509v3 Subject Alternative Name: IP Address:127.0.0.1, IP Address:127.0.0.0, DNS:localhost Signature Algorithm: sha1WithRSAEncryption 77:97:e4:ef:db:10:8e:62:50:96:4a:6e:f5:a4:f9:1f:19:3b: c8:a4:dd:b3:f6:11:41:1a:fb:e3:f8:dd:0e:64:e5:2b:00:b9: e6:25:9f:2e:e1:d2:9a:cd:b6:f2:41:4d:27:dd:2c:9a:af:97: 79:e8:cf:61:fb:cf:be:25:c6:e1:19:a0:c8:90:44:a0:76:8a: 45:d4:37:22:e5:d4:80:b4:b3:0f:a8:33:08:24:ad:21:0b:b7: 98:46:93:90:8a:ae:77:0c:cb:b8:59:d3:3b:9b:fb:16:5a:22: ca:c2:97:9d:78:1b:fc:23:fc:a0:42:54:40:de:88:4b:07:2b: 19:4e:0e:79:bf:c9:9f:01:a6:46:c5:55:fa:9f:c0:0d:8a:a6: e1:47:16:a6:0e:be:23:c9:e9:58:d6:31:71:8c:80:9c:16:64: f0:14:08:22:a1:23:7c:98:b9:62:d1:4a:ce:e3:5c:59:fb:41: 87:a5:3b:36:dd:3d:45:48:b0:b0:77:6f:de:58:2a:27:4d:56: 20:54:08:20:c8:6d:79:b5:b9:e6:3a:03:24:0f:6d:67:39:20: 78:10:2f:47:85:83:c1:4d:17:33:79:84:75:27:fa:47:67:59: 56:cc:33:7b:a5:77:aa:59:9a:98:30:10:1a:78:43:34:8f:ed: c2:a1:a3:ea -----BEGIN CERTIFICATE----- MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADBpMQswCQYDVQQGEwJVUzET MBEGA1UECgwKUHJvbWV0aGV1czEpMCcGA1UECwwgUHJvbWV0aGV1cyBDZXJ0aWZp Y2F0ZSBBdXRob3JpdHkxGjAYBgNVBAMMEVByb21ldGhldXMgVExTIENBMCAXDTE5 MDQwNTA4MDY1N1oYDzIwNTkwMzI2MDgwNjU3WjBDMQswCQYDVQQGEwJVUzETMBEG A1UECgwKUHJvbWV0aGV1czEfMB0GA1UEAwwWcHJvbWV0aGV1cy5leGFtcGxlLmNv bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL1stn/RL77kQetd/1B4 Ayt2A9oBSCATkGbJzm4G5fotDcCwRihEEKBheYeimEwp+vm7D0THkFxcVWDNRdq4 5N0ocsiLoT5LAAmCsCzc1hfJAvTNJscRKPN3tZfCdsLgB9c0W+DtGlmltLcWCT01 vdkDB518O/BjvV4Cmc8y4axMej5Mso6YaAdPWdwNv8yDBFzYkPBz2isIF8Q2p9iU PbbArykK0xlf633MTQVWEQrusfPXyVo8jFcWkVEU+CBODymeBCHm8eToRK/XJZII ZPwsHC5PcVORUx3l+XtSDyHaXN0ZaJbKcGrxxA0Hr/hlE5Lp72WziYb9wHRcpGtJ YsUCAwEAAaOCAREwggENMA4GA1UdDwEB/wQEAwIFoDAJBgNVHRMEAjAAMB0GA1Ud JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUAGEBrSVEiu/hLOyD Wjo76qC94UUwHwYDVR0jBBgwFoAUTQK/cZVqqljFnLiDZ15kFpnhKp4wPAYIKwYB BQUHAQEEMDAuMCwGCCsGAQUFBzAChiBodHRwOi8vZXhhbXBsZS5jb20vY2EvdGxz LWNhLmNlcjAxBgNVHR8EKjAoMCagJKAihiBodHRwOi8vZXhhbXBsZS5jb20vY2Ev dGxzLWNhLmNybDAgBgNVHREEGTAXhwR/AAABhwR/AAAAgglsb2NhbGhvc3QwDQYJ KoZIhvcNAQEFBQADggEBAHeX5O/bEI5iUJZKbvWk+R8ZO8ik3bP2EUEa++P43Q5k 5SsAueYlny7h0prNtvJBTSfdLJqvl3noz2H7z74lxuEZoMiQRKB2ikXUNyLl1IC0 sw+oMwgkrSELt5hGk5CKrncMy7hZ0zub+xZaIsrCl514G/wj/KBCVEDeiEsHKxlO Dnm/yZ8BpkbFVfqfwA2KpuFHFqYOviPJ6VjWMXGMgJwWZPAUCCKhI3yYuWLRSs7j XFn7QYelOzbdPUVIsLB3b95YKidNViBUCCDIbXm1ueY6AyQPbWc5IHgQL0eFg8FN FzN5hHUn+kdnWVbMM3uld6pZmpgwEBp4QzSP7cKho+o= -----END CERTIFICATE----- golang-github-prometheus-common-0.32.1/config/testdata/server.key000066400000000000000000000032501413406232300250770ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9bLZ/0S++5EHr Xf9QeAMrdgPaAUggE5Bmyc5uBuX6LQ3AsEYoRBCgYXmHophMKfr5uw9Ex5BcXFVg zUXauOTdKHLIi6E+SwAJgrAs3NYXyQL0zSbHESjzd7WXwnbC4AfXNFvg7RpZpbS3 Fgk9Nb3ZAwedfDvwY71eApnPMuGsTHo+TLKOmGgHT1ncDb/MgwRc2JDwc9orCBfE NqfYlD22wK8pCtMZX+t9zE0FVhEK7rHz18laPIxXFpFRFPggTg8pngQh5vHk6ESv 1yWSCGT8LBwuT3FTkVMd5fl7Ug8h2lzdGWiWynBq8cQNB6/4ZROS6e9ls4mG/cB0 XKRrSWLFAgMBAAECggEAezQ0V1o11dEc1vuiTjJgzWnLA4aF5OcUquZjb8jo2Blp soR0fUgYEFiV9RRaPl+nr7ptKe0rBgfAOGALKUHNCdN/JNU8oQmjEoyADg3s6jeB xruQlzWgDwszf2uqVwHj16Nkhx1wYBKZQeQBSmCkBHwl/daKHcahqn3CkLOleKx+ Qlc3BzWNaGte6qpJMs0It3by1FuxRwVz5VkL8uhzj0WIOYMA84t0gTnFH9gfRO3F licotxg/Nl5M36wWcfL8Jq++72AtaKcD1jUEwuQpogrVeqflmeHwn/TlL++Hv6Xe Lq0jt3OCUKUV40eq9c5uEgTmyrVHMDkfFdXzutdMAQKBgQDsSMXk7P4SX6u6uTjV In9eWw6ZyJ2aL6VB9co/NMsj49GrrFT8VX9d+JPe9P/n6tuGcFbymNep22njRksR 0ItpW1NFRR/R3g0kYe1EhkRpNm6fhY9oIuR9xhcNnPNYkqAKT3T/dxrzbwsNhomi X8aht/eCz4ZsK/KdOGTkPozxgQKBgQDNOvrclT1Wl4bxONp9pEV5XpRSD/qigfIp i5wxy7ihX/QY9RToIWJDnzMVLnEYe64RB2WB8/4WwNPOQcuaxXbFUFct/2NdhTnS ToJPgPe819zW9t1FLTf1fHtsRBpGFtbhdlUDOiOtJiMXYiwlRh2uyWFhjOo8TNUE qMwai0vLRQKBgQCDH4t6lC4W4jK5x2oLlT5bjWqX2uXjF8e8x/q5gsGspBPKEjOD aKrq6jSdSRbui73RaGxH6pvb7iBf+LVWKIYFLKIUUdzrqS9f3lw+Z8h1HrjbG9JO dvaX+aL3cf71S0E3F4sU7fLt3tSiZ+PfUQk424+mbyXox6a2qwIKS9AJgQKBgHCu dHROYJo9ojKpo5Ueb6K+4jLYYSV+sYZMCBtzHlFETNKzJaJ6SeiU7Ugw8pmdtqnU 5M/gNl8pymFR0MeOqbKWdPdlZJpBfsjQoE2kouEFqFRCwKStui7IBUAheEeJXLv3 659U+aek69l35oMkp0GDgjs8UpN/H+pp/36Hgrr9AoGAftWU405rpStHEdRVrazP FibQesT9HOdJgmm1gNIhj+PnFs7lKER9p0Wdl79QnIqjwyhjCXL94TFerzTKLY2c IRj5dcRHiiT0iK8wq8bzGNYCqV73oQXaUFMiutNAArXwzwuvPFPWNBQsjLzeDLeC mcOsCcPAk8cLYtVfZo2sP3g= -----END PRIVATE KEY----- golang-github-prometheus-common-0.32.1/config/testdata/tls-ca-chain.pem000066400000000000000000000223441413406232300260320ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus Root CA Validity Not Before: Apr 5 08:00:37 2019 GMT Not After : Mar 26 08:00:37 2059 GMT Subject: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus TLS CA Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:aa:d2:34:6b:ed:f1:f4:01:08:e5:00:9f:75:c8: ba:fc:4b:72:c6:04:93:af:f1:f6:b5:ce:01:0d:c6: bd:d3:16:98:9d:e5:51:56:12:58:16:ee:18:6e:f0: 68:a9:42:16:65:cf:e3:31:f5:90:79:9d:13:32:87: 3b:1f:65:fd:84:88:a4:56:3d:26:54:69:05:27:5a: ea:89:02:e7:31:9b:7d:7f:76:93:54:70:bc:17:92: 06:9f:9f:90:4a:8a:cf:82:a7:7b:7c:71:c4:fa:34: 56:00:32:1a:85:c5:f8:e4:4a:63:43:37:9d:60:84: 4d:78:6e:87:12:c4:2b:1f:93:a5:fe:cc:5e:f1:df: c1:97:ff:b7:3e:20:38:1d:71:15:11:ec:6c:7a:cc: 0e:87:52:31:b1:b9:74:c3:07:1c:42:4b:1e:c1:17: bc:e4:13:b7:b0:20:2e:c4:07:93:bd:a8:11:f9:da: a7:d0:df:4a:48:be:9b:6d:65:c3:ae:58:56:c0:9f: 17:c5:d8:32:b1:04:22:fb:5b:18:f6:20:10:50:ec: 2d:10:4f:cc:48:8f:f2:75:dd:33:a4:0e:f5:55:da: 2c:89:a1:3a:52:bb:11:11:0b:97:27:17:73:35:da: 10:71:b3:9f:a8:42:91:e6:3a:66:00:f9:e5:11:8f: 5b:57 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical Certificate Sign, CRL Sign X509v3 Basic Constraints: critical CA:TRUE, pathlen:0 X509v3 Subject Key Identifier: 4D:02:BF:71:95:6A:AA:58:C5:9C:B8:83:67:5E:64:16:99:E1:2A:9E X509v3 Authority Key Identifier: keyid:3C:1E:A8:C6:4C:05:4D:20:EC:88:DB:29:D4:7B:F9:12:5D:CE:EA:1A Authority Information Access: CA Issuers - URI:https://example.com/ca/root-ca.cer X509v3 CRL Distribution Points: Full Name: URI:https://example.com/ca/root-ca.crl Signature Algorithm: sha1WithRSAEncryption 63:fc:ba:30:a5:05:d6:76:14:f1:77:38:b1:41:6f:81:d9:b4: 02:fd:bc:e5:f6:d9:e6:73:e0:71:cf:4c:fb:13:b5:6b:bd:b9: c6:f6:28:18:36:e1:8c:d9:93:b3:78:4a:3d:39:1b:f4:fb:69: 75:24:ae:e1:a0:2f:94:05:bf:10:3c:3e:d2:2b:a8:f3:31:25: 2e:ed:13:ad:60:5d:22:9a:26:15:20:86:98:73:4c:f6:4b:48: b8:1f:67:ba:4e:c9:47:ed:85:dc:38:dc:02:0c:fb:54:d5:2e: 6c:b4:95:18:51:d1:ae:ea:e8:fb:b4:19:50:04:bc:31:7e:51: 9e:85:29:4d:c8:f7:26:d6:d6:8d:35:2d:9e:e2:06:16:38:e2: 56:80:ec:f3:a3:34:e3:28:c4:e8:10:d0:8a:a6:6f:20:9a:b9: dc:b9:90:6b:ba:8a:27:2c:29:72:28:55:e7:59:a6:a7:90:ec: 32:e8:d0:26:4a:c1:44:dd:20:bf:dc:4d:1e:7e:cc:e5:a2:5b: e8:df:3d:4b:01:aa:48:56:17:e9:29:d8:71:83:05:36:8c:11: 4f:77:b8:95:20:b7:c7:21:06:c2:87:97:b4:6b:d3:f7:23:ba: 4d:5f:15:d1:0c:4d:6e:f1:6a:9d:57:5c:02:6a:d7:31:18:ef: 5c:fc:f8:04 -----BEGIN CERTIFICATE----- MIIELTCCAxWgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJVUzET MBEGA1UECgwKUHJvbWV0aGV1czEpMCcGA1UECwwgUHJvbWV0aGV1cyBDZXJ0aWZp Y2F0ZSBBdXRob3JpdHkxGzAZBgNVBAMMElByb21ldGhldXMgUm9vdCBDQTAgFw0x OTA0MDUwODAwMzdaGA8yMDU5MDMyNjA4MDAzN1owaTELMAkGA1UEBhMCVVMxEzAR BgNVBAoMClByb21ldGhldXMxKTAnBgNVBAsMIFByb21ldGhldXMgQ2VydGlmaWNh dGUgQXV0aG9yaXR5MRowGAYDVQQDDBFQcm9tZXRoZXVzIFRMUyBDQTCCASIwDQYJ KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKrSNGvt8fQBCOUAn3XIuvxLcsYEk6/x 9rXOAQ3GvdMWmJ3lUVYSWBbuGG7waKlCFmXP4zH1kHmdEzKHOx9l/YSIpFY9JlRp BSda6okC5zGbfX92k1RwvBeSBp+fkEqKz4Kne3xxxPo0VgAyGoXF+ORKY0M3nWCE TXhuhxLEKx+Tpf7MXvHfwZf/tz4gOB1xFRHsbHrMDodSMbG5dMMHHEJLHsEXvOQT t7AgLsQHk72oEfnap9DfSki+m21lw65YVsCfF8XYMrEEIvtbGPYgEFDsLRBPzEiP 8nXdM6QO9VXaLImhOlK7ERELlycXczXaEHGzn6hCkeY6ZgD55RGPW1cCAwEAAaOB 3DCB2TAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E FgQUTQK/cZVqqljFnLiDZ15kFpnhKp4wHwYDVR0jBBgwFoAUPB6oxkwFTSDsiNsp 1Hv5El3O6howPgYIKwYBBQUHAQEEMjAwMC4GCCsGAQUFBzAChiJodHRwczovL2V4 YW1wbGUuY29tL2NhL3Jvb3QtY2EuY2VyMDMGA1UdHwQsMCowKKAmoCSGImh0dHBz Oi8vZXhhbXBsZS5jb20vY2Evcm9vdC1jYS5jcmwwDQYJKoZIhvcNAQEFBQADggEB AGP8ujClBdZ2FPF3OLFBb4HZtAL9vOX22eZz4HHPTPsTtWu9ucb2KBg24YzZk7N4 Sj05G/T7aXUkruGgL5QFvxA8PtIrqPMxJS7tE61gXSKaJhUghphzTPZLSLgfZ7pO yUfthdw43AIM+1TVLmy0lRhR0a7q6Pu0GVAEvDF+UZ6FKU3I9ybW1o01LZ7iBhY4 4laA7POjNOMoxOgQ0IqmbyCaudy5kGu6iicsKXIoVedZpqeQ7DLo0CZKwUTdIL/c TR5+zOWiW+jfPUsBqkhWF+kp2HGDBTaMEU93uJUgt8chBsKHl7Rr0/cjuk1fFdEM TW7xap1XXAJq1zEY71z8+AQ= -----END CERTIFICATE----- Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus Root CA Validity Not Before: Apr 5 07:55:00 2019 GMT Not After : Mar 26 07:55:00 2059 GMT Subject: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus Root CA Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:bf:b9:e2:ab:5f:61:22:e1:4e:cd:ee:da:b0:26: 2e:bb:b0:7e:1c:ce:10:be:16:29:35:0c:0c:1d:93: 01:29:2a:f6:f9:c2:6e:5c:10:44:ca:f8:dc:ad:7a: 06:64:0f:8a:18:ad:b2:a2:94:49:c9:ba:8c:45:94: 7c:d9:e0:11:45:d8:16:79:a2:20:9f:8c:63:60:72: 2a:5b:f9:66:80:ac:85:67:01:5a:eb:91:c1:d2:88: 87:9e:4c:18:c9:f2:f0:7a:18:c0:e6:ab:2c:78:de: 5f:b2:22:4e:94:9c:f5:cd:e6:e2:33:30:e9:20:10: a6:a1:75:eb:59:ab:45:a9:f7:3e:54:40:ae:05:25: be:74:c5:3a:fd:af:73:16:60:45:7c:4a:e0:0e:0d: a1:15:7f:9a:1f:c2:a7:04:ad:ef:b3:e4:f6:00:2c: 4e:0b:04:90:49:ee:d3:db:a6:12:c4:91:0b:32:4f: 11:84:c7:c4:8a:ef:51:66:7a:b0:20:2f:cb:95:8d: 96:57:60:66:5e:f9:4f:5a:94:9c:71:ad:eb:ca:70: 3e:62:06:c2:3a:29:f8:9e:86:af:da:07:78:f8:31: af:42:48:49:9e:4a:df:1b:27:1f:44:35:81:6d:fa: 7a:c5:6a:0a:35:23:c7:c4:d5:fe:c9:9e:61:c9:30: cd:1f Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical Certificate Sign, CRL Sign X509v3 Basic Constraints: critical CA:TRUE X509v3 Subject Key Identifier: 3C:1E:A8:C6:4C:05:4D:20:EC:88:DB:29:D4:7B:F9:12:5D:CE:EA:1A X509v3 Authority Key Identifier: keyid:3C:1E:A8:C6:4C:05:4D:20:EC:88:DB:29:D4:7B:F9:12:5D:CE:EA:1A Signature Algorithm: sha1WithRSAEncryption 56:2f:79:e5:12:91:f5:19:a7:d1:32:28:fd:e3:9d:8f:e1:3c: bb:a3:a5:f2:55:8a:03:ad:2c:1d:18:82:e1:7f:19:75:d9:47: 5b:e7:7c:e4:a5:e0:eb:dc:7e:24:a3:7d:99:1a:cf:39:ba:a5: b4:b8:45:68:83:cf:70:ad:56:f2:34:73:65:fc:6c:b0:53:9a: 79:04:f7:3e:7e:4b:22:1b:e7:76:23:20:bc:9c:05:a2:5d:01: d2:f0:09:49:17:b2:61:74:1a:5b:f4:e0:fd:ce:11:ba:13:4a: e6:07:11:7d:30:e2:11:87:ee:33:1a:68:de:67:f4:ac:b5:58: 1a:ac:cf:7a:2d:fd:c3:44:5b:4b:cd:6c:ff:f6:49:b4:55:4a: 09:a0:92:2d:57:3b:69:85:54:3e:e9:ec:ef:b2:a5:7a:29:75: 2b:f8:eb:4b:d4:cf:68:ee:3e:c8:63:7e:12:eb:e4:2f:63:a3: a7:c8:0f:e9:39:ff:5c:29:65:7f:25:f0:42:bf:07:ba:06:b8: 5e:d6:56:ba:f8:67:56:1b:42:aa:b3:04:d8:6e:88:10:a5:70: b5:81:04:a4:90:a3:f0:83:4d:0c:6b:12:5d:a4:4c:83:5a:ff: a8:7a:86:61:ff:0f:4c:e5:0f:17:d1:64:3c:bd:d9:22:7e:b7: fa:9b:83:ba -----BEGIN CERTIFICATE----- MIIDtDCCApygAwIBAgIBATANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJVUzET MBEGA1UECgwKUHJvbWV0aGV1czEpMCcGA1UECwwgUHJvbWV0aGV1cyBDZXJ0aWZp Y2F0ZSBBdXRob3JpdHkxGzAZBgNVBAMMElByb21ldGhldXMgUm9vdCBDQTAgFw0x OTA0MDUwNzU1MDBaGA8yMDU5MDMyNjA3NTUwMFowajELMAkGA1UEBhMCVVMxEzAR BgNVBAoMClByb21ldGhldXMxKTAnBgNVBAsMIFByb21ldGhldXMgQ2VydGlmaWNh dGUgQXV0aG9yaXR5MRswGQYDVQQDDBJQcm9tZXRoZXVzIFJvb3QgQ0EwggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/ueKrX2Ei4U7N7tqwJi67sH4czhC+ Fik1DAwdkwEpKvb5wm5cEETK+NytegZkD4oYrbKilEnJuoxFlHzZ4BFF2BZ5oiCf jGNgcipb+WaArIVnAVrrkcHSiIeeTBjJ8vB6GMDmqyx43l+yIk6UnPXN5uIzMOkg EKahdetZq0Wp9z5UQK4FJb50xTr9r3MWYEV8SuAODaEVf5ofwqcEre+z5PYALE4L BJBJ7tPbphLEkQsyTxGEx8SK71FmerAgL8uVjZZXYGZe+U9alJxxrevKcD5iBsI6 Kfiehq/aB3j4Ma9CSEmeSt8bJx9ENYFt+nrFago1I8fE1f7JnmHJMM0fAgMBAAGj YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQ8 HqjGTAVNIOyI2ynUe/kSXc7qGjAfBgNVHSMEGDAWgBQ8HqjGTAVNIOyI2ynUe/kS Xc7qGjANBgkqhkiG9w0BAQUFAAOCAQEAVi955RKR9Rmn0TIo/eOdj+E8u6Ol8lWK A60sHRiC4X8ZddlHW+d85KXg69x+JKN9mRrPObqltLhFaIPPcK1W8jRzZfxssFOa eQT3Pn5LIhvndiMgvJwFol0B0vAJSReyYXQaW/Tg/c4RuhNK5gcRfTDiEYfuMxpo 3mf0rLVYGqzPei39w0RbS81s//ZJtFVKCaCSLVc7aYVUPuns77Kleil1K/jrS9TP aO4+yGN+EuvkL2Ojp8gP6Tn/XCllfyXwQr8Huga4XtZWuvhnVhtCqrME2G6IEKVw tYEEpJCj8INNDGsSXaRMg1r/qHqGYf8PTOUPF9FkPL3ZIn63+puDug== -----END CERTIFICATE----- golang-github-prometheus-common-0.32.1/config/testdata/tls_config.cert_no_key.bad.yml000066400000000000000000000000241413406232300307520ustar00rootroot00000000000000cert_file: somefile golang-github-prometheus-common-0.32.1/config/testdata/tls_config.empty.good.yml000066400000000000000000000000001413406232300300030ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/config/testdata/tls_config.insecure.good.yml000066400000000000000000000000331413406232300304700ustar00rootroot00000000000000insecure_skip_verify: true golang-github-prometheus-common-0.32.1/config/testdata/tls_config.invalid_field.bad.yml000066400000000000000000000000301413406232300312370ustar00rootroot00000000000000something_invalid: true golang-github-prometheus-common-0.32.1/config/testdata/tls_config.key_no_cert.bad.yml000066400000000000000000000000231413406232300307510ustar00rootroot00000000000000key_file: somefile golang-github-prometheus-common-0.32.1/config/tls_config_test.go000066400000000000000000000033131413406232300247630ustar00rootroot00000000000000// Copyright 2016 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +build go1.8 package config import ( "crypto/tls" "io/ioutil" "reflect" "testing" "gopkg.in/yaml.v2" ) // LoadTLSConfig parses the given YAML file into a tls.Config. func LoadTLSConfig(filename string) (*tls.Config, error) { content, err := ioutil.ReadFile(filename) if err != nil { return nil, err } cfg := TLSConfig{} if err = yaml.UnmarshalStrict(content, &cfg); err != nil { return nil, err } return NewTLSConfig(&cfg) } var expectedTLSConfigs = []struct { filename string config *tls.Config }{ { filename: "tls_config.empty.good.yml", config: &tls.Config{}, }, { filename: "tls_config.insecure.good.yml", config: &tls.Config{InsecureSkipVerify: true}, }, } func TestValidTLSConfig(t *testing.T) { for _, cfg := range expectedTLSConfigs { got, err := LoadTLSConfig("testdata/" + cfg.filename) if err != nil { t.Errorf("Error parsing %s: %s", cfg.filename, err) } // non-nil functions are never equal. got.GetClientCertificate = nil if !reflect.DeepEqual(got, cfg.config) { t.Fatalf("%v: unexpected config result: \n\n%v\n expected\n\n%v", cfg.filename, got, cfg.config) } } } golang-github-prometheus-common-0.32.1/expfmt/000077500000000000000000000000001413406232300213045ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/expfmt/bench_test.go000066400000000000000000000113261413406232300237540ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "bytes" "compress/gzip" "io" "io/ioutil" "testing" "github.com/matttproud/golang_protobuf_extensions/pbutil" dto "github.com/prometheus/client_model/go" ) var parser TextParser // Benchmarks to show how much penalty text format parsing actually inflicts. // // Example results on Linux 3.13.0, Intel(R) Core(TM) i7-4700MQ CPU @ 2.40GHz, go1.4. // // BenchmarkParseText 1000 1188535 ns/op 205085 B/op 6135 allocs/op // BenchmarkParseTextGzip 1000 1376567 ns/op 246224 B/op 6151 allocs/op // BenchmarkParseProto 10000 172790 ns/op 52258 B/op 1160 allocs/op // BenchmarkParseProtoGzip 5000 324021 ns/op 94931 B/op 1211 allocs/op // BenchmarkParseProtoMap 10000 187946 ns/op 58714 B/op 1203 allocs/op // // CONCLUSION: The overhead for the map is negligible. Text format needs ~5x more allocations. // Without compression, it needs ~7x longer, but with compression (the more relevant scenario), // the difference becomes less relevant, only ~4x. // // The test data contains 248 samples. // BenchmarkParseText benchmarks the parsing of a text-format scrape into metric // family DTOs. func BenchmarkParseText(b *testing.B) { b.StopTimer() data, err := ioutil.ReadFile("testdata/text") if err != nil { b.Fatal(err) } b.StartTimer() for i := 0; i < b.N; i++ { if _, err := parser.TextToMetricFamilies(bytes.NewReader(data)); err != nil { b.Fatal(err) } } } // BenchmarkParseTextGzip benchmarks the parsing of a gzipped text-format scrape // into metric family DTOs. func BenchmarkParseTextGzip(b *testing.B) { b.StopTimer() data, err := ioutil.ReadFile("testdata/text.gz") if err != nil { b.Fatal(err) } b.StartTimer() for i := 0; i < b.N; i++ { in, err := gzip.NewReader(bytes.NewReader(data)) if err != nil { b.Fatal(err) } if _, err := parser.TextToMetricFamilies(in); err != nil { b.Fatal(err) } } } // BenchmarkParseProto benchmarks the parsing of a protobuf-format scrape into // metric family DTOs. Note that this does not build a map of metric families // (as the text version does), because it is not required for Prometheus // ingestion either. (However, it is required for the text-format parsing, as // the metric family might be sprinkled all over the text, while the // protobuf-format guarantees bundling at one place.) func BenchmarkParseProto(b *testing.B) { b.StopTimer() data, err := ioutil.ReadFile("testdata/protobuf") if err != nil { b.Fatal(err) } b.StartTimer() for i := 0; i < b.N; i++ { family := &dto.MetricFamily{} in := bytes.NewReader(data) for { family.Reset() if _, err := pbutil.ReadDelimited(in, family); err != nil { if err == io.EOF { break } b.Fatal(err) } } } } // BenchmarkParseProtoGzip is like BenchmarkParseProto above, but parses gzipped // protobuf format. func BenchmarkParseProtoGzip(b *testing.B) { b.StopTimer() data, err := ioutil.ReadFile("testdata/protobuf.gz") if err != nil { b.Fatal(err) } b.StartTimer() for i := 0; i < b.N; i++ { family := &dto.MetricFamily{} in, err := gzip.NewReader(bytes.NewReader(data)) if err != nil { b.Fatal(err) } for { family.Reset() if _, err := pbutil.ReadDelimited(in, family); err != nil { if err == io.EOF { break } b.Fatal(err) } } } } // BenchmarkParseProtoMap is like BenchmarkParseProto but DOES put the parsed // metric family DTOs into a map. This is not happening during Prometheus // ingestion. It is just here to measure the overhead of that map creation and // separate it from the overhead of the text format parsing. func BenchmarkParseProtoMap(b *testing.B) { b.StopTimer() data, err := ioutil.ReadFile("testdata/protobuf") if err != nil { b.Fatal(err) } b.StartTimer() for i := 0; i < b.N; i++ { families := map[string]*dto.MetricFamily{} in := bytes.NewReader(data) for { family := &dto.MetricFamily{} if _, err := pbutil.ReadDelimited(in, family); err != nil { if err == io.EOF { break } b.Fatal(err) } families[family.GetName()] = family } } } golang-github-prometheus-common-0.32.1/expfmt/decode.go000066400000000000000000000262611413406232300230650ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "fmt" "io" "math" "mime" "net/http" dto "github.com/prometheus/client_model/go" "github.com/matttproud/golang_protobuf_extensions/pbutil" "github.com/prometheus/common/model" ) // Decoder types decode an input stream into metric families. type Decoder interface { Decode(*dto.MetricFamily) error } // DecodeOptions contains options used by the Decoder and in sample extraction. type DecodeOptions struct { // Timestamp is added to each value from the stream that has no explicit timestamp set. Timestamp model.Time } // ResponseFormat extracts the correct format from a HTTP response header. // If no matching format can be found FormatUnknown is returned. func ResponseFormat(h http.Header) Format { ct := h.Get(hdrContentType) mediatype, params, err := mime.ParseMediaType(ct) if err != nil { return FmtUnknown } const textType = "text/plain" switch mediatype { case ProtoType: if p, ok := params["proto"]; ok && p != ProtoProtocol { return FmtUnknown } if e, ok := params["encoding"]; ok && e != "delimited" { return FmtUnknown } return FmtProtoDelim case textType: if v, ok := params["version"]; ok && v != TextVersion { return FmtUnknown } return FmtText } return FmtUnknown } // NewDecoder returns a new decoder based on the given input format. // If the input format does not imply otherwise, a text format decoder is returned. func NewDecoder(r io.Reader, format Format) Decoder { switch format { case FmtProtoDelim: return &protoDecoder{r: r} } return &textDecoder{r: r} } // protoDecoder implements the Decoder interface for protocol buffers. type protoDecoder struct { r io.Reader } // Decode implements the Decoder interface. func (d *protoDecoder) Decode(v *dto.MetricFamily) error { _, err := pbutil.ReadDelimited(d.r, v) if err != nil { return err } if !model.IsValidMetricName(model.LabelValue(v.GetName())) { return fmt.Errorf("invalid metric name %q", v.GetName()) } for _, m := range v.GetMetric() { if m == nil { continue } for _, l := range m.GetLabel() { if l == nil { continue } if !model.LabelValue(l.GetValue()).IsValid() { return fmt.Errorf("invalid label value %q", l.GetValue()) } if !model.LabelName(l.GetName()).IsValid() { return fmt.Errorf("invalid label name %q", l.GetName()) } } } return nil } // textDecoder implements the Decoder interface for the text protocol. type textDecoder struct { r io.Reader p TextParser fams []*dto.MetricFamily } // Decode implements the Decoder interface. func (d *textDecoder) Decode(v *dto.MetricFamily) error { // TODO(fabxc): Wrap this as a line reader to make streaming safer. if len(d.fams) == 0 { // No cached metric families, read everything and parse metrics. fams, err := d.p.TextToMetricFamilies(d.r) if err != nil { return err } if len(fams) == 0 { return io.EOF } d.fams = make([]*dto.MetricFamily, 0, len(fams)) for _, f := range fams { d.fams = append(d.fams, f) } } *v = *d.fams[0] d.fams = d.fams[1:] return nil } // SampleDecoder wraps a Decoder to extract samples from the metric families // decoded by the wrapped Decoder. type SampleDecoder struct { Dec Decoder Opts *DecodeOptions f dto.MetricFamily } // Decode calls the Decode method of the wrapped Decoder and then extracts the // samples from the decoded MetricFamily into the provided model.Vector. func (sd *SampleDecoder) Decode(s *model.Vector) error { err := sd.Dec.Decode(&sd.f) if err != nil { return err } *s, err = extractSamples(&sd.f, sd.Opts) return err } // ExtractSamples builds a slice of samples from the provided metric // families. If an error occurs during sample extraction, it continues to // extract from the remaining metric families. The returned error is the last // error that has occurred. func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) { var ( all model.Vector lastErr error ) for _, f := range fams { some, err := extractSamples(f, o) if err != nil { lastErr = err continue } all = append(all, some...) } return all, lastErr } func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error) { switch f.GetType() { case dto.MetricType_COUNTER: return extractCounter(o, f), nil case dto.MetricType_GAUGE: return extractGauge(o, f), nil case dto.MetricType_SUMMARY: return extractSummary(o, f), nil case dto.MetricType_UNTYPED: return extractUntyped(o, f), nil case dto.MetricType_HISTOGRAM: return extractHistogram(o, f), nil } return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType()) } func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector { samples := make(model.Vector, 0, len(f.Metric)) for _, m := range f.Metric { if m.Counter == nil { continue } lset := make(model.LabelSet, len(m.Label)+1) for _, p := range m.Label { lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } lset[model.MetricNameLabel] = model.LabelValue(f.GetName()) smpl := &model.Sample{ Metric: model.Metric(lset), Value: model.SampleValue(m.Counter.GetValue()), } if m.TimestampMs != nil { smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000) } else { smpl.Timestamp = o.Timestamp } samples = append(samples, smpl) } return samples } func extractGauge(o *DecodeOptions, f *dto.MetricFamily) model.Vector { samples := make(model.Vector, 0, len(f.Metric)) for _, m := range f.Metric { if m.Gauge == nil { continue } lset := make(model.LabelSet, len(m.Label)+1) for _, p := range m.Label { lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } lset[model.MetricNameLabel] = model.LabelValue(f.GetName()) smpl := &model.Sample{ Metric: model.Metric(lset), Value: model.SampleValue(m.Gauge.GetValue()), } if m.TimestampMs != nil { smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000) } else { smpl.Timestamp = o.Timestamp } samples = append(samples, smpl) } return samples } func extractUntyped(o *DecodeOptions, f *dto.MetricFamily) model.Vector { samples := make(model.Vector, 0, len(f.Metric)) for _, m := range f.Metric { if m.Untyped == nil { continue } lset := make(model.LabelSet, len(m.Label)+1) for _, p := range m.Label { lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } lset[model.MetricNameLabel] = model.LabelValue(f.GetName()) smpl := &model.Sample{ Metric: model.Metric(lset), Value: model.SampleValue(m.Untyped.GetValue()), } if m.TimestampMs != nil { smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000) } else { smpl.Timestamp = o.Timestamp } samples = append(samples, smpl) } return samples } func extractSummary(o *DecodeOptions, f *dto.MetricFamily) model.Vector { samples := make(model.Vector, 0, len(f.Metric)) for _, m := range f.Metric { if m.Summary == nil { continue } timestamp := o.Timestamp if m.TimestampMs != nil { timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000) } for _, q := range m.Summary.Quantile { lset := make(model.LabelSet, len(m.Label)+2) for _, p := range m.Label { lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } // BUG(matt): Update other names to "quantile". lset[model.LabelName(model.QuantileLabel)] = model.LabelValue(fmt.Sprint(q.GetQuantile())) lset[model.MetricNameLabel] = model.LabelValue(f.GetName()) samples = append(samples, &model.Sample{ Metric: model.Metric(lset), Value: model.SampleValue(q.GetValue()), Timestamp: timestamp, }) } lset := make(model.LabelSet, len(m.Label)+1) for _, p := range m.Label { lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum") samples = append(samples, &model.Sample{ Metric: model.Metric(lset), Value: model.SampleValue(m.Summary.GetSampleSum()), Timestamp: timestamp, }) lset = make(model.LabelSet, len(m.Label)+1) for _, p := range m.Label { lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count") samples = append(samples, &model.Sample{ Metric: model.Metric(lset), Value: model.SampleValue(m.Summary.GetSampleCount()), Timestamp: timestamp, }) } return samples } func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector { samples := make(model.Vector, 0, len(f.Metric)) for _, m := range f.Metric { if m.Histogram == nil { continue } timestamp := o.Timestamp if m.TimestampMs != nil { timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000) } infSeen := false for _, q := range m.Histogram.Bucket { lset := make(model.LabelSet, len(m.Label)+2) for _, p := range m.Label { lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } lset[model.LabelName(model.BucketLabel)] = model.LabelValue(fmt.Sprint(q.GetUpperBound())) lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket") if math.IsInf(q.GetUpperBound(), +1) { infSeen = true } samples = append(samples, &model.Sample{ Metric: model.Metric(lset), Value: model.SampleValue(q.GetCumulativeCount()), Timestamp: timestamp, }) } lset := make(model.LabelSet, len(m.Label)+1) for _, p := range m.Label { lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum") samples = append(samples, &model.Sample{ Metric: model.Metric(lset), Value: model.SampleValue(m.Histogram.GetSampleSum()), Timestamp: timestamp, }) lset = make(model.LabelSet, len(m.Label)+1) for _, p := range m.Label { lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count") count := &model.Sample{ Metric: model.Metric(lset), Value: model.SampleValue(m.Histogram.GetSampleCount()), Timestamp: timestamp, } samples = append(samples, count) if !infSeen { // Append an infinity bucket sample. lset := make(model.LabelSet, len(m.Label)+2) for _, p := range m.Label { lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } lset[model.LabelName(model.BucketLabel)] = model.LabelValue("+Inf") lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket") samples = append(samples, &model.Sample{ Metric: model.Metric(lset), Value: count.Value, Timestamp: timestamp, }) } } return samples } golang-github-prometheus-common-0.32.1/expfmt/decode_test.go000066400000000000000000000316231413406232300241220ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "io" "net/http" "reflect" "sort" "strings" "testing" "github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/model" ) func TestTextDecoder(t *testing.T) { var ( ts = model.Now() in = ` # Only a quite simple scenario with two metric families. # More complicated tests of the parser itself can be found in the text package. # TYPE mf2 counter mf2 3 mf1{label="value1"} -3.14 123456 mf1{label="value2"} 42 mf2 4 ` out = model.Vector{ &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "mf1", "label": "value1", }, Value: -3.14, Timestamp: 123456, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "mf1", "label": "value2", }, Value: 42, Timestamp: ts, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "mf2", }, Value: 3, Timestamp: ts, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "mf2", }, Value: 4, Timestamp: ts, }, } ) dec := &SampleDecoder{ Dec: &textDecoder{r: strings.NewReader(in)}, Opts: &DecodeOptions{ Timestamp: ts, }, } var all model.Vector for { var smpls model.Vector err := dec.Decode(&smpls) if err == io.EOF { break } if err != nil { t.Fatal(err) } all = append(all, smpls...) } sort.Sort(all) sort.Sort(out) if !reflect.DeepEqual(all, out) { t.Fatalf("output does not match") } } func TestProtoDecoder(t *testing.T) { var testTime = model.Now() scenarios := []struct { in string expected model.Vector fail bool }{ { in: "", }, { in: "\x8f\x01\n\rrequest_count\x12\x12Number of requests\x18\x00\"0\n#\n\x0fsome_!abel_name\x12\x10some_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00E\xc0\"6\n)\n\x12another_label_name\x12\x13another_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00U@", fail: true, }, { in: "\x8f\x01\n\rrequest_count\x12\x12Number of requests\x18\x00\"0\n#\n\x0fsome_label_name\x12\x10some_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00E\xc0\"6\n)\n\x12another_label_name\x12\x13another_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00U@", expected: model.Vector{ &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_count", "some_label_name": "some_label_value", }, Value: -42, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_count", "another_label_name": "another_label_value", }, Value: 84, Timestamp: testTime, }, }, }, { in: "\xb9\x01\n\rrequest_count\x12\x12Number of requests\x18\x02\"O\n#\n\x0fsome_label_name\x12\x10some_label_value\"(\x1a\x12\t\xaeG\xe1z\x14\xae\xef?\x11\x00\x00\x00\x00\x00\x00E\xc0\x1a\x12\t+\x87\x16\xd9\xce\xf7\xef?\x11\x00\x00\x00\x00\x00\x00U\xc0\"A\n)\n\x12another_label_name\x12\x13another_label_value\"\x14\x1a\x12\t\x00\x00\x00\x00\x00\x00\xe0?\x11\x00\x00\x00\x00\x00\x00$@", expected: model.Vector{ &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_count_count", "some_label_name": "some_label_value", }, Value: 0, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_count_sum", "some_label_name": "some_label_value", }, Value: 0, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_count", "some_label_name": "some_label_value", "quantile": "0.99", }, Value: -42, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_count", "some_label_name": "some_label_value", "quantile": "0.999", }, Value: -84, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_count_count", "another_label_name": "another_label_value", }, Value: 0, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_count_sum", "another_label_name": "another_label_value", }, Value: 0, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_count", "another_label_name": "another_label_value", "quantile": "0.5", }, Value: 10, Timestamp: testTime, }, }, }, { in: "\x8d\x01\n\x1drequest_duration_microseconds\x12\x15The response latency.\x18\x04\"S:Q\b\x85\x15\x11\xcd\xcc\xccL\x8f\xcb:A\x1a\v\b{\x11\x00\x00\x00\x00\x00\x00Y@\x1a\f\b\x9c\x03\x11\x00\x00\x00\x00\x00\x00^@\x1a\f\b\xd0\x04\x11\x00\x00\x00\x00\x00\x00b@\x1a\f\b\xf4\v\x11\x9a\x99\x99\x99\x99\x99e@\x1a\f\b\x85\x15\x11\x00\x00\x00\x00\x00\x00\xf0\u007f", expected: model.Vector{ &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_duration_microseconds_bucket", "le": "100", }, Value: 123, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_duration_microseconds_bucket", "le": "120", }, Value: 412, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_duration_microseconds_bucket", "le": "144", }, Value: 592, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_duration_microseconds_bucket", "le": "172.8", }, Value: 1524, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_duration_microseconds_bucket", "le": "+Inf", }, Value: 2693, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_duration_microseconds_sum", }, Value: 1756047.3, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_duration_microseconds_count", }, Value: 2693, Timestamp: testTime, }, }, }, { in: "\u007f\n\x1drequest_duration_microseconds\x12\x15The response latency.\x18\x04\"E:C\b\x85\x15\x11\xcd\xcc\xccL\x8f\xcb:A\x1a\v\b{\x11\x00\x00\x00\x00\x00\x00Y@\x1a\f\b\x9c\x03\x11\x00\x00\x00\x00\x00\x00^@\x1a\f\b\xd0\x04\x11\x00\x00\x00\x00\x00\x00b@\x1a\f\b\xf4\v\x11\x9a\x99\x99\x99\x99\x99e@", expected: model.Vector{ &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_duration_microseconds_count", }, Value: 2693, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ "le": "+Inf", model.MetricNameLabel: "request_duration_microseconds_bucket", }, Value: 2693, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_duration_microseconds_sum", }, Value: 1756047.3, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ "le": "172.8", model.MetricNameLabel: "request_duration_microseconds_bucket", }, Value: 1524, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ "le": "144", model.MetricNameLabel: "request_duration_microseconds_bucket", }, Value: 592, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ "le": "120", model.MetricNameLabel: "request_duration_microseconds_bucket", }, Value: 412, Timestamp: testTime, }, &model.Sample{ Metric: model.Metric{ "le": "100", model.MetricNameLabel: "request_duration_microseconds_bucket", }, Value: 123, Timestamp: testTime, }, }, }, { // The metric type is unset in this protobuf, which needs to be handled // correctly by the decoder. in: "\x1c\n\rrequest_count\"\v\x1a\t\t\x00\x00\x00\x00\x00\x00\xf0?", expected: model.Vector{ &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "request_count", }, Value: 1, Timestamp: testTime, }, }, }, } for i, scenario := range scenarios { dec := &SampleDecoder{ Dec: &protoDecoder{r: strings.NewReader(scenario.in)}, Opts: &DecodeOptions{ Timestamp: testTime, }, } var all model.Vector for { var smpls model.Vector err := dec.Decode(&smpls) if err == io.EOF { break } if scenario.fail { if err == nil { t.Fatal("Expected error but got none") } break } if err != nil { t.Fatal(err) } all = append(all, smpls...) } sort.Sort(all) sort.Sort(scenario.expected) if !reflect.DeepEqual(all, scenario.expected) { t.Fatalf("%d. output does not match, want: %#v, got %#v", i, scenario.expected, all) } } } func testDiscriminatorHTTPHeader(t testing.TB) { var scenarios = []struct { input map[string]string output Format }{ { input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`}, output: FmtProtoDelim, }, { input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="illegal"; encoding="delimited"`}, output: FmtUnknown, }, { input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="illegal"`}, output: FmtUnknown, }, { input: map[string]string{"Content-Type": `text/plain; version=0.0.4`}, output: FmtText, }, { input: map[string]string{"Content-Type": `text/plain`}, output: FmtText, }, { input: map[string]string{"Content-Type": `text/plain; version=0.0.3`}, output: FmtUnknown, }, } for i, scenario := range scenarios { var header http.Header if len(scenario.input) > 0 { header = http.Header{} } for key, value := range scenario.input { header.Add(key, value) } actual := ResponseFormat(header) if scenario.output != actual { t.Errorf("%d. expected %s, got %s", i, scenario.output, actual) } } } func TestDiscriminatorHTTPHeader(t *testing.T) { testDiscriminatorHTTPHeader(t) } func BenchmarkDiscriminatorHTTPHeader(b *testing.B) { for i := 0; i < b.N; i++ { testDiscriminatorHTTPHeader(b) } } func TestExtractSamples(t *testing.T) { var ( goodMetricFamily1 = &dto.MetricFamily{ Name: proto.String("foo"), Help: proto.String("Help for foo."), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Counter: &dto.Counter{ Value: proto.Float64(4711), }, }, }, } goodMetricFamily2 = &dto.MetricFamily{ Name: proto.String("bar"), Help: proto.String("Help for bar."), Type: dto.MetricType_GAUGE.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Gauge: &dto.Gauge{ Value: proto.Float64(3.14), }, }, }, } badMetricFamily = &dto.MetricFamily{ Name: proto.String("bad"), Help: proto.String("Help for bad."), Type: dto.MetricType(42).Enum(), Metric: []*dto.Metric{ &dto.Metric{ Gauge: &dto.Gauge{ Value: proto.Float64(2.7), }, }, }, } opts = &DecodeOptions{ Timestamp: 42, } ) got, err := ExtractSamples(opts, goodMetricFamily1, goodMetricFamily2) if err != nil { t.Error("Unexpected error from ExtractSamples:", err) } want := model.Vector{ &model.Sample{Metric: model.Metric{model.MetricNameLabel: "foo"}, Value: 4711, Timestamp: 42}, &model.Sample{Metric: model.Metric{model.MetricNameLabel: "bar"}, Value: 3.14, Timestamp: 42}, } if !reflect.DeepEqual(got, want) { t.Errorf("unexpected samples extracted, got: %v, want: %v", got, want) } got, err = ExtractSamples(opts, goodMetricFamily1, badMetricFamily, goodMetricFamily2) if err == nil { t.Error("Expected error from ExtractSamples") } if !reflect.DeepEqual(got, want) { t.Errorf("unexpected samples extracted, got: %v, want: %v", got, want) } } golang-github-prometheus-common-0.32.1/expfmt/encode.go000066400000000000000000000121251413406232300230710ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "fmt" "io" "net/http" "github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. "github.com/matttproud/golang_protobuf_extensions/pbutil" "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg" dto "github.com/prometheus/client_model/go" ) // Encoder types encode metric families into an underlying wire protocol. type Encoder interface { Encode(*dto.MetricFamily) error } // Closer is implemented by Encoders that need to be closed to finalize // encoding. (For example, OpenMetrics needs a final `# EOF` line.) // // Note that all Encoder implementations returned from this package implement // Closer, too, even if the Close call is a no-op. This happens in preparation // for adding a Close method to the Encoder interface directly in a (mildly // breaking) release in the future. type Closer interface { Close() error } type encoderCloser struct { encode func(*dto.MetricFamily) error close func() error } func (ec encoderCloser) Encode(v *dto.MetricFamily) error { return ec.encode(v) } func (ec encoderCloser) Close() error { return ec.close() } // Negotiate returns the Content-Type based on the given Accept header. If no // appropriate accepted type is found, FmtText is returned (which is the // Prometheus text format). This function will never negotiate FmtOpenMetrics, // as the support is still experimental. To include the option to negotiate // FmtOpenMetrics, use NegotiateOpenMetrics. func Negotiate(h http.Header) Format { for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) { ver := ac.Params["version"] if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol { switch ac.Params["encoding"] { case "delimited": return FmtProtoDelim case "text": return FmtProtoText case "compact-text": return FmtProtoCompact } } if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") { return FmtText } } return FmtText } // NegotiateIncludingOpenMetrics works like Negotiate but includes // FmtOpenMetrics as an option for the result. Note that this function is // temporary and will disappear once FmtOpenMetrics is fully supported and as // such may be negotiated by the normal Negotiate function. func NegotiateIncludingOpenMetrics(h http.Header) Format { for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) { ver := ac.Params["version"] if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol { switch ac.Params["encoding"] { case "delimited": return FmtProtoDelim case "text": return FmtProtoText case "compact-text": return FmtProtoCompact } } if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") { return FmtText } if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion || ver == "") { return FmtOpenMetrics } } return FmtText } // NewEncoder returns a new encoder based on content type negotiation. All // Encoder implementations returned by NewEncoder also implement Closer, and // callers should always call the Close method. It is currently only required // for FmtOpenMetrics, but a future (breaking) release will add the Close method // to the Encoder interface directly. The current version of the Encoder // interface is kept for backwards compatibility. func NewEncoder(w io.Writer, format Format) Encoder { switch format { case FmtProtoDelim: return encoderCloser{ encode: func(v *dto.MetricFamily) error { _, err := pbutil.WriteDelimited(w, v) return err }, close: func() error { return nil }, } case FmtProtoCompact: return encoderCloser{ encode: func(v *dto.MetricFamily) error { _, err := fmt.Fprintln(w, v.String()) return err }, close: func() error { return nil }, } case FmtProtoText: return encoderCloser{ encode: func(v *dto.MetricFamily) error { _, err := fmt.Fprintln(w, proto.MarshalTextString(v)) return err }, close: func() error { return nil }, } case FmtText: return encoderCloser{ encode: func(v *dto.MetricFamily) error { _, err := MetricFamilyToText(w, v) return err }, close: func() error { return nil }, } case FmtOpenMetrics: return encoderCloser{ encode: func(v *dto.MetricFamily) error { _, err := MetricFamilyToOpenMetrics(w, v) return err }, close: func() error { _, err := FinalizeOpenMetrics(w) return err }, } } panic(fmt.Errorf("expfmt.NewEncoder: unknown format %q", format)) } golang-github-prometheus-common-0.32.1/expfmt/encode_test.go000066400000000000000000000070661413406232300241400ustar00rootroot00000000000000// Copyright 2018 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "bytes" "net/http" "testing" "github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. dto "github.com/prometheus/client_model/go" ) func TestNegotiate(t *testing.T) { acceptValuePrefix := "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily" tests := []struct { name string acceptHeaderValue string expectedFmt string }{ { name: "delimited format", acceptHeaderValue: acceptValuePrefix + ";encoding=delimited", expectedFmt: string(FmtProtoDelim), }, { name: "text format", acceptHeaderValue: acceptValuePrefix + ";encoding=text", expectedFmt: string(FmtProtoText), }, { name: "compact text format", acceptHeaderValue: acceptValuePrefix + ";encoding=compact-text", expectedFmt: string(FmtProtoCompact), }, { name: "plain text format", acceptHeaderValue: "text/plain;version=0.0.4", expectedFmt: string(FmtText), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { h := http.Header{} h.Add(hdrAccept, test.acceptHeaderValue) actualFmt := string(Negotiate(h)) if actualFmt != test.expectedFmt { t.Errorf("expected Negotiate to return format %s, but got %s instead", test.expectedFmt, actualFmt) } }) } } func TestEncode(t *testing.T) { var buff bytes.Buffer delimEncoder := NewEncoder(&buff, FmtProtoDelim) metric := &dto.MetricFamily{ Name: proto.String("foo_metric"), Type: dto.MetricType_UNTYPED.Enum(), Metric: []*dto.Metric{ { Untyped: &dto.Untyped{ Value: proto.Float64(1.234), }, }, }, } err := delimEncoder.Encode(metric) if err != nil { t.Errorf("unexpected error during encode: %s", err.Error()) } out := buff.Bytes() if len(out) == 0 { t.Errorf("expected the output bytes buffer to be non-empty") } buff.Reset() compactEncoder := NewEncoder(&buff, FmtProtoCompact) err = compactEncoder.Encode(metric) if err != nil { t.Errorf("unexpected error during encode: %s", err.Error()) } out = buff.Bytes() if len(out) == 0 { t.Errorf("expected the output bytes buffer to be non-empty") } buff.Reset() protoTextEncoder := NewEncoder(&buff, FmtProtoText) err = protoTextEncoder.Encode(metric) if err != nil { t.Errorf("unexpected error during encode: %s", err.Error()) } out = buff.Bytes() if len(out) == 0 { t.Errorf("expected the output bytes buffer to be non-empty") } buff.Reset() textEncoder := NewEncoder(&buff, FmtText) err = textEncoder.Encode(metric) if err != nil { t.Errorf("unexpected error during encode: %s", err.Error()) } out = buff.Bytes() if len(out) == 0 { t.Errorf("expected the output bytes buffer to be non-empty") } expected := "# TYPE foo_metric untyped\n" + "foo_metric 1.234\n" if string(out) != expected { t.Errorf("expected TextEncoder to return %s, but got %s instead", expected, string(out)) } } golang-github-prometheus-common-0.32.1/expfmt/expfmt.go000066400000000000000000000032401413406232300231350ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package expfmt contains tools for reading and writing Prometheus metrics. package expfmt // Format specifies the HTTP content type of the different wire protocols. type Format string // Constants to assemble the Content-Type values for the different wire protocols. const ( TextVersion = "0.0.4" ProtoType = `application/vnd.google.protobuf` ProtoProtocol = `io.prometheus.client.MetricFamily` ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";" OpenMetricsType = `application/openmetrics-text` OpenMetricsVersion = "0.0.1" // The Content-Type values for the different wire protocols. FmtUnknown Format = `` FmtText Format = `text/plain; version=` + TextVersion + `; charset=utf-8` FmtProtoDelim Format = ProtoFmt + ` encoding=delimited` FmtProtoText Format = ProtoFmt + ` encoding=text` FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text` FmtOpenMetrics Format = OpenMetricsType + `; version=` + OpenMetricsVersion + `; charset=utf-8` ) const ( hdrContentType = "Content-Type" hdrAccept = "Accept" ) golang-github-prometheus-common-0.32.1/expfmt/fuzz.go000066400000000000000000000020631413406232300226320ustar00rootroot00000000000000// Copyright 2014 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Build only when actually fuzzing // +build gofuzz package expfmt import "bytes" // Fuzz text metric parser with with github.com/dvyukov/go-fuzz: // // go-fuzz-build github.com/prometheus/common/expfmt // go-fuzz -bin expfmt-fuzz.zip -workdir fuzz // // Further input samples should go in the folder fuzz/corpus. func Fuzz(in []byte) int { parser := TextParser{} _, err := parser.TextToMetricFamilies(bytes.NewReader(in)) if err != nil { return 0 } return 1 } golang-github-prometheus-common-0.32.1/expfmt/fuzz/000077500000000000000000000000001413406232300223025ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/000077500000000000000000000000001413406232300236155ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_0000066400000000000000000000000021413406232300271430ustar00rootroot00000000000000 golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_1000066400000000000000000000002011413406232300271450ustar00rootroot00000000000000 minimal_metric 1.234 another_metric -3e3 103948 # Even that: no_labels{} 3 # HELP line for non-existing metric will be ignored. golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_2000066400000000000000000000005421413406232300271560ustar00rootroot00000000000000 # A normal comment. # # TYPE name counter name{labelname="val1",basename="basevalue"} NaN name {labelname="val2",basename="base\"v\\al\nue"} 0.23 1234567890 # HELP name two-line\n doc str\\ing # HELP name2 doc str"ing 2 # TYPE name2 gauge name2{labelname="val2" ,basename = "basevalue2" } +Inf 54321 name2{ labelname = "val1" , }-Inf golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_3000066400000000000000000000012011413406232300271500ustar00rootroot00000000000000 # TYPE my_summary summary my_summary{n1="val1",quantile="0.5"} 110 decoy -1 -2 my_summary{n1="val1",quantile="0.9"} 140 1 my_summary_count{n1="val1"} 42 # Latest timestamp wins in case of a summary. my_summary_sum{n1="val1"} 4711 2 fake_sum{n1="val1"} 2001 # TYPE another_summary summary another_summary_count{n2="val2",n1="val1"} 20 my_summary_count{n2="val2",n1="val1"} 5 5 another_summary{n1="val1",n2="val2",quantile=".3"} -1.2 my_summary_sum{n1="val2"} 08 15 my_summary{n1="val3", quantile="0.2"} 4711 my_summary{n1="val1",n2="val2",quantile="-12.34",} NaN # some # funny comments # HELP # HELP # HELP my_summary # HELP my_summary golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_4000066400000000000000000000007101413406232300271550ustar00rootroot00000000000000 # HELP request_duration_microseconds The response latency. # TYPE request_duration_microseconds histogram request_duration_microseconds_bucket{le="100"} 123 request_duration_microseconds_bucket{le="120"} 412 request_duration_microseconds_bucket{le="144"} 592 request_duration_microseconds_bucket{le="172.8"} 1524 request_duration_microseconds_bucket{le="+Inf"} 2693 request_duration_microseconds_sum 1.7560473e+06 request_duration_microseconds_count 2693 golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_0000066400000000000000000000000101413406232300303530ustar00rootroot00000000000000bla 3.14golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_1000066400000000000000000000000271413406232300303640ustar00rootroot00000000000000metric{label="\t"} 3.14golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_10000066400000000000000000000000351413406232300304430ustar00rootroot00000000000000metric{label="bla"} 3.14 2 3 golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_11000066400000000000000000000000321413406232300304410ustar00rootroot00000000000000metric{label="bla"} blubb golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_12000066400000000000000000000000451413406232300304460ustar00rootroot00000000000000 # HELP metric one # HELP metric two golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_13000066400000000000000000000000551413406232300304500ustar00rootroot00000000000000 # TYPE metric counter # TYPE metric untyped golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_14000066400000000000000000000000431413406232300304460ustar00rootroot00000000000000 metric 4.12 # TYPE metric counter golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_15000066400000000000000000000000231413406232300304450ustar00rootroot00000000000000 # TYPE metric bla golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_16000066400000000000000000000000201413406232300304430ustar00rootroot00000000000000 # TYPE met-ric golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_17000066400000000000000000000000421413406232300304500ustar00rootroot00000000000000@invalidmetric{label="bla"} 3.14 2golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_18000066400000000000000000000000241413406232300304510ustar00rootroot00000000000000{label="bla"} 3.14 2golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_19000066400000000000000000000000661413406232300304600ustar00rootroot00000000000000 # TYPE metric histogram metric_bucket{le="bla"} 3.14 golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_2000066400000000000000000000000371413406232300303660ustar00rootroot00000000000000 metric{label="new line"} 3.14 golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_3000066400000000000000000000000241413406232300303630ustar00rootroot00000000000000metric{@="bla"} 3.14golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_4000066400000000000000000000000331413406232300303640ustar00rootroot00000000000000metric{__name__="bla"} 3.14golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_5000066400000000000000000000000311413406232300303630ustar00rootroot00000000000000metric{label+="bla"} 3.14golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_6000066400000000000000000000000261413406232300303700ustar00rootroot00000000000000metric{label=bla} 3.14golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_7000066400000000000000000000000631413406232300303720ustar00rootroot00000000000000 # TYPE metric summary metric{quantile="bla"} 3.14 golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_8000066400000000000000000000000311413406232300303660ustar00rootroot00000000000000metric{label="bla"+} 3.14golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/from_test_parse_error_9000066400000000000000000000000361413406232300303740ustar00rootroot00000000000000metric{label="bla"} 3.14 2.72 golang-github-prometheus-common-0.32.1/expfmt/fuzz/corpus/minimal000066400000000000000000000000061413406232300251620ustar00rootroot00000000000000m{} 0 golang-github-prometheus-common-0.32.1/expfmt/openmetrics_create.go000066400000000000000000000321611413406232300255110ustar00rootroot00000000000000// Copyright 2020 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "bufio" "bytes" "fmt" "io" "math" "strconv" "strings" "github.com/golang/protobuf/ptypes" "github.com/prometheus/common/model" dto "github.com/prometheus/client_model/go" ) // MetricFamilyToOpenMetrics converts a MetricFamily proto message into the // OpenMetrics text format and writes the resulting lines to 'out'. It returns // the number of bytes written and any error encountered. The output will have // the same order as the input, no further sorting is performed. Furthermore, // this function assumes the input is already sanitized and does not perform any // sanity checks. If the input contains duplicate metrics or invalid metric or // label names, the conversion will result in invalid text format output. // // This function fulfills the type 'expfmt.encoder'. // // Note that OpenMetrics requires a final `# EOF` line. Since this function acts // on individual metric families, it is the responsibility of the caller to // append this line to 'out' once all metric families have been written. // Conveniently, this can be done by calling FinalizeOpenMetrics. // // The output should be fully OpenMetrics compliant. However, there are a few // missing features and peculiarities to avoid complications when switching from // Prometheus to OpenMetrics or vice versa: // // - Counters are expected to have the `_total` suffix in their metric name. In // the output, the suffix will be truncated from the `# TYPE` and `# HELP` // line. A counter with a missing `_total` suffix is not an error. However, // its type will be set to `unknown` in that case to avoid invalid OpenMetrics // output. // // - No support for the following (optional) features: `# UNIT` line, `_created` // line, info type, stateset type, gaugehistogram type. // // - The size of exemplar labels is not checked (i.e. it's possible to create // exemplars that are larger than allowed by the OpenMetrics specification). // // - The value of Counters is not checked. (OpenMetrics doesn't allow counters // with a `NaN` value.) func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int, err error) { name := in.GetName() if name == "" { return 0, fmt.Errorf("MetricFamily has no name: %s", in) } // Try the interface upgrade. If it doesn't work, we'll use a // bufio.Writer from the sync.Pool. w, ok := out.(enhancedWriter) if !ok { b := bufPool.Get().(*bufio.Writer) b.Reset(out) w = b defer func() { bErr := b.Flush() if err == nil { err = bErr } bufPool.Put(b) }() } var ( n int metricType = in.GetType() shortName = name ) if metricType == dto.MetricType_COUNTER && strings.HasSuffix(shortName, "_total") { shortName = name[:len(name)-6] } // Comments, first HELP, then TYPE. if in.Help != nil { n, err = w.WriteString("# HELP ") written += n if err != nil { return } n, err = w.WriteString(shortName) written += n if err != nil { return } err = w.WriteByte(' ') written++ if err != nil { return } n, err = writeEscapedString(w, *in.Help, true) written += n if err != nil { return } err = w.WriteByte('\n') written++ if err != nil { return } } n, err = w.WriteString("# TYPE ") written += n if err != nil { return } n, err = w.WriteString(shortName) written += n if err != nil { return } switch metricType { case dto.MetricType_COUNTER: if strings.HasSuffix(name, "_total") { n, err = w.WriteString(" counter\n") } else { n, err = w.WriteString(" unknown\n") } case dto.MetricType_GAUGE: n, err = w.WriteString(" gauge\n") case dto.MetricType_SUMMARY: n, err = w.WriteString(" summary\n") case dto.MetricType_UNTYPED: n, err = w.WriteString(" unknown\n") case dto.MetricType_HISTOGRAM: n, err = w.WriteString(" histogram\n") default: return written, fmt.Errorf("unknown metric type %s", metricType.String()) } written += n if err != nil { return } // Finally the samples, one line for each. for _, metric := range in.Metric { switch metricType { case dto.MetricType_COUNTER: if metric.Counter == nil { return written, fmt.Errorf( "expected counter in metric %s %s", name, metric, ) } // Note that we have ensured above that either the name // ends on `_total` or that the rendered type is // `unknown`. Therefore, no `_total` must be added here. n, err = writeOpenMetricsSample( w, name, "", metric, "", 0, metric.Counter.GetValue(), 0, false, metric.Counter.Exemplar, ) case dto.MetricType_GAUGE: if metric.Gauge == nil { return written, fmt.Errorf( "expected gauge in metric %s %s", name, metric, ) } n, err = writeOpenMetricsSample( w, name, "", metric, "", 0, metric.Gauge.GetValue(), 0, false, nil, ) case dto.MetricType_UNTYPED: if metric.Untyped == nil { return written, fmt.Errorf( "expected untyped in metric %s %s", name, metric, ) } n, err = writeOpenMetricsSample( w, name, "", metric, "", 0, metric.Untyped.GetValue(), 0, false, nil, ) case dto.MetricType_SUMMARY: if metric.Summary == nil { return written, fmt.Errorf( "expected summary in metric %s %s", name, metric, ) } for _, q := range metric.Summary.Quantile { n, err = writeOpenMetricsSample( w, name, "", metric, model.QuantileLabel, q.GetQuantile(), q.GetValue(), 0, false, nil, ) written += n if err != nil { return } } n, err = writeOpenMetricsSample( w, name, "_sum", metric, "", 0, metric.Summary.GetSampleSum(), 0, false, nil, ) written += n if err != nil { return } n, err = writeOpenMetricsSample( w, name, "_count", metric, "", 0, 0, metric.Summary.GetSampleCount(), true, nil, ) case dto.MetricType_HISTOGRAM: if metric.Histogram == nil { return written, fmt.Errorf( "expected histogram in metric %s %s", name, metric, ) } infSeen := false for _, b := range metric.Histogram.Bucket { n, err = writeOpenMetricsSample( w, name, "_bucket", metric, model.BucketLabel, b.GetUpperBound(), 0, b.GetCumulativeCount(), true, b.Exemplar, ) written += n if err != nil { return } if math.IsInf(b.GetUpperBound(), +1) { infSeen = true } } if !infSeen { n, err = writeOpenMetricsSample( w, name, "_bucket", metric, model.BucketLabel, math.Inf(+1), 0, metric.Histogram.GetSampleCount(), true, nil, ) written += n if err != nil { return } } n, err = writeOpenMetricsSample( w, name, "_sum", metric, "", 0, metric.Histogram.GetSampleSum(), 0, false, nil, ) written += n if err != nil { return } n, err = writeOpenMetricsSample( w, name, "_count", metric, "", 0, 0, metric.Histogram.GetSampleCount(), true, nil, ) default: return written, fmt.Errorf( "unexpected type in metric %s %s", name, metric, ) } written += n if err != nil { return } } return } // FinalizeOpenMetrics writes the final `# EOF\n` line required by OpenMetrics. func FinalizeOpenMetrics(w io.Writer) (written int, err error) { return w.Write([]byte("# EOF\n")) } // writeOpenMetricsSample writes a single sample in OpenMetrics text format to // w, given the metric name, the metric proto message itself, optionally an // additional label name with a float64 value (use empty string as label name if // not required), the value (optionally as float64 or uint64, determined by // useIntValue), and optionally an exemplar (use nil if not required). The // function returns the number of bytes written and any error encountered. func writeOpenMetricsSample( w enhancedWriter, name, suffix string, metric *dto.Metric, additionalLabelName string, additionalLabelValue float64, floatValue float64, intValue uint64, useIntValue bool, exemplar *dto.Exemplar, ) (int, error) { var written int n, err := w.WriteString(name) written += n if err != nil { return written, err } if suffix != "" { n, err = w.WriteString(suffix) written += n if err != nil { return written, err } } n, err = writeOpenMetricsLabelPairs( w, metric.Label, additionalLabelName, additionalLabelValue, ) written += n if err != nil { return written, err } err = w.WriteByte(' ') written++ if err != nil { return written, err } if useIntValue { n, err = writeUint(w, intValue) } else { n, err = writeOpenMetricsFloat(w, floatValue) } written += n if err != nil { return written, err } if metric.TimestampMs != nil { err = w.WriteByte(' ') written++ if err != nil { return written, err } // TODO(beorn7): Format this directly without converting to a float first. n, err = writeOpenMetricsFloat(w, float64(*metric.TimestampMs)/1000) written += n if err != nil { return written, err } } if exemplar != nil { n, err = writeExemplar(w, exemplar) written += n if err != nil { return written, err } } err = w.WriteByte('\n') written++ if err != nil { return written, err } return written, nil } // writeOpenMetricsLabelPairs works like writeOpenMetrics but formats the float // in OpenMetrics style. func writeOpenMetricsLabelPairs( w enhancedWriter, in []*dto.LabelPair, additionalLabelName string, additionalLabelValue float64, ) (int, error) { if len(in) == 0 && additionalLabelName == "" { return 0, nil } var ( written int separator byte = '{' ) for _, lp := range in { err := w.WriteByte(separator) written++ if err != nil { return written, err } n, err := w.WriteString(lp.GetName()) written += n if err != nil { return written, err } n, err = w.WriteString(`="`) written += n if err != nil { return written, err } n, err = writeEscapedString(w, lp.GetValue(), true) written += n if err != nil { return written, err } err = w.WriteByte('"') written++ if err != nil { return written, err } separator = ',' } if additionalLabelName != "" { err := w.WriteByte(separator) written++ if err != nil { return written, err } n, err := w.WriteString(additionalLabelName) written += n if err != nil { return written, err } n, err = w.WriteString(`="`) written += n if err != nil { return written, err } n, err = writeOpenMetricsFloat(w, additionalLabelValue) written += n if err != nil { return written, err } err = w.WriteByte('"') written++ if err != nil { return written, err } } err := w.WriteByte('}') written++ if err != nil { return written, err } return written, nil } // writeExemplar writes the provided exemplar in OpenMetrics format to w. The // function returns the number of bytes written and any error encountered. func writeExemplar(w enhancedWriter, e *dto.Exemplar) (int, error) { written := 0 n, err := w.WriteString(" # ") written += n if err != nil { return written, err } n, err = writeOpenMetricsLabelPairs(w, e.Label, "", 0) written += n if err != nil { return written, err } err = w.WriteByte(' ') written++ if err != nil { return written, err } n, err = writeOpenMetricsFloat(w, e.GetValue()) written += n if err != nil { return written, err } if e.Timestamp != nil { err = w.WriteByte(' ') written++ if err != nil { return written, err } ts, err := ptypes.Timestamp((*e).Timestamp) if err != nil { return written, err } // TODO(beorn7): Format this directly from components of ts to // avoid overflow/underflow and precision issues of the float // conversion. n, err = writeOpenMetricsFloat(w, float64(ts.UnixNano())/1e9) written += n if err != nil { return written, err } } return written, nil } // writeOpenMetricsFloat works like writeFloat but appends ".0" if the resulting // number would otherwise contain neither a "." nor an "e". func writeOpenMetricsFloat(w enhancedWriter, f float64) (int, error) { switch { case f == 1: return w.WriteString("1.0") case f == 0: return w.WriteString("0.0") case f == -1: return w.WriteString("-1.0") case math.IsNaN(f): return w.WriteString("NaN") case math.IsInf(f, +1): return w.WriteString("+Inf") case math.IsInf(f, -1): return w.WriteString("-Inf") default: bp := numBufPool.Get().(*[]byte) *bp = strconv.AppendFloat((*bp)[:0], f, 'g', -1, 64) if !bytes.ContainsAny(*bp, "e.") { *bp = append(*bp, '.', '0') } written, err := w.Write(*bp) numBufPool.Put(bp) return written, err } } // writeUint is like writeInt just for uint64. func writeUint(w enhancedWriter, u uint64) (int, error) { bp := numBufPool.Get().(*[]byte) *bp = strconv.AppendUint((*bp)[:0], u, 10) written, err := w.Write(*bp) numBufPool.Put(bp) return written, err } golang-github-prometheus-common-0.32.1/expfmt/openmetrics_create_test.go000066400000000000000000000404171413406232300265530ustar00rootroot00000000000000// Copyright 2020 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "bytes" "math" "strings" "testing" "time" "github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. "github.com/golang/protobuf/ptypes" dto "github.com/prometheus/client_model/go" ) func TestCreateOpenMetrics(t *testing.T) { openMetricsTimestamp, err := ptypes.TimestampProto(time.Unix(12345, 600000000)) if err != nil { t.Error(err) } var scenarios = []struct { in *dto.MetricFamily out string }{ // 0: Counter, timestamp given, no _total suffix. { in: &dto.MetricFamily{ Name: proto.String("name"), Help: proto.String("two-line\n doc str\\ing"), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("labelname"), Value: proto.String("val1"), }, &dto.LabelPair{ Name: proto.String("basename"), Value: proto.String("basevalue"), }, }, Counter: &dto.Counter{ Value: proto.Float64(42), }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("labelname"), Value: proto.String("val2"), }, &dto.LabelPair{ Name: proto.String("basename"), Value: proto.String("basevalue"), }, }, Counter: &dto.Counter{ Value: proto.Float64(.23), }, TimestampMs: proto.Int64(1234567890), }, }, }, out: `# HELP name two-line\n doc str\\ing # TYPE name unknown name{labelname="val1",basename="basevalue"} 42.0 name{labelname="val2",basename="basevalue"} 0.23 1.23456789e+06 `, }, // 1: Gauge, some escaping required, +Inf as value, multi-byte characters in label values. { in: &dto.MetricFamily{ Name: proto.String("gauge_name"), Help: proto.String("gauge\ndoc\nstr\"ing"), Type: dto.MetricType_GAUGE.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("val with\nnew line"), }, &dto.LabelPair{ Name: proto.String("name_2"), Value: proto.String("val with \\backslash and \"quotes\""), }, }, Gauge: &dto.Gauge{ Value: proto.Float64(math.Inf(+1)), }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("Björn"), }, &dto.LabelPair{ Name: proto.String("name_2"), Value: proto.String("ä½–ä½¥"), }, }, Gauge: &dto.Gauge{ Value: proto.Float64(3.14e42), }, }, }, }, out: `# HELP gauge_name gauge\ndoc\nstr\"ing # TYPE gauge_name gauge gauge_name{name_1="val with\nnew line",name_2="val with \\backslash and \"quotes\""} +Inf gauge_name{name_1="Björn",name_2="ä½–ä½¥"} 3.14e+42 `, }, // 2: Unknown, no help, one sample with no labels and -Inf as value, another sample with one label. { in: &dto.MetricFamily{ Name: proto.String("unknown_name"), Type: dto.MetricType_UNTYPED.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Untyped: &dto.Untyped{ Value: proto.Float64(math.Inf(-1)), }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("value 1"), }, }, Untyped: &dto.Untyped{ Value: proto.Float64(-1.23e-45), }, }, }, }, out: `# TYPE unknown_name unknown unknown_name -Inf unknown_name{name_1="value 1"} -1.23e-45 `, }, // 3: Summary. { in: &dto.MetricFamily{ Name: proto.String("summary_name"), Help: proto.String("summary docstring"), Type: dto.MetricType_SUMMARY.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Summary: &dto.Summary{ SampleCount: proto.Uint64(42), SampleSum: proto.Float64(-3.4567), Quantile: []*dto.Quantile{ &dto.Quantile{ Quantile: proto.Float64(0.5), Value: proto.Float64(-1.23), }, &dto.Quantile{ Quantile: proto.Float64(0.9), Value: proto.Float64(.2342354), }, &dto.Quantile{ Quantile: proto.Float64(0.99), Value: proto.Float64(0), }, }, }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("value 1"), }, &dto.LabelPair{ Name: proto.String("name_2"), Value: proto.String("value 2"), }, }, Summary: &dto.Summary{ SampleCount: proto.Uint64(4711), SampleSum: proto.Float64(2010.1971), Quantile: []*dto.Quantile{ &dto.Quantile{ Quantile: proto.Float64(0.5), Value: proto.Float64(1), }, &dto.Quantile{ Quantile: proto.Float64(0.9), Value: proto.Float64(2), }, &dto.Quantile{ Quantile: proto.Float64(0.99), Value: proto.Float64(3), }, }, }, }, }, }, out: `# HELP summary_name summary docstring # TYPE summary_name summary summary_name{quantile="0.5"} -1.23 summary_name{quantile="0.9"} 0.2342354 summary_name{quantile="0.99"} 0.0 summary_name_sum -3.4567 summary_name_count 42 summary_name{name_1="value 1",name_2="value 2",quantile="0.5"} 1.0 summary_name{name_1="value 1",name_2="value 2",quantile="0.9"} 2.0 summary_name{name_1="value 1",name_2="value 2",quantile="0.99"} 3.0 summary_name_sum{name_1="value 1",name_2="value 2"} 2010.1971 summary_name_count{name_1="value 1",name_2="value 2"} 4711 `, }, // 4: Histogram { in: &dto.MetricFamily{ Name: proto.String("request_duration_microseconds"), Help: proto.String("The response latency."), Type: dto.MetricType_HISTOGRAM.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Histogram: &dto.Histogram{ SampleCount: proto.Uint64(2693), SampleSum: proto.Float64(1756047.3), Bucket: []*dto.Bucket{ &dto.Bucket{ UpperBound: proto.Float64(100), CumulativeCount: proto.Uint64(123), }, &dto.Bucket{ UpperBound: proto.Float64(120), CumulativeCount: proto.Uint64(412), }, &dto.Bucket{ UpperBound: proto.Float64(144), CumulativeCount: proto.Uint64(592), }, &dto.Bucket{ UpperBound: proto.Float64(172.8), CumulativeCount: proto.Uint64(1524), }, &dto.Bucket{ UpperBound: proto.Float64(math.Inf(+1)), CumulativeCount: proto.Uint64(2693), }, }, }, }, }, }, out: `# HELP request_duration_microseconds The response latency. # TYPE request_duration_microseconds histogram request_duration_microseconds_bucket{le="100.0"} 123 request_duration_microseconds_bucket{le="120.0"} 412 request_duration_microseconds_bucket{le="144.0"} 592 request_duration_microseconds_bucket{le="172.8"} 1524 request_duration_microseconds_bucket{le="+Inf"} 2693 request_duration_microseconds_sum 1.7560473e+06 request_duration_microseconds_count 2693 `, }, // 5: Histogram with missing +Inf bucket. { in: &dto.MetricFamily{ Name: proto.String("request_duration_microseconds"), Help: proto.String("The response latency."), Type: dto.MetricType_HISTOGRAM.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Histogram: &dto.Histogram{ SampleCount: proto.Uint64(2693), SampleSum: proto.Float64(1756047.3), Bucket: []*dto.Bucket{ &dto.Bucket{ UpperBound: proto.Float64(100), CumulativeCount: proto.Uint64(123), }, &dto.Bucket{ UpperBound: proto.Float64(120), CumulativeCount: proto.Uint64(412), }, &dto.Bucket{ UpperBound: proto.Float64(144), CumulativeCount: proto.Uint64(592), }, &dto.Bucket{ UpperBound: proto.Float64(172.8), CumulativeCount: proto.Uint64(1524), }, }, }, }, }, }, out: `# HELP request_duration_microseconds The response latency. # TYPE request_duration_microseconds histogram request_duration_microseconds_bucket{le="100.0"} 123 request_duration_microseconds_bucket{le="120.0"} 412 request_duration_microseconds_bucket{le="144.0"} 592 request_duration_microseconds_bucket{le="172.8"} 1524 request_duration_microseconds_bucket{le="+Inf"} 2693 request_duration_microseconds_sum 1.7560473e+06 request_duration_microseconds_count 2693 `, }, // 6: Histogram with missing +Inf bucket but with different exemplars. { in: &dto.MetricFamily{ Name: proto.String("request_duration_microseconds"), Help: proto.String("The response latency."), Type: dto.MetricType_HISTOGRAM.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Histogram: &dto.Histogram{ SampleCount: proto.Uint64(2693), SampleSum: proto.Float64(1756047.3), Bucket: []*dto.Bucket{ &dto.Bucket{ UpperBound: proto.Float64(100), CumulativeCount: proto.Uint64(123), }, &dto.Bucket{ UpperBound: proto.Float64(120), CumulativeCount: proto.Uint64(412), Exemplar: &dto.Exemplar{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("foo"), Value: proto.String("bar"), }, }, Value: proto.Float64(119.9), Timestamp: openMetricsTimestamp, }, }, &dto.Bucket{ UpperBound: proto.Float64(144), CumulativeCount: proto.Uint64(592), Exemplar: &dto.Exemplar{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("foo"), Value: proto.String("baz"), }, &dto.LabelPair{ Name: proto.String("dings"), Value: proto.String("bums"), }, }, Value: proto.Float64(140.14), }, }, &dto.Bucket{ UpperBound: proto.Float64(172.8), CumulativeCount: proto.Uint64(1524), }, }, }, }, }, }, out: `# HELP request_duration_microseconds The response latency. # TYPE request_duration_microseconds histogram request_duration_microseconds_bucket{le="100.0"} 123 request_duration_microseconds_bucket{le="120.0"} 412 # {foo="bar"} 119.9 12345.6 request_duration_microseconds_bucket{le="144.0"} 592 # {foo="baz",dings="bums"} 140.14 request_duration_microseconds_bucket{le="172.8"} 1524 request_duration_microseconds_bucket{le="+Inf"} 2693 request_duration_microseconds_sum 1.7560473e+06 request_duration_microseconds_count 2693 `, }, // 7: Simple Counter. { in: &dto.MetricFamily{ Name: proto.String("foos_total"), Help: proto.String("Number of foos."), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Counter: &dto.Counter{ Value: proto.Float64(42), }, }, }, }, out: `# HELP foos Number of foos. # TYPE foos counter foos_total 42.0 `, }, // 8: No metric. { in: &dto.MetricFamily{ Name: proto.String("name_total"), Help: proto.String("doc string"), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{}, }, out: `# HELP name doc string # TYPE name counter `, }, } for i, scenario := range scenarios { out := bytes.NewBuffer(make([]byte, 0, len(scenario.out))) n, err := MetricFamilyToOpenMetrics(out, scenario.in) if err != nil { t.Errorf("%d. error: %s", i, err) continue } if expected, got := len(scenario.out), n; expected != got { t.Errorf( "%d. expected %d bytes written, got %d", i, expected, got, ) } if expected, got := scenario.out, out.String(); expected != got { t.Errorf( "%d. expected out=%q, got %q", i, expected, got, ) } } } func BenchmarkOpenMetricsCreate(b *testing.B) { mf := &dto.MetricFamily{ Name: proto.String("request_duration_microseconds"), Help: proto.String("The response latency."), Type: dto.MetricType_HISTOGRAM.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("val with\nnew line"), }, &dto.LabelPair{ Name: proto.String("name_2"), Value: proto.String("val with \\backslash and \"quotes\""), }, &dto.LabelPair{ Name: proto.String("name_3"), Value: proto.String("Just a quite long label value to test performance."), }, }, Histogram: &dto.Histogram{ SampleCount: proto.Uint64(2693), SampleSum: proto.Float64(1756047.3), Bucket: []*dto.Bucket{ &dto.Bucket{ UpperBound: proto.Float64(100), CumulativeCount: proto.Uint64(123), }, &dto.Bucket{ UpperBound: proto.Float64(120), CumulativeCount: proto.Uint64(412), }, &dto.Bucket{ UpperBound: proto.Float64(144), CumulativeCount: proto.Uint64(592), }, &dto.Bucket{ UpperBound: proto.Float64(172.8), CumulativeCount: proto.Uint64(1524), }, &dto.Bucket{ UpperBound: proto.Float64(math.Inf(+1)), CumulativeCount: proto.Uint64(2693), }, }, }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("Björn"), }, &dto.LabelPair{ Name: proto.String("name_2"), Value: proto.String("ä½–ä½¥"), }, &dto.LabelPair{ Name: proto.String("name_3"), Value: proto.String("Just a quite long label value to test performance."), }, }, Histogram: &dto.Histogram{ SampleCount: proto.Uint64(5699), SampleSum: proto.Float64(49484343543.4343), Bucket: []*dto.Bucket{ &dto.Bucket{ UpperBound: proto.Float64(100), CumulativeCount: proto.Uint64(120), }, &dto.Bucket{ UpperBound: proto.Float64(120), CumulativeCount: proto.Uint64(412), }, &dto.Bucket{ UpperBound: proto.Float64(144), CumulativeCount: proto.Uint64(596), }, &dto.Bucket{ UpperBound: proto.Float64(172.8), CumulativeCount: proto.Uint64(1535), }, }, }, TimestampMs: proto.Int64(1234567890), }, }, } out := bytes.NewBuffer(make([]byte, 0, 1024)) for i := 0; i < b.N; i++ { _, err := MetricFamilyToOpenMetrics(out, mf) if err != nil { b.Fatal(err) } out.Reset() } } func TestOpenMetricsCreateError(t *testing.T) { var scenarios = []struct { in *dto.MetricFamily err string }{ // 0: No metric name. { in: &dto.MetricFamily{ Help: proto.String("doc string"), Type: dto.MetricType_UNTYPED.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Untyped: &dto.Untyped{ Value: proto.Float64(math.Inf(-1)), }, }, }, }, err: "MetricFamily has no name", }, // 1: Wrong type. { in: &dto.MetricFamily{ Name: proto.String("name"), Help: proto.String("doc string"), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Untyped: &dto.Untyped{ Value: proto.Float64(math.Inf(-1)), }, }, }, }, err: "expected counter in metric", }, } for i, scenario := range scenarios { var out bytes.Buffer _, err := MetricFamilyToOpenMetrics(&out, scenario.in) if err == nil { t.Errorf("%d. expected error, got nil", i) continue } if expected, got := scenario.err, err.Error(); strings.Index(got, expected) != 0 { t.Errorf( "%d. expected error starting with %q, got %q", i, expected, got, ) } } } golang-github-prometheus-common-0.32.1/expfmt/testdata/000077500000000000000000000000001413406232300231155ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/expfmt/testdata/json2000066400000000000000000000014111413406232300240700ustar00rootroot00000000000000[ { "baseLabels": { "__name__": "rpc_calls_total", "job": "batch_job" }, "docstring": "RPC calls.", "metric": { "type": "counter", "value": [ { "labels": { "service": "zed" }, "value": 25 }, { "labels": { "service": "bar" }, "value": 24 } ] } }, { "baseLabels": { "__name__": "rpc_latency_microseconds" }, "docstring": "RPC latency.", "metric": { "type": "histogram", "value": [ { "labels": { "service": "foo" }, "value": { "0.010000": 15, "0.990000": 17 } } ] } } ] golang-github-prometheus-common-0.32.1/expfmt/testdata/json2_bad000066400000000000000000000014121413406232300246770ustar00rootroot00000000000000[ { "baseLabels": { "__name__": "rpc_calls_total", "job": "batch_job" }, "docstring": "RPC calls.", "metric": { "type": "counter", "value": [ { "labels": { "servic|e": "zed" }, "value": 25 }, { "labels": { "service": "bar" }, "value": 24 } ] } }, { "baseLabels": { "__name__": "rpc_latency_microseconds" }, "docstring": "RPC latency.", "metric": { "type": "histogram", "value": [ { "labels": { "service": "foo" }, "value": { "0.010000": 15, "0.990000": 17 } } ] } } ] golang-github-prometheus-common-0.32.1/expfmt/testdata/protobuf000066400000000000000000000200571413406232300247040ustar00rootroot00000000000000L process_virtual_memory_bytesVirtual memory size in bytes."  (#ÀA£ 9prometheus_local_storage_checkpoint_duration_millisecondsWThe duration (in milliseconds) it took to checkpoint in-memory metrics and head chunks."  ¸ 5prometheus_local_storage_persist_latency_microseconds1A summary of latencies for persisting each chunk."J"H®ö(\òÓ@ à?—nƒ`>@ ÍÌÌÌÌÌì?h‘í|?qi@ ®Gáz®ï?V턤@j /prometheus_local_storage_persist_queue_capacity(The total capacity of the persist queue."  @Ö -prometheus_notifications_latency_millisecondsXLatency quantiles for sending alert notifications (not including dropped notifications)."I"G à? ÍÌÌÌÌÌì? ®Gáz®ï?\ process_cpu_seconds_total0Total user and system CPU time spent in seconds."  š™™™™™á?| http_requests_total#Total number of HTTP requests made."> code200  handler prometheus methodget À]@g /prometheus_local_storage_ingested_samples_total%The total number of samples ingested."  @ÂÝ@o *prometheus_local_storage_memory_chunkdescs2The current number of chunk descriptors in memory."  Œ@` &prometheus_local_storage_memory_series'The current number of series in memory."  €z@` !prometheus_samples_queue_capacity,Capacity of the queue for unwritten samples."  °@Ú prometheus_samples_queue_length§Current number of items in the queue for unwritten samples. Each item comprises all samples exposed by one target as one metric family (i.e. metrics of the same name)."  ü "http_request_duration_microseconds+The HTTP request latencies in microseconds."W handler/"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"]  handler/alerts"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"b  handler /api/metrics"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"`  handler /api/query"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"f  handler/api/query_range"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"b  handler /api/targets"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"`  handler /consoles/"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"\  handler/graph"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"[  handler/heap"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"^  handler/static/"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"`  handler prometheus"GwtõA à?š™™™m”@ ÍÌÌÌÌÌì?J +‡ @ ®Gáz®ï?T㥛Ĩ@J process_max_fds(Maximum number of open file descriptors."  À@æ http_request_size_bytes The HTTP request sizes in bytes."W handler/"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"]  handler/alerts"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"b  handler /api/metrics"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"`  handler /api/query"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"f  handler/api/query_range"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"b  handler /api/targets"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"`  handler /consoles/"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"\  handler/graph"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"[  handler/heap"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"^  handler/static/"G à? ÍÌÌÌÌÌì? ®Gáz®ï?"`  handler prometheus"Gw×à@ à?0r@ ÍÌÌÌÌÌì?0r@ ®Gáz®ï?0r@O prometheus_dns_sd_lookups_totalThe number of DNS-SD lookups."  @¿ -prometheus_local_storage_indexing_batch_sizesAQuantiles for indexing batch sizes (number of metrics per batch)."I"G@ à?@ ÍÌÌÌÌÌì?@ ®Gáz®ï?@m .prometheus_local_storage_indexing_queue_length,The number of metrics waiting to be indexed."  g %prometheus_notifications_queue_length/The number of alert notifications in the queue."  N process_resident_memory_bytesResident memory size in bytes."  °‰Ah 'prometheus_notifications_queue_capacity.The capacity of the alert notifications queue."  Y@¬ )prometheus_local_storage_series_ops_total4The total number of series operations by their type."  typecreate @"*  typemaintenance_in_memory &@C process_open_fds Number of open file descriptors."  =@ò (prometheus_local_storage_chunk_ops_total3The total number of chunk operations by their type."  typecreate °‚@"  typepersist Àe@" typepin ÀŒ@"  type transcode ðy@" typeunpin ÀŒ@Ä ¾Ø!8ŸÂuRž>Š©À"ŠÎÁ\ð]f3Õ@,£.ÇNèáq< JÂsš’ò÷KÝ=Rɘ0N¹°}GÐí‚‚.‹r/`ž²¢NI(á¸1œ-¤aÇí¤²Scµ“µ«—ŒÉÕϼ0ýáï¬i3Säêüäó? Íïž½OŽýömùûûüdç<5žÊ±K¼wa÷¥ÌOîÝôØÎžý¹õ-Ô\Sñó IÀÖNì¸Ttͺ´©ˆ`>&u¹ÆX­Ðô¢õÍ–d…‘ -ꪓá%ã”Nïk§£ÀÏ õ3»pzÒ(ŽO˜À\‡O8?×O‰Ç¢8&žNtTÚð¡ÚÕÊd~€e–Æz&,õL˜A{swwãÄΔ·•…̹Ee¨„¦œˆw¹ >ñÈW± Á<&Êáp¶¬1U©m›ž˜øÉåï½ù§Ð­!b›°)9ÛC)Û0 –€1XÿÁÅÅGpN„Ç#’Ó}hÚäF1Çïš›C·¡­PÌf¢â$дEþyæx›ˆéìÄ®O¡\LIz™‘›mÐ éX¿É cj¬­¿–+ÏèöÉ\v¨*¿E„—Hº-Y½èùjYÖjBšºaåk93ï—s íÏ##œzÙë}•_Ɇ×l¬T.¡ƒîXCí^¥ÒPUP_µ2Lý!zÝzÃ@GGGZIÙQÏS?=´€Lë-ˆº,“e°£ ‹n¬JÝÛÑN´I~˜[\F è9tŪCû³ÉÛ‡†P&8¡+}!3`öˆuíÊ­,ƒ¼Jð©øïµ>4P}•žÔ©¥ j؆Óbö†ö{ù«¶Ý’MnÍZ2½ªžXµ)ÙP³ã1 {3ÏYµ€âéÌ„€0Çeë Ÿ¿ÖµjÓhG6¿9 õµoèó× C›=÷è’BGIúj»bÝrÜX·ÐÜõ"Gïvv;zйïZÓ®Dïmé˜ìI˜âEÞI2.¸ª]šÏƒ7±LdµîBœM“Åg 8ójNbŽ¿›×¢êötgW‰ÄÉoï–û}6ßíŒKûL¯kc}Ök ÁÝÆÜmXµïà•…cÇ¥I:ÿô‡è N¡O‡ë=Ákóƒ:\›?‰v´#»±(èT¹¦´%œ¯ÑÓ”Jåý*šÊÊ+ÃÖI¹¾:Xª¯ÒÙë#ÉØ(°nÂZÖÿUõÖ‘ Æ.ƵÝã¾hýÆ@÷¬’TQÏŽñ#Çë©éƒ¸Œ¶x°a¤EËJ ²êpV…$™Ì§Éo/v(À—|‹”ÙE–s«œ4¤G$IKÄÏÊ”¸|ÉXûÕ#}Ý4ÝU^XJ¯3iò‡6×B’?fÿìc™Š¨Û*Rf½éõëÂ*%§þ™SŸ[UǼpËê©UÞ%+•WÆ´¤¯?d ćq¸ôø+]sBqTYsUEP \“—Ãò:¿UsÐ'Ê&(È<]Dã&DT-[+µöBÜÿ|ÿî[f,Iéý³òîÿ"Î1–!€îvY¨(¯3fŽë Bø‡U‹uSÑX¨§¤±ÔPrY*i„îâŠäç3eeÝ@Ô).e'¹låíÚjlL1CËòr™L–"«˜HÜÐ-Ø„@ Ê0;ÒÛ‰¬â["µ’o©à’t©ìF4–Gz‹Ì<Åe)ºškà-á±Í(:`ø¹ÊSZ-/&˜,Džb²”øJg)°)Az,Ãâ´XZ‹…¬*KI*K9MÀ؉†Åb%Ç$B*H,uL–2`;¨ŠÁíhùjWòXh©æ±”³%³TÔ ~eE܉֊½ÊÝ8ûÕzïÝ„Ëâ¼Ãj·Y߆3nœÃ~µ9lõî°B\{ÿpØ­½¹ËÎEv ‰ê²„PÒ ¡/IÅÞâèãa}¤wýk{g¨Â9| žãŠü3|IÎõ¹ãisè.y†#1­¢2)X^ŽûÜâ›§ ¯\=|"ç7?>ê ØÖÛåõhˆa–$1(œé9‡â§q³ÿ–WIsU¸á±žç臠nã­6‡½ãùkúg? VuÉ>¯Ú A¾ðKÍTM`æ¶!Â,¢¤Î’B‹<:ÍMBq?XEwµ]¯7»ƒ¿=lvíHÈ?“’ÕaÚïß›‹8• ýÀ”M¼ÕÖÝî¾ËUÖoA0v¾L“ÉTK£FX Õ(O®ñYöÑêžmí:Øð•†G¶ÌTFÑOe¦™¶ª¸Ë:¥Áõ}xO8PsfãV¬ðU´jò‹ÖµêåþíyG‹åV¼£ ±xí­"ÿkpTck^…OðõKã~—p©Œÿ†š ^>RSÄâ7mÕ•´5—vhMu;¦Ñ'öL†TÃ‘çƒæ-÷æk¾ýÝÿ`#k7 Rgolang-github-prometheus-common-0.32.1/expfmt/text_create.go000066400000000000000000000255771413406232300241620ustar00rootroot00000000000000// Copyright 2014 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "bufio" "fmt" "io" "io/ioutil" "math" "strconv" "strings" "sync" "github.com/prometheus/common/model" dto "github.com/prometheus/client_model/go" ) // enhancedWriter has all the enhanced write functions needed here. bufio.Writer // implements it. type enhancedWriter interface { io.Writer WriteRune(r rune) (n int, err error) WriteString(s string) (n int, err error) WriteByte(c byte) error } const ( initialNumBufSize = 24 ) var ( bufPool = sync.Pool{ New: func() interface{} { return bufio.NewWriter(ioutil.Discard) }, } numBufPool = sync.Pool{ New: func() interface{} { b := make([]byte, 0, initialNumBufSize) return &b }, } ) // MetricFamilyToText converts a MetricFamily proto message into text format and // writes the resulting lines to 'out'. It returns the number of bytes written // and any error encountered. The output will have the same order as the input, // no further sorting is performed. Furthermore, this function assumes the input // is already sanitized and does not perform any sanity checks. If the input // contains duplicate metrics or invalid metric or label names, the conversion // will result in invalid text format output. // // This method fulfills the type 'prometheus.encoder'. func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err error) { // Fail-fast checks. if len(in.Metric) == 0 { return 0, fmt.Errorf("MetricFamily has no metrics: %s", in) } name := in.GetName() if name == "" { return 0, fmt.Errorf("MetricFamily has no name: %s", in) } // Try the interface upgrade. If it doesn't work, we'll use a // bufio.Writer from the sync.Pool. w, ok := out.(enhancedWriter) if !ok { b := bufPool.Get().(*bufio.Writer) b.Reset(out) w = b defer func() { bErr := b.Flush() if err == nil { err = bErr } bufPool.Put(b) }() } var n int // Comments, first HELP, then TYPE. if in.Help != nil { n, err = w.WriteString("# HELP ") written += n if err != nil { return } n, err = w.WriteString(name) written += n if err != nil { return } err = w.WriteByte(' ') written++ if err != nil { return } n, err = writeEscapedString(w, *in.Help, false) written += n if err != nil { return } err = w.WriteByte('\n') written++ if err != nil { return } } n, err = w.WriteString("# TYPE ") written += n if err != nil { return } n, err = w.WriteString(name) written += n if err != nil { return } metricType := in.GetType() switch metricType { case dto.MetricType_COUNTER: n, err = w.WriteString(" counter\n") case dto.MetricType_GAUGE: n, err = w.WriteString(" gauge\n") case dto.MetricType_SUMMARY: n, err = w.WriteString(" summary\n") case dto.MetricType_UNTYPED: n, err = w.WriteString(" untyped\n") case dto.MetricType_HISTOGRAM: n, err = w.WriteString(" histogram\n") default: return written, fmt.Errorf("unknown metric type %s", metricType.String()) } written += n if err != nil { return } // Finally the samples, one line for each. for _, metric := range in.Metric { switch metricType { case dto.MetricType_COUNTER: if metric.Counter == nil { return written, fmt.Errorf( "expected counter in metric %s %s", name, metric, ) } n, err = writeSample( w, name, "", metric, "", 0, metric.Counter.GetValue(), ) case dto.MetricType_GAUGE: if metric.Gauge == nil { return written, fmt.Errorf( "expected gauge in metric %s %s", name, metric, ) } n, err = writeSample( w, name, "", metric, "", 0, metric.Gauge.GetValue(), ) case dto.MetricType_UNTYPED: if metric.Untyped == nil { return written, fmt.Errorf( "expected untyped in metric %s %s", name, metric, ) } n, err = writeSample( w, name, "", metric, "", 0, metric.Untyped.GetValue(), ) case dto.MetricType_SUMMARY: if metric.Summary == nil { return written, fmt.Errorf( "expected summary in metric %s %s", name, metric, ) } for _, q := range metric.Summary.Quantile { n, err = writeSample( w, name, "", metric, model.QuantileLabel, q.GetQuantile(), q.GetValue(), ) written += n if err != nil { return } } n, err = writeSample( w, name, "_sum", metric, "", 0, metric.Summary.GetSampleSum(), ) written += n if err != nil { return } n, err = writeSample( w, name, "_count", metric, "", 0, float64(metric.Summary.GetSampleCount()), ) case dto.MetricType_HISTOGRAM: if metric.Histogram == nil { return written, fmt.Errorf( "expected histogram in metric %s %s", name, metric, ) } infSeen := false for _, b := range metric.Histogram.Bucket { n, err = writeSample( w, name, "_bucket", metric, model.BucketLabel, b.GetUpperBound(), float64(b.GetCumulativeCount()), ) written += n if err != nil { return } if math.IsInf(b.GetUpperBound(), +1) { infSeen = true } } if !infSeen { n, err = writeSample( w, name, "_bucket", metric, model.BucketLabel, math.Inf(+1), float64(metric.Histogram.GetSampleCount()), ) written += n if err != nil { return } } n, err = writeSample( w, name, "_sum", metric, "", 0, metric.Histogram.GetSampleSum(), ) written += n if err != nil { return } n, err = writeSample( w, name, "_count", metric, "", 0, float64(metric.Histogram.GetSampleCount()), ) default: return written, fmt.Errorf( "unexpected type in metric %s %s", name, metric, ) } written += n if err != nil { return } } return } // writeSample writes a single sample in text format to w, given the metric // name, the metric proto message itself, optionally an additional label name // with a float64 value (use empty string as label name if not required), and // the value. The function returns the number of bytes written and any error // encountered. func writeSample( w enhancedWriter, name, suffix string, metric *dto.Metric, additionalLabelName string, additionalLabelValue float64, value float64, ) (int, error) { var written int n, err := w.WriteString(name) written += n if err != nil { return written, err } if suffix != "" { n, err = w.WriteString(suffix) written += n if err != nil { return written, err } } n, err = writeLabelPairs( w, metric.Label, additionalLabelName, additionalLabelValue, ) written += n if err != nil { return written, err } err = w.WriteByte(' ') written++ if err != nil { return written, err } n, err = writeFloat(w, value) written += n if err != nil { return written, err } if metric.TimestampMs != nil { err = w.WriteByte(' ') written++ if err != nil { return written, err } n, err = writeInt(w, *metric.TimestampMs) written += n if err != nil { return written, err } } err = w.WriteByte('\n') written++ if err != nil { return written, err } return written, nil } // writeLabelPairs converts a slice of LabelPair proto messages plus the // explicitly given additional label pair into text formatted as required by the // text format and writes it to 'w'. An empty slice in combination with an empty // string 'additionalLabelName' results in nothing being written. Otherwise, the // label pairs are written, escaped as required by the text format, and enclosed // in '{...}'. The function returns the number of bytes written and any error // encountered. func writeLabelPairs( w enhancedWriter, in []*dto.LabelPair, additionalLabelName string, additionalLabelValue float64, ) (int, error) { if len(in) == 0 && additionalLabelName == "" { return 0, nil } var ( written int separator byte = '{' ) for _, lp := range in { err := w.WriteByte(separator) written++ if err != nil { return written, err } n, err := w.WriteString(lp.GetName()) written += n if err != nil { return written, err } n, err = w.WriteString(`="`) written += n if err != nil { return written, err } n, err = writeEscapedString(w, lp.GetValue(), true) written += n if err != nil { return written, err } err = w.WriteByte('"') written++ if err != nil { return written, err } separator = ',' } if additionalLabelName != "" { err := w.WriteByte(separator) written++ if err != nil { return written, err } n, err := w.WriteString(additionalLabelName) written += n if err != nil { return written, err } n, err = w.WriteString(`="`) written += n if err != nil { return written, err } n, err = writeFloat(w, additionalLabelValue) written += n if err != nil { return written, err } err = w.WriteByte('"') written++ if err != nil { return written, err } } err := w.WriteByte('}') written++ if err != nil { return written, err } return written, nil } // writeEscapedString replaces '\' by '\\', new line character by '\n', and - if // includeDoubleQuote is true - '"' by '\"'. var ( escaper = strings.NewReplacer("\\", `\\`, "\n", `\n`) quotedEscaper = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`) ) func writeEscapedString(w enhancedWriter, v string, includeDoubleQuote bool) (int, error) { if includeDoubleQuote { return quotedEscaper.WriteString(w, v) } return escaper.WriteString(w, v) } // writeFloat is equivalent to fmt.Fprint with a float64 argument but hardcodes // a few common cases for increased efficiency. For non-hardcoded cases, it uses // strconv.AppendFloat to avoid allocations, similar to writeInt. func writeFloat(w enhancedWriter, f float64) (int, error) { switch { case f == 1: return 1, w.WriteByte('1') case f == 0: return 1, w.WriteByte('0') case f == -1: return w.WriteString("-1") case math.IsNaN(f): return w.WriteString("NaN") case math.IsInf(f, +1): return w.WriteString("+Inf") case math.IsInf(f, -1): return w.WriteString("-Inf") default: bp := numBufPool.Get().(*[]byte) *bp = strconv.AppendFloat((*bp)[:0], f, 'g', -1, 64) written, err := w.Write(*bp) numBufPool.Put(bp) return written, err } } // writeInt is equivalent to fmt.Fprint with an int64 argument but uses // strconv.AppendInt with a byte slice taken from a sync.Pool to avoid // allocations. func writeInt(w enhancedWriter, i int64) (int, error) { bp := numBufPool.Get().(*[]byte) *bp = strconv.AppendInt((*bp)[:0], i, 10) written, err := w.Write(*bp) numBufPool.Put(bp) return written, err } golang-github-prometheus-common-0.32.1/expfmt/text_create_test.go000066400000000000000000000353631413406232300252130ustar00rootroot00000000000000// Copyright 2014 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "bytes" "math" "strings" "testing" "github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. dto "github.com/prometheus/client_model/go" ) func TestCreate(t *testing.T) { var scenarios = []struct { in *dto.MetricFamily out string }{ // 0: Counter, NaN as value, timestamp given. { in: &dto.MetricFamily{ Name: proto.String("name"), Help: proto.String("two-line\n doc str\\ing"), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("labelname"), Value: proto.String("val1"), }, &dto.LabelPair{ Name: proto.String("basename"), Value: proto.String("basevalue"), }, }, Counter: &dto.Counter{ Value: proto.Float64(math.NaN()), }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("labelname"), Value: proto.String("val2"), }, &dto.LabelPair{ Name: proto.String("basename"), Value: proto.String("basevalue"), }, }, Counter: &dto.Counter{ Value: proto.Float64(.23), }, TimestampMs: proto.Int64(1234567890), }, }, }, out: `# HELP name two-line\n doc str\\ing # TYPE name counter name{labelname="val1",basename="basevalue"} NaN name{labelname="val2",basename="basevalue"} 0.23 1234567890 `, }, // 1: Gauge, some escaping required, +Inf as value, multi-byte characters in label values. { in: &dto.MetricFamily{ Name: proto.String("gauge_name"), Help: proto.String("gauge\ndoc\nstr\"ing"), Type: dto.MetricType_GAUGE.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("val with\nnew line"), }, &dto.LabelPair{ Name: proto.String("name_2"), Value: proto.String("val with \\backslash and \"quotes\""), }, }, Gauge: &dto.Gauge{ Value: proto.Float64(math.Inf(+1)), }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("Björn"), }, &dto.LabelPair{ Name: proto.String("name_2"), Value: proto.String("ä½–ä½¥"), }, }, Gauge: &dto.Gauge{ Value: proto.Float64(3.14e42), }, }, }, }, out: `# HELP gauge_name gauge\ndoc\nstr"ing # TYPE gauge_name gauge gauge_name{name_1="val with\nnew line",name_2="val with \\backslash and \"quotes\""} +Inf gauge_name{name_1="Björn",name_2="ä½–ä½¥"} 3.14e+42 `, }, // 2: Untyped, no help, one sample with no labels and -Inf as value, another sample with one label. { in: &dto.MetricFamily{ Name: proto.String("untyped_name"), Type: dto.MetricType_UNTYPED.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Untyped: &dto.Untyped{ Value: proto.Float64(math.Inf(-1)), }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("value 1"), }, }, Untyped: &dto.Untyped{ Value: proto.Float64(-1.23e-45), }, }, }, }, out: `# TYPE untyped_name untyped untyped_name -Inf untyped_name{name_1="value 1"} -1.23e-45 `, }, // 3: Summary. { in: &dto.MetricFamily{ Name: proto.String("summary_name"), Help: proto.String("summary docstring"), Type: dto.MetricType_SUMMARY.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Summary: &dto.Summary{ SampleCount: proto.Uint64(42), SampleSum: proto.Float64(-3.4567), Quantile: []*dto.Quantile{ &dto.Quantile{ Quantile: proto.Float64(0.5), Value: proto.Float64(-1.23), }, &dto.Quantile{ Quantile: proto.Float64(0.9), Value: proto.Float64(.2342354), }, &dto.Quantile{ Quantile: proto.Float64(0.99), Value: proto.Float64(0), }, }, }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("value 1"), }, &dto.LabelPair{ Name: proto.String("name_2"), Value: proto.String("value 2"), }, }, Summary: &dto.Summary{ SampleCount: proto.Uint64(4711), SampleSum: proto.Float64(2010.1971), Quantile: []*dto.Quantile{ &dto.Quantile{ Quantile: proto.Float64(0.5), Value: proto.Float64(1), }, &dto.Quantile{ Quantile: proto.Float64(0.9), Value: proto.Float64(2), }, &dto.Quantile{ Quantile: proto.Float64(0.99), Value: proto.Float64(3), }, }, }, }, }, }, out: `# HELP summary_name summary docstring # TYPE summary_name summary summary_name{quantile="0.5"} -1.23 summary_name{quantile="0.9"} 0.2342354 summary_name{quantile="0.99"} 0 summary_name_sum -3.4567 summary_name_count 42 summary_name{name_1="value 1",name_2="value 2",quantile="0.5"} 1 summary_name{name_1="value 1",name_2="value 2",quantile="0.9"} 2 summary_name{name_1="value 1",name_2="value 2",quantile="0.99"} 3 summary_name_sum{name_1="value 1",name_2="value 2"} 2010.1971 summary_name_count{name_1="value 1",name_2="value 2"} 4711 `, }, // 4: Histogram { in: &dto.MetricFamily{ Name: proto.String("request_duration_microseconds"), Help: proto.String("The response latency."), Type: dto.MetricType_HISTOGRAM.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Histogram: &dto.Histogram{ SampleCount: proto.Uint64(2693), SampleSum: proto.Float64(1756047.3), Bucket: []*dto.Bucket{ &dto.Bucket{ UpperBound: proto.Float64(100), CumulativeCount: proto.Uint64(123), }, &dto.Bucket{ UpperBound: proto.Float64(120), CumulativeCount: proto.Uint64(412), }, &dto.Bucket{ UpperBound: proto.Float64(144), CumulativeCount: proto.Uint64(592), }, &dto.Bucket{ UpperBound: proto.Float64(172.8), CumulativeCount: proto.Uint64(1524), }, &dto.Bucket{ UpperBound: proto.Float64(math.Inf(+1)), CumulativeCount: proto.Uint64(2693), }, }, }, }, }, }, out: `# HELP request_duration_microseconds The response latency. # TYPE request_duration_microseconds histogram request_duration_microseconds_bucket{le="100"} 123 request_duration_microseconds_bucket{le="120"} 412 request_duration_microseconds_bucket{le="144"} 592 request_duration_microseconds_bucket{le="172.8"} 1524 request_duration_microseconds_bucket{le="+Inf"} 2693 request_duration_microseconds_sum 1.7560473e+06 request_duration_microseconds_count 2693 `, }, // 5: Histogram with missing +Inf bucket. { in: &dto.MetricFamily{ Name: proto.String("request_duration_microseconds"), Help: proto.String("The response latency."), Type: dto.MetricType_HISTOGRAM.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Histogram: &dto.Histogram{ SampleCount: proto.Uint64(2693), SampleSum: proto.Float64(1756047.3), Bucket: []*dto.Bucket{ &dto.Bucket{ UpperBound: proto.Float64(100), CumulativeCount: proto.Uint64(123), }, &dto.Bucket{ UpperBound: proto.Float64(120), CumulativeCount: proto.Uint64(412), }, &dto.Bucket{ UpperBound: proto.Float64(144), CumulativeCount: proto.Uint64(592), }, &dto.Bucket{ UpperBound: proto.Float64(172.8), CumulativeCount: proto.Uint64(1524), }, }, }, }, }, }, out: `# HELP request_duration_microseconds The response latency. # TYPE request_duration_microseconds histogram request_duration_microseconds_bucket{le="100"} 123 request_duration_microseconds_bucket{le="120"} 412 request_duration_microseconds_bucket{le="144"} 592 request_duration_microseconds_bucket{le="172.8"} 1524 request_duration_microseconds_bucket{le="+Inf"} 2693 request_duration_microseconds_sum 1.7560473e+06 request_duration_microseconds_count 2693 `, }, // 6: No metric type, should result in default type Counter. { in: &dto.MetricFamily{ Name: proto.String("name"), Help: proto.String("doc string"), Metric: []*dto.Metric{ &dto.Metric{ Counter: &dto.Counter{ Value: proto.Float64(math.Inf(-1)), }, }, }, }, out: `# HELP name doc string # TYPE name counter name -Inf `, }, } for i, scenario := range scenarios { out := bytes.NewBuffer(make([]byte, 0, len(scenario.out))) n, err := MetricFamilyToText(out, scenario.in) if err != nil { t.Errorf("%d. error: %s", i, err) continue } if expected, got := len(scenario.out), n; expected != got { t.Errorf( "%d. expected %d bytes written, got %d", i, expected, got, ) } if expected, got := scenario.out, out.String(); expected != got { t.Errorf( "%d. expected out=%q, got %q", i, expected, got, ) } } } func BenchmarkCreate(b *testing.B) { mf := &dto.MetricFamily{ Name: proto.String("request_duration_microseconds"), Help: proto.String("The response latency."), Type: dto.MetricType_HISTOGRAM.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("val with\nnew line"), }, &dto.LabelPair{ Name: proto.String("name_2"), Value: proto.String("val with \\backslash and \"quotes\""), }, &dto.LabelPair{ Name: proto.String("name_3"), Value: proto.String("Just a quite long label value to test performance."), }, }, Histogram: &dto.Histogram{ SampleCount: proto.Uint64(2693), SampleSum: proto.Float64(1756047.3), Bucket: []*dto.Bucket{ &dto.Bucket{ UpperBound: proto.Float64(100), CumulativeCount: proto.Uint64(123), }, &dto.Bucket{ UpperBound: proto.Float64(120), CumulativeCount: proto.Uint64(412), }, &dto.Bucket{ UpperBound: proto.Float64(144), CumulativeCount: proto.Uint64(592), }, &dto.Bucket{ UpperBound: proto.Float64(172.8), CumulativeCount: proto.Uint64(1524), }, &dto.Bucket{ UpperBound: proto.Float64(math.Inf(+1)), CumulativeCount: proto.Uint64(2693), }, }, }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("name_1"), Value: proto.String("Björn"), }, &dto.LabelPair{ Name: proto.String("name_2"), Value: proto.String("ä½–ä½¥"), }, &dto.LabelPair{ Name: proto.String("name_3"), Value: proto.String("Just a quite long label value to test performance."), }, }, Histogram: &dto.Histogram{ SampleCount: proto.Uint64(5699), SampleSum: proto.Float64(49484343543.4343), Bucket: []*dto.Bucket{ &dto.Bucket{ UpperBound: proto.Float64(100), CumulativeCount: proto.Uint64(120), }, &dto.Bucket{ UpperBound: proto.Float64(120), CumulativeCount: proto.Uint64(412), }, &dto.Bucket{ UpperBound: proto.Float64(144), CumulativeCount: proto.Uint64(596), }, &dto.Bucket{ UpperBound: proto.Float64(172.8), CumulativeCount: proto.Uint64(1535), }, }, }, TimestampMs: proto.Int64(1234567890), }, }, } out := bytes.NewBuffer(make([]byte, 0, 1024)) for i := 0; i < b.N; i++ { _, err := MetricFamilyToText(out, mf) if err != nil { b.Fatal(err) } out.Reset() } } func BenchmarkCreateBuildInfo(b *testing.B) { mf := &dto.MetricFamily{ Name: proto.String("benchmark_build_info"), Help: proto.String("Test the creation of constant 1-value build_info metric."), Type: dto.MetricType_GAUGE.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("version"), Value: proto.String("1.2.3"), }, &dto.LabelPair{ Name: proto.String("revision"), Value: proto.String("2e84f5e4eacdffb574035810305191ff390360fe"), }, &dto.LabelPair{ Name: proto.String("go_version"), Value: proto.String("1.11.1"), }, }, Gauge: &dto.Gauge{ Value: proto.Float64(1), }, }, }, } out := bytes.NewBuffer(make([]byte, 0, 1024)) for i := 0; i < b.N; i++ { _, err := MetricFamilyToText(out, mf) if err != nil { b.Fatal(err) } out.Reset() } } func TestCreateError(t *testing.T) { var scenarios = []struct { in *dto.MetricFamily err string }{ // 0: No metric. { in: &dto.MetricFamily{ Name: proto.String("name"), Help: proto.String("doc string"), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{}, }, err: "MetricFamily has no metrics", }, // 1: No metric name. { in: &dto.MetricFamily{ Help: proto.String("doc string"), Type: dto.MetricType_UNTYPED.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Untyped: &dto.Untyped{ Value: proto.Float64(math.Inf(-1)), }, }, }, }, err: "MetricFamily has no name", }, // 2: Wrong type. { in: &dto.MetricFamily{ Name: proto.String("name"), Help: proto.String("doc string"), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Untyped: &dto.Untyped{ Value: proto.Float64(math.Inf(-1)), }, }, }, }, err: "expected counter in metric", }, } for i, scenario := range scenarios { var out bytes.Buffer _, err := MetricFamilyToText(&out, scenario.in) if err == nil { t.Errorf("%d. expected error, got nil", i) continue } if expected, got := scenario.err, err.Error(); strings.Index(got, expected) != 0 { t.Errorf( "%d. expected error starting with %q, got %q", i, expected, got, ) } } } golang-github-prometheus-common-0.32.1/expfmt/text_parse.go000066400000000000000000000625161413406232300240230ustar00rootroot00000000000000// Copyright 2014 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "bufio" "bytes" "fmt" "io" "math" "strconv" "strings" dto "github.com/prometheus/client_model/go" "github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. "github.com/prometheus/common/model" ) // A stateFn is a function that represents a state in a state machine. By // executing it, the state is progressed to the next state. The stateFn returns // another stateFn, which represents the new state. The end state is represented // by nil. type stateFn func() stateFn // ParseError signals errors while parsing the simple and flat text-based // exchange format. type ParseError struct { Line int Msg string } // Error implements the error interface. func (e ParseError) Error() string { return fmt.Sprintf("text format parsing error in line %d: %s", e.Line, e.Msg) } // TextParser is used to parse the simple and flat text-based exchange format. Its // zero value is ready to use. type TextParser struct { metricFamiliesByName map[string]*dto.MetricFamily buf *bufio.Reader // Where the parsed input is read through. err error // Most recent error. lineCount int // Tracks the line count for error messages. currentByte byte // The most recent byte read. currentToken bytes.Buffer // Re-used each time a token has to be gathered from multiple bytes. currentMF *dto.MetricFamily currentMetric *dto.Metric currentLabelPair *dto.LabelPair // The remaining member variables are only used for summaries/histograms. currentLabels map[string]string // All labels including '__name__' but excluding 'quantile'/'le' // Summary specific. summaries map[uint64]*dto.Metric // Key is created with LabelsToSignature. currentQuantile float64 // Histogram specific. histograms map[uint64]*dto.Metric // Key is created with LabelsToSignature. currentBucket float64 // These tell us if the currently processed line ends on '_count' or // '_sum' respectively and belong to a summary/histogram, representing the sample // count and sum of that summary/histogram. currentIsSummaryCount, currentIsSummarySum bool currentIsHistogramCount, currentIsHistogramSum bool } // TextToMetricFamilies reads 'in' as the simple and flat text-based exchange // format and creates MetricFamily proto messages. It returns the MetricFamily // proto messages in a map where the metric names are the keys, along with any // error encountered. // // If the input contains duplicate metrics (i.e. lines with the same metric name // and exactly the same label set), the resulting MetricFamily will contain // duplicate Metric proto messages. Similar is true for duplicate label // names. Checks for duplicates have to be performed separately, if required. // Also note that neither the metrics within each MetricFamily are sorted nor // the label pairs within each Metric. Sorting is not required for the most // frequent use of this method, which is sample ingestion in the Prometheus // server. However, for presentation purposes, you might want to sort the // metrics, and in some cases, you must sort the labels, e.g. for consumption by // the metric family injection hook of the Prometheus registry. // // Summaries and histograms are rather special beasts. You would probably not // use them in the simple text format anyway. This method can deal with // summaries and histograms if they are presented in exactly the way the // text.Create function creates them. // // This method must not be called concurrently. If you want to parse different // input concurrently, instantiate a separate Parser for each goroutine. func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) { p.reset(in) for nextState := p.startOfLine; nextState != nil; nextState = nextState() { // Magic happens here... } // Get rid of empty metric families. for k, mf := range p.metricFamiliesByName { if len(mf.GetMetric()) == 0 { delete(p.metricFamiliesByName, k) } } // If p.err is io.EOF now, we have run into a premature end of the input // stream. Turn this error into something nicer and more // meaningful. (io.EOF is often used as a signal for the legitimate end // of an input stream.) if p.err == io.EOF { p.parseError("unexpected end of input stream") } return p.metricFamiliesByName, p.err } func (p *TextParser) reset(in io.Reader) { p.metricFamiliesByName = map[string]*dto.MetricFamily{} if p.buf == nil { p.buf = bufio.NewReader(in) } else { p.buf.Reset(in) } p.err = nil p.lineCount = 0 if p.summaries == nil || len(p.summaries) > 0 { p.summaries = map[uint64]*dto.Metric{} } if p.histograms == nil || len(p.histograms) > 0 { p.histograms = map[uint64]*dto.Metric{} } p.currentQuantile = math.NaN() p.currentBucket = math.NaN() } // startOfLine represents the state where the next byte read from p.buf is the // start of a line (or whitespace leading up to it). func (p *TextParser) startOfLine() stateFn { p.lineCount++ if p.skipBlankTab(); p.err != nil { // End of input reached. This is the only case where // that is not an error but a signal that we are done. p.err = nil return nil } switch p.currentByte { case '#': return p.startComment case '\n': return p.startOfLine // Empty line, start the next one. } return p.readingMetricName } // startComment represents the state where the next byte read from p.buf is the // start of a comment (or whitespace leading up to it). func (p *TextParser) startComment() stateFn { if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } if p.currentByte == '\n' { return p.startOfLine } if p.readTokenUntilWhitespace(); p.err != nil { return nil // Unexpected end of input. } // If we have hit the end of line already, there is nothing left // to do. This is not considered a syntax error. if p.currentByte == '\n' { return p.startOfLine } keyword := p.currentToken.String() if keyword != "HELP" && keyword != "TYPE" { // Generic comment, ignore by fast forwarding to end of line. for p.currentByte != '\n' { if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil { return nil // Unexpected end of input. } } return p.startOfLine } // There is something. Next has to be a metric name. if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } if p.readTokenAsMetricName(); p.err != nil { return nil // Unexpected end of input. } if p.currentByte == '\n' { // At the end of the line already. // Again, this is not considered a syntax error. return p.startOfLine } if !isBlankOrTab(p.currentByte) { p.parseError("invalid metric name in comment") return nil } p.setOrCreateCurrentMF() if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } if p.currentByte == '\n' { // At the end of the line already. // Again, this is not considered a syntax error. return p.startOfLine } switch keyword { case "HELP": return p.readingHelp case "TYPE": return p.readingType } panic(fmt.Sprintf("code error: unexpected keyword %q", keyword)) } // readingMetricName represents the state where the last byte read (now in // p.currentByte) is the first byte of a metric name. func (p *TextParser) readingMetricName() stateFn { if p.readTokenAsMetricName(); p.err != nil { return nil } if p.currentToken.Len() == 0 { p.parseError("invalid metric name") return nil } p.setOrCreateCurrentMF() // Now is the time to fix the type if it hasn't happened yet. if p.currentMF.Type == nil { p.currentMF.Type = dto.MetricType_UNTYPED.Enum() } p.currentMetric = &dto.Metric{} // Do not append the newly created currentMetric to // currentMF.Metric right now. First wait if this is a summary, // and the metric exists already, which we can only know after // having read all the labels. if p.skipBlankTabIfCurrentBlankTab(); p.err != nil { return nil // Unexpected end of input. } return p.readingLabels } // readingLabels represents the state where the last byte read (now in // p.currentByte) is either the first byte of the label set (i.e. a '{'), or the // first byte of the value (otherwise). func (p *TextParser) readingLabels() stateFn { // Summaries/histograms are special. We have to reset the // currentLabels map, currentQuantile and currentBucket before starting to // read labels. if p.currentMF.GetType() == dto.MetricType_SUMMARY || p.currentMF.GetType() == dto.MetricType_HISTOGRAM { p.currentLabels = map[string]string{} p.currentLabels[string(model.MetricNameLabel)] = p.currentMF.GetName() p.currentQuantile = math.NaN() p.currentBucket = math.NaN() } if p.currentByte != '{' { return p.readingValue } return p.startLabelName } // startLabelName represents the state where the next byte read from p.buf is // the start of a label name (or whitespace leading up to it). func (p *TextParser) startLabelName() stateFn { if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } if p.currentByte == '}' { if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } return p.readingValue } if p.readTokenAsLabelName(); p.err != nil { return nil // Unexpected end of input. } if p.currentToken.Len() == 0 { p.parseError(fmt.Sprintf("invalid label name for metric %q", p.currentMF.GetName())) return nil } p.currentLabelPair = &dto.LabelPair{Name: proto.String(p.currentToken.String())} if p.currentLabelPair.GetName() == string(model.MetricNameLabel) { p.parseError(fmt.Sprintf("label name %q is reserved", model.MetricNameLabel)) return nil } // Special summary/histogram treatment. Don't add 'quantile' and 'le' // labels to 'real' labels. if !(p.currentMF.GetType() == dto.MetricType_SUMMARY && p.currentLabelPair.GetName() == model.QuantileLabel) && !(p.currentMF.GetType() == dto.MetricType_HISTOGRAM && p.currentLabelPair.GetName() == model.BucketLabel) { p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPair) } if p.skipBlankTabIfCurrentBlankTab(); p.err != nil { return nil // Unexpected end of input. } if p.currentByte != '=' { p.parseError(fmt.Sprintf("expected '=' after label name, found %q", p.currentByte)) return nil } // Check for duplicate label names. labels := make(map[string]struct{}) for _, l := range p.currentMetric.Label { lName := l.GetName() if _, exists := labels[lName]; !exists { labels[lName] = struct{}{} } else { p.parseError(fmt.Sprintf("duplicate label names for metric %q", p.currentMF.GetName())) return nil } } return p.startLabelValue } // startLabelValue represents the state where the next byte read from p.buf is // the start of a (quoted) label value (or whitespace leading up to it). func (p *TextParser) startLabelValue() stateFn { if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } if p.currentByte != '"' { p.parseError(fmt.Sprintf("expected '\"' at start of label value, found %q", p.currentByte)) return nil } if p.readTokenAsLabelValue(); p.err != nil { return nil } if !model.LabelValue(p.currentToken.String()).IsValid() { p.parseError(fmt.Sprintf("invalid label value %q", p.currentToken.String())) return nil } p.currentLabelPair.Value = proto.String(p.currentToken.String()) // Special treatment of summaries: // - Quantile labels are special, will result in dto.Quantile later. // - Other labels have to be added to currentLabels for signature calculation. if p.currentMF.GetType() == dto.MetricType_SUMMARY { if p.currentLabelPair.GetName() == model.QuantileLabel { if p.currentQuantile, p.err = parseFloat(p.currentLabelPair.GetValue()); p.err != nil { // Create a more helpful error message. p.parseError(fmt.Sprintf("expected float as value for 'quantile' label, got %q", p.currentLabelPair.GetValue())) return nil } } else { p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue() } } // Similar special treatment of histograms. if p.currentMF.GetType() == dto.MetricType_HISTOGRAM { if p.currentLabelPair.GetName() == model.BucketLabel { if p.currentBucket, p.err = parseFloat(p.currentLabelPair.GetValue()); p.err != nil { // Create a more helpful error message. p.parseError(fmt.Sprintf("expected float as value for 'le' label, got %q", p.currentLabelPair.GetValue())) return nil } } else { p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue() } } if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } switch p.currentByte { case ',': return p.startLabelName case '}': if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } return p.readingValue default: p.parseError(fmt.Sprintf("unexpected end of label value %q", p.currentLabelPair.GetValue())) return nil } } // readingValue represents the state where the last byte read (now in // p.currentByte) is the first byte of the sample value (i.e. a float). func (p *TextParser) readingValue() stateFn { // When we are here, we have read all the labels, so for the // special case of a summary/histogram, we can finally find out // if the metric already exists. if p.currentMF.GetType() == dto.MetricType_SUMMARY { signature := model.LabelsToSignature(p.currentLabels) if summary := p.summaries[signature]; summary != nil { p.currentMetric = summary } else { p.summaries[signature] = p.currentMetric p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric) } } else if p.currentMF.GetType() == dto.MetricType_HISTOGRAM { signature := model.LabelsToSignature(p.currentLabels) if histogram := p.histograms[signature]; histogram != nil { p.currentMetric = histogram } else { p.histograms[signature] = p.currentMetric p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric) } } else { p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric) } if p.readTokenUntilWhitespace(); p.err != nil { return nil // Unexpected end of input. } value, err := parseFloat(p.currentToken.String()) if err != nil { // Create a more helpful error message. p.parseError(fmt.Sprintf("expected float as value, got %q", p.currentToken.String())) return nil } switch p.currentMF.GetType() { case dto.MetricType_COUNTER: p.currentMetric.Counter = &dto.Counter{Value: proto.Float64(value)} case dto.MetricType_GAUGE: p.currentMetric.Gauge = &dto.Gauge{Value: proto.Float64(value)} case dto.MetricType_UNTYPED: p.currentMetric.Untyped = &dto.Untyped{Value: proto.Float64(value)} case dto.MetricType_SUMMARY: // *sigh* if p.currentMetric.Summary == nil { p.currentMetric.Summary = &dto.Summary{} } switch { case p.currentIsSummaryCount: p.currentMetric.Summary.SampleCount = proto.Uint64(uint64(value)) case p.currentIsSummarySum: p.currentMetric.Summary.SampleSum = proto.Float64(value) case !math.IsNaN(p.currentQuantile): p.currentMetric.Summary.Quantile = append( p.currentMetric.Summary.Quantile, &dto.Quantile{ Quantile: proto.Float64(p.currentQuantile), Value: proto.Float64(value), }, ) } case dto.MetricType_HISTOGRAM: // *sigh* if p.currentMetric.Histogram == nil { p.currentMetric.Histogram = &dto.Histogram{} } switch { case p.currentIsHistogramCount: p.currentMetric.Histogram.SampleCount = proto.Uint64(uint64(value)) case p.currentIsHistogramSum: p.currentMetric.Histogram.SampleSum = proto.Float64(value) case !math.IsNaN(p.currentBucket): p.currentMetric.Histogram.Bucket = append( p.currentMetric.Histogram.Bucket, &dto.Bucket{ UpperBound: proto.Float64(p.currentBucket), CumulativeCount: proto.Uint64(uint64(value)), }, ) } default: p.err = fmt.Errorf("unexpected type for metric name %q", p.currentMF.GetName()) } if p.currentByte == '\n' { return p.startOfLine } return p.startTimestamp } // startTimestamp represents the state where the next byte read from p.buf is // the start of the timestamp (or whitespace leading up to it). func (p *TextParser) startTimestamp() stateFn { if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } if p.readTokenUntilWhitespace(); p.err != nil { return nil // Unexpected end of input. } timestamp, err := strconv.ParseInt(p.currentToken.String(), 10, 64) if err != nil { // Create a more helpful error message. p.parseError(fmt.Sprintf("expected integer as timestamp, got %q", p.currentToken.String())) return nil } p.currentMetric.TimestampMs = proto.Int64(timestamp) if p.readTokenUntilNewline(false); p.err != nil { return nil // Unexpected end of input. } if p.currentToken.Len() > 0 { p.parseError(fmt.Sprintf("spurious string after timestamp: %q", p.currentToken.String())) return nil } return p.startOfLine } // readingHelp represents the state where the last byte read (now in // p.currentByte) is the first byte of the docstring after 'HELP'. func (p *TextParser) readingHelp() stateFn { if p.currentMF.Help != nil { p.parseError(fmt.Sprintf("second HELP line for metric name %q", p.currentMF.GetName())) return nil } // Rest of line is the docstring. if p.readTokenUntilNewline(true); p.err != nil { return nil // Unexpected end of input. } p.currentMF.Help = proto.String(p.currentToken.String()) return p.startOfLine } // readingType represents the state where the last byte read (now in // p.currentByte) is the first byte of the type hint after 'HELP'. func (p *TextParser) readingType() stateFn { if p.currentMF.Type != nil { p.parseError(fmt.Sprintf("second TYPE line for metric name %q, or TYPE reported after samples", p.currentMF.GetName())) return nil } // Rest of line is the type. if p.readTokenUntilNewline(false); p.err != nil { return nil // Unexpected end of input. } metricType, ok := dto.MetricType_value[strings.ToUpper(p.currentToken.String())] if !ok { p.parseError(fmt.Sprintf("unknown metric type %q", p.currentToken.String())) return nil } p.currentMF.Type = dto.MetricType(metricType).Enum() return p.startOfLine } // parseError sets p.err to a ParseError at the current line with the given // message. func (p *TextParser) parseError(msg string) { p.err = ParseError{ Line: p.lineCount, Msg: msg, } } // skipBlankTab reads (and discards) bytes from p.buf until it encounters a byte // that is neither ' ' nor '\t'. That byte is left in p.currentByte. func (p *TextParser) skipBlankTab() { for { if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil || !isBlankOrTab(p.currentByte) { return } } } // skipBlankTabIfCurrentBlankTab works exactly as skipBlankTab but doesn't do // anything if p.currentByte is neither ' ' nor '\t'. func (p *TextParser) skipBlankTabIfCurrentBlankTab() { if isBlankOrTab(p.currentByte) { p.skipBlankTab() } } // readTokenUntilWhitespace copies bytes from p.buf into p.currentToken. The // first byte considered is the byte already read (now in p.currentByte). The // first whitespace byte encountered is still copied into p.currentByte, but not // into p.currentToken. func (p *TextParser) readTokenUntilWhitespace() { p.currentToken.Reset() for p.err == nil && !isBlankOrTab(p.currentByte) && p.currentByte != '\n' { p.currentToken.WriteByte(p.currentByte) p.currentByte, p.err = p.buf.ReadByte() } } // readTokenUntilNewline copies bytes from p.buf into p.currentToken. The first // byte considered is the byte already read (now in p.currentByte). The first // newline byte encountered is still copied into p.currentByte, but not into // p.currentToken. If recognizeEscapeSequence is true, two escape sequences are // recognized: '\\' translates into '\', and '\n' into a line-feed character. // All other escape sequences are invalid and cause an error. func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) { p.currentToken.Reset() escaped := false for p.err == nil { if recognizeEscapeSequence && escaped { switch p.currentByte { case '\\': p.currentToken.WriteByte(p.currentByte) case 'n': p.currentToken.WriteByte('\n') default: p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte)) return } escaped = false } else { switch p.currentByte { case '\n': return case '\\': escaped = true default: p.currentToken.WriteByte(p.currentByte) } } p.currentByte, p.err = p.buf.ReadByte() } } // readTokenAsMetricName copies a metric name from p.buf into p.currentToken. // The first byte considered is the byte already read (now in p.currentByte). // The first byte not part of a metric name is still copied into p.currentByte, // but not into p.currentToken. func (p *TextParser) readTokenAsMetricName() { p.currentToken.Reset() if !isValidMetricNameStart(p.currentByte) { return } for { p.currentToken.WriteByte(p.currentByte) p.currentByte, p.err = p.buf.ReadByte() if p.err != nil || !isValidMetricNameContinuation(p.currentByte) { return } } } // readTokenAsLabelName copies a label name from p.buf into p.currentToken. // The first byte considered is the byte already read (now in p.currentByte). // The first byte not part of a label name is still copied into p.currentByte, // but not into p.currentToken. func (p *TextParser) readTokenAsLabelName() { p.currentToken.Reset() if !isValidLabelNameStart(p.currentByte) { return } for { p.currentToken.WriteByte(p.currentByte) p.currentByte, p.err = p.buf.ReadByte() if p.err != nil || !isValidLabelNameContinuation(p.currentByte) { return } } } // readTokenAsLabelValue copies a label value from p.buf into p.currentToken. // In contrast to the other 'readTokenAs...' functions, which start with the // last read byte in p.currentByte, this method ignores p.currentByte and starts // with reading a new byte from p.buf. The first byte not part of a label value // is still copied into p.currentByte, but not into p.currentToken. func (p *TextParser) readTokenAsLabelValue() { p.currentToken.Reset() escaped := false for { if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil { return } if escaped { switch p.currentByte { case '"', '\\': p.currentToken.WriteByte(p.currentByte) case 'n': p.currentToken.WriteByte('\n') default: p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte)) return } escaped = false continue } switch p.currentByte { case '"': return case '\n': p.parseError(fmt.Sprintf("label value %q contains unescaped new-line", p.currentToken.String())) return case '\\': escaped = true default: p.currentToken.WriteByte(p.currentByte) } } } func (p *TextParser) setOrCreateCurrentMF() { p.currentIsSummaryCount = false p.currentIsSummarySum = false p.currentIsHistogramCount = false p.currentIsHistogramSum = false name := p.currentToken.String() if p.currentMF = p.metricFamiliesByName[name]; p.currentMF != nil { return } // Try out if this is a _sum or _count for a summary/histogram. summaryName := summaryMetricName(name) if p.currentMF = p.metricFamiliesByName[summaryName]; p.currentMF != nil { if p.currentMF.GetType() == dto.MetricType_SUMMARY { if isCount(name) { p.currentIsSummaryCount = true } if isSum(name) { p.currentIsSummarySum = true } return } } histogramName := histogramMetricName(name) if p.currentMF = p.metricFamiliesByName[histogramName]; p.currentMF != nil { if p.currentMF.GetType() == dto.MetricType_HISTOGRAM { if isCount(name) { p.currentIsHistogramCount = true } if isSum(name) { p.currentIsHistogramSum = true } return } } p.currentMF = &dto.MetricFamily{Name: proto.String(name)} p.metricFamiliesByName[name] = p.currentMF } func isValidLabelNameStart(b byte) bool { return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' } func isValidLabelNameContinuation(b byte) bool { return isValidLabelNameStart(b) || (b >= '0' && b <= '9') } func isValidMetricNameStart(b byte) bool { return isValidLabelNameStart(b) || b == ':' } func isValidMetricNameContinuation(b byte) bool { return isValidLabelNameContinuation(b) || b == ':' } func isBlankOrTab(b byte) bool { return b == ' ' || b == '\t' } func isCount(name string) bool { return len(name) > 6 && name[len(name)-6:] == "_count" } func isSum(name string) bool { return len(name) > 4 && name[len(name)-4:] == "_sum" } func isBucket(name string) bool { return len(name) > 7 && name[len(name)-7:] == "_bucket" } func summaryMetricName(name string) string { switch { case isCount(name): return name[:len(name)-6] case isSum(name): return name[:len(name)-4] default: return name } } func histogramMetricName(name string) string { switch { case isCount(name): return name[:len(name)-6] case isSum(name): return name[:len(name)-4] case isBucket(name): return name[:len(name)-7] default: return name } } func parseFloat(s string) (float64, error) { if strings.ContainsAny(s, "pP_") { return 0, fmt.Errorf("unsupported character in float") } return strconv.ParseFloat(s, 64) } golang-github-prometheus-common-0.32.1/expfmt/text_parse_test.go000066400000000000000000000403641413406232300250570ustar00rootroot00000000000000// Copyright 2014 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package expfmt import ( "math" "strings" "testing" "github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. dto "github.com/prometheus/client_model/go" ) func testTextParse(t testing.TB) { var scenarios = []struct { in string out []*dto.MetricFamily }{ // 0: Empty lines as input. { in: ` `, out: []*dto.MetricFamily{}, }, // 1: Minimal case. { in: ` minimal_metric 1.234 another_metric -3e3 103948 # Even that: no_labels{} 3 # HELP line for non-existing metric will be ignored. `, out: []*dto.MetricFamily{ &dto.MetricFamily{ Name: proto.String("minimal_metric"), Type: dto.MetricType_UNTYPED.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Untyped: &dto.Untyped{ Value: proto.Float64(1.234), }, }, }, }, &dto.MetricFamily{ Name: proto.String("another_metric"), Type: dto.MetricType_UNTYPED.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Untyped: &dto.Untyped{ Value: proto.Float64(-3e3), }, TimestampMs: proto.Int64(103948), }, }, }, &dto.MetricFamily{ Name: proto.String("no_labels"), Type: dto.MetricType_UNTYPED.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Untyped: &dto.Untyped{ Value: proto.Float64(3), }, }, }, }, }, }, // 2: Counters & gauges, docstrings, various whitespace, escape sequences. { in: ` # A normal comment. # # TYPE name counter name{labelname="val1",basename="basevalue"} NaN name {labelname="val2",basename="base\"v\\al\nue"} 0.23 1234567890 # HELP name two-line\n doc str\\ing # HELP name2 doc str"ing 2 # TYPE name2 gauge name2{labelname="val2" ,basename = "basevalue2" } +Inf 54321 name2{ labelname = "val1" , }-Inf `, out: []*dto.MetricFamily{ &dto.MetricFamily{ Name: proto.String("name"), Help: proto.String("two-line\n doc str\\ing"), Type: dto.MetricType_COUNTER.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("labelname"), Value: proto.String("val1"), }, &dto.LabelPair{ Name: proto.String("basename"), Value: proto.String("basevalue"), }, }, Counter: &dto.Counter{ Value: proto.Float64(math.NaN()), }, }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("labelname"), Value: proto.String("val2"), }, &dto.LabelPair{ Name: proto.String("basename"), Value: proto.String("base\"v\\al\nue"), }, }, Counter: &dto.Counter{ Value: proto.Float64(.23), }, TimestampMs: proto.Int64(1234567890), }, }, }, &dto.MetricFamily{ Name: proto.String("name2"), Help: proto.String("doc str\"ing 2"), Type: dto.MetricType_GAUGE.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("labelname"), Value: proto.String("val2"), }, &dto.LabelPair{ Name: proto.String("basename"), Value: proto.String("basevalue2"), }, }, Gauge: &dto.Gauge{ Value: proto.Float64(math.Inf(+1)), }, TimestampMs: proto.Int64(54321), }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("labelname"), Value: proto.String("val1"), }, }, Gauge: &dto.Gauge{ Value: proto.Float64(math.Inf(-1)), }, }, }, }, }, }, // 3: The evil summary, mixed with other types and funny comments. { in: ` # TYPE my_summary summary my_summary{n1="val1",quantile="0.5"} 110 decoy -1 -2 my_summary{n1="val1",quantile="0.9"} 140 1 my_summary_count{n1="val1"} 42 # Latest timestamp wins in case of a summary. my_summary_sum{n1="val1"} 4711 2 fake_sum{n1="val1"} 2001 # TYPE another_summary summary another_summary_count{n2="val2",n1="val1"} 20 my_summary_count{n2="val2",n1="val1"} 5 5 another_summary{n1="val1",n2="val2",quantile=".3"} -1.2 my_summary_sum{n1="val2"} 08 15 my_summary{n1="val3", quantile="0.2"} 4711 my_summary{n1="val1",n2="val2",quantile="-12.34",} NaN # some # funny comments # HELP # HELP # HELP my_summary # HELP my_summary `, out: []*dto.MetricFamily{ &dto.MetricFamily{ Name: proto.String("fake_sum"), Type: dto.MetricType_UNTYPED.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("n1"), Value: proto.String("val1"), }, }, Untyped: &dto.Untyped{ Value: proto.Float64(2001), }, }, }, }, &dto.MetricFamily{ Name: proto.String("decoy"), Type: dto.MetricType_UNTYPED.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Untyped: &dto.Untyped{ Value: proto.Float64(-1), }, TimestampMs: proto.Int64(-2), }, }, }, &dto.MetricFamily{ Name: proto.String("my_summary"), Type: dto.MetricType_SUMMARY.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("n1"), Value: proto.String("val1"), }, }, Summary: &dto.Summary{ SampleCount: proto.Uint64(42), SampleSum: proto.Float64(4711), Quantile: []*dto.Quantile{ &dto.Quantile{ Quantile: proto.Float64(0.5), Value: proto.Float64(110), }, &dto.Quantile{ Quantile: proto.Float64(0.9), Value: proto.Float64(140), }, }, }, TimestampMs: proto.Int64(2), }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("n2"), Value: proto.String("val2"), }, &dto.LabelPair{ Name: proto.String("n1"), Value: proto.String("val1"), }, }, Summary: &dto.Summary{ SampleCount: proto.Uint64(5), Quantile: []*dto.Quantile{ &dto.Quantile{ Quantile: proto.Float64(-12.34), Value: proto.Float64(math.NaN()), }, }, }, TimestampMs: proto.Int64(5), }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("n1"), Value: proto.String("val2"), }, }, Summary: &dto.Summary{ SampleSum: proto.Float64(8), }, TimestampMs: proto.Int64(15), }, &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("n1"), Value: proto.String("val3"), }, }, Summary: &dto.Summary{ Quantile: []*dto.Quantile{ &dto.Quantile{ Quantile: proto.Float64(0.2), Value: proto.Float64(4711), }, }, }, }, }, }, &dto.MetricFamily{ Name: proto.String("another_summary"), Type: dto.MetricType_SUMMARY.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Label: []*dto.LabelPair{ &dto.LabelPair{ Name: proto.String("n2"), Value: proto.String("val2"), }, &dto.LabelPair{ Name: proto.String("n1"), Value: proto.String("val1"), }, }, Summary: &dto.Summary{ SampleCount: proto.Uint64(20), Quantile: []*dto.Quantile{ &dto.Quantile{ Quantile: proto.Float64(0.3), Value: proto.Float64(-1.2), }, }, }, }, }, }, }, }, // 4: The histogram. { in: ` # HELP request_duration_microseconds The response latency. # TYPE request_duration_microseconds histogram request_duration_microseconds_bucket{le="100"} 123 request_duration_microseconds_bucket{le="120"} 412 request_duration_microseconds_bucket{le="144"} 592 request_duration_microseconds_bucket{le="172.8"} 1524 request_duration_microseconds_bucket{le="+Inf"} 2693 request_duration_microseconds_sum 1.7560473e+06 request_duration_microseconds_count 2693 `, out: []*dto.MetricFamily{ { Name: proto.String("request_duration_microseconds"), Help: proto.String("The response latency."), Type: dto.MetricType_HISTOGRAM.Enum(), Metric: []*dto.Metric{ &dto.Metric{ Histogram: &dto.Histogram{ SampleCount: proto.Uint64(2693), SampleSum: proto.Float64(1756047.3), Bucket: []*dto.Bucket{ &dto.Bucket{ UpperBound: proto.Float64(100), CumulativeCount: proto.Uint64(123), }, &dto.Bucket{ UpperBound: proto.Float64(120), CumulativeCount: proto.Uint64(412), }, &dto.Bucket{ UpperBound: proto.Float64(144), CumulativeCount: proto.Uint64(592), }, &dto.Bucket{ UpperBound: proto.Float64(172.8), CumulativeCount: proto.Uint64(1524), }, &dto.Bucket{ UpperBound: proto.Float64(math.Inf(+1)), CumulativeCount: proto.Uint64(2693), }, }, }, }, }, }, }, }, } for i, scenario := range scenarios { out, err := parser.TextToMetricFamilies(strings.NewReader(scenario.in)) if err != nil { t.Errorf("%d. error: %s", i, err) continue } if expected, got := len(scenario.out), len(out); expected != got { t.Errorf( "%d. expected %d MetricFamilies, got %d", i, expected, got, ) } for _, expected := range scenario.out { got, ok := out[expected.GetName()] if !ok { t.Errorf( "%d. expected MetricFamily %q, found none", i, expected.GetName(), ) continue } if expected.String() != got.String() { t.Errorf( "%d. expected MetricFamily %s, got %s", i, expected, got, ) } } } } func TestTextParse(t *testing.T) { testTextParse(t) } func BenchmarkTextParse(b *testing.B) { for i := 0; i < b.N; i++ { testTextParse(b) } } func testTextParseError(t testing.TB) { var scenarios = []struct { in string err string }{ // 0: No new-line at end of input. { in: ` bla 3.14 blubber 42`, err: "text format parsing error in line 3: unexpected end of input stream", }, // 1: Invalid escape sequence in label value. { in: `metric{label="\t"} 3.14`, err: "text format parsing error in line 1: invalid escape sequence", }, // 2: Newline in label value. { in: ` metric{label="new line"} 3.14 `, err: `text format parsing error in line 2: label value "new" contains unescaped new-line`, }, // 3: { in: `metric{@="bla"} 3.14`, err: "text format parsing error in line 1: invalid label name for metric", }, // 4: { in: `metric{__name__="bla"} 3.14`, err: `text format parsing error in line 1: label name "__name__" is reserved`, }, // 5: { in: `metric{label+="bla"} 3.14`, err: "text format parsing error in line 1: expected '=' after label name", }, // 6: { in: `metric{label=bla} 3.14`, err: "text format parsing error in line 1: expected '\"' at start of label value", }, // 7: { in: ` # TYPE metric summary metric{quantile="bla"} 3.14 `, err: "text format parsing error in line 3: expected float as value for 'quantile' label", }, // 8: { in: `metric{label="bla"+} 3.14`, err: "text format parsing error in line 1: unexpected end of label value", }, // 9: { in: `metric{label="bla"} 3.14 2.72 `, err: "text format parsing error in line 1: expected integer as timestamp", }, // 10: { in: `metric{label="bla"} 3.14 2 3 `, err: "text format parsing error in line 1: spurious string after timestamp", }, // 11: { in: `metric{label="bla"} blubb `, err: "text format parsing error in line 1: expected float as value", }, // 12: { in: ` # HELP metric one # HELP metric two `, err: "text format parsing error in line 3: second HELP line for metric name", }, // 13: { in: ` # TYPE metric counter # TYPE metric untyped `, err: `text format parsing error in line 3: second TYPE line for metric name "metric", or TYPE reported after samples`, }, // 14: { in: ` metric 4.12 # TYPE metric counter `, err: `text format parsing error in line 3: second TYPE line for metric name "metric", or TYPE reported after samples`, }, // 14: { in: ` # TYPE metric bla `, err: "text format parsing error in line 2: unknown metric type", }, // 15: { in: ` # TYPE met-ric `, err: "text format parsing error in line 2: invalid metric name in comment", }, // 16: { in: `@invalidmetric{label="bla"} 3.14 2`, err: "text format parsing error in line 1: invalid metric name", }, // 17: { in: `{label="bla"} 3.14 2`, err: "text format parsing error in line 1: invalid metric name", }, // 18: { in: ` # TYPE metric histogram metric_bucket{le="bla"} 3.14 `, err: "text format parsing error in line 3: expected float as value for 'le' label", }, // 19: Invalid UTF-8 in label value. { in: "metric{l=\"\xbd\"} 3.14\n", err: "text format parsing error in line 1: invalid label value \"\\xbd\"", }, // 20: Go 1.13 sometimes allows underscores in numbers. { in: "foo 1_2\n", err: "text format parsing error in line 1: expected float as value", }, // 21: Go 1.13 supports hex floating point. { in: "foo 0x1p-3\n", err: "text format parsing error in line 1: expected float as value", }, // 22: Check for various other literals variants, just in case. { in: "foo 0x1P-3\n", err: "text format parsing error in line 1: expected float as value", }, // 23: { in: "foo 0B1\n", err: "text format parsing error in line 1: expected float as value", }, // 24: { in: "foo 0O1\n", err: "text format parsing error in line 1: expected float as value", }, // 25: { in: "foo 0X1\n", err: "text format parsing error in line 1: expected float as value", }, // 26: { in: "foo 0x1\n", err: "text format parsing error in line 1: expected float as value", }, // 27: { in: "foo 0b1\n", err: "text format parsing error in line 1: expected float as value", }, // 28: { in: "foo 0o1\n", err: "text format parsing error in line 1: expected float as value", }, // 29: { in: "foo 0x1\n", err: "text format parsing error in line 1: expected float as value", }, // 30: { in: "foo 0x1\n", err: "text format parsing error in line 1: expected float as value", }, // 31: Check histogram label. { in: ` # TYPE metric histogram metric_bucket{le="0x1p-3"} 3.14 `, err: "text format parsing error in line 3: expected float as value for 'le' label", }, // 32: Check quantile label. { in: ` # TYPE metric summary metric{quantile="0x1p-3"} 3.14 `, err: "text format parsing error in line 3: expected float as value for 'quantile' label", }, // 33: Check duplicate label. { in: `metric{label="bla",label="bla"} 3.14`, err: "text format parsing error in line 1: duplicate label names for metric", }, } for i, scenario := range scenarios { _, err := parser.TextToMetricFamilies(strings.NewReader(scenario.in)) if err == nil { t.Errorf("%d. expected error, got nil", i) continue } if expected, got := scenario.err, err.Error(); strings.Index(got, expected) != 0 { t.Errorf( "%d. expected error starting with %q, got %q", i, expected, got, ) } } } func TestTextParseError(t *testing.T) { testTextParseError(t) } func BenchmarkParseError(b *testing.B) { for i := 0; i < b.N; i++ { testTextParseError(b) } } golang-github-prometheus-common-0.32.1/go.mod000066400000000000000000000011071413406232300211060ustar00rootroot00000000000000module github.com/prometheus/common require ( github.com/go-kit/log v0.1.0 github.com/golang/protobuf v1.4.3 github.com/julienschmidt/httprouter v1.3.0 github.com/matttproud/golang_protobuf_extensions v1.0.1 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_model v0.2.0 golang.org/x/net v0.0.0-20210525063256-abc453219eb5 golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/yaml.v2 v2.4.0 ) go 1.13 golang-github-prometheus-common-0.32.1/go.sum000066400000000000000000001343051413406232300211420ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= golang-github-prometheus-common-0.32.1/internal/000077500000000000000000000000001413406232300216155ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/internal/bitbucket.org/000077500000000000000000000000001413406232300243575ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/internal/bitbucket.org/ww/000077500000000000000000000000001413406232300250145ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/internal/bitbucket.org/ww/goautoneg/000077500000000000000000000000001413406232300270045ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/internal/bitbucket.org/ww/goautoneg/README.txt000066400000000000000000000043341413406232300305060ustar00rootroot00000000000000PACKAGE package goautoneg import "bitbucket.org/ww/goautoneg" HTTP Content-Type Autonegotiation. The functions in this package implement the behaviour specified in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html Copyright (c) 2011, Open Knowledge Foundation Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Open Knowledge Foundation Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. FUNCTIONS func Negotiate(header string, alternatives []string) (content_type string) Negotiate the most appropriate content_type given the accept header and a list of alternatives. func ParseAccept(header string) (accept []Accept) Parse an Accept Header string returning a sorted list of clauses TYPES type Accept struct { Type, SubType string Q float32 Params map[string]string } Structure to represent a clause in an HTTP Accept Header SUBDIRECTORIES .hg golang-github-prometheus-common-0.32.1/internal/bitbucket.org/ww/goautoneg/autoneg.go000066400000000000000000000103151413406232300307750ustar00rootroot00000000000000/* Copyright (c) 2011, Open Knowledge Foundation Ltd. All rights reserved. HTTP Content-Type Autonegotiation. The functions in this package implement the behaviour specified in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Open Knowledge Foundation Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package goautoneg import ( "sort" "strconv" "strings" ) // Structure to represent a clause in an HTTP Accept Header type Accept struct { Type, SubType string Q float64 Params map[string]string } // For internal use, so that we can use the sort interface type accept_slice []Accept func (accept accept_slice) Len() int { slice := []Accept(accept) return len(slice) } func (accept accept_slice) Less(i, j int) bool { slice := []Accept(accept) ai, aj := slice[i], slice[j] if ai.Q > aj.Q { return true } if ai.Type != "*" && aj.Type == "*" { return true } if ai.SubType != "*" && aj.SubType == "*" { return true } return false } func (accept accept_slice) Swap(i, j int) { slice := []Accept(accept) slice[i], slice[j] = slice[j], slice[i] } // Parse an Accept Header string returning a sorted list // of clauses func ParseAccept(header string) (accept []Accept) { parts := strings.Split(header, ",") accept = make([]Accept, 0, len(parts)) for _, part := range parts { part := strings.Trim(part, " ") a := Accept{} a.Params = make(map[string]string) a.Q = 1.0 mrp := strings.Split(part, ";") media_range := mrp[0] sp := strings.Split(media_range, "/") a.Type = strings.Trim(sp[0], " ") switch { case len(sp) == 1 && a.Type == "*": a.SubType = "*" case len(sp) == 2: a.SubType = strings.Trim(sp[1], " ") default: continue } if len(mrp) == 1 { accept = append(accept, a) continue } for _, param := range mrp[1:] { sp := strings.SplitN(param, "=", 2) if len(sp) != 2 { continue } token := strings.Trim(sp[0], " ") if token == "q" { a.Q, _ = strconv.ParseFloat(sp[1], 32) } else { a.Params[token] = strings.Trim(sp[1], " ") } } accept = append(accept, a) } slice := accept_slice(accept) sort.Sort(slice) return } // Negotiate the most appropriate content_type given the accept header // and a list of alternatives. func Negotiate(header string, alternatives []string) (content_type string) { asp := make([][]string, 0, len(alternatives)) for _, ctype := range alternatives { asp = append(asp, strings.SplitN(ctype, "/", 2)) } for _, clause := range ParseAccept(header) { for i, ctsp := range asp { if clause.Type == ctsp[0] && clause.SubType == ctsp[1] { content_type = alternatives[i] return } if clause.Type == ctsp[0] && clause.SubType == "*" { content_type = alternatives[i] return } if clause.Type == "*" && clause.SubType == "*" { content_type = alternatives[i] return } } } return } golang-github-prometheus-common-0.32.1/internal/bitbucket.org/ww/goautoneg/autoneg_test.go000066400000000000000000000051731413406232300320420ustar00rootroot00000000000000/* Copyright (c) 2011, Open Knowledge Foundation Ltd. All rights reserved. HTTP Content-Type Autonegotiation. The functions in this package implement the behaviour specified in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Open Knowledge Foundation Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package goautoneg import ( "testing" ) var chrome = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" func TestParseAccept(t *testing.T) { alternatives := []string{"text/html", "image/png"} content_type := Negotiate(chrome, alternatives) if content_type != "image/png" { t.Errorf("got %s expected image/png", content_type) } alternatives = []string{"text/html", "text/plain", "text/n3"} content_type = Negotiate(chrome, alternatives) if content_type != "text/html" { t.Errorf("got %s expected text/html", content_type) } alternatives = []string{"text/n3", "text/plain"} content_type = Negotiate(chrome, alternatives) if content_type != "text/plain" { t.Errorf("got %s expected text/plain", content_type) } alternatives = []string{"text/n3", "application/rdf+xml"} content_type = Negotiate(chrome, alternatives) if content_type != "text/n3" { t.Errorf("got %s expected text/n3", content_type) } } golang-github-prometheus-common-0.32.1/model/000077500000000000000000000000001413406232300211015ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/model/alert.go000066400000000000000000000072541413406232300225470ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "fmt" "time" ) type AlertStatus string const ( AlertFiring AlertStatus = "firing" AlertResolved AlertStatus = "resolved" ) // Alert is a generic representation of an alert in the Prometheus eco-system. type Alert struct { // Label value pairs for purpose of aggregation, matching, and disposition // dispatching. This must minimally include an "alertname" label. Labels LabelSet `json:"labels"` // Extra key/value information which does not define alert identity. Annotations LabelSet `json:"annotations"` // The known time range for this alert. Both ends are optional. StartsAt time.Time `json:"startsAt,omitempty"` EndsAt time.Time `json:"endsAt,omitempty"` GeneratorURL string `json:"generatorURL"` } // Name returns the name of the alert. It is equivalent to the "alertname" label. func (a *Alert) Name() string { return string(a.Labels[AlertNameLabel]) } // Fingerprint returns a unique hash for the alert. It is equivalent to // the fingerprint of the alert's label set. func (a *Alert) Fingerprint() Fingerprint { return a.Labels.Fingerprint() } func (a *Alert) String() string { s := fmt.Sprintf("%s[%s]", a.Name(), a.Fingerprint().String()[:7]) if a.Resolved() { return s + "[resolved]" } return s + "[active]" } // Resolved returns true iff the activity interval ended in the past. func (a *Alert) Resolved() bool { return a.ResolvedAt(time.Now()) } // ResolvedAt returns true off the activity interval ended before // the given timestamp. func (a *Alert) ResolvedAt(ts time.Time) bool { if a.EndsAt.IsZero() { return false } return !a.EndsAt.After(ts) } // Status returns the status of the alert. func (a *Alert) Status() AlertStatus { if a.Resolved() { return AlertResolved } return AlertFiring } // Validate checks whether the alert data is inconsistent. func (a *Alert) Validate() error { if a.StartsAt.IsZero() { return fmt.Errorf("start time missing") } if !a.EndsAt.IsZero() && a.EndsAt.Before(a.StartsAt) { return fmt.Errorf("start time must be before end time") } if err := a.Labels.Validate(); err != nil { return fmt.Errorf("invalid label set: %s", err) } if len(a.Labels) == 0 { return fmt.Errorf("at least one label pair required") } if err := a.Annotations.Validate(); err != nil { return fmt.Errorf("invalid annotations: %s", err) } return nil } // Alert is a list of alerts that can be sorted in chronological order. type Alerts []*Alert func (as Alerts) Len() int { return len(as) } func (as Alerts) Swap(i, j int) { as[i], as[j] = as[j], as[i] } func (as Alerts) Less(i, j int) bool { if as[i].StartsAt.Before(as[j].StartsAt) { return true } if as[i].EndsAt.Before(as[j].EndsAt) { return true } return as[i].Fingerprint() < as[j].Fingerprint() } // HasFiring returns true iff one of the alerts is not resolved. func (as Alerts) HasFiring() bool { for _, a := range as { if !a.Resolved() { return true } } return false } // Status returns StatusFiring iff at least one of the alerts is firing. func (as Alerts) Status() AlertStatus { if as.HasFiring() { return AlertFiring } return AlertResolved } golang-github-prometheus-common-0.32.1/model/alert_test.go000066400000000000000000000134271413406232300236050ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "fmt" "sort" "strings" "testing" "time" ) func TestAlertValidate(t *testing.T) { ts := time.Now() var cases = []struct { alert *Alert err string }{ { alert: &Alert{ Labels: LabelSet{"a": "b"}, StartsAt: ts, }, }, { alert: &Alert{ Labels: LabelSet{"a": "b"}, }, err: "start time missing", }, { alert: &Alert{ Labels: LabelSet{"a": "b"}, StartsAt: ts, EndsAt: ts, }, }, { alert: &Alert{ Labels: LabelSet{"a": "b"}, StartsAt: ts, EndsAt: ts.Add(1 * time.Minute), }, }, { alert: &Alert{ Labels: LabelSet{"a": "b"}, StartsAt: ts, EndsAt: ts.Add(-1 * time.Minute), }, err: "start time must be before end time", }, { alert: &Alert{ StartsAt: ts, }, err: "at least one label pair required", }, { alert: &Alert{ Labels: LabelSet{"a": "b", "!bad": "label"}, StartsAt: ts, }, err: "invalid label set: invalid name", }, { alert: &Alert{ Labels: LabelSet{"a": "b", "bad": "\xfflabel"}, StartsAt: ts, }, err: "invalid label set: invalid value", }, { alert: &Alert{ Labels: LabelSet{"a": "b"}, Annotations: LabelSet{"!bad": "label"}, StartsAt: ts, }, err: "invalid annotations: invalid name", }, { alert: &Alert{ Labels: LabelSet{"a": "b"}, Annotations: LabelSet{"bad": "\xfflabel"}, StartsAt: ts, }, err: "invalid annotations: invalid value", }, } for i, c := range cases { err := c.alert.Validate() if err == nil { if c.err == "" { continue } t.Errorf("%d. Expected error %q but got none", i, c.err) continue } if c.err == "" { t.Errorf("%d. Expected no error but got %q", i, err) continue } if !strings.Contains(err.Error(), c.err) { t.Errorf("%d. Expected error to contain %q but got %q", i, c.err, err) } } } func TestAlert(t *testing.T) { // Verifying that an alert with no EndsAt field is unresolved and has firing status. alert := &Alert{ Labels: LabelSet{"foo": "bar", "lorem": "ipsum"}, StartsAt: time.Now(), } actual := fmt.Sprint(alert) expected := "[d181d0f][active]" if actual != expected { t.Errorf("expected %s, but got %s", expected, actual) } actualStatus := string(alert.Status()) expectedStatus := "firing" if actualStatus != expectedStatus { t.Errorf("expected alertStatus %s, but got %s", expectedStatus, actualStatus) } // Verifying that an alert with an EndsAt time before the current time is resolved and has resolved status. ts := time.Now() ts1 := ts.Add(-2 * time.Minute) ts2 := ts.Add(-1 * time.Minute) alert = &Alert{ Labels: LabelSet{"foo": "bar", "lorem": "ipsum"}, StartsAt: ts1, EndsAt: ts2, } actual = fmt.Sprint(alert) expected = "[d181d0f][resolved]" if actual != expected { t.Errorf("expected %s, but got %s", expected, actual) } actualStatus = string(alert.Status()) expectedStatus = "resolved" if actualStatus != expectedStatus { t.Errorf("expected alertStatus %s, but got %s", expectedStatus, actualStatus) } } func TestSortAlerts(t *testing.T) { ts := time.Now() alerts := Alerts{ { Labels: LabelSet{ "alertname": "InternalError", "dev": "sda3", }, StartsAt: ts.Add(-6 * time.Minute), EndsAt: ts.Add(-3 * time.Minute), }, { Labels: LabelSet{ "alertname": "DiskFull", "dev": "sda1", }, StartsAt: ts.Add(-5 * time.Minute), EndsAt: ts.Add(-4 * time.Minute), }, { Labels: LabelSet{ "alertname": "OutOfMemory", "dev": "sda1", }, StartsAt: ts.Add(-2 * time.Minute), EndsAt: ts.Add(-1 * time.Minute), }, { Labels: LabelSet{ "alertname": "DiskFull", "dev": "sda2", }, StartsAt: ts.Add(-2 * time.Minute), EndsAt: ts.Add(-3 * time.Minute), }, { Labels: LabelSet{ "alertname": "OutOfMemory", "dev": "sda2", }, StartsAt: ts.Add(-5 * time.Minute), EndsAt: ts.Add(-2 * time.Minute), }, } sort.Sort(alerts) expected := []string{ "DiskFull[5ffe595][resolved]", "InternalError[09cfd46][resolved]", "OutOfMemory[d43a602][resolved]", "DiskFull[5ff4595][resolved]", "OutOfMemory[d444602][resolved]", } for i := range alerts { if alerts[i].String() != expected[i] { t.Errorf("expected alert %s at index %d, but got %s", expected[i], i, alerts[i].String()) } } } func TestAlertsStatus(t *testing.T) { firingAlerts := Alerts{ { Labels: LabelSet{ "foo": "bar", }, StartsAt: time.Now(), }, { Labels: LabelSet{ "bar": "baz", }, StartsAt: time.Now(), }, } actualStatus := firingAlerts.Status() expectedStatus := AlertFiring if actualStatus != expectedStatus { t.Errorf("expected status %s, but got %s", expectedStatus, actualStatus) } ts := time.Now() resolvedAlerts := Alerts{ { Labels: LabelSet{ "foo": "bar", }, StartsAt: ts.Add(-1 * time.Minute), EndsAt: ts, }, { Labels: LabelSet{ "bar": "baz", }, StartsAt: ts.Add(-1 * time.Minute), EndsAt: ts, }, } actualStatus = resolvedAlerts.Status() expectedStatus = AlertResolved if actualStatus != expectedStatus { t.Errorf("expected status %s, but got %s", expectedStatus, actualStatus) } } golang-github-prometheus-common-0.32.1/model/fingerprinting.go000066400000000000000000000050071413406232300244570ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "fmt" "strconv" ) // Fingerprint provides a hash-capable representation of a Metric. // For our purposes, FNV-1A 64-bit is used. type Fingerprint uint64 // FingerprintFromString transforms a string representation into a Fingerprint. func FingerprintFromString(s string) (Fingerprint, error) { num, err := strconv.ParseUint(s, 16, 64) return Fingerprint(num), err } // ParseFingerprint parses the input string into a fingerprint. func ParseFingerprint(s string) (Fingerprint, error) { num, err := strconv.ParseUint(s, 16, 64) if err != nil { return 0, err } return Fingerprint(num), nil } func (f Fingerprint) String() string { return fmt.Sprintf("%016x", uint64(f)) } // Fingerprints represents a collection of Fingerprint subject to a given // natural sorting scheme. It implements sort.Interface. type Fingerprints []Fingerprint // Len implements sort.Interface. func (f Fingerprints) Len() int { return len(f) } // Less implements sort.Interface. func (f Fingerprints) Less(i, j int) bool { return f[i] < f[j] } // Swap implements sort.Interface. func (f Fingerprints) Swap(i, j int) { f[i], f[j] = f[j], f[i] } // FingerprintSet is a set of Fingerprints. type FingerprintSet map[Fingerprint]struct{} // Equal returns true if both sets contain the same elements (and not more). func (s FingerprintSet) Equal(o FingerprintSet) bool { if len(s) != len(o) { return false } for k := range s { if _, ok := o[k]; !ok { return false } } return true } // Intersection returns the elements contained in both sets. func (s FingerprintSet) Intersection(o FingerprintSet) FingerprintSet { myLength, otherLength := len(s), len(o) if myLength == 0 || otherLength == 0 { return FingerprintSet{} } subSet := s superSet := o if otherLength < myLength { subSet = o superSet = s } out := FingerprintSet{} for k := range subSet { if _, ok := superSet[k]; ok { out[k] = struct{}{} } } return out } golang-github-prometheus-common-0.32.1/model/fingerprinting_test.go000066400000000000000000000116601413406232300255200ustar00rootroot00000000000000// Copyright 2019 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "sort" "testing" ) func TestFingerprintFromString(t *testing.T) { fs := "4294967295" f, err := FingerprintFromString(fs) if err != nil { t.Errorf("unexpected error while getting Fingerprint from string: %s", err.Error()) } expected := Fingerprint(285960729237) if expected != f { t.Errorf("expected to get %d, but got %d instead", f, expected) } f, err = ParseFingerprint(fs) if err != nil { t.Errorf("unexpected error while getting Fingerprint from string: %s", err.Error()) } if expected != f { t.Errorf("expected to get %d, but got %d instead", f, expected) } } func TestFingerprintsSort(t *testing.T) { fingerPrints := Fingerprints{ 14695981039346656037, 285960729237, 0, 4294967295, 285960729237, 18446744073709551615, } sort.Sort(fingerPrints) expected := Fingerprints{ 0, 4294967295, 285960729237, 285960729237, 14695981039346656037, 18446744073709551615, } for i, f := range fingerPrints { if f != expected[i] { t.Errorf("expected Fingerprint %d, but got %d for index %d", expected[i], f, i) } } } func TestFingerprintSet(t *testing.T) { // Testing with two sets of unequal length. f := FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 4294967295: struct{}{}, 285960729237: struct{}{}, 18446744073709551615: struct{}{}, } f2 := FingerprintSet{ 285960729237: struct{}{}, } if f.Equal(f2) { t.Errorf("expected two FingerPrintSets of unequal length to be unequal") } // Testing with two unequal sets of equal length. f = FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 4294967295: struct{}{}, } f2 = FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 285960729237: struct{}{}, } if f.Equal(f2) { t.Errorf("expected two FingerPrintSets of unequal content to be unequal") } // Testing with equal sets of equal length. f = FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 4294967295: struct{}{}, } f2 = FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 4294967295: struct{}{}, } if !f.Equal(f2) { t.Errorf("expected two FingerPrintSets of equal content to be equal") } } func TestFingerprintIntersection(t *testing.T) { scenarios := []struct { name string input1 FingerprintSet input2 FingerprintSet expected FingerprintSet }{ { name: "two empty sets", input1: FingerprintSet{}, input2: FingerprintSet{}, expected: FingerprintSet{}, }, { name: "one empty set", input1: FingerprintSet{ 0: struct{}{}, }, input2: FingerprintSet{}, expected: FingerprintSet{}, }, { name: "two non-empty unequal sets", input1: FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 4294967295: struct{}{}, }, input2: FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 4294967295: struct{}{}, }, expected: FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 4294967295: struct{}{}, }, }, { name: "two non-empty equal sets", input1: FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 285960729237: struct{}{}, }, input2: FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 4294967295: struct{}{}, }, expected: FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, }, }, { name: "two non-empty equal sets of unequal length", input1: FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, 285960729237: struct{}{}, }, input2: FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, }, expected: FingerprintSet{ 14695981039346656037: struct{}{}, 0: struct{}{}, }, }, } for _, scenario := range scenarios { s1 := scenario.input1 s2 := scenario.input2 actual := s1.Intersection(s2) if !actual.Equal(scenario.expected) { t.Errorf("expected %v to be equal to %v", actual, scenario.expected) } } } golang-github-prometheus-common-0.32.1/model/fnv.go000066400000000000000000000022471413406232300222260ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model // Inline and byte-free variant of hash/fnv's fnv64a. const ( offset64 = 14695981039346656037 prime64 = 1099511628211 ) // hashNew initializes a new fnv64a hash value. func hashNew() uint64 { return offset64 } // hashAdd adds a string to a fnv64a hash value, returning the updated hash. func hashAdd(h uint64, s string) uint64 { for i := 0; i < len(s); i++ { h ^= uint64(s[i]) h *= prime64 } return h } // hashAddByte adds a byte to a fnv64a hash value, returning the updated hash. func hashAddByte(h uint64, b byte) uint64 { h ^= uint64(b) h *= prime64 return h } golang-github-prometheus-common-0.32.1/model/labels.go000066400000000000000000000136721413406232300227030ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "encoding/json" "fmt" "regexp" "strings" "unicode/utf8" ) const ( // AlertNameLabel is the name of the label containing the an alert's name. AlertNameLabel = "alertname" // ExportedLabelPrefix is the prefix to prepend to the label names present in // exported metrics if a label of the same name is added by the server. ExportedLabelPrefix = "exported_" // MetricNameLabel is the label name indicating the metric name of a // timeseries. MetricNameLabel = "__name__" // SchemeLabel is the name of the label that holds the scheme on which to // scrape a target. SchemeLabel = "__scheme__" // AddressLabel is the name of the label that holds the address of // a scrape target. AddressLabel = "__address__" // MetricsPathLabel is the name of the label that holds the path on which to // scrape a target. MetricsPathLabel = "__metrics_path__" // ScrapeIntervalLabel is the name of the label that holds the scrape interval // used to scrape a target. ScrapeIntervalLabel = "__scrape_interval__" // ScrapeTimeoutLabel is the name of the label that holds the scrape // timeout used to scrape a target. ScrapeTimeoutLabel = "__scrape_timeout__" // ReservedLabelPrefix is a prefix which is not legal in user-supplied // label names. ReservedLabelPrefix = "__" // MetaLabelPrefix is a prefix for labels that provide meta information. // Labels with this prefix are used for intermediate label processing and // will not be attached to time series. MetaLabelPrefix = "__meta_" // TmpLabelPrefix is a prefix for temporary labels as part of relabelling. // Labels with this prefix are used for intermediate label processing and // will not be attached to time series. This is reserved for use in // Prometheus configuration files by users. TmpLabelPrefix = "__tmp_" // ParamLabelPrefix is a prefix for labels that provide URL parameters // used to scrape a target. ParamLabelPrefix = "__param_" // JobLabel is the label name indicating the job from which a timeseries // was scraped. JobLabel = "job" // InstanceLabel is the label name used for the instance label. InstanceLabel = "instance" // BucketLabel is used for the label that defines the upper bound of a // bucket of a histogram ("le" -> "less or equal"). BucketLabel = "le" // QuantileLabel is used for the label that defines the quantile in a // summary. QuantileLabel = "quantile" ) // LabelNameRE is a regular expression matching valid label names. Note that the // IsValid method of LabelName performs the same check but faster than a match // with this regular expression. var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") // A LabelName is a key for a LabelSet or Metric. It has a value associated // therewith. type LabelName string // IsValid is true iff the label name matches the pattern of LabelNameRE. This // method, however, does not use LabelNameRE for the check but a much faster // hardcoded implementation. func (ln LabelName) IsValid() bool { if len(ln) == 0 { return false } for i, b := range ln { if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || (b >= '0' && b <= '9' && i > 0)) { return false } } return true } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string if err := unmarshal(&s); err != nil { return err } if !LabelName(s).IsValid() { return fmt.Errorf("%q is not a valid label name", s) } *ln = LabelName(s) return nil } // UnmarshalJSON implements the json.Unmarshaler interface. func (ln *LabelName) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { return err } if !LabelName(s).IsValid() { return fmt.Errorf("%q is not a valid label name", s) } *ln = LabelName(s) return nil } // LabelNames is a sortable LabelName slice. In implements sort.Interface. type LabelNames []LabelName func (l LabelNames) Len() int { return len(l) } func (l LabelNames) Less(i, j int) bool { return l[i] < l[j] } func (l LabelNames) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l LabelNames) String() string { labelStrings := make([]string, 0, len(l)) for _, label := range l { labelStrings = append(labelStrings, string(label)) } return strings.Join(labelStrings, ", ") } // A LabelValue is an associated value for a LabelName. type LabelValue string // IsValid returns true iff the string is a valid UTF8. func (lv LabelValue) IsValid() bool { return utf8.ValidString(string(lv)) } // LabelValues is a sortable LabelValue slice. It implements sort.Interface. type LabelValues []LabelValue func (l LabelValues) Len() int { return len(l) } func (l LabelValues) Less(i, j int) bool { return string(l[i]) < string(l[j]) } func (l LabelValues) Swap(i, j int) { l[i], l[j] = l[j], l[i] } // LabelPair pairs a name with a value. type LabelPair struct { Name LabelName Value LabelValue } // LabelPairs is a sortable slice of LabelPair pointers. It implements // sort.Interface. type LabelPairs []*LabelPair func (l LabelPairs) Len() int { return len(l) } func (l LabelPairs) Less(i, j int) bool { switch { case l[i].Name > l[j].Name: return false case l[i].Name < l[j].Name: return true case l[i].Value > l[j].Value: return false case l[i].Value < l[j].Value: return true default: return false } } func (l LabelPairs) Swap(i, j int) { l[i], l[j] = l[j], l[i] } golang-github-prometheus-common-0.32.1/model/labels_test.go000066400000000000000000000071121413406232300237320ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "sort" "testing" ) func testLabelNames(t testing.TB) { var scenarios = []struct { in LabelNames out LabelNames }{ { in: LabelNames{"ZZZ", "zzz"}, out: LabelNames{"ZZZ", "zzz"}, }, { in: LabelNames{"aaa", "AAA"}, out: LabelNames{"AAA", "aaa"}, }, } for i, scenario := range scenarios { sort.Sort(scenario.in) for j, expected := range scenario.out { if expected != scenario.in[j] { t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j]) } } } } func TestLabelNames(t *testing.T) { testLabelNames(t) } func BenchmarkLabelNames(b *testing.B) { for i := 0; i < b.N; i++ { testLabelNames(b) } } func testLabelValues(t testing.TB) { var scenarios = []struct { in LabelValues out LabelValues }{ { in: LabelValues{"ZZZ", "zzz"}, out: LabelValues{"ZZZ", "zzz"}, }, { in: LabelValues{"aaa", "AAA"}, out: LabelValues{"AAA", "aaa"}, }, } for i, scenario := range scenarios { sort.Sort(scenario.in) for j, expected := range scenario.out { if expected != scenario.in[j] { t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j]) } } } } func TestLabelValues(t *testing.T) { testLabelValues(t) } func BenchmarkLabelValues(b *testing.B) { for i := 0; i < b.N; i++ { testLabelValues(b) } } func TestLabelNameIsValid(t *testing.T) { var scenarios = []struct { ln LabelName valid bool }{ { ln: "Avalid_23name", valid: true, }, { ln: "_Avalid_23name", valid: true, }, { ln: "1valid_23name", valid: false, }, { ln: "avalid_23name", valid: true, }, { ln: "Ava:lid_23name", valid: false, }, { ln: "a lid_23name", valid: false, }, { ln: ":leading_colon", valid: false, }, { ln: "colon:in:the:middle", valid: false, }, } for _, s := range scenarios { if s.ln.IsValid() != s.valid { t.Errorf("Expected %v for %q using IsValid method", s.valid, s.ln) } if LabelNameRE.MatchString(string(s.ln)) != s.valid { t.Errorf("Expected %v for %q using regexp match", s.valid, s.ln) } } } func TestSortLabelPairs(t *testing.T) { labelPairs := LabelPairs{ { Name: "FooName", Value: "FooValue", }, { Name: "FooName", Value: "BarValue", }, { Name: "BarName", Value: "FooValue", }, { Name: "BazName", Value: "BazValue", }, { Name: "BarName", Value: "FooValue", }, { Name: "BazName", Value: "FazValue", }, } sort.Sort(labelPairs) expectedLabelPairs := LabelPairs{ { Name: "BarName", Value: "FooValue", }, { Name: "BarName", Value: "FooValue", }, { Name: "BazName", Value: "BazValue", }, { Name: "BazName", Value: "FazValue", }, { Name: "FooName", Value: "BarValue", }, } for i, expected := range expectedLabelPairs { if expected.Name != labelPairs[i].Name || expected.Value != labelPairs[i].Value { t.Errorf("%d expected %s, got %s", i, expected, labelPairs[i]) } } } golang-github-prometheus-common-0.32.1/model/labelset.go000066400000000000000000000102741413406232300232270ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "encoding/json" "fmt" "sort" "strings" ) // A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet // may be fully-qualified down to the point where it may resolve to a single // Metric in the data store or not. All operations that occur within the realm // of a LabelSet can emit a vector of Metric entities to which the LabelSet may // match. type LabelSet map[LabelName]LabelValue // Validate checks whether all names and values in the label set // are valid. func (ls LabelSet) Validate() error { for ln, lv := range ls { if !ln.IsValid() { return fmt.Errorf("invalid name %q", ln) } if !lv.IsValid() { return fmt.Errorf("invalid value %q", lv) } } return nil } // Equal returns true iff both label sets have exactly the same key/value pairs. func (ls LabelSet) Equal(o LabelSet) bool { if len(ls) != len(o) { return false } for ln, lv := range ls { olv, ok := o[ln] if !ok { return false } if olv != lv { return false } } return true } // Before compares the metrics, using the following criteria: // // If m has fewer labels than o, it is before o. If it has more, it is not. // // If the number of labels is the same, the superset of all label names is // sorted alphanumerically. The first differing label pair found in that order // determines the outcome: If the label does not exist at all in m, then m is // before o, and vice versa. Otherwise the label value is compared // alphanumerically. // // If m and o are equal, the method returns false. func (ls LabelSet) Before(o LabelSet) bool { if len(ls) < len(o) { return true } if len(ls) > len(o) { return false } lns := make(LabelNames, 0, len(ls)+len(o)) for ln := range ls { lns = append(lns, ln) } for ln := range o { lns = append(lns, ln) } // It's probably not worth it to de-dup lns. sort.Sort(lns) for _, ln := range lns { mlv, ok := ls[ln] if !ok { return true } olv, ok := o[ln] if !ok { return false } if mlv < olv { return true } if mlv > olv { return false } } return false } // Clone returns a copy of the label set. func (ls LabelSet) Clone() LabelSet { lsn := make(LabelSet, len(ls)) for ln, lv := range ls { lsn[ln] = lv } return lsn } // Merge is a helper function to non-destructively merge two label sets. func (l LabelSet) Merge(other LabelSet) LabelSet { result := make(LabelSet, len(l)) for k, v := range l { result[k] = v } for k, v := range other { result[k] = v } return result } func (l LabelSet) String() string { lstrs := make([]string, 0, len(l)) for l, v := range l { lstrs = append(lstrs, fmt.Sprintf("%s=%q", l, v)) } sort.Strings(lstrs) return fmt.Sprintf("{%s}", strings.Join(lstrs, ", ")) } // Fingerprint returns the LabelSet's fingerprint. func (ls LabelSet) Fingerprint() Fingerprint { return labelSetToFingerprint(ls) } // FastFingerprint returns the LabelSet's Fingerprint calculated by a faster hashing // algorithm, which is, however, more susceptible to hash collisions. func (ls LabelSet) FastFingerprint() Fingerprint { return labelSetToFastFingerprint(ls) } // UnmarshalJSON implements the json.Unmarshaler interface. func (l *LabelSet) UnmarshalJSON(b []byte) error { var m map[LabelName]LabelValue if err := json.Unmarshal(b, &m); err != nil { return err } // encoding/json only unmarshals maps of the form map[string]T. It treats // LabelName as a string and does not call its UnmarshalJSON method. // Thus, we have to replicate the behavior here. for ln := range m { if !ln.IsValid() { return fmt.Errorf("%q is not a valid label name", ln) } } *l = LabelSet(m) return nil } golang-github-prometheus-common-0.32.1/model/labelset_test.go000066400000000000000000000054231413406232300242660ustar00rootroot00000000000000// Copyright 2019 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "encoding/json" "testing" ) func TestUnmarshalJSONLabelSet(t *testing.T) { type testConfig struct { LabelSet LabelSet `yaml:"labelSet,omitempty"` } // valid LabelSet JSON labelSetJSON := `{ "labelSet": { "monitor": "codelab", "foo": "bar" } }` var c testConfig err := json.Unmarshal([]byte(labelSetJSON), &c) if err != nil { t.Errorf("unexpected error while marshalling JSON : %s", err.Error()) } labelSetString := c.LabelSet.String() expected := `{foo="bar", monitor="codelab"}` if expected != labelSetString { t.Errorf("expected %s but got %s", expected, labelSetString) } // invalid LabelSet JSON invalidlabelSetJSON := `{ "labelSet": { "1nvalid_23name": "codelab", "foo": "bar" } }` err = json.Unmarshal([]byte(invalidlabelSetJSON), &c) expectedErr := `"1nvalid_23name" is not a valid label name` if err == nil || err.Error() != expectedErr { t.Errorf("expected an error with message '%s' to be thrown", expectedErr) } } func TestLabelSetClone(t *testing.T) { labelSet := LabelSet{ "monitor": "codelab", "foo": "bar", "bar": "baz", } cloneSet := labelSet.Clone() if len(labelSet) != len(cloneSet) { t.Errorf("expected the length of the cloned Label set to be %d, but got %d", len(labelSet), len(cloneSet)) } for ln, lv := range labelSet { expected := cloneSet[ln] if expected != lv { t.Errorf("expected to get LabelValue %s, but got %s for LabelName %s", expected, lv, ln) } } } func TestLabelSetMerge(t *testing.T) { labelSet := LabelSet{ "monitor": "codelab", "foo": "bar", "bar": "baz", } labelSet2 := LabelSet{ "monitor": "codelab", "dolor": "mi", "lorem": "ipsum", } expectedSet := LabelSet{ "monitor": "codelab", "foo": "bar", "bar": "baz", "dolor": "mi", "lorem": "ipsum", } mergedSet := labelSet.Merge(labelSet2) if len(mergedSet) != len(expectedSet) { t.Errorf("expected the length of the cloned Label set to be %d, but got %d", len(expectedSet), len(mergedSet)) } for ln, lv := range mergedSet { expected := expectedSet[ln] if expected != lv { t.Errorf("expected to get LabelValue %s, but got %s for LabelName %s", expected, lv, ln) } } } golang-github-prometheus-common-0.32.1/model/metric.go000066400000000000000000000054711413406232300227220ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "fmt" "regexp" "sort" "strings" ) var ( // MetricNameRE is a regular expression matching valid metric // names. Note that the IsValidMetricName function performs the same // check but faster than a match with this regular expression. MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`) ) // A Metric is similar to a LabelSet, but the key difference is that a Metric is // a singleton and refers to one and only one stream of samples. type Metric LabelSet // Equal compares the metrics. func (m Metric) Equal(o Metric) bool { return LabelSet(m).Equal(LabelSet(o)) } // Before compares the metrics' underlying label sets. func (m Metric) Before(o Metric) bool { return LabelSet(m).Before(LabelSet(o)) } // Clone returns a copy of the Metric. func (m Metric) Clone() Metric { clone := make(Metric, len(m)) for k, v := range m { clone[k] = v } return clone } func (m Metric) String() string { metricName, hasName := m[MetricNameLabel] numLabels := len(m) - 1 if !hasName { numLabels = len(m) } labelStrings := make([]string, 0, numLabels) for label, value := range m { if label != MetricNameLabel { labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value)) } } switch numLabels { case 0: if hasName { return string(metricName) } return "{}" default: sort.Strings(labelStrings) return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ", ")) } } // Fingerprint returns a Metric's Fingerprint. func (m Metric) Fingerprint() Fingerprint { return LabelSet(m).Fingerprint() } // FastFingerprint returns a Metric's Fingerprint calculated by a faster hashing // algorithm, which is, however, more susceptible to hash collisions. func (m Metric) FastFingerprint() Fingerprint { return LabelSet(m).FastFingerprint() } // IsValidMetricName returns true iff name matches the pattern of MetricNameRE. // This function, however, does not use MetricNameRE for the check but a much // faster hardcoded implementation. func IsValidMetricName(n LabelValue) bool { if len(n) == 0 { return false } for i, b := range n { if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0)) { return false } } return true } golang-github-prometheus-common-0.32.1/model/metric_test.go000066400000000000000000000105671413406232300237630ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import "testing" func testMetric(t testing.TB) { var scenarios = []struct { input LabelSet fingerprint Fingerprint fastFingerprint Fingerprint }{ { input: LabelSet{}, fingerprint: 14695981039346656037, fastFingerprint: 14695981039346656037, }, { input: LabelSet{ "first_name": "electro", "occupation": "robot", "manufacturer": "westinghouse", }, fingerprint: 5911716720268894962, fastFingerprint: 11310079640881077873, }, { input: LabelSet{ "x": "y", }, fingerprint: 8241431561484471700, fastFingerprint: 13948396922932177635, }, { input: LabelSet{ "a": "bb", "b": "c", }, fingerprint: 3016285359649981711, fastFingerprint: 3198632812309449502, }, { input: LabelSet{ "a": "b", "bb": "c", }, fingerprint: 7122421792099404749, fastFingerprint: 5774953389407657638, }, } for i, scenario := range scenarios { input := Metric(scenario.input) if scenario.fingerprint != input.Fingerprint() { t.Errorf("%d. expected %d, got %d", i, scenario.fingerprint, input.Fingerprint()) } if scenario.fastFingerprint != input.FastFingerprint() { t.Errorf("%d. expected %d, got %d", i, scenario.fastFingerprint, input.FastFingerprint()) } } } func TestMetric(t *testing.T) { testMetric(t) } func BenchmarkMetric(b *testing.B) { for i := 0; i < b.N; i++ { testMetric(b) } } func TestMetricNameIsValid(t *testing.T) { var scenarios = []struct { mn LabelValue valid bool }{ { mn: "Avalid_23name", valid: true, }, { mn: "_Avalid_23name", valid: true, }, { mn: "1valid_23name", valid: false, }, { mn: "avalid_23name", valid: true, }, { mn: "Ava:lid_23name", valid: true, }, { mn: "a lid_23name", valid: false, }, { mn: ":leading_colon", valid: true, }, { mn: "colon:in:the:middle", valid: true, }, { mn: "", valid: false, }, } for _, s := range scenarios { if IsValidMetricName(s.mn) != s.valid { t.Errorf("Expected %v for %q using IsValidMetricName function", s.valid, s.mn) } if MetricNameRE.MatchString(string(s.mn)) != s.valid { t.Errorf("Expected %v for %q using regexp matching", s.valid, s.mn) } } } func TestMetricClone(t *testing.T) { m := Metric{ "first_name": "electro", "occupation": "robot", "manufacturer": "westinghouse", } m2 := m.Clone() if len(m) != len(m2) { t.Errorf("expected the length of the cloned metric to be equal to the input metric") } for ln, lv := range m2 { expected := m[ln] if expected != lv { t.Errorf("expected label value %s but got %s for label name %s", expected, lv, ln) } } } func TestMetricToString(t *testing.T) { scenarios := []struct { name string input Metric expected string }{ { name: "valid metric without __name__ label", input: Metric{ "first_name": "electro", "occupation": "robot", "manufacturer": "westinghouse", }, expected: `{first_name="electro", manufacturer="westinghouse", occupation="robot"}`, }, { name: "valid metric with __name__ label", input: Metric{ "__name__": "electro", "occupation": "robot", "manufacturer": "westinghouse", }, expected: `electro{manufacturer="westinghouse", occupation="robot"}`, }, { name: "empty metric with __name__ label", input: Metric{ "__name__": "fooname", }, expected: "fooname", }, { name: "empty metric", input: Metric{}, expected: "{}", }, } for _, scenario := range scenarios { t.Run(scenario.name, func(t *testing.T) { actual := scenario.input.String() if actual != scenario.expected { t.Errorf("expected string output %s but got %s", actual, scenario.expected) } }) } } golang-github-prometheus-common-0.32.1/model/model.go000066400000000000000000000013171413406232300225320ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package model contains common data structures that are shared across // Prometheus components and libraries. package model golang-github-prometheus-common-0.32.1/model/signature.go000066400000000000000000000105261413406232300234350ustar00rootroot00000000000000// Copyright 2014 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "sort" ) // SeparatorByte is a byte that cannot occur in valid UTF-8 sequences and is // used to separate label names, label values, and other strings from each other // when calculating their combined hash value (aka signature aka fingerprint). const SeparatorByte byte = 255 var ( // cache the signature of an empty label set. emptyLabelSignature = hashNew() ) // LabelsToSignature returns a quasi-unique signature (i.e., fingerprint) for a // given label set. (Collisions are possible but unlikely if the number of label // sets the function is applied to is small.) func LabelsToSignature(labels map[string]string) uint64 { if len(labels) == 0 { return emptyLabelSignature } labelNames := make([]string, 0, len(labels)) for labelName := range labels { labelNames = append(labelNames, labelName) } sort.Strings(labelNames) sum := hashNew() for _, labelName := range labelNames { sum = hashAdd(sum, labelName) sum = hashAddByte(sum, SeparatorByte) sum = hashAdd(sum, labels[labelName]) sum = hashAddByte(sum, SeparatorByte) } return sum } // labelSetToFingerprint works exactly as LabelsToSignature but takes a LabelSet as // parameter (rather than a label map) and returns a Fingerprint. func labelSetToFingerprint(ls LabelSet) Fingerprint { if len(ls) == 0 { return Fingerprint(emptyLabelSignature) } labelNames := make(LabelNames, 0, len(ls)) for labelName := range ls { labelNames = append(labelNames, labelName) } sort.Sort(labelNames) sum := hashNew() for _, labelName := range labelNames { sum = hashAdd(sum, string(labelName)) sum = hashAddByte(sum, SeparatorByte) sum = hashAdd(sum, string(ls[labelName])) sum = hashAddByte(sum, SeparatorByte) } return Fingerprint(sum) } // labelSetToFastFingerprint works similar to labelSetToFingerprint but uses a // faster and less allocation-heavy hash function, which is more susceptible to // create hash collisions. Therefore, collision detection should be applied. func labelSetToFastFingerprint(ls LabelSet) Fingerprint { if len(ls) == 0 { return Fingerprint(emptyLabelSignature) } var result uint64 for labelName, labelValue := range ls { sum := hashNew() sum = hashAdd(sum, string(labelName)) sum = hashAddByte(sum, SeparatorByte) sum = hashAdd(sum, string(labelValue)) result ^= sum } return Fingerprint(result) } // SignatureForLabels works like LabelsToSignature but takes a Metric as // parameter (rather than a label map) and only includes the labels with the // specified LabelNames into the signature calculation. The labels passed in // will be sorted by this function. func SignatureForLabels(m Metric, labels ...LabelName) uint64 { if len(labels) == 0 { return emptyLabelSignature } sort.Sort(LabelNames(labels)) sum := hashNew() for _, label := range labels { sum = hashAdd(sum, string(label)) sum = hashAddByte(sum, SeparatorByte) sum = hashAdd(sum, string(m[label])) sum = hashAddByte(sum, SeparatorByte) } return sum } // SignatureWithoutLabels works like LabelsToSignature but takes a Metric as // parameter (rather than a label map) and excludes the labels with any of the // specified LabelNames from the signature calculation. func SignatureWithoutLabels(m Metric, labels map[LabelName]struct{}) uint64 { if len(m) == 0 { return emptyLabelSignature } labelNames := make(LabelNames, 0, len(m)) for labelName := range m { if _, exclude := labels[labelName]; !exclude { labelNames = append(labelNames, labelName) } } if len(labelNames) == 0 { return emptyLabelSignature } sort.Sort(labelNames) sum := hashNew() for _, labelName := range labelNames { sum = hashAdd(sum, string(labelName)) sum = hashAddByte(sum, SeparatorByte) sum = hashAdd(sum, string(m[labelName])) sum = hashAddByte(sum, SeparatorByte) } return sum } golang-github-prometheus-common-0.32.1/model/signature_test.go000066400000000000000000000221611413406232300244720ustar00rootroot00000000000000// Copyright 2014 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "fmt" "runtime" "sync" "testing" ) func TestLabelsToSignature(t *testing.T) { var scenarios = []struct { in map[string]string out uint64 }{ { in: map[string]string{}, out: 14695981039346656037, }, { in: map[string]string{"name": "garland, briggs", "fear": "love is not enough"}, out: 5799056148416392346, }, } for i, scenario := range scenarios { actual := LabelsToSignature(scenario.in) if actual != scenario.out { t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) } } } func TestMetricToFingerprint(t *testing.T) { var scenarios = []struct { in LabelSet out Fingerprint }{ { in: LabelSet{}, out: 14695981039346656037, }, { in: LabelSet{"name": "garland, briggs", "fear": "love is not enough"}, out: 5799056148416392346, }, } for i, scenario := range scenarios { actual := labelSetToFingerprint(scenario.in) if actual != scenario.out { t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) } } } func TestMetricToFastFingerprint(t *testing.T) { var scenarios = []struct { in LabelSet out Fingerprint }{ { in: LabelSet{}, out: 14695981039346656037, }, { in: LabelSet{"name": "garland, briggs", "fear": "love is not enough"}, out: 12952432476264840823, }, } for i, scenario := range scenarios { actual := labelSetToFastFingerprint(scenario.in) if actual != scenario.out { t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) } } } func TestSignatureForLabels(t *testing.T) { var scenarios = []struct { in Metric labels LabelNames out uint64 }{ { in: Metric{}, labels: nil, out: 14695981039346656037, }, { in: Metric{}, labels: LabelNames{"empty"}, out: 7187873163539638612, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: LabelNames{"empty"}, out: 7187873163539638612, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: LabelNames{"fear", "name"}, out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"}, labels: LabelNames{"fear", "name"}, out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: LabelNames{}, out: 14695981039346656037, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: nil, out: 14695981039346656037, }, } for i, scenario := range scenarios { actual := SignatureForLabels(scenario.in, scenario.labels...) if actual != scenario.out { t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) } } } func TestSignatureWithoutLabels(t *testing.T) { var scenarios = []struct { in Metric labels map[LabelName]struct{} out uint64 }{ { in: Metric{}, labels: nil, out: 14695981039346656037, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: map[LabelName]struct{}{"fear": struct{}{}, "name": struct{}{}}, out: 14695981039346656037, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"}, labels: map[LabelName]struct{}{"foo": struct{}{}}, out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: map[LabelName]struct{}{}, out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: nil, out: 5799056148416392346, }, } for i, scenario := range scenarios { actual := SignatureWithoutLabels(scenario.in, scenario.labels) if actual != scenario.out { t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) } } } func benchmarkLabelToSignature(b *testing.B, l map[string]string, e uint64) { for i := 0; i < b.N; i++ { if a := LabelsToSignature(l); a != e { b.Fatalf("expected signature of %d for %s, got %d", e, l, a) } } } func BenchmarkLabelToSignatureScalar(b *testing.B) { benchmarkLabelToSignature(b, nil, 14695981039346656037) } func BenchmarkLabelToSignatureSingle(b *testing.B) { benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value"}, 5146282821936882169) } func BenchmarkLabelToSignatureDouble(b *testing.B) { benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717) } func BenchmarkLabelToSignatureTriple(b *testing.B) { benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121) } func benchmarkMetricToFingerprint(b *testing.B, ls LabelSet, e Fingerprint) { for i := 0; i < b.N; i++ { if a := labelSetToFingerprint(ls); a != e { b.Fatalf("expected signature of %d for %s, got %d", e, ls, a) } } } func BenchmarkMetricToFingerprintScalar(b *testing.B) { benchmarkMetricToFingerprint(b, nil, 14695981039346656037) } func BenchmarkMetricToFingerprintSingle(b *testing.B) { benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value"}, 5146282821936882169) } func BenchmarkMetricToFingerprintDouble(b *testing.B) { benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717) } func BenchmarkMetricToFingerprintTriple(b *testing.B) { benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121) } func benchmarkMetricToFastFingerprint(b *testing.B, ls LabelSet, e Fingerprint) { for i := 0; i < b.N; i++ { if a := labelSetToFastFingerprint(ls); a != e { b.Fatalf("expected signature of %d for %s, got %d", e, ls, a) } } } func BenchmarkMetricToFastFingerprintScalar(b *testing.B) { benchmarkMetricToFastFingerprint(b, nil, 14695981039346656037) } func BenchmarkMetricToFastFingerprintSingle(b *testing.B) { benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value"}, 5147259542624943964) } func BenchmarkMetricToFastFingerprintDouble(b *testing.B) { benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528) } func BenchmarkMetricToFastFingerprintTriple(b *testing.B) { benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676) } func BenchmarkEmptyLabelSignature(b *testing.B) { input := []map[string]string{nil, {}} var ms runtime.MemStats runtime.ReadMemStats(&ms) alloc := ms.Alloc for _, labels := range input { LabelsToSignature(labels) } runtime.ReadMemStats(&ms) if got := ms.Alloc; alloc != got { b.Fatal("expected LabelsToSignature with empty labels not to perform allocations") } } func benchmarkMetricToFastFingerprintConc(b *testing.B, ls LabelSet, e Fingerprint, concLevel int) { var start, end sync.WaitGroup start.Add(1) end.Add(concLevel) errc := make(chan error, 1) for i := 0; i < concLevel; i++ { go func() { start.Wait() for j := b.N / concLevel; j >= 0; j-- { if a := labelSetToFastFingerprint(ls); a != e { select { case errc <- fmt.Errorf("expected signature of %d for %s, got %d", e, ls, a): default: } } } end.Done() }() } b.ResetTimer() start.Done() end.Wait() select { case err := <-errc: b.Fatal(err) default: } } func BenchmarkMetricToFastFingerprintTripleConc1(b *testing.B) { benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 1) } func BenchmarkMetricToFastFingerprintTripleConc2(b *testing.B) { benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 2) } func BenchmarkMetricToFastFingerprintTripleConc4(b *testing.B) { benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 4) } func BenchmarkMetricToFastFingerprintTripleConc8(b *testing.B) { benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 8) } golang-github-prometheus-common-0.32.1/model/silence.go000066400000000000000000000054241413406232300230570ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "encoding/json" "fmt" "regexp" "time" ) // Matcher describes a matches the value of a given label. type Matcher struct { Name LabelName `json:"name"` Value string `json:"value"` IsRegex bool `json:"isRegex"` } func (m *Matcher) UnmarshalJSON(b []byte) error { type plain Matcher if err := json.Unmarshal(b, (*plain)(m)); err != nil { return err } if len(m.Name) == 0 { return fmt.Errorf("label name in matcher must not be empty") } if m.IsRegex { if _, err := regexp.Compile(m.Value); err != nil { return err } } return nil } // Validate returns true iff all fields of the matcher have valid values. func (m *Matcher) Validate() error { if !m.Name.IsValid() { return fmt.Errorf("invalid name %q", m.Name) } if m.IsRegex { if _, err := regexp.Compile(m.Value); err != nil { return fmt.Errorf("invalid regular expression %q", m.Value) } } else if !LabelValue(m.Value).IsValid() || len(m.Value) == 0 { return fmt.Errorf("invalid value %q", m.Value) } return nil } // Silence defines the representation of a silence definition in the Prometheus // eco-system. type Silence struct { ID uint64 `json:"id,omitempty"` Matchers []*Matcher `json:"matchers"` StartsAt time.Time `json:"startsAt"` EndsAt time.Time `json:"endsAt"` CreatedAt time.Time `json:"createdAt,omitempty"` CreatedBy string `json:"createdBy"` Comment string `json:"comment,omitempty"` } // Validate returns true iff all fields of the silence have valid values. func (s *Silence) Validate() error { if len(s.Matchers) == 0 { return fmt.Errorf("at least one matcher required") } for _, m := range s.Matchers { if err := m.Validate(); err != nil { return fmt.Errorf("invalid matcher: %s", err) } } if s.StartsAt.IsZero() { return fmt.Errorf("start time missing") } if s.EndsAt.IsZero() { return fmt.Errorf("end time missing") } if s.EndsAt.Before(s.StartsAt) { return fmt.Errorf("start time must be before end time") } if s.CreatedBy == "" { return fmt.Errorf("creator information missing") } if s.Comment == "" { return fmt.Errorf("comment missing") } if s.CreatedAt.IsZero() { return fmt.Errorf("creation timestamp missing") } return nil } golang-github-prometheus-common-0.32.1/model/silence_test.go000066400000000000000000000110021413406232300241030ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "strings" "testing" "time" ) func TestMatcherValidate(t *testing.T) { var cases = []struct { matcher *Matcher err string }{ { matcher: &Matcher{ Name: "name", Value: "value", }, }, { matcher: &Matcher{ Name: "name", Value: "value", IsRegex: true, }, }, { matcher: &Matcher{ Name: "name!", Value: "value", }, err: "invalid name", }, { matcher: &Matcher{ Name: "", Value: "value", }, err: "invalid name", }, { matcher: &Matcher{ Name: "name", Value: "value\xff", }, err: "invalid value", }, { matcher: &Matcher{ Name: "name", Value: "", }, err: "invalid value", }, } for i, c := range cases { err := c.matcher.Validate() if err == nil { if c.err == "" { continue } t.Errorf("%d. Expected error %q but got none", i, c.err) continue } if c.err == "" { t.Errorf("%d. Expected no error but got %q", i, err) continue } if !strings.Contains(err.Error(), c.err) { t.Errorf("%d. Expected error to contain %q but got %q", i, c.err, err) } } } func TestSilenceValidate(t *testing.T) { ts := time.Now() var cases = []struct { sil *Silence err string }{ { sil: &Silence{ Matchers: []*Matcher{ {Name: "name", Value: "value"}, }, StartsAt: ts, EndsAt: ts, CreatedAt: ts, CreatedBy: "name", Comment: "comment", }, }, { sil: &Silence{ Matchers: []*Matcher{ {Name: "name", Value: "value"}, {Name: "name", Value: "value"}, {Name: "name", Value: "value"}, {Name: "name", Value: "value", IsRegex: true}, }, StartsAt: ts, EndsAt: ts, CreatedAt: ts, CreatedBy: "name", Comment: "comment", }, }, { sil: &Silence{ Matchers: []*Matcher{ {Name: "name", Value: "value"}, }, StartsAt: ts, EndsAt: ts.Add(-1 * time.Minute), CreatedAt: ts, CreatedBy: "name", Comment: "comment", }, err: "start time must be before end time", }, { sil: &Silence{ Matchers: []*Matcher{ {Name: "name", Value: "value"}, }, StartsAt: ts, CreatedAt: ts, CreatedBy: "name", Comment: "comment", }, err: "end time missing", }, { sil: &Silence{ Matchers: []*Matcher{ {Name: "name", Value: "value"}, }, EndsAt: ts, CreatedAt: ts, CreatedBy: "name", Comment: "comment", }, err: "start time missing", }, { sil: &Silence{ Matchers: []*Matcher{ {Name: "!name", Value: "value"}, }, StartsAt: ts, EndsAt: ts, CreatedAt: ts, CreatedBy: "name", Comment: "comment", }, err: "invalid matcher", }, { sil: &Silence{ Matchers: []*Matcher{ {Name: "name", Value: "value"}, }, StartsAt: ts, EndsAt: ts, CreatedAt: ts, CreatedBy: "name", }, err: "comment missing", }, { sil: &Silence{ Matchers: []*Matcher{ {Name: "name", Value: "value"}, }, StartsAt: ts, EndsAt: ts, CreatedBy: "name", Comment: "comment", }, err: "creation timestamp missing", }, { sil: &Silence{ Matchers: []*Matcher{ {Name: "name", Value: "value"}, }, StartsAt: ts, EndsAt: ts, CreatedAt: ts, Comment: "comment", }, err: "creator information missing", }, { sil: &Silence{ Matchers: []*Matcher{}, StartsAt: ts, EndsAt: ts, CreatedAt: ts, Comment: "comment", }, err: "at least one matcher required", }, } for i, c := range cases { err := c.sil.Validate() if err == nil { if c.err == "" { continue } t.Errorf("%d. Expected error %q but got none", i, c.err) continue } if c.err == "" { t.Errorf("%d. Expected no error but got %q", i, err) continue } if !strings.Contains(err.Error(), c.err) { t.Errorf("%d. Expected error to contain %q but got %q", i, c.err, err) } } } golang-github-prometheus-common-0.32.1/model/time.go000066400000000000000000000176371413406232300224040ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "encoding/json" "errors" "fmt" "math" "regexp" "strconv" "strings" "time" ) const ( // MinimumTick is the minimum supported time resolution. This has to be // at least time.Second in order for the code below to work. minimumTick = time.Millisecond // second is the Time duration equivalent to one second. second = int64(time.Second / minimumTick) // The number of nanoseconds per minimum tick. nanosPerTick = int64(minimumTick / time.Nanosecond) // Earliest is the earliest Time representable. Handy for // initializing a high watermark. Earliest = Time(math.MinInt64) // Latest is the latest Time representable. Handy for initializing // a low watermark. Latest = Time(math.MaxInt64) ) // Time is the number of milliseconds since the epoch // (1970-01-01 00:00 UTC) excluding leap seconds. type Time int64 // Interval describes an interval between two timestamps. type Interval struct { Start, End Time } // Now returns the current time as a Time. func Now() Time { return TimeFromUnixNano(time.Now().UnixNano()) } // TimeFromUnix returns the Time equivalent to the Unix Time t // provided in seconds. func TimeFromUnix(t int64) Time { return Time(t * second) } // TimeFromUnixNano returns the Time equivalent to the Unix Time // t provided in nanoseconds. func TimeFromUnixNano(t int64) Time { return Time(t / nanosPerTick) } // Equal reports whether two Times represent the same instant. func (t Time) Equal(o Time) bool { return t == o } // Before reports whether the Time t is before o. func (t Time) Before(o Time) bool { return t < o } // After reports whether the Time t is after o. func (t Time) After(o Time) bool { return t > o } // Add returns the Time t + d. func (t Time) Add(d time.Duration) Time { return t + Time(d/minimumTick) } // Sub returns the Duration t - o. func (t Time) Sub(o Time) time.Duration { return time.Duration(t-o) * minimumTick } // Time returns the time.Time representation of t. func (t Time) Time() time.Time { return time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick) } // Unix returns t as a Unix time, the number of seconds elapsed // since January 1, 1970 UTC. func (t Time) Unix() int64 { return int64(t) / second } // UnixNano returns t as a Unix time, the number of nanoseconds elapsed // since January 1, 1970 UTC. func (t Time) UnixNano() int64 { return int64(t) * nanosPerTick } // The number of digits after the dot. var dotPrecision = int(math.Log10(float64(second))) // String returns a string representation of the Time. func (t Time) String() string { return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64) } // MarshalJSON implements the json.Marshaler interface. func (t Time) MarshalJSON() ([]byte, error) { return []byte(t.String()), nil } // UnmarshalJSON implements the json.Unmarshaler interface. func (t *Time) UnmarshalJSON(b []byte) error { p := strings.Split(string(b), ".") switch len(p) { case 1: v, err := strconv.ParseInt(string(p[0]), 10, 64) if err != nil { return err } *t = Time(v * second) case 2: v, err := strconv.ParseInt(string(p[0]), 10, 64) if err != nil { return err } v *= second prec := dotPrecision - len(p[1]) if prec < 0 { p[1] = p[1][:dotPrecision] } else if prec > 0 { p[1] = p[1] + strings.Repeat("0", prec) } va, err := strconv.ParseInt(p[1], 10, 32) if err != nil { return err } // If the value was something like -0.1 the negative is lost in the // parsing because of the leading zero, this ensures that we capture it. if len(p[0]) > 0 && p[0][0] == '-' && v+va > 0 { *t = Time(v+va) * -1 } else { *t = Time(v + va) } default: return fmt.Errorf("invalid time %q", string(b)) } return nil } // Duration wraps time.Duration. It is used to parse the custom duration format // from YAML. // This type should not propagate beyond the scope of input/output processing. type Duration time.Duration // Set implements pflag/flag.Value func (d *Duration) Set(s string) error { var err error *d, err = ParseDuration(s) return err } // Type implements pflag.Value func (d *Duration) Type() string { return "duration" } var durationRE = regexp.MustCompile("^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$") // ParseDuration parses a string into a time.Duration, assuming that a year // always has 365d, a week always has 7d, and a day always has 24h. func ParseDuration(durationStr string) (Duration, error) { switch durationStr { case "0": // Allow 0 without a unit. return 0, nil case "": return 0, fmt.Errorf("empty duration string") } matches := durationRE.FindStringSubmatch(durationStr) if matches == nil { return 0, fmt.Errorf("not a valid duration string: %q", durationStr) } var dur time.Duration // Parse the match at pos `pos` in the regex and use `mult` to turn that // into ms, then add that value to the total parsed duration. var overflowErr error m := func(pos int, mult time.Duration) { if matches[pos] == "" { return } n, _ := strconv.Atoi(matches[pos]) // Check if the provided duration overflows time.Duration (> ~ 290years). if n > int((1<<63-1)/mult/time.Millisecond) { overflowErr = errors.New("duration out of range") } d := time.Duration(n) * time.Millisecond dur += d * mult if dur < 0 { overflowErr = errors.New("duration out of range") } } m(2, 1000*60*60*24*365) // y m(4, 1000*60*60*24*7) // w m(6, 1000*60*60*24) // d m(8, 1000*60*60) // h m(10, 1000*60) // m m(12, 1000) // s m(14, 1) // ms return Duration(dur), overflowErr } func (d Duration) String() string { var ( ms = int64(time.Duration(d) / time.Millisecond) r = "" ) if ms == 0 { return "0s" } f := func(unit string, mult int64, exact bool) { if exact && ms%mult != 0 { return } if v := ms / mult; v > 0 { r += fmt.Sprintf("%d%s", v, unit) ms -= v * mult } } // Only format years and weeks if the remainder is zero, as it is often // easier to read 90d than 12w6d. f("y", 1000*60*60*24*365, true) f("w", 1000*60*60*24*7, true) f("d", 1000*60*60*24, false) f("h", 1000*60*60, false) f("m", 1000*60, false) f("s", 1000, false) f("ms", 1, false) return r } // MarshalJSON implements the json.Marshaler interface. func (d Duration) MarshalJSON() ([]byte, error) { return json.Marshal(d.String()) } // UnmarshalJSON implements the json.Unmarshaler interface. func (d *Duration) UnmarshalJSON(bytes []byte) error { var s string if err := json.Unmarshal(bytes, &s); err != nil { return err } dur, err := ParseDuration(s) if err != nil { return err } *d = dur return nil } // MarshalText implements the encoding.TextMarshaler interface. func (d *Duration) MarshalText() ([]byte, error) { return []byte(d.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (d *Duration) UnmarshalText(text []byte) error { var err error *d, err = ParseDuration(string(text)) return err } // MarshalYAML implements the yaml.Marshaler interface. func (d Duration) MarshalYAML() (interface{}, error) { return d.String(), nil } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string if err := unmarshal(&s); err != nil { return err } dur, err := ParseDuration(s) if err != nil { return err } *d = dur return nil } golang-github-prometheus-common-0.32.1/model/time_test.go000066400000000000000000000171721413406232300234350ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "encoding/json" "strconv" "testing" "time" ) func TestComparators(t *testing.T) { t1a := TimeFromUnix(0) t1b := TimeFromUnix(0) t2 := TimeFromUnix(2*second - 1) if !t1a.Equal(t1b) { t.Fatalf("Expected %s to be equal to %s", t1a, t1b) } if t1a.Equal(t2) { t.Fatalf("Expected %s to not be equal to %s", t1a, t2) } if !t1a.Before(t2) { t.Fatalf("Expected %s to be before %s", t1a, t2) } if t1a.Before(t1b) { t.Fatalf("Expected %s to not be before %s", t1a, t1b) } if !t2.After(t1a) { t.Fatalf("Expected %s to be after %s", t2, t1a) } if t1b.After(t1a) { t.Fatalf("Expected %s to not be after %s", t1b, t1a) } } func TestTimeConversions(t *testing.T) { unixSecs := int64(1136239445) unixNsecs := int64(123456789) unixNano := unixSecs*1e9 + unixNsecs t1 := time.Unix(unixSecs, unixNsecs-unixNsecs%nanosPerTick) t2 := time.Unix(unixSecs, unixNsecs) ts := TimeFromUnixNano(unixNano) if !ts.Time().Equal(t1) { t.Fatalf("Expected %s, got %s", t1, ts.Time()) } // Test available precision. ts = TimeFromUnixNano(t2.UnixNano()) if !ts.Time().Equal(t1) { t.Fatalf("Expected %s, got %s", t1, ts.Time()) } if ts.UnixNano() != unixNano-unixNano%nanosPerTick { t.Fatalf("Expected %d, got %d", unixNano, ts.UnixNano()) } } func TestDuration(t *testing.T) { duration := time.Second + time.Minute + time.Hour goTime := time.Unix(1136239445, 0) ts := TimeFromUnix(goTime.Unix()) if !goTime.Add(duration).Equal(ts.Add(duration).Time()) { t.Fatalf("Expected %s to be equal to %s", goTime.Add(duration), ts.Add(duration)) } earlier := ts.Add(-duration) delta := ts.Sub(earlier) if delta != duration { t.Fatalf("Expected %s to be equal to %s", delta, duration) } } func TestParseDuration(t *testing.T) { var cases = []struct { in string out time.Duration expectedString string }{ { in: "0", out: 0, expectedString: "0s", }, { in: "0w", out: 0, expectedString: "0s", }, { in: "0s", out: 0, }, { in: "324ms", out: 324 * time.Millisecond, }, { in: "3s", out: 3 * time.Second, }, { in: "5m", out: 5 * time.Minute, }, { in: "1h", out: time.Hour, }, { in: "4d", out: 4 * 24 * time.Hour, }, { in: "4d1h", out: 4*24*time.Hour + time.Hour, }, { in: "14d", out: 14 * 24 * time.Hour, expectedString: "2w", }, { in: "3w", out: 3 * 7 * 24 * time.Hour, }, { in: "3w2d1h", out: 3*7*24*time.Hour + 2*24*time.Hour + time.Hour, expectedString: "23d1h", }, { in: "10y", out: 10 * 365 * 24 * time.Hour, }, } for _, c := range cases { d, err := ParseDuration(c.in) if err != nil { t.Errorf("Unexpected error on input %q", c.in) } if time.Duration(d) != c.out { t.Errorf("Expected %v but got %v", c.out, d) } expectedString := c.expectedString if c.expectedString == "" { expectedString = c.in } if d.String() != expectedString { t.Errorf("Expected duration string %q but got %q", c.in, d.String()) } } } func TestDuration_UnmarshalText(t *testing.T) { var cases = []struct { in string out time.Duration expectedString string }{ { in: "0", out: 0, expectedString: "0s", }, { in: "0w", out: 0, expectedString: "0s", }, { in: "0s", out: 0, }, { in: "324ms", out: 324 * time.Millisecond, }, { in: "3s", out: 3 * time.Second, }, { in: "5m", out: 5 * time.Minute, }, { in: "1h", out: time.Hour, }, { in: "4d", out: 4 * 24 * time.Hour, }, { in: "4d1h", out: 4*24*time.Hour + time.Hour, }, { in: "14d", out: 14 * 24 * time.Hour, expectedString: "2w", }, { in: "3w", out: 3 * 7 * 24 * time.Hour, }, { in: "3w2d1h", out: 3*7*24*time.Hour + 2*24*time.Hour + time.Hour, expectedString: "23d1h", }, { in: "10y", out: 10 * 365 * 24 * time.Hour, }, } for _, c := range cases { var d Duration err := d.UnmarshalText([]byte(c.in)) if err != nil { t.Errorf("Unexpected error on input %q", c.in) } if time.Duration(d) != c.out { t.Errorf("Expected %v but got %v", c.out, d) } expectedString := c.expectedString if c.expectedString == "" { expectedString = c.in } text, _ := d.MarshalText() // MarshalText returns hardcoded nil if string(text) != expectedString { t.Errorf("Expected duration string %q but got %q", c.in, d.String()) } } } func TestDuration_UnmarshalJSON(t *testing.T) { var cases = []struct { in string out time.Duration expectedString string }{ { in: `"0"`, out: 0, expectedString: `"0s"`, }, { in: `"0w"`, out: 0, expectedString: `"0s"`, }, { in: `"0s"`, out: 0, }, { in: `"324ms"`, out: 324 * time.Millisecond, }, { in: `"3s"`, out: 3 * time.Second, }, { in: `"5m"`, out: 5 * time.Minute, }, { in: `"1h"`, out: time.Hour, }, { in: `"4d"`, out: 4 * 24 * time.Hour, }, { in: `"4d1h"`, out: 4*24*time.Hour + time.Hour, }, { in: `"14d"`, out: 14 * 24 * time.Hour, expectedString: `"2w"`, }, { in: `"3w"`, out: 3 * 7 * 24 * time.Hour, }, { in: `"3w2d1h"`, out: 3*7*24*time.Hour + 2*24*time.Hour + time.Hour, expectedString: `"23d1h"`, }, { in: `"10y"`, out: 10 * 365 * 24 * time.Hour, }, { in: `"289y"`, out: 289 * 365 * 24 * time.Hour, }, } for _, c := range cases { var d Duration err := json.Unmarshal([]byte(c.in), &d) if err != nil { t.Errorf("Unexpected error on input %q", c.in) } if time.Duration(d) != c.out { t.Errorf("Expected %v but got %v", c.out, d) } expectedString := c.expectedString if c.expectedString == "" { expectedString = c.in } bytes, err := json.Marshal(d) if err != nil { t.Errorf("Unexpected error on marshal of %v: %s", d, err) } if string(bytes) != expectedString { t.Errorf("Expected duration string %q but got %q", c.in, d.String()) } } } func TestParseBadDuration(t *testing.T) { var cases = []string{ "1", "1y1m1d", "-1w", "1.5d", "d", "294y", "200y10400w", "107675d", "2584200h", "", } for _, c := range cases { _, err := ParseDuration(c) if err == nil { t.Errorf("Expected error on input %s", c) } } } func TestTimeJSON(t *testing.T) { tests := []struct { in Time out string }{ {Time(1), `0.001`}, {Time(-1), `-0.001`}, } for i, test := range tests { t.Run(strconv.Itoa(i), func(t *testing.T) { b, err := test.in.MarshalJSON() if err != nil { t.Fatalf("Error marshaling time: %v", err) } if string(b) != test.out { t.Errorf("Mismatch in marshal expected=%s actual=%s", test.out, b) } var tm Time if err := tm.UnmarshalJSON(b); err != nil { t.Fatalf("Error Unmarshaling time: %v", err) } if !test.in.Equal(tm) { t.Fatalf("Mismatch after Unmarshal expected=%v actual=%v", test.in, tm) } }) } } golang-github-prometheus-common-0.32.1/model/value.go000066400000000000000000000237421413406232300225540ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "encoding/json" "fmt" "math" "sort" "strconv" "strings" ) var ( // ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a // non-existing sample pair. It is a SamplePair with timestamp Earliest and // value 0.0. Note that the natural zero value of SamplePair has a timestamp // of 0, which is possible to appear in a real SamplePair and thus not // suitable to signal a non-existing SamplePair. ZeroSamplePair = SamplePair{Timestamp: Earliest} // ZeroSample is the pseudo zero-value of Sample used to signal a // non-existing sample. It is a Sample with timestamp Earliest, value 0.0, // and metric nil. Note that the natural zero value of Sample has a timestamp // of 0, which is possible to appear in a real Sample and thus not suitable // to signal a non-existing Sample. ZeroSample = Sample{Timestamp: Earliest} ) // A SampleValue is a representation of a value for a given sample at a given // time. type SampleValue float64 // MarshalJSON implements json.Marshaler. func (v SampleValue) MarshalJSON() ([]byte, error) { return json.Marshal(v.String()) } // UnmarshalJSON implements json.Unmarshaler. func (v *SampleValue) UnmarshalJSON(b []byte) error { if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { return fmt.Errorf("sample value must be a quoted string") } f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) if err != nil { return err } *v = SampleValue(f) return nil } // Equal returns true if the value of v and o is equal or if both are NaN. Note // that v==o is false if both are NaN. If you want the conventional float // behavior, use == to compare two SampleValues. func (v SampleValue) Equal(o SampleValue) bool { if v == o { return true } return math.IsNaN(float64(v)) && math.IsNaN(float64(o)) } func (v SampleValue) String() string { return strconv.FormatFloat(float64(v), 'f', -1, 64) } // SamplePair pairs a SampleValue with a Timestamp. type SamplePair struct { Timestamp Time Value SampleValue } // MarshalJSON implements json.Marshaler. func (s SamplePair) MarshalJSON() ([]byte, error) { t, err := json.Marshal(s.Timestamp) if err != nil { return nil, err } v, err := json.Marshal(s.Value) if err != nil { return nil, err } return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil } // UnmarshalJSON implements json.Unmarshaler. func (s *SamplePair) UnmarshalJSON(b []byte) error { v := [...]json.Unmarshaler{&s.Timestamp, &s.Value} return json.Unmarshal(b, &v) } // Equal returns true if this SamplePair and o have equal Values and equal // Timestamps. The semantics of Value equality is defined by SampleValue.Equal. func (s *SamplePair) Equal(o *SamplePair) bool { return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp)) } func (s SamplePair) String() string { return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp) } // Sample is a sample pair associated with a metric. type Sample struct { Metric Metric `json:"metric"` Value SampleValue `json:"value"` Timestamp Time `json:"timestamp"` } // Equal compares first the metrics, then the timestamp, then the value. The // semantics of value equality is defined by SampleValue.Equal. func (s *Sample) Equal(o *Sample) bool { if s == o { return true } if !s.Metric.Equal(o.Metric) { return false } if !s.Timestamp.Equal(o.Timestamp) { return false } return s.Value.Equal(o.Value) } func (s Sample) String() string { return fmt.Sprintf("%s => %s", s.Metric, SamplePair{ Timestamp: s.Timestamp, Value: s.Value, }) } // MarshalJSON implements json.Marshaler. func (s Sample) MarshalJSON() ([]byte, error) { v := struct { Metric Metric `json:"metric"` Value SamplePair `json:"value"` }{ Metric: s.Metric, Value: SamplePair{ Timestamp: s.Timestamp, Value: s.Value, }, } return json.Marshal(&v) } // UnmarshalJSON implements json.Unmarshaler. func (s *Sample) UnmarshalJSON(b []byte) error { v := struct { Metric Metric `json:"metric"` Value SamplePair `json:"value"` }{ Metric: s.Metric, Value: SamplePair{ Timestamp: s.Timestamp, Value: s.Value, }, } if err := json.Unmarshal(b, &v); err != nil { return err } s.Metric = v.Metric s.Timestamp = v.Value.Timestamp s.Value = v.Value.Value return nil } // Samples is a sortable Sample slice. It implements sort.Interface. type Samples []*Sample func (s Samples) Len() int { return len(s) } // Less compares first the metrics, then the timestamp. func (s Samples) Less(i, j int) bool { switch { case s[i].Metric.Before(s[j].Metric): return true case s[j].Metric.Before(s[i].Metric): return false case s[i].Timestamp.Before(s[j].Timestamp): return true default: return false } } func (s Samples) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // Equal compares two sets of samples and returns true if they are equal. func (s Samples) Equal(o Samples) bool { if len(s) != len(o) { return false } for i, sample := range s { if !sample.Equal(o[i]) { return false } } return true } // SampleStream is a stream of Values belonging to an attached COWMetric. type SampleStream struct { Metric Metric `json:"metric"` Values []SamplePair `json:"values"` } func (ss SampleStream) String() string { vals := make([]string, len(ss.Values)) for i, v := range ss.Values { vals[i] = v.String() } return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n")) } // Value is a generic interface for values resulting from a query evaluation. type Value interface { Type() ValueType String() string } func (Matrix) Type() ValueType { return ValMatrix } func (Vector) Type() ValueType { return ValVector } func (*Scalar) Type() ValueType { return ValScalar } func (*String) Type() ValueType { return ValString } type ValueType int const ( ValNone ValueType = iota ValScalar ValVector ValMatrix ValString ) // MarshalJSON implements json.Marshaler. func (et ValueType) MarshalJSON() ([]byte, error) { return json.Marshal(et.String()) } func (et *ValueType) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { return err } switch s { case "": *et = ValNone case "scalar": *et = ValScalar case "vector": *et = ValVector case "matrix": *et = ValMatrix case "string": *et = ValString default: return fmt.Errorf("unknown value type %q", s) } return nil } func (e ValueType) String() string { switch e { case ValNone: return "" case ValScalar: return "scalar" case ValVector: return "vector" case ValMatrix: return "matrix" case ValString: return "string" } panic("ValueType.String: unhandled value type") } // Scalar is a scalar value evaluated at the set timestamp. type Scalar struct { Value SampleValue `json:"value"` Timestamp Time `json:"timestamp"` } func (s Scalar) String() string { return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp) } // MarshalJSON implements json.Marshaler. func (s Scalar) MarshalJSON() ([]byte, error) { v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64) return json.Marshal([...]interface{}{s.Timestamp, string(v)}) } // UnmarshalJSON implements json.Unmarshaler. func (s *Scalar) UnmarshalJSON(b []byte) error { var f string v := [...]interface{}{&s.Timestamp, &f} if err := json.Unmarshal(b, &v); err != nil { return err } value, err := strconv.ParseFloat(f, 64) if err != nil { return fmt.Errorf("error parsing sample value: %s", err) } s.Value = SampleValue(value) return nil } // String is a string value evaluated at the set timestamp. type String struct { Value string `json:"value"` Timestamp Time `json:"timestamp"` } func (s *String) String() string { return s.Value } // MarshalJSON implements json.Marshaler. func (s String) MarshalJSON() ([]byte, error) { return json.Marshal([]interface{}{s.Timestamp, s.Value}) } // UnmarshalJSON implements json.Unmarshaler. func (s *String) UnmarshalJSON(b []byte) error { v := [...]interface{}{&s.Timestamp, &s.Value} return json.Unmarshal(b, &v) } // Vector is basically only an alias for Samples, but the // contract is that in a Vector, all Samples have the same timestamp. type Vector []*Sample func (vec Vector) String() string { entries := make([]string, len(vec)) for i, s := range vec { entries[i] = s.String() } return strings.Join(entries, "\n") } func (vec Vector) Len() int { return len(vec) } func (vec Vector) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] } // Less compares first the metrics, then the timestamp. func (vec Vector) Less(i, j int) bool { switch { case vec[i].Metric.Before(vec[j].Metric): return true case vec[j].Metric.Before(vec[i].Metric): return false case vec[i].Timestamp.Before(vec[j].Timestamp): return true default: return false } } // Equal compares two sets of samples and returns true if they are equal. func (vec Vector) Equal(o Vector) bool { if len(vec) != len(o) { return false } for i, sample := range vec { if !sample.Equal(o[i]) { return false } } return true } // Matrix is a list of time series. type Matrix []*SampleStream func (m Matrix) Len() int { return len(m) } func (m Matrix) Less(i, j int) bool { return m[i].Metric.Before(m[j].Metric) } func (m Matrix) Swap(i, j int) { m[i], m[j] = m[j], m[i] } func (mat Matrix) String() string { matCp := make(Matrix, len(mat)) copy(matCp, mat) sort.Sort(matCp) strs := make([]string, len(matCp)) for i, ss := range matCp { strs[i] = ss.String() } return strings.Join(strs, "\n") } golang-github-prometheus-common-0.32.1/model/value_test.go000066400000000000000000000211611413406232300236040ustar00rootroot00000000000000// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "encoding/json" "math" "reflect" "sort" "testing" ) func TestEqualValues(t *testing.T) { tests := map[string]struct { in1, in2 SampleValue want bool }{ "equal floats": { in1: 3.14, in2: 3.14, want: true, }, "unequal floats": { in1: 3.14, in2: 3.1415, want: false, }, "positive inifinities": { in1: SampleValue(math.Inf(+1)), in2: SampleValue(math.Inf(+1)), want: true, }, "negative inifinities": { in1: SampleValue(math.Inf(-1)), in2: SampleValue(math.Inf(-1)), want: true, }, "different inifinities": { in1: SampleValue(math.Inf(+1)), in2: SampleValue(math.Inf(-1)), want: false, }, "number and infinity": { in1: 42, in2: SampleValue(math.Inf(+1)), want: false, }, "number and NaN": { in1: 42, in2: SampleValue(math.NaN()), want: false, }, "NaNs": { in1: SampleValue(math.NaN()), in2: SampleValue(math.NaN()), want: true, // !!! }, } for name, test := range tests { got := test.in1.Equal(test.in2) if got != test.want { t.Errorf("Comparing %s, %f and %f: got %t, want %t", name, test.in1, test.in2, got, test.want) } } } func TestEqualSamples(t *testing.T) { testSample := &Sample{} tests := map[string]struct { in1, in2 *Sample want bool }{ "equal pointers": { in1: testSample, in2: testSample, want: true, }, "different metrics": { in1: &Sample{Metric: Metric{"foo": "bar"}}, in2: &Sample{Metric: Metric{"foo": "biz"}}, want: false, }, "different timestamp": { in1: &Sample{Timestamp: 0}, in2: &Sample{Timestamp: 1}, want: false, }, "different value": { in1: &Sample{Value: 0}, in2: &Sample{Value: 1}, want: false, }, "equal samples": { in1: &Sample{ Metric: Metric{"foo": "bar"}, Timestamp: 0, Value: 1, }, in2: &Sample{ Metric: Metric{"foo": "bar"}, Timestamp: 0, Value: 1, }, want: true, }, } for name, test := range tests { got := test.in1.Equal(test.in2) if got != test.want { t.Errorf("Comparing %s, %v and %v: got %t, want %t", name, test.in1, test.in2, got, test.want) } } } func TestSamplePairJSON(t *testing.T) { input := []struct { plain string value SamplePair }{ { plain: `[1234.567,"123.1"]`, value: SamplePair{ Value: 123.1, Timestamp: 1234567, }, }, } for _, test := range input { b, err := json.Marshal(test.value) if err != nil { t.Error(err) continue } if string(b) != test.plain { t.Errorf("encoding error: expected %q, got %q", test.plain, b) continue } var sp SamplePair err = json.Unmarshal(b, &sp) if err != nil { t.Error(err) continue } if sp != test.value { t.Errorf("decoding error: expected %v, got %v", test.value, sp) } } } func TestSampleJSON(t *testing.T) { input := []struct { plain string value Sample }{ { plain: `{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}`, value: Sample{ Metric: Metric{ MetricNameLabel: "test_metric", }, Value: 123.1, Timestamp: 1234567, }, }, } for _, test := range input { b, err := json.Marshal(test.value) if err != nil { t.Error(err) continue } if string(b) != test.plain { t.Errorf("encoding error: expected %q, got %q", test.plain, b) continue } var sv Sample err = json.Unmarshal(b, &sv) if err != nil { t.Error(err) continue } if !reflect.DeepEqual(sv, test.value) { t.Errorf("decoding error: expected %v, got %v", test.value, sv) } } } func TestVectorJSON(t *testing.T) { input := []struct { plain string value Vector }{ { plain: `[]`, value: Vector{}, }, { plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}]`, value: Vector{&Sample{ Metric: Metric{ MetricNameLabel: "test_metric", }, Value: 123.1, Timestamp: 1234567, }}, }, { plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]},{"metric":{"foo":"bar"},"value":[1.234,"+Inf"]}]`, value: Vector{ &Sample{ Metric: Metric{ MetricNameLabel: "test_metric", }, Value: 123.1, Timestamp: 1234567, }, &Sample{ Metric: Metric{ "foo": "bar", }, Value: SampleValue(math.Inf(1)), Timestamp: 1234, }, }, }, } for _, test := range input { b, err := json.Marshal(test.value) if err != nil { t.Error(err) continue } if string(b) != test.plain { t.Errorf("encoding error: expected %q, got %q", test.plain, b) continue } var vec Vector err = json.Unmarshal(b, &vec) if err != nil { t.Error(err) continue } if !reflect.DeepEqual(vec, test.value) { t.Errorf("decoding error: expected %v, got %v", test.value, vec) } } } func TestScalarJSON(t *testing.T) { input := []struct { plain string value Scalar }{ { plain: `[123.456,"456"]`, value: Scalar{ Timestamp: 123456, Value: 456, }, }, { plain: `[123123.456,"+Inf"]`, value: Scalar{ Timestamp: 123123456, Value: SampleValue(math.Inf(1)), }, }, { plain: `[123123.456,"-Inf"]`, value: Scalar{ Timestamp: 123123456, Value: SampleValue(math.Inf(-1)), }, }, } for _, test := range input { b, err := json.Marshal(test.value) if err != nil { t.Error(err) continue } if string(b) != test.plain { t.Errorf("encoding error: expected %q, got %q", test.plain, b) continue } var sv Scalar err = json.Unmarshal(b, &sv) if err != nil { t.Error(err) continue } if sv != test.value { t.Errorf("decoding error: expected %v, got %v", test.value, sv) } } } func TestStringJSON(t *testing.T) { input := []struct { plain string value String }{ { plain: `[123.456,"test"]`, value: String{ Timestamp: 123456, Value: "test", }, }, { plain: `[123123.456,"å°åŒ—"]`, value: String{ Timestamp: 123123456, Value: "å°åŒ—", }, }, } for _, test := range input { b, err := json.Marshal(test.value) if err != nil { t.Error(err) continue } if string(b) != test.plain { t.Errorf("encoding error: expected %q, got %q", test.plain, b) continue } var sv String err = json.Unmarshal(b, &sv) if err != nil { t.Error(err) continue } if sv != test.value { t.Errorf("decoding error: expected %v, got %v", test.value, sv) } } } func TestVectorSort(t *testing.T) { input := Vector{ &Sample{ Metric: Metric{ MetricNameLabel: "A", }, Timestamp: 1, }, &Sample{ Metric: Metric{ MetricNameLabel: "A", }, Timestamp: 2, }, &Sample{ Metric: Metric{ MetricNameLabel: "C", }, Timestamp: 1, }, &Sample{ Metric: Metric{ MetricNameLabel: "C", }, Timestamp: 2, }, &Sample{ Metric: Metric{ MetricNameLabel: "B", }, Timestamp: 3, }, &Sample{ Metric: Metric{ MetricNameLabel: "B", }, Timestamp: 2, }, &Sample{ Metric: Metric{ MetricNameLabel: "B", }, Timestamp: 1, }, } expected := Vector{ &Sample{ Metric: Metric{ MetricNameLabel: "A", }, Timestamp: 1, }, &Sample{ Metric: Metric{ MetricNameLabel: "A", }, Timestamp: 2, }, &Sample{ Metric: Metric{ MetricNameLabel: "B", }, Timestamp: 1, }, &Sample{ Metric: Metric{ MetricNameLabel: "B", }, Timestamp: 2, }, &Sample{ Metric: Metric{ MetricNameLabel: "B", }, Timestamp: 3, }, &Sample{ Metric: Metric{ MetricNameLabel: "C", }, Timestamp: 1, }, &Sample{ Metric: Metric{ MetricNameLabel: "C", }, Timestamp: 2, }, } sort.Sort(input) for i, actual := range input { actualFp := actual.Metric.Fingerprint() expectedFp := expected[i].Metric.Fingerprint() if actualFp != expectedFp { t.Fatalf("%d. Incorrect fingerprint. Got %s; want %s", i, actualFp.String(), expectedFp.String()) } if actual.Timestamp != expected[i].Timestamp { t.Fatalf("%d. Incorrect timestamp. Got %s; want %s", i, actual.Timestamp, expected[i].Timestamp) } } } golang-github-prometheus-common-0.32.1/promlog/000077500000000000000000000000001413406232300214605ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/promlog/flag/000077500000000000000000000000001413406232300223715ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/promlog/flag/flag.go000066400000000000000000000033631413406232300236360ustar00rootroot00000000000000// Copyright 2017 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package flag import ( "github.com/prometheus/common/promlog" kingpin "gopkg.in/alecthomas/kingpin.v2" ) // LevelFlagName is the canonical flag name to configure the allowed log level // within Prometheus projects. const LevelFlagName = "log.level" // LevelFlagHelp is the help description for the log.level flag. const LevelFlagHelp = "Only log messages with the given severity or above. One of: [debug, info, warn, error]" // FormatFlagName is the canonical flag name to configure the log format // within Prometheus projects. const FormatFlagName = "log.format" // FormatFlagHelp is the help description for the log.format flag. const FormatFlagHelp = "Output format of log messages. One of: [logfmt, json]" // AddFlags adds the flags used by this package to the Kingpin application. // To use the default Kingpin application, call AddFlags(kingpin.CommandLine) func AddFlags(a *kingpin.Application, config *promlog.Config) { config.Level = &promlog.AllowedLevel{} a.Flag(LevelFlagName, LevelFlagHelp). Default("info").SetValue(config.Level) config.Format = &promlog.AllowedFormat{} a.Flag(FormatFlagName, FormatFlagHelp). Default("logfmt").SetValue(config.Format) } golang-github-prometheus-common-0.32.1/promlog/log.go000066400000000000000000000107611413406232300225750ustar00rootroot00000000000000// Copyright 2017 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package promlog defines standardised ways to initialize Go kit loggers // across Prometheus components. // It should typically only ever be imported by main packages. package promlog import ( "os" "sync" "time" "github.com/go-kit/log" "github.com/go-kit/log/level" "github.com/pkg/errors" ) var ( // This timestamp format differs from RFC3339Nano by using .000 instead // of .999999999 which changes the timestamp from 9 variable to 3 fixed // decimals (.130 instead of .130987456). timestampFormat = log.TimestampFormat( func() time.Time { return time.Now().UTC() }, "2006-01-02T15:04:05.000Z07:00", ) ) // AllowedLevel is a settable identifier for the minimum level a log entry // must be have. type AllowedLevel struct { s string o level.Option } func (l *AllowedLevel) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string type plain string if err := unmarshal((*plain)(&s)); err != nil { return err } if s == "" { return nil } lo := &AllowedLevel{} if err := lo.Set(s); err != nil { return err } *l = *lo return nil } func (l *AllowedLevel) String() string { return l.s } // Set updates the value of the allowed level. func (l *AllowedLevel) Set(s string) error { switch s { case "debug": l.o = level.AllowDebug() case "info": l.o = level.AllowInfo() case "warn": l.o = level.AllowWarn() case "error": l.o = level.AllowError() default: return errors.Errorf("unrecognized log level %q", s) } l.s = s return nil } // AllowedFormat is a settable identifier for the output format that the logger can have. type AllowedFormat struct { s string } func (f *AllowedFormat) String() string { return f.s } // Set updates the value of the allowed format. func (f *AllowedFormat) Set(s string) error { switch s { case "logfmt", "json": f.s = s default: return errors.Errorf("unrecognized log format %q", s) } return nil } // Config is a struct containing configurable settings for the logger type Config struct { Level *AllowedLevel Format *AllowedFormat } // New returns a new leveled oklog logger. Each logged line will be annotated // with a timestamp. The output always goes to stderr. func New(config *Config) log.Logger { var l log.Logger if config.Format != nil && config.Format.s == "json" { l = log.NewJSONLogger(log.NewSyncWriter(os.Stderr)) } else { l = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) } if config.Level != nil { l = log.With(l, "ts", timestampFormat, "caller", log.Caller(5)) l = level.NewFilter(l, config.Level.o) } else { l = log.With(l, "ts", timestampFormat, "caller", log.DefaultCaller) } return l } // NewDynamic returns a new leveled logger. Each logged line will be annotated // with a timestamp. The output always goes to stderr. Some properties can be // changed, like the level. func NewDynamic(config *Config) *logger { var l log.Logger if config.Format != nil && config.Format.s == "json" { l = log.NewJSONLogger(log.NewSyncWriter(os.Stderr)) } else { l = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) } lo := &logger{ base: l, leveled: l, } if config.Level != nil { lo.SetLevel(config.Level) } return lo } type logger struct { base log.Logger leveled log.Logger currentLevel *AllowedLevel mtx sync.Mutex } // Log implements logger.Log. func (l *logger) Log(keyvals ...interface{}) error { l.mtx.Lock() defer l.mtx.Unlock() return l.leveled.Log(keyvals...) } // SetLevel changes the log level. func (l *logger) SetLevel(lvl *AllowedLevel) { l.mtx.Lock() defer l.mtx.Unlock() if lvl == nil { l.leveled = log.With(l.base, "ts", timestampFormat, "caller", log.DefaultCaller) l.currentLevel = nil return } if l.currentLevel != nil && l.currentLevel.s != lvl.s { _ = l.base.Log("msg", "Log level changed", "prev", l.currentLevel, "current", lvl) } l.currentLevel = lvl l.leveled = level.NewFilter(log.With(l.base, "ts", timestampFormat, "caller", log.Caller(5)), lvl.o) } golang-github-prometheus-common-0.32.1/promlog/log_test.go000066400000000000000000000055541413406232300236400ustar00rootroot00000000000000// Copyright 2020 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package promlog import ( "fmt" "testing" "github.com/go-kit/log/level" "gopkg.in/yaml.v2" ) // Make sure creating and using a logger with an empty configuration doesn't // result in a panic. func TestDefaultConfig(t *testing.T) { logger := New(&Config{}) if err := logger.Log("hello", "world"); err != nil { t.Fatal(err) } } func TestUnmarshallLevel(t *testing.T) { l := &AllowedLevel{} err := yaml.Unmarshal([]byte(`debug`), l) if err != nil { t.Error(err) } if l.s != "debug" { t.Errorf("expected %s, got %s", "debug", l.s) } } func TestUnmarshallEmptyLevel(t *testing.T) { l := &AllowedLevel{} err := yaml.Unmarshal([]byte(``), l) if err != nil { t.Error(err) } if l.s != "" { t.Errorf("expected empty level, got %s", l.s) } } func TestUnmarshallBadLevel(t *testing.T) { l := &AllowedLevel{} err := yaml.Unmarshal([]byte(`debugg`), l) if err == nil { t.Error("expected error") } expErr := `unrecognized log level "debugg"` if err.Error() != expErr { t.Errorf("expected error %s, got %s", expErr, err.Error()) } if l.s != "" { t.Errorf("expected empty level, got %s", l.s) } } type recordKeyvalLogger struct { count int } func (r *recordKeyvalLogger) Log(keyvals ...interface{}) error { for _, v := range keyvals { if fmt.Sprintf("%v", v) == "Log level changed" { return nil } } r.count++ return nil } func TestDynamic(t *testing.T) { logger := NewDynamic(&Config{}) debugLevel := &AllowedLevel{} if err := debugLevel.Set("debug"); err != nil { t.Fatal(err) } infoLevel := &AllowedLevel{} if err := infoLevel.Set("info"); err != nil { t.Fatal(err) } recorder := &recordKeyvalLogger{} logger.base = recorder logger.SetLevel(debugLevel) if err := level.Debug(logger).Log("hello", "world"); err != nil { t.Fatal(err) } if recorder.count != 1 { t.Fatal("log not found") } recorder.count = 0 logger.SetLevel(infoLevel) if err := level.Debug(logger).Log("hello", "world"); err != nil { t.Fatal(err) } if recorder.count != 0 { t.Fatal("log found") } if err := level.Info(logger).Log("hello", "world"); err != nil { t.Fatal(err) } if recorder.count != 1 { t.Fatal("log not found") } if err := level.Debug(logger).Log("hello", "world"); err != nil { t.Fatal(err) } if recorder.count != 1 { t.Fatal("extra log found") } } golang-github-prometheus-common-0.32.1/route/000077500000000000000000000000001413406232300211375ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/route/route.go000066400000000000000000000104701413406232300226260ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package route import ( "context" "net/http" "github.com/julienschmidt/httprouter" ) type param string // Param returns param p for the context, or the empty string when // param does not exist in context. func Param(ctx context.Context, p string) string { if v := ctx.Value(param(p)); v != nil { return v.(string) } return "" } // WithParam returns a new context with param p set to v. func WithParam(ctx context.Context, p, v string) context.Context { return context.WithValue(ctx, param(p), v) } // Router wraps httprouter.Router and adds support for prefixed sub-routers, // per-request context injections and instrumentation. type Router struct { rtr *httprouter.Router prefix string instrh func(handlerName string, handler http.HandlerFunc) http.HandlerFunc } // New returns a new Router. func New() *Router { return &Router{ rtr: httprouter.New(), } } // WithInstrumentation returns a router with instrumentation support. func (r *Router) WithInstrumentation(instrh func(handlerName string, handler http.HandlerFunc) http.HandlerFunc) *Router { if r.instrh != nil { newInstrh := instrh instrh = func(handlerName string, handler http.HandlerFunc) http.HandlerFunc { return newInstrh(handlerName, r.instrh(handlerName, handler)) } } return &Router{rtr: r.rtr, prefix: r.prefix, instrh: instrh} } // WithPrefix returns a router that prefixes all registered routes with prefix. func (r *Router) WithPrefix(prefix string) *Router { return &Router{rtr: r.rtr, prefix: r.prefix + prefix, instrh: r.instrh} } // handle turns a HandlerFunc into an httprouter.Handle. func (r *Router) handle(handlerName string, h http.HandlerFunc) httprouter.Handle { if r.instrh != nil { // This needs to be outside the closure to avoid data race when reading and writing to 'h'. h = r.instrh(handlerName, h) } return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() for _, p := range params { ctx = context.WithValue(ctx, param(p.Key), p.Value) } h(w, req.WithContext(ctx)) } } // Get registers a new GET route. func (r *Router) Get(path string, h http.HandlerFunc) { r.rtr.GET(r.prefix+path, r.handle(path, h)) } // Options registers a new OPTIONS route. func (r *Router) Options(path string, h http.HandlerFunc) { r.rtr.OPTIONS(r.prefix+path, r.handle(path, h)) } // Del registers a new DELETE route. func (r *Router) Del(path string, h http.HandlerFunc) { r.rtr.DELETE(r.prefix+path, r.handle(path, h)) } // Put registers a new PUT route. func (r *Router) Put(path string, h http.HandlerFunc) { r.rtr.PUT(r.prefix+path, r.handle(path, h)) } // Post registers a new POST route. func (r *Router) Post(path string, h http.HandlerFunc) { r.rtr.POST(r.prefix+path, r.handle(path, h)) } // Head registers a new HEAD route. func (r *Router) Head(path string, h http.HandlerFunc) { r.rtr.HEAD(r.prefix+path, r.handle(path, h)) } // Redirect takes an absolute path and sends an internal HTTP redirect for it, // prefixed by the router's path prefix. Note that this method does not include // functionality for handling relative paths or full URL redirects. func (r *Router) Redirect(w http.ResponseWriter, req *http.Request, path string, code int) { http.Redirect(w, req, r.prefix+path, code) } // ServeHTTP implements http.Handler. func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { r.rtr.ServeHTTP(w, req) } // FileServe returns a new http.HandlerFunc that serves files from dir. // Using routes must provide the *filepath parameter. func FileServe(dir string) http.HandlerFunc { fs := http.FileServer(http.Dir(dir)) return func(w http.ResponseWriter, r *http.Request) { r.URL.Path = Param(r.Context(), "filepath") fs.ServeHTTP(w, r) } } golang-github-prometheus-common-0.32.1/route/route_test.go000066400000000000000000000116511413406232300236670ustar00rootroot00000000000000// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package route import ( "net/http" "net/http/httptest" "testing" ) func TestRedirect(t *testing.T) { router := New().WithPrefix("/test/prefix") w := httptest.NewRecorder() r, err := http.NewRequest("GET", "http://localhost:9090/foo", nil) if err != nil { t.Fatalf("Error building test request: %s", err) } router.Redirect(w, r, "/some/endpoint", http.StatusFound) if w.Code != http.StatusFound { t.Fatalf("Unexpected redirect status code: got %d, want %d", w.Code, http.StatusFound) } want := "/test/prefix/some/endpoint" got := w.Header()["Location"][0] if want != got { t.Fatalf("Unexpected redirect location: got %s, want %s", got, want) } } func TestContext(t *testing.T) { router := New() router.Get("/test/:foo/", func(w http.ResponseWriter, r *http.Request) { want := "bar" got := Param(r.Context(), "foo") if want != got { t.Fatalf("Unexpected context value: want %q, got %q", want, got) } }) r, err := http.NewRequest("GET", "http://localhost:9090/test/bar/", nil) if err != nil { t.Fatalf("Error building test request: %s", err) } router.ServeHTTP(nil, r) } func TestContextWithValue(t *testing.T) { router := New() router.Get("/test/:foo/", func(w http.ResponseWriter, r *http.Request) { want := "bar" got := Param(r.Context(), "foo") if want != got { t.Fatalf("Unexpected context value: want %q, got %q", want, got) } want = "ipsum" got = Param(r.Context(), "lorem") if want != got { t.Fatalf("Unexpected context value: want %q, got %q", want, got) } want = "sit" got = Param(r.Context(), "dolor") if want != got { t.Fatalf("Unexpected context value: want %q, got %q", want, got) } }) r, err := http.NewRequest("GET", "http://localhost:9090/test/bar/", nil) if err != nil { t.Fatalf("Error building test request: %s", err) } params := map[string]string{ "lorem": "ipsum", "dolor": "sit", } ctx := r.Context() for p, v := range params { ctx = WithParam(ctx, p, v) } r = r.WithContext(ctx) router.ServeHTTP(nil, r) } func TestContextWithoutValue(t *testing.T) { router := New() router.Get("/test", func(w http.ResponseWriter, r *http.Request) { want := "" got := Param(r.Context(), "foo") if want != got { t.Fatalf("Unexpected context value: want %q, got %q", want, got) } }) r, err := http.NewRequest("GET", "http://localhost:9090/test", nil) if err != nil { t.Fatalf("Error building test request: %s", err) } router.ServeHTTP(nil, r) } func TestInstrumentation(t *testing.T) { var got string cases := []struct { router *Router want string }{ { router: New(), want: "", }, { router: New().WithInstrumentation(func(handlerName string, handler http.HandlerFunc) http.HandlerFunc { got = handlerName return handler }), want: "/foo", }, } for _, c := range cases { c.router.Get("/foo", func(w http.ResponseWriter, r *http.Request) {}) r, err := http.NewRequest("GET", "http://localhost:9090/foo", nil) if err != nil { t.Fatalf("Error building test request: %s", err) } c.router.ServeHTTP(nil, r) if c.want != got { t.Fatalf("Unexpected value: want %q, got %q", c.want, got) } } } func TestInstrumentations(t *testing.T) { got := make([]string, 0) cases := []struct { router *Router want []string }{ { router: New(), want: []string{}, }, { router: New(). WithInstrumentation( func(handlerName string, handler http.HandlerFunc) http.HandlerFunc { got = append(got, "1"+handlerName) return handler }). WithInstrumentation( func(handlerName string, handler http.HandlerFunc) http.HandlerFunc { got = append(got, "2"+handlerName) return handler }). WithInstrumentation( func(handlerName string, handler http.HandlerFunc) http.HandlerFunc { got = append(got, "3"+handlerName) return handler }), want: []string{"1/foo", "2/foo", "3/foo"}, }, } for _, c := range cases { c.router.Get("/foo", func(w http.ResponseWriter, r *http.Request) {}) r, err := http.NewRequest("GET", "http://localhost:9090/foo", nil) if err != nil { t.Fatalf("Error building test request: %s", err) } c.router.ServeHTTP(nil, r) if len(c.want) != len(got) { t.Fatalf("Unexpected value: want %q, got %q", c.want, got) } for i, v := range c.want { if v != got[i] { t.Fatalf("Unexpected value: want %q, got %q", c.want, got) } } } } golang-github-prometheus-common-0.32.1/server/000077500000000000000000000000001413406232300213075ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/server/static_file_server.go000066400000000000000000000021401413406232300255070ustar00rootroot00000000000000// Copyright 2019 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package server import ( "net/http" "path/filepath" ) var mimeTypes = map[string]string{ ".js": "application/javascript", ".css": "text/css", ".png": "image/png", ".jpg": "image/jpeg", ".gif": "image/gif", } func StaticFileServer(root http.FileSystem) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { fileExt := filepath.Ext(r.URL.Path) if t, ok := mimeTypes[fileExt]; ok { w.Header().Set("Content-Type", t) } http.FileServer(root).ServeHTTP(w, r) }, ) } golang-github-prometheus-common-0.32.1/server/static_file_server_test.go000066400000000000000000000035611413406232300265560ustar00rootroot00000000000000// Copyright 2019 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package server import ( "net/http" "net/http/httptest" "testing" ) type dummyFileSystem struct{} func (fs dummyFileSystem) Open(path string) (http.File, error) { return http.Dir(".").Open(".") } func TestServeHttp(t *testing.T) { cases := []struct { name string path string contentType string }{ { name: "normal file", path: "index.html", contentType: "", }, { name: "javascript", path: "test.js", contentType: "application/javascript", }, { name: "css", path: "test.css", contentType: "text/css", }, { name: "png", path: "test.png", contentType: "image/png", }, { name: "jpg", path: "test.jpg", contentType: "image/jpeg", }, { name: "gif", path: "test.gif", contentType: "image/gif", }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { rr := httptest.NewRecorder() req, err := http.NewRequest("GET", "http://localhost/"+c.path, nil) if err != nil { t.Fatal(err) } s := StaticFileServer(dummyFileSystem{}) s.ServeHTTP(rr, req) if rr.Header().Get("Content-Type") != c.contentType { t.Fatalf("Unexpected Content-Type: %s", rr.Header().Get("Content-Type")) } }) } } golang-github-prometheus-common-0.32.1/sigv4/000077500000000000000000000000001413406232300210355ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/sigv4/Makefile000066400000000000000000000014301413406232300224730ustar00rootroot00000000000000# Copyright 2018 The Prometheus Authors # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. include ../Makefile.common .PHONY: test @echo ">> Running sigv4 tests" test:: deps check_license unused common-test ifeq (,$(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(7|8|9|10)\.')) test:: lint endif golang-github-prometheus-common-0.32.1/sigv4/README.md000066400000000000000000000007611413406232300223200ustar00rootroot00000000000000github.com/prometheus/common/sigv4 module ========================================= sigv4 provides a http.RoundTripper that will sign requests using Amazon's Signature Verification V4 signing procedure, using credentials from the default AWS credential chain. This is a separate module from github.com/prometheus/common to prevent it from having and propagating a dependency on the AWS SDK. This module is considered internal to Prometheus, without any stability guarantees for external usage. golang-github-prometheus-common-0.32.1/sigv4/go.mod000066400000000000000000000003631413406232300221450ustar00rootroot00000000000000module github.com/prometheus/common/sigv4 go 1.15 require ( github.com/aws/aws-sdk-go v1.41.6 github.com/prometheus/client_golang v1.11.0 github.com/prometheus/common v0.31.1 github.com/stretchr/testify v1.7.0 gopkg.in/yaml.v2 v2.4.0 ) golang-github-prometheus-common-0.32.1/sigv4/go.sum000066400000000000000000001353001413406232300221720ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/aws/aws-sdk-go v1.41.6 h1:ojO1jWhE3lkJlTFQOq0rlWZ11q18LIdsZNtGJ07FFEA= github.com/aws/aws-sdk-go v1.41.6/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.31.1 h1:d18hG4PkHnNAKNMOmFuXFaiY8Us0nird/2m60uS1AMs= github.com/prometheus/common v0.31.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= golang-github-prometheus-common-0.32.1/sigv4/sigv4.go000066400000000000000000000100231413406232300224140ustar00rootroot00000000000000// Copyright 2021 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sigv4 import ( "bytes" "fmt" "io" "io/ioutil" "net/http" "net/textproto" "sync" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go/aws/session" signer "github.com/aws/aws-sdk-go/aws/signer/v4" ) var sigv4HeaderDenylist = []string{ "uber-trace-id", } type sigV4RoundTripper struct { region string next http.RoundTripper pool sync.Pool signer *signer.Signer } // NewSigV4RoundTripper returns a new http.RoundTripper that will sign requests // using Amazon's Signature Verification V4 signing procedure. The request will // then be handed off to the next RoundTripper provided by next. If next is nil, // http.DefaultTransport will be used. // // Credentials for signing are retrieved using the the default AWS credential // chain. If credentials cannot be found, an error will be returned. func NewSigV4RoundTripper(cfg *SigV4Config, next http.RoundTripper) (http.RoundTripper, error) { if next == nil { next = http.DefaultTransport } creds := credentials.NewStaticCredentials(cfg.AccessKey, string(cfg.SecretKey), "") if cfg.AccessKey == "" && cfg.SecretKey == "" { creds = nil } sess, err := session.NewSessionWithOptions(session.Options{ Config: aws.Config{ Region: aws.String(cfg.Region), Credentials: creds, }, Profile: cfg.Profile, }) if err != nil { return nil, fmt.Errorf("could not create new AWS session: %w", err) } if _, err := sess.Config.Credentials.Get(); err != nil { return nil, fmt.Errorf("could not get SigV4 credentials: %w", err) } if aws.StringValue(sess.Config.Region) == "" { return nil, fmt.Errorf("region not configured in sigv4 or in default credentials chain") } signerCreds := sess.Config.Credentials if cfg.RoleARN != "" { signerCreds = stscreds.NewCredentials(sess, cfg.RoleARN) } rt := &sigV4RoundTripper{ region: cfg.Region, next: next, signer: signer.NewSigner(signerCreds), } rt.pool.New = rt.newBuf return rt, nil } func (rt *sigV4RoundTripper) newBuf() interface{} { return bytes.NewBuffer(make([]byte, 0, 1024)) } func (rt *sigV4RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { // rt.signer.Sign needs a seekable body, so we replace the body with a // buffered reader filled with the contents of original body. buf := rt.pool.Get().(*bytes.Buffer) defer func() { buf.Reset() rt.pool.Put(buf) }() if _, err := io.Copy(buf, req.Body); err != nil { return nil, err } // Close the original body since we don't need it anymore. _ = req.Body.Close() // Ensure our seeker is back at the start of the buffer once we return. var seeker io.ReadSeeker = bytes.NewReader(buf.Bytes()) defer func() { _, _ = seeker.Seek(0, io.SeekStart) }() req.Body = ioutil.NopCloser(seeker) // Clone the request and trim out headers that we don't want to sign. signReq := req.Clone(req.Context()) for _, header := range sigv4HeaderDenylist { signReq.Header.Del(header) } headers, err := rt.signer.Sign(signReq, seeker, "aps", rt.region, time.Now().UTC()) if err != nil { return nil, fmt.Errorf("failed to sign request: %w", err) } // Copy over signed headers. Authorization header is not returned by // rt.signer.Sign and needs to be copied separately. for k, v := range headers { req.Header[textproto.CanonicalMIMEHeaderKey(k)] = v } req.Header.Set("Authorization", signReq.Header.Get("Authorization")) return rt.next.RoundTrip(req) } golang-github-prometheus-common-0.32.1/sigv4/sigv4_config.go000066400000000000000000000030661413406232300237520ustar00rootroot00000000000000// Copyright 2021 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sigv4 import ( "fmt" "github.com/prometheus/common/config" ) // SigV4Config is the configuration for signing remote write requests with // AWS's SigV4 verification process. Empty values will be retrieved using the // AWS default credentials chain. type SigV4Config struct { Region string `yaml:"region,omitempty"` AccessKey string `yaml:"access_key,omitempty"` SecretKey config.Secret `yaml:"secret_key,omitempty"` Profile string `yaml:"profile,omitempty"` RoleARN string `yaml:"role_arn,omitempty"` } func (c *SigV4Config) Validate() error { if (c.AccessKey == "") != (c.SecretKey == "") { return fmt.Errorf("must provide a AWS SigV4 Access key and Secret Key if credentials are specified in the SigV4 config") } return nil } func (c *SigV4Config) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain SigV4Config *c = SigV4Config{} if err := unmarshal((*plain)(c)); err != nil { return err } return c.Validate() } golang-github-prometheus-common-0.32.1/sigv4/sigv4_config_test.go000066400000000000000000000032571413406232300250130ustar00rootroot00000000000000// Copyright 2021 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sigv4 import ( "io/ioutil" "strings" "testing" "gopkg.in/yaml.v2" ) func loadSigv4Config(filename string) (*SigV4Config, error) { content, err := ioutil.ReadFile(filename) if err != nil { return nil, err } cfg := SigV4Config{} if err = yaml.UnmarshalStrict(content, &cfg); err != nil { return nil, err } return &cfg, nil } func testGoodConfig(t *testing.T, filename string) { _, err := loadSigv4Config(filename) if err != nil { t.Fatalf("Unexpected error parsing %s: %s", filename, err) } } func TestGoodSigV4Configs(t *testing.T) { filesToTest := []string{"testdata/sigv4_good.yaml", "testdata/sigv4_good.yaml"} for _, filename := range filesToTest { testGoodConfig(t, filename) } } func TestBadSigV4Config(t *testing.T) { filename := "testdata/sigv4_bad.yaml" _, err := loadSigv4Config(filename) if err == nil { t.Fatalf("Did not receive expected error unmarshaling bad sigv4 config") } if !strings.Contains(err.Error(), "must provide a AWS SigV4 Access key and Secret Key") { t.Errorf("Received unexpected error from unmarshal of %s: %s", filename, err.Error()) } } golang-github-prometheus-common-0.32.1/sigv4/sigv4_test.go000066400000000000000000000053761413406232300234720ustar00rootroot00000000000000// Copyright 2021 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sigv4 import ( "net/http" "os" "strings" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" signer "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/stretchr/testify/require" ) func TestSigV4_Inferred_Region(t *testing.T) { os.Setenv("AWS_ACCESS_KEY_ID", "secret") os.Setenv("AWS_SECRET_ACCESS_KEY", "token") os.Setenv("AWS_REGION", "us-west-2") sess, err := session.NewSession(&aws.Config{ // Setting to an empty string to demostrate the default value from the yaml // won't override the environment's region. Region: aws.String(""), }) require.NoError(t, err) _, err = sess.Config.Credentials.Get() require.NoError(t, err) require.NotNil(t, sess.Config.Region) require.Equal(t, "us-west-2", *sess.Config.Region) } func TestSigV4RoundTripper(t *testing.T) { var gotReq *http.Request rt := &sigV4RoundTripper{ region: "us-east-2", next: promhttp.RoundTripperFunc(func(req *http.Request) (*http.Response, error) { gotReq = req return &http.Response{StatusCode: http.StatusOK}, nil }), signer: signer.NewSigner(credentials.NewStaticCredentials( "test-id", "secret", "token", )), } rt.pool.New = rt.newBuf cli := &http.Client{Transport: rt} req, err := http.NewRequest(http.MethodPost, "google.com", strings.NewReader("Hello, world!")) require.NoError(t, err) _, err = cli.Do(req) require.NoError(t, err) require.NotNil(t, gotReq) origReq := gotReq require.NotEmpty(t, origReq.Header.Get("Authorization")) require.NotEmpty(t, origReq.Header.Get("X-Amz-Date")) // Perform the same request but with a header that shouldn't included in the // signature; validate that the Authorization signature matches. t.Run("Ignored Headers", func(t *testing.T) { req, err := http.NewRequest(http.MethodPost, "google.com", strings.NewReader("Hello, world!")) require.NoError(t, err) req.Header.Add("Uber-Trace-Id", "some-trace-id") _, err = cli.Do(req) require.NoError(t, err) require.NotNil(t, gotReq) require.Equal(t, origReq.Header.Get("Authorization"), gotReq.Header.Get("Authorization")) }) } golang-github-prometheus-common-0.32.1/sigv4/testdata/000077500000000000000000000000001413406232300226465ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/sigv4/testdata/sigv4_bad.yaml000066400000000000000000000001211413406232300253660ustar00rootroot00000000000000region: us-east-2 access_key: AccessKey profile: profile role_arn: blah:role/arn golang-github-prometheus-common-0.32.1/sigv4/testdata/sigv4_good.yaml000066400000000000000000000001471413406232300256000ustar00rootroot00000000000000region: us-east-2 access_key: AccessKey secret_key: SecretKey profile: profile role_arn: blah:role/arn golang-github-prometheus-common-0.32.1/sigv4/testdata/sigv4_good_empty_keys.yaml000066400000000000000000000000721413406232300300460ustar00rootroot00000000000000region: us-east-2 profile: profile role_arn: blah:role/arngolang-github-prometheus-common-0.32.1/version/000077500000000000000000000000001413406232300214665ustar00rootroot00000000000000golang-github-prometheus-common-0.32.1/version/info.go000066400000000000000000000052301413406232300227500ustar00rootroot00000000000000// Copyright 2016 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package version import ( "bytes" "fmt" "runtime" "strings" "text/template" "github.com/prometheus/client_golang/prometheus" ) // Build information. Populated at build-time. var ( Version string Revision string Branch string BuildUser string BuildDate string GoVersion = runtime.Version() ) // NewCollector returns a collector that exports metrics about current version // information. func NewCollector(program string) prometheus.Collector { return prometheus.NewGaugeFunc( prometheus.GaugeOpts{ Namespace: program, Name: "build_info", Help: fmt.Sprintf( "A metric with a constant '1' value labeled by version, revision, branch, and goversion from which %s was built.", program, ), ConstLabels: prometheus.Labels{ "version": Version, "revision": Revision, "branch": Branch, "goversion": GoVersion, }, }, func() float64 { return 1 }, ) } // versionInfoTmpl contains the template used by Info. var versionInfoTmpl = ` {{.program}}, version {{.version}} (branch: {{.branch}}, revision: {{.revision}}) build user: {{.buildUser}} build date: {{.buildDate}} go version: {{.goVersion}} platform: {{.platform}} ` // Print returns version information. func Print(program string) string { m := map[string]string{ "program": program, "version": Version, "revision": Revision, "branch": Branch, "buildUser": BuildUser, "buildDate": BuildDate, "goVersion": GoVersion, "platform": runtime.GOOS + "/" + runtime.GOARCH, } t := template.Must(template.New("version").Parse(versionInfoTmpl)) var buf bytes.Buffer if err := t.ExecuteTemplate(&buf, "version", m); err != nil { panic(err) } return strings.TrimSpace(buf.String()) } // Info returns version, branch and revision information. func Info() string { return fmt.Sprintf("(version=%s, branch=%s, revision=%s)", Version, Branch, Revision) } // BuildContext returns goVersion, buildUser and buildDate information. func BuildContext() string { return fmt.Sprintf("(go=%s, user=%s, date=%s)", GoVersion, BuildUser, BuildDate) }