From 4d30f926a4d82f9db52604b0e4d10396a2994360 Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Wed, 26 Jul 2017 12:15:50 +0200 Subject: [PATCH] Update build * Fix use of `prometheus` to use `promhttp`. * Enable `vet` and `staticcheck` in build. * Update to Go 1.8.x. --- .travis.yml | 6 +- Makefile | 33 ++- circle.yml | 1 + main.go | 3 +- .../client_golang/prometheus/promhttp/http.go | 201 ++++++++++++++++++ vendor/vendor.json | 8 + 6 files changed, 239 insertions(+), 13 deletions(-) create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go diff --git a/.travis.yml b/.travis.yml index 24225ea..4591c99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,10 @@ sudo: false language: go go: -- 1.5.4 -- 1.6.2 +- 1.8.x +- master + +go_import_path: github.com/prometheus/influxdb_exporter script: - make diff --git a/Makefile b/Makefile index 8d2f055..f44b3cb 100644 --- a/Makefile +++ b/Makefile @@ -12,8 +12,11 @@ # limitations under the License. GO := GO15VENDOREXPERIMENT=1 go -PROMU := $(GOPATH)/bin/promu -pkgs = $(shell $(GO) list ./... | grep -v /vendor/) +GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH))) + +PROMU ?= $(GOPATH)/bin/promu +STATICCHECK ?= $(GOPATH)/bin/staticcheck +pkgs = $(shell $(GO) list ./... | grep -v /vendor/) PREFIX ?= $(shell pwd) BIN_DIR ?= $(shell pwd) @@ -21,7 +24,7 @@ DOCKER_IMAGE_NAME ?= influxdb-exporter DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD)) -all: format build test +all: format vet staticcheck build test style: @echo ">> checking code style" @@ -39,11 +42,15 @@ vet: @echo ">> vetting code" @$(GO) vet $(pkgs) -build: promu +staticcheck: $(STATICCHECK) + @echo ">> running staticcheck" + @$(STATICCHECK) $(pkgs) + +build: $(PROMU) @echo ">> building binaries" @$(PROMU) build --prefix $(PREFIX) -tarball: promu +tarball: $(PROMU) @echo ">> building release tarball" @$(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR) @@ -51,10 +58,16 @@ docker: @echo ">> building docker image" @docker build -t "$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" . -promu: - @GOOS=$(shell uname -s | tr A-Z a-z) \ - GOARCH=$(subst x86_64,amd64,$(patsubst i%86,386,$(shell uname -m))) \ - $(GO) get -u github.com/prometheus/promu +$(GOPATH)/bin/promu promu: + @GOOS= GOARCH= $(GO) get -u github.com/prometheus/promu + +$(GOPATH)/bin/staticcheck: + @GOOS= GOARCH= $(GO) get -u honnef.co/go/tools/cmd/staticcheck +.PHONY: all style format build test vet tarball docker promu staticcheck -.PHONY: all style format build test vet tarball docker promu +# Declaring the binaries at their default locations as PHONY targets is a hack +# to ensure the latest version is downloaded on every make execution. +# If this is not desired, copy/symlink these binaries to a different path and +# set the respective environment variables. +.PHONY: $(GOPATH)/bin/promu $(GOPATH)/bin/staticcheck diff --git a/circle.yml b/circle.yml index 571de08..691cff8 100644 --- a/circle.yml +++ b/circle.yml @@ -48,6 +48,7 @@ deployment: owner: prometheus commands: - promu crossbuild tarballs + - promu checksum .tarballs - promu release .tarballs - mkdir $CIRCLE_ARTIFACTS/releases/ && cp -a .tarballs/* $CIRCLE_ARTIFACTS/releases/ - docker login -e $DOCKER_EMAIL -u $DOCKER_LOGIN -p $DOCKER_PASSWORD diff --git a/main.go b/main.go index 392a684..51b2bb8 100644 --- a/main.go +++ b/main.go @@ -26,6 +26,7 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/log" "github.com/prometheus/common/version" @@ -281,7 +282,7 @@ func main() { fmt.Fprintf(w, `{"results": []}`) }) - http.Handle(*metricsPath, prometheus.Handler()) + http.Handle(*metricsPath, promhttp.Handler()) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go new file mode 100644 index 0000000..b6dd5a2 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go @@ -0,0 +1,201 @@ +// 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. + +// Copyright (c) 2013, The Prometheus Authors +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +// Package promhttp contains functions to create http.Handler instances to +// expose Prometheus metrics via HTTP. In later versions of this package, it +// will also contain tooling to instrument instances of http.Handler and +// http.RoundTripper. +// +// promhttp.Handler acts on the prometheus.DefaultGatherer. With HandlerFor, +// you can create a handler for a custom registry or anything that implements +// the Gatherer interface. It also allows to create handlers that act +// differently on errors or allow to log errors. +package promhttp + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "net/http" + "strings" + "sync" + + "github.com/prometheus/common/expfmt" + + "github.com/prometheus/client_golang/prometheus" +) + +const ( + contentTypeHeader = "Content-Type" + contentLengthHeader = "Content-Length" + contentEncodingHeader = "Content-Encoding" + acceptEncodingHeader = "Accept-Encoding" +) + +var bufPool sync.Pool + +func getBuf() *bytes.Buffer { + buf := bufPool.Get() + if buf == nil { + return &bytes.Buffer{} + } + return buf.(*bytes.Buffer) +} + +func giveBuf(buf *bytes.Buffer) { + buf.Reset() + bufPool.Put(buf) +} + +// Handler returns an HTTP handler for the prometheus.DefaultGatherer. The +// Handler uses the default HandlerOpts, i.e. report the first error as an HTTP +// error, no error logging, and compression if requested by the client. +// +// If you want to create a Handler for the DefaultGatherer with different +// HandlerOpts, create it with HandlerFor with prometheus.DefaultGatherer and +// your desired HandlerOpts. +func Handler() http.Handler { + return HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}) +} + +// HandlerFor returns an http.Handler for the provided Gatherer. The behavior +// of the Handler is defined by the provided HandlerOpts. +func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + mfs, err := reg.Gather() + if err != nil { + if opts.ErrorLog != nil { + opts.ErrorLog.Println("error gathering metrics:", err) + } + switch opts.ErrorHandling { + case PanicOnError: + panic(err) + case ContinueOnError: + if len(mfs) == 0 { + http.Error(w, "No metrics gathered, last error:\n\n"+err.Error(), http.StatusInternalServerError) + return + } + case HTTPErrorOnError: + http.Error(w, "An error has occurred during metrics gathering:\n\n"+err.Error(), http.StatusInternalServerError) + return + } + } + + contentType := expfmt.Negotiate(req.Header) + buf := getBuf() + defer giveBuf(buf) + writer, encoding := decorateWriter(req, buf, opts.DisableCompression) + enc := expfmt.NewEncoder(writer, contentType) + var lastErr error + for _, mf := range mfs { + if err := enc.Encode(mf); err != nil { + lastErr = err + if opts.ErrorLog != nil { + opts.ErrorLog.Println("error encoding metric family:", err) + } + switch opts.ErrorHandling { + case PanicOnError: + panic(err) + case ContinueOnError: + // Handled later. + case HTTPErrorOnError: + http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError) + return + } + } + } + if closer, ok := writer.(io.Closer); ok { + closer.Close() + } + if lastErr != nil && buf.Len() == 0 { + http.Error(w, "No metrics encoded, last error:\n\n"+err.Error(), http.StatusInternalServerError) + return + } + header := w.Header() + header.Set(contentTypeHeader, string(contentType)) + header.Set(contentLengthHeader, fmt.Sprint(buf.Len())) + if encoding != "" { + header.Set(contentEncodingHeader, encoding) + } + w.Write(buf.Bytes()) + // TODO(beorn7): Consider streaming serving of metrics. + }) +} + +// HandlerErrorHandling defines how a Handler serving metrics will handle +// errors. +type HandlerErrorHandling int + +// These constants cause handlers serving metrics to behave as described if +// errors are encountered. +const ( + // Serve an HTTP status code 500 upon the first error + // encountered. Report the error message in the body. + HTTPErrorOnError HandlerErrorHandling = iota + // Ignore errors and try to serve as many metrics as possible. However, + // if no metrics can be served, serve an HTTP status code 500 and the + // last error message in the body. Only use this in deliberate "best + // effort" metrics collection scenarios. It is recommended to at least + // log errors (by providing an ErrorLog in HandlerOpts) to not mask + // errors completely. + ContinueOnError + // Panic upon the first error encountered (useful for "crash only" apps). + PanicOnError +) + +// Logger is the minimal interface HandlerOpts needs for logging. Note that +// log.Logger from the standard library implements this interface, and it is +// easy to implement by custom loggers, if they don't do so already anyway. +type Logger interface { + Println(v ...interface{}) +} + +// HandlerOpts specifies options how to serve metrics via an http.Handler. The +// zero value of HandlerOpts is a reasonable default. +type HandlerOpts struct { + // ErrorLog specifies an optional logger for errors collecting and + // serving metrics. If nil, errors are not logged at all. + ErrorLog Logger + // ErrorHandling defines how errors are handled. Note that errors are + // logged regardless of the configured ErrorHandling provided ErrorLog + // is not nil. + ErrorHandling HandlerErrorHandling + // If DisableCompression is true, the handler will never compress the + // response, even if requested by the client. + DisableCompression bool +} + +// decorateWriter wraps a writer to handle gzip compression if requested. It +// returns the decorated writer and the appropriate "Content-Encoding" header +// (which is empty if no compression is enabled). +func decorateWriter(request *http.Request, writer io.Writer, compressionDisabled bool) (io.Writer, string) { + if compressionDisabled { + return writer, "" + } + header := request.Header.Get(acceptEncodingHeader) + parts := strings.Split(header, ",") + for _, part := range parts { + part := strings.TrimSpace(part) + if part == "gzip" || strings.HasPrefix(part, "gzip;") { + return gzip.NewWriter(writer), "gzip" + } + } + return writer, "" +} diff --git a/vendor/vendor.json b/vendor/vendor.json index b2a4bbd..deca13c 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -38,6 +38,14 @@ "version": "v0.8.0", "versionExact": "v0.8.0" }, + { + "checksumSHA1": "lG3//eDlwqA4IOuAPrNtLh9G0TA=", + "path": "github.com/prometheus/client_golang/prometheus/promhttp", + "revision": "c5b7fccd204277076155f10851dad72b76a49317", + "revisionTime": "2016-08-17T15:48:24Z", + "version": "v0.8.0", + "versionExact": "v0.8.0" + }, { "checksumSHA1": "DvwvOlPNAgRntBzt3b3OSRMS2N4=", "path": "github.com/prometheus/client_model/go",