diff --git a/LICENSE.BOILERPLATE b/LICENSE.BOILERPLATE new file mode 100644 index 0000000..d1dabea --- /dev/null +++ b/LICENSE.BOILERPLATE @@ -0,0 +1,19 @@ + +DISCLAIMER + +Copyright 2020 ArangoDB GmbH, Cologne, Germany + +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 holder is ArangoDB GmbH, Cologne, Germany + diff --git a/Makefile b/Makefile index c99f45e..1148d2f 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,8 @@ DOCKERIMAGE := $(DOCKERNAMESPACE)/arangodb-exporter:$(DOCKERTAG) RELEASE := $(SRCDIR)/bin/release$(shell go env GOEXE) GHRELEASE := $(SRCDIR)/bin/github-release$(shell go env GOEXE) GOX := $(SRCDIR)/bin/gox$(shell go env GOEXE) +GOIMPORTS := $(SRCDIR)/bin/fmt$(shell go env GOEXE) +ADDLICENSE := $(SRCDIR)/bin/addlicense$(shell go env GOEXE) # Magical rubbish to teach make what commas and spaces are. EMPTY := @@ -53,6 +55,12 @@ COMMA := $(EMPTY),$(EMPTY) ARCHS:=amd64 arm arm64 PLATFORMS:=$(subst $(SPACE),$(COMMA),$(foreach arch,$(ARCHS),linux/$(arch))) +GO_IGNORED:=vendor .gobuild + +GO_SOURCES_QUERY := find $(SRCDIR) -name '*.go' -type f $(foreach IGNORED,$(GO_IGNORED),-not -path '$(SRCDIR)/$(IGNORED)/*' ) +GO_SOURCES := $(shell $(GO_SOURCES_QUERY) | sort | uniq) +GO_SOURCES_PACKAGES := $(shell $(GO_SOURCES_QUERY) -exec dirname {} \; | sort | uniq) + SOURCES := $(shell find $(SRCDIR) -name '*.go' -not -path './test/*') .PHONY: all clean build docker @@ -91,31 +99,45 @@ docker-ubi-base: check-vars docker build --no-cache -t $(DOCKERIMAGE)-base-image-ubi -f Dockerfile.ubi . docker-ubi: docker-ubi-base build +ifndef LOCAL for arch in amd64; do \ docker build --build-arg "GOARCH=$$arch" --build-arg "VERSION=$(VERSION_MAJOR_MINOR_PATCH)" -t $(DOCKERIMAGE)-ubi --build-arg "BASE_IMAGE=$(DOCKERIMAGE)-base-image-ubi" -f Dockerfile.scratch . ; \ docker push $(DOCKERIMAGE)-ubi ; \ done +else + for arch in amd64; do \ + docker build --build-arg "GOARCH=$$arch" --build-arg "VERSION=$(VERSION_MAJOR_MINOR_PATCH)" -t $(DOCKERIMAGE)-ubi --build-arg "BASE_IMAGE=$(DOCKERIMAGE)-base-image-ubi" -f Dockerfile.scratch . ; \ + done +endif ifndef IGNORE_UBI docker: docker-ubi endif docker: check-vars build +ifndef LOCAL for arch in $(ARCHS); do \ docker build --build-arg "GOARCH=$$arch" --build-arg "VERSION=$(VERSION_MAJOR_MINOR_PATCH)" -t $(DOCKERIMAGE)-$$arch -f Dockerfile.scratch . ; \ docker push $(DOCKERIMAGE)-$$arch ; \ done - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create --amend $(DOCKERIMAGE) $(foreach arch,$(ARCHS),$(DOCKERIMAGE)-$(arch)) $(DOCKERIMAGE)-ubi - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push $(DOCKERIMAGE) + docker tag $(DOCKERIMAGE)-amd64 $(DOCKERIMAGE) + docker push $(DOCKERIMAGE) +else + for arch in $(ARCHS); do \ + docker build --build-arg "GOARCH=$$arch" --build-arg "VERSION=$(VERSION_MAJOR_MINOR_PATCH)" -t $(DOCKERIMAGE)-$$arch -f Dockerfile.scratch . ; \ + done +endif $(RELEASE): $(GOBUILDDIR) $(SOURCES) $(GHRELEASE) go build -o $(RELEASE) $(REPOPATH)/tools/release -$(GHRELEASE): $(GOBUILDDIR) - go build -o $(GHRELEASE) github.com/aktau/github-release +$(GHRELEASE): $(GOBUILDDIR) + @go build -mod='' -o "$(GHRELEASE)" github.com/aktau/github-release + +github-release: $(GHRELEASE) $(GOX): - go build -o $(GOX) github.com/mitchellh/gox + go build -mod='' -o $(GOX) github.com/mitchellh/gox release-patch: $(RELEASE) $(GHRELEASE) $(RELEASE) -type=patch @@ -126,3 +148,48 @@ release-minor: $(RELEASE) $(GHRELEASE) release-major: $(RELEASE) $(GHRELEASE) $(RELEASE) -type=major +## LINT + +GOLANGCI_ENABLED=deadcode gocyclo golint varcheck structcheck maligned errcheck \ + ineffassign interfacer unconvert goconst \ + megacheck + +.PHONY: tools +tools: $(ADDLICENSE) $(GOIMPORTS) $(GHRELEASE) + +$(ADDLICENSE): + @go build -mod='' -o "$(ADDLICENSE)" github.com/google/addlicense + +.PHONY: license +license: $(ADDLICENSE) + @echo ">> Verify license of files" + @$(ADDLICENSE) -f "./LICENSE.BOILERPLATE" $(GO_SOURCES) + +.PHONY: license-verify +license-verify: $(ADDLICENSE) + @echo ">> Ensuring license of files" + @$(ADDLICENSE) -f "./LICENSE.BOILERPLATE" -check $(GO_SOURCES) + +$(GOIMPORTS): + @go build -mod='' -o "$(GOIMPORTS)" golang.org/x/tools/cmd/goimports + +.PHONY: fmt +fmt: $(GOIMPORTS) + @echo ">> Ensuring style of files" + @$(GOIMPORTS) -w $(GO_SOURCES) + +.PHONY: fmt-verify +fmt-verify: license-verify $(GOIMPORTS) + @echo ">> Verify files style" + @if [ X"$$($(GOIMPORTS) -l $(GO_SOURCES) | wc -l)" != X"0" ]; then echo ">> Style errors"; $(GOIMPORTS) -l $(GO_SOURCES); exit 1; fi + +.PHONY: linter +linter: fmt + @golangci-lint run --no-config --issues-exit-code=1 --deadline=30m --disable-all \ + $(foreach MODE,$(GOLANGCI_ENABLED),--enable $(MODE) ) \ + --exclude-use-default=false \ + $(GO_SOURCES_PACKAGES) + +.PHONY: vendor +vendor: + @go mod vendor \ No newline at end of file diff --git a/connection.go b/connection.go new file mode 100644 index 0000000..5808660 --- /dev/null +++ b/connection.go @@ -0,0 +1,51 @@ +// +// DISCLAIMER +// +// Copyright 2018 ArangoDB GmbH, Cologne, Germany +// +// 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 holder is ArangoDB GmbH, Cologne, Germany +// +// Author Adam Janikowski +// + +package main + +import ( + "io/ioutil" + "strings" +) + +type Authentication func() (string, error) + +func newAuthentication() Authentication { + if arangodbOptions.jwtFile != "" { + return func() (string, error) { + + data, err := ioutil.ReadFile(arangodbOptions.jwtFile) + if err != nil { + return "", err + } + + return strings.TrimSpace(string(data)), nil + } + } else if arangodbOptions.jwtSecret != "" { + return func() (string, error) { + return CreateArangodJWT(arangodbOptions.jwtSecret) + } + } + return func() (string, error) { + return "", nil + } +} diff --git a/exporter.go b/exporter.go index 27415b7..f0f14b0 100644 --- a/exporter.go +++ b/exporter.go @@ -92,7 +92,7 @@ func newMetric(group StatisticGroup, figure StatisticFigure) []prometheus.Collec // Exporter collects ArangoDB statistics from the given endpoint and exports them using // the prometheus metrics package. type Exporter struct { - conn driver.Connection + factory connClientFactory timeout time.Duration mutex sync.RWMutex @@ -102,31 +102,10 @@ type Exporter struct { } // NewExporter returns an initialized Exporter. -func NewExporter(arangodbEndpoint, jwt string, sslVerify bool, timeout time.Duration) (*Exporter, error) { - connCfg := driver_http.ConnectionConfig{ - Endpoints: []string{arangodbEndpoint}, - } - if !sslVerify { - connCfg.TLSConfig = &tls.Config{InsecureSkipVerify: true} - } - conn, err := driver_http.NewConnection(connCfg) - if err != nil { - return nil, maskAny(err) - } - if jwt != "" { - hdr, err := CreateArangodJwtAuthorizationHeader(jwt) - if err != nil { - return nil, maskAny(err) - } - auth := driver.RawAuthentication(hdr) - conn, err = conn.SetAuthentication(auth) - if err != nil { - return nil, maskAny(err) - } - } +func NewExporter(arangodbEndpoint string, jwt Authentication, sslVerify bool, timeout time.Duration) (*Exporter, error) { return &Exporter{ - conn: conn, + factory: newConnClientFactory(arangodbEndpoint, jwt, sslVerify, timeout), timeout: timeout, up: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, @@ -147,6 +126,42 @@ func NewExporter(arangodbEndpoint, jwt string, sslVerify bool, timeout time.Dura }, nil } +type connClientFactory func() (driver.Connection, error) + +func newConnClientFactory(arangodbEndpoint string, auth Authentication, sslVerify bool, timeout time.Duration) connClientFactory { + return func() (driver.Connection, error) { + connCfg := driver_http.ConnectionConfig{ + Endpoints: []string{arangodbEndpoint}, + } + if !sslVerify { + connCfg.TLSConfig = &tls.Config{InsecureSkipVerify: true} + } + + jwt, err := auth() + if err != nil { + return nil, err + } + + conn, err := driver_http.NewConnection(connCfg) + if err != nil { + return nil, maskAny(err) + } + if jwt != "" { + hdr, err := CreateArangodJwtAuthorizationHeader(jwt) + if err != nil { + return nil, maskAny(err) + } + auth := driver.RawAuthentication(hdr) + conn, err = conn.SetAuthentication(auth) + if err != nil { + return nil, maskAny(err) + } + } + + return conn, nil + } +} + // Describe describes all the metrics ever exported by the HAProxy exporter. It // implements prometheus.Collector. func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { @@ -181,8 +196,15 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) { func (e *Exporter) scrape(ctx context.Context) { e.totalScrapes.Inc() + conn, err := e.factory() + if err != nil { + e.up.Set(0) + log.Errorf("Failed to create client: %v", err) + return + } + // Gather descriptions - descr, err := GetStatisticsDescription(ctx, e.conn) + descr, err := GetStatisticsDescription(ctx, conn) if err != nil { e.up.Set(0) log.Errorf("Failed to fetch statistic descriptions: %v", err) @@ -190,7 +212,7 @@ func (e *Exporter) scrape(ctx context.Context) { } // Collect statistics - stats, err := GetStatistics(ctx, e.conn) + stats, err := GetStatistics(ctx, conn) if err != nil { e.up.Set(0) log.Errorf("Failed to fetch statistics: %v", err) diff --git a/go.mod b/go.mod index 903bc78..e99699e 100644 --- a/go.mod +++ b/go.mod @@ -5,37 +5,20 @@ go 1.12 replace github.com/Sirupsen/logrus => github.com/sirupsen/logrus v1.4.1 require ( - github.com/Sirupsen/logrus v0.0.0-00010101000000-000000000000 // indirect - github.com/aktau/github-release v0.7.2 // indirect github.com/arangodb-helper/go-certificates v0.0.0-20180821055445-9fca24fc2680 github.com/arangodb/go-driver v0.0.0-20190430103524-b14f41496c3d - github.com/codegangsta/cli v1.20.0 // indirect github.com/coreos/go-semver v0.3.0 github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/docker/docker v1.13.1 // indirect - github.com/docker/go-connections v0.4.0 // indirect - github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 // indirect - github.com/docker/go-units v0.4.0 // indirect - github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/estesp/manifest-tool v0.9.0 // indirect - github.com/go-yaml/yaml v2.1.0+incompatible // indirect - github.com/gorilla/mux v1.7.1 // indirect + github.com/google/addlicense v0.0.0-20200906110928-a0294312aa76 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/mitchellh/gox v1.0.1 // indirect - github.com/opencontainers/go-digest v1.0.0-rc1 // indirect - github.com/opencontainers/image-spec v1.0.1 // indirect - github.com/opencontainers/runc v0.1.1 // indirect - github.com/opencontainers/runtime-spec v1.0.1 // indirect github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible // indirect github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v0.9.2 github.com/prometheus/common v0.3.0 github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 // indirect - github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect - github.com/vbatts/tar-split v0.11.1 // indirect - github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2 // indirect - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - google.golang.org/grpc v1.20.1 // indirect + golang.org/x/sync v0.0.0-20201207232520-09787c993a3a // indirect + golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d // indirect + golang.org/x/tools v0.0.0-20201208233053-a543418bbed2 // indirect ) diff --git a/go.sum b/go.sum index 75aa581..a45d82b 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,3 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/aktau/github-release v0.7.2 h1:la7AnShr2MQPIlBEcRA9MPbI8av0YFmpFP9WM5EoqJs= -github.com/aktau/github-release v0.7.2/go.mod h1:cPkP83iRnV8pAJyQlQ4vjLJoC+JE+aT5sOrYz3sTsX0= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= @@ -14,31 +10,21 @@ github.com/arangodb/go-velocypack v0.0.0-20190129082528-7896a965b4ad h1:Ah+VRYUW github.com/arangodb/go-velocypack v0.0.0-20190129082528-7896a965b4ad/go.mod h1:7QCjpWXdB49P6fql1CxmsBWd8z/T4L4pqFLTnc10xNM= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/estesp/manifest-tool v0.9.0/go.mod h1:w/oandYlJC/m8nkP8UaJVxsm/LwjurJQHXR27njws74= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +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/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/google/addlicense v0.0.0-20200906110928-a0294312aa76 h1:JypWNzPMSgH5yL0NvFoAIsDRlKFgL0AsS3GO5bg4Pto= +github.com/google/addlicense v0.0.0-20200906110928-a0294312aa76/go.mod h1:EMjYTRimagHs1FwlIqKyX3wAM0u3rA+McvlIIWmSamA= github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -54,15 +40,12 @@ github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18 github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible h1:Jd6xfriVlJ6hWPvYOE0Ni0QWcNTLRehfGPFxr3eSL80= github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible/go.mod h1:xlUlxe/2ItGlQyMTstqeDv9r3U4obH7xYd26TbDQutY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.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 v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= @@ -77,40 +60,54 @@ github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nL github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= -github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= -github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= -github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2 h1:txplJASvd6b/hrE0s/Ixfpp2cuwH9IO9oZBAN9iYa4A= -github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2/go.mod h1:DGCIhurYgnLz8J9ga1fMV/fbLDyUvTyrWXVWUIyJon4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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 h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d h1:MiWWjyhUzZ+jvhZvloX6ZrUsdEghn8a64Upd8EMHglE= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2 h1:vEtypaVub6UvKkiXZ2xx9QIvp9TL7sI7xp7vdi2kezA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 663b598..0bf762c 100644 --- a/main.go +++ b/main.go @@ -24,10 +24,8 @@ package main import ( "fmt" - "io/ioutil" "net/http" _ "net/http/pprof" - "strings" "time" "github.com/pkg/errors" @@ -87,30 +85,16 @@ func main() { func cmdMainRun(cmd *cobra.Command, args []string) { log.Infoln(fmt.Sprintf("Starting arangodb-exporter %s, build %s", projectVersion, projectBuild)) - var token string - if arangodbOptions.jwtFile != "" { - data, err := ioutil.ReadFile(arangodbOptions.jwtFile) - if err != nil { - log.Fatal(err) - } - token = strings.TrimSpace(string(data)) - } else if arangodbOptions.jwtSecret != "" { - var err error - token, err = CreateArangodJWT(arangodbOptions.jwtSecret) - if err != nil { - log.Fatal(err) - } - } mux := http.NewServeMux() switch ExporterMode(arangodbOptions.mode) { case ModePassthru: - passthru, err := NewPassthru(arangodbOptions.endpoint, token, false, arangodbOptions.timeout) + passthru, err := NewPassthru(arangodbOptions.endpoint, newAuthentication(), false, arangodbOptions.timeout) if err != nil { log.Fatal(err) } mux.Handle("/metrics", passthru) default: - exporter, err := NewExporter(arangodbOptions.endpoint, token, false, arangodbOptions.timeout) + exporter, err := NewExporter(arangodbOptions.endpoint, newAuthentication(), false, arangodbOptions.timeout) if err != nil { log.Fatal(err) } diff --git a/passthru.go b/passthru.go index dfed6a4..570d9f1 100644 --- a/passthru.go +++ b/passthru.go @@ -25,51 +25,69 @@ package main import ( "crypto/tls" "fmt" - "io" + "io/ioutil" "net/http" + "strings" "time" ) var _ http.Handler = &passthru{} -func NewPassthru(arangodbEndpoint, jwt string, sslVerify bool, timeout time.Duration) (http.Handler, error) { - transport := &http.Transport{} +func NewPassthru(arangodbEndpoint string, auth Authentication, sslVerify bool, timeout time.Duration) (http.Handler, error) { + return &passthru{ + factory: newHttpClientFactory(arangodbEndpoint, auth, sslVerify, timeout), + }, nil +} - req, err := http.NewRequest("GET", fmt.Sprintf("%s/_admin/metrics", arangodbEndpoint), nil) - if err != nil { - return nil, maskAny(err) - } +type httpClientFactory func() (*http.Client, *http.Request, error) - if !sslVerify { - transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - } +func newHttpClientFactory(arangodbEndpoint string, auth Authentication, sslVerify bool, timeout time.Duration) httpClientFactory { + return func() (*http.Client, *http.Request, error) { + transport := &http.Transport{} - if jwt != "" { - hdr, err := CreateArangodJwtAuthorizationHeader(jwt) + req, err := http.NewRequest("GET", fmt.Sprintf("%s/_admin/metrics", arangodbEndpoint), nil) if err != nil { - return nil, maskAny(err) + return nil, nil, maskAny(err) } - req.Header.Add("Authorization", hdr) - } - client := &http.Client{ - Transport: transport, - Timeout: timeout, - } + if !sslVerify { + transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + } - return &passthru{ - client: client, - request: req, - }, nil + jwt, err := auth() + if err != nil { + return nil, nil, err + } + + if jwt != "" { + hdr, err := CreateArangodJwtAuthorizationHeader(jwt) + if err != nil { + return nil, nil, maskAny(err) + } + req.Header.Add("Authorization", hdr) + } + + req.Header.Add("x-arango-allow-dirty-read", "true") // Allow read from follower in AF mode + + client := &http.Client{ + Transport: transport, + Timeout: timeout, + } + + return client, req, nil + } } type passthru struct { - request *http.Request - client *http.Client + factory httpClientFactory } func (p passthru) get() (*http.Response, error) { - return p.client.Do(p.request) + c, req, err := p.factory() + if err != nil { + return nil, err + } + return c.Do(req) } func (p passthru) ServeHTTP(resp http.ResponseWriter, req *http.Request) { @@ -91,8 +109,20 @@ func (p passthru) ServeHTTP(resp http.ResponseWriter, req *http.Request) { defer data.Body.Close() - _, err = io.Copy(resp, data.Body) + response, err := ioutil.ReadAll(data.Body) + if err != nil { + // Ignore error + resp.WriteHeader(http.StatusInternalServerError) + resp.Write([]byte(err.Error())) + return + } + + responseStr := string(response) + + // Fix Header response + responseStr = strings.ReplaceAll(responseStr, "guage", "gauge") + _, err = resp.Write([]byte(responseStr)) if err != nil { // Ignore error resp.WriteHeader(http.StatusInternalServerError)