Skip to content

Commit

Permalink
Merge pull request #200 from yeoldegrove/packages_and_tls
Browse files Browse the repository at this point in the history
prometheus best practices, TLS and basic auth support
  • Loading branch information
stefanotorresi authored May 24, 2022
2 parents 8dcedc5 + d120241 commit 529c6ee
Show file tree
Hide file tree
Showing 30 changed files with 1,338 additions and 565 deletions.
18 changes: 9 additions & 9 deletions .github/workflows/exporter-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v2
with:
go-version: ^1.14
id: go
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-go@v3
with:
go-version-file: 'go.mod'
id: go
- name: static analysis
run: make static-checks
- name: test
Expand All @@ -43,7 +43,7 @@ jobs:
for FILE in build/bin/*; do
gzip $FILE
done
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: ha_cluster_exporter
path: build/bin
Expand Down Expand Up @@ -104,10 +104,10 @@ jobs:
if: github.event.release
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: ha_cluster_exporter
- uses: AButler/upload-release-assets@v2.0
- uses: softprops/action-gh-release@v1
with:
files: 'ha_cluster_exporter-*'
repo-token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ ha_cluster_exporter
# we don't use vendoring
/vendor
/build

# ignore tmp promu config
.promu.release.yml
21 changes: 21 additions & 0 deletions .promu.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
go:
version: 1.16
cgo: false
repository:
path: github.com/ClusterLabs/ha_cluster_exporter
build:
flags: -a -tags netgo
ldflags: |
-X github.com/prometheus/common/version.Version={{.Version}}
-X github.com/prometheus/common/version.Revision={{.Revision}}
-X github.com/prometheus/common/version.Branch={{.Branch}}
-X github.com/prometheus/common/version.BuildUser={{user}}@{{host}}
-X github.com/prometheus/common/version.BuildDate={{date "20060102-15:04:05"}}
binaries:
- name: ha_cluster_exporter-amd64
- name: ha_cluster_exporter-arm64
- name: ha_cluster_exporter-ppc64le
- name: ha_cluster_exporter-s390x
tarball:
files:
- LICENSE
61 changes: 45 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
GO := GO111MODULE=on go
FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH)))
GOHOSTOS ?= $(shell $(GO) env GOHOSTOS)
GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH)
ifeq (arm, $(GOHOSTARCH))
GOHOSTARM ?= $(shell GOARM= $(GO) env GOARM)
GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)v$(GOHOSTARM)
else
GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)
endif
PROMU := $(FIRST_GOPATH)/bin/promu
PROMU_VERSION ?= 0.13.0
PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz

# this is the what ends up in the RPM "Version" field and embedded in the --version CLI flag
VERSION ?= $(shell .ci/get_version_from_git.sh)

# this will be used as the build date by the Go compile task
DATE = $(shell date --iso-8601=seconds)

# if you want to release to OBS, this must be a remotely available Git reference
REVISION ?= $(shell git rev-parse --abbrev-ref HEAD)

Expand All @@ -19,47 +30,65 @@ ARCHS ?= amd64 arm64 ppc64le s390x

default: clean mod-tidy generate fmt vet-check test build

build: amd64
promu-prepare:
sed "s/{{.Version}}/$(VERSION)/" .promu.yml >.promu.release.yml
mkdir -p build/bin

# from https://github.com/prometheus/prometheus/blob/main/Makefile.common
$(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)

build:
$(MAKE) clean
$(MAKE) promu-prepare $(PROMU)
$(MAKE) amd64

build-all: clean $(ARCHS)
build-all:
$(MAKE) clean
$(MAKE) promu-prepare $(PROMU)
$(MAKE) $(ARCHS)

$(ARCHS):
@mkdir -p build/bin
CGO_ENABLED=0 GOOS=linux GOARCH=$@ go build -trimpath -ldflags "-s -w -X main.version=$(VERSION) -X main.buildDate=$(DATE)" -o build/bin/ha_cluster_exporter-$@
GOOS=linux GOARCH=$@ $(PROMU) build --config .promu.release.yml --prefix=build/bin ha_cluster_exporter-$@

install:
go install
$(GO) install

static-checks: vet-check fmt-check

vet-check:
go vet ./...
$(GO) vet ./...

fmt:
go fmt ./...
$(GO) fmt ./...

mod-tidy:
go mod tidy
$(GO) mod tidy

fmt-check:
.ci/go_lint.sh

generate:
go generate ./...
$(GO) generate ./...

test:
go test -v ./...
$(GO) test -v ./...

checks: static-checks test

coverage:
@mkdir -p build
go test -cover -coverprofile=build/coverage ./...
go tool cover -html=build/coverage
$(GO) test -cover -coverprofile=build/coverage ./...
$(GO) tool cover -html=build/coverage

clean:
go clean
$(GO) clean
rm -rf build
rm -f .promu.release.yml

exporter-obs-workdir: build/obs/prometheus-ha_cluster_exporter
build/obs/prometheus-ha_cluster_exporter:
Expand Down
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,47 @@ The first match has precedence, and the CLI flags have precedence over the confi

Please refer to the example [YAML configuration](ha_cluster_exporter.yaml) for more details.

Additional CLI flags can also be passed via `/etc/sysconfig/prometheus-ha_cluster_exporter`.

#### General Flags

Name | Description
---- | -----------
web.listen-address | Address to listen on for web interface and telemetry (default `:9664`).
web.telemetry-path | Path under which to expose metrics (default `/metrics`).
web.config.file | Path to a [web configuration file](#tls-and-basic-authentication) (default `/etc/ha_cluster_exporter.web.yaml`).
log.level | Logging verbosity (default `info`).
version | Print the version information.

##### Deprecated Flags
Name | Description
---- | -----------
address | deprecated: please use --web.listen-address or --web.config.file to use Prometheus Exporter Toolkit
port | deprecated: please use --web.listen-address or --web.config.file to use Prometheus Exporter Toolkit
log-level | deprecated: please use log.level
enable-timestamps | deprecated: server-side metric timestamping is discouraged by Prometheus best-practices and should be avoided

#### Collector Flags

Name | Description
---- | -----------
crm-mon-path | Path to crm_mon executable (default `/usr/sbin/crm_mon`).
cibadmin-path | Path to cibadmin executable (default `/usr/sbin/cibadmin`).
corosync-cfgtoolpath-path | Path to corosync-cfgtool executable (default `/usr/sbin/corosync-cfgtool`).
corosync-quorumtool-path | Path to corosync-quorumtool executable (default `/usr/sbin/corosync-quorumtool`).
sbd-path | Path to sbd executable (default `/usr/sbin/sbd`).
sbd-config-path | Path to sbd configuration (default `/etc/sysconfig/sbd`).
drbdsetup-path | Path to drbdsetup executable (default `/sbin/drbdsetup`).
drbdsplitbrain-path | Path to drbd splitbrain hooks temporary files (default `/var/run/drbd/splitbrain`).

### TLS and basic authentication

The ha_cluster_exporter supports TLS and basic authentication.

To use TLS and/or basic authentication, you need to pass a configuration file
using the `--web.config.file` parameter. The format of the file is described
[in the exporter-toolkit repository](https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md).

### systemd integration

A [systemd unit file](ha_cluster_exporter.service) is provided with the RPM packages. You can enable and start it as usual:
Expand All @@ -114,7 +155,7 @@ We recommend having a look at the [design document](doc/design.md) and the [deve

## License

Copyright 2019-2020 SUSE LLC
Copyright 2019-2022 SUSE LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
13 changes: 8 additions & 5 deletions collector/corosync/corosync.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@ package corosync
import (
"os/exec"

"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"

"github.com/ClusterLabs/ha_cluster_exporter/collector"
)

const subsystem = "corosync"

func NewCollector(cfgToolPath string, quorumToolPath string) (*corosyncCollector, error) {
func NewCollector(cfgToolPath string, quorumToolPath string, timestamps bool, logger log.Logger) (*corosyncCollector, error) {
err := collector.CheckExecutables(cfgToolPath, quorumToolPath)
if err != nil {
return nil, errors.Wrapf(err, "could not initialize '%s' collector", subsystem)
}

c := &corosyncCollector{
collector.NewDefaultCollector(subsystem),
collector.NewDefaultCollector(subsystem, timestamps, logger),
cfgToolPath,
quorumToolPath,
NewParser(),
Expand All @@ -41,7 +42,7 @@ type corosyncCollector struct {
}

func (c *corosyncCollector) CollectWithError(ch chan<- prometheus.Metric) error {
log.Debugln("Collecting corosync metrics...")
level.Debug(c.Logger).Log("msg", "Collecting corosync metrics...")

// We suppress the exec errors because if any interface is faulty the tools will exit with code 1, but we still want to parse the output.
cfgToolOutput, _ := exec.Command(c.cfgToolPath, "-s").Output()
Expand All @@ -62,9 +63,11 @@ func (c *corosyncCollector) CollectWithError(ch chan<- prometheus.Metric) error
}

func (c *corosyncCollector) Collect(ch chan<- prometheus.Metric) {
level.Debug(c.Logger).Log("msg", "Collecting corosync metrics...")

err := c.CollectWithError(ch)
if err != nil {
log.Warnf("'%s' collector scrape failed: %s", c.GetSubsystem(), err)
level.Warn(c.Logger).Log("msg", c.GetSubsystem()+" collector scrape failed", "err", err)
}
}

Expand Down
13 changes: 7 additions & 6 deletions collector/corosync/corosync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,46 @@ package corosync
import (
"testing"

"github.com/go-kit/log"
"github.com/stretchr/testify/assert"

assertcustom "github.com/ClusterLabs/ha_cluster_exporter/internal/assert"
)

func TestNewCorosyncCollector(t *testing.T) {
_, err := NewCollector("../../test/fake_corosync-cfgtool.sh", "../../test/fake_corosync-quorumtool.sh")
_, err := NewCollector("../../test/fake_corosync-cfgtool.sh", "../../test/fake_corosync-quorumtool.sh", false, log.NewNopLogger())
assert.Nil(t, err)
}

func TestNewCorosyncCollectorChecksCfgtoolExistence(t *testing.T) {
_, err := NewCollector("../../test/nonexistent", "../../test/fake_corosync-quorumtool.sh")
_, err := NewCollector("../../test/nonexistent", "../../test/fake_corosync-quorumtool.sh", false, log.NewNopLogger())

assert.Error(t, err)
assert.Contains(t, err.Error(), "'../../test/nonexistent' does not exist")
}

func TestNewCorosyncCollectorChecksQuorumtoolExistence(t *testing.T) {
_, err := NewCollector("../../test/fake_corosync-cfgtool.sh", "../../test/nonexistent")
_, err := NewCollector("../../test/fake_corosync-cfgtool.sh", "../../test/nonexistent", false, log.NewNopLogger())

assert.Error(t, err)
assert.Contains(t, err.Error(), "'../../test/nonexistent' does not exist")
}

func TestNewCorosyncCollectorChecksCfgtoolExecutableBits(t *testing.T) {
_, err := NewCollector("../../test/dummy", "../../test/fake_corosync-quorumtool.sh")
_, err := NewCollector("../../test/dummy", "../../test/fake_corosync-quorumtool.sh", false, log.NewNopLogger())

assert.Error(t, err)
assert.Contains(t, err.Error(), "'../../test/dummy' is not executable")
}

func TestNewCorosyncCollectorChecksQuorumtoolExecutableBits(t *testing.T) {
_, err := NewCollector("../../test/fake_corosync-cfgtool.sh", "../../test/dummy")
_, err := NewCollector("../../test/fake_corosync-cfgtool.sh", "../../test/dummy", false, log.NewNopLogger())

assert.Error(t, err)
assert.Contains(t, err.Error(), "'../../test/dummy' is not executable")
}

func TestCorosyncCollector(t *testing.T) {
collector, _ := NewCollector("../../test/fake_corosync-cfgtool.sh", "../../test/fake_corosync-quorumtool.sh")
collector, _ := NewCollector("../../test/fake_corosync-cfgtool.sh", "../../test/fake_corosync-quorumtool.sh", false, log.NewNopLogger())
assertcustom.Metrics(t, collector, "corosync.metrics")
}
10 changes: 7 additions & 3 deletions collector/default_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package collector

import (
"github.com/ClusterLabs/ha_cluster_exporter/internal/clock"
"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
config "github.com/spf13/viper"
"os"
)

Expand All @@ -18,13 +18,17 @@ type DefaultCollector struct {
subsystem string
descriptors map[string]*prometheus.Desc
Clock clock.Clock
timestamps bool
Logger log.Logger
}

func NewDefaultCollector(subsystem string) DefaultCollector {
func NewDefaultCollector(subsystem string, timestamps bool, logger log.Logger) DefaultCollector {
return DefaultCollector{
subsystem,
make(map[string]*prometheus.Desc),
&clock.SystemClock{},
timestamps,
logger,
}
}

Expand Down Expand Up @@ -67,7 +71,7 @@ func (c *DefaultCollector) MakeCounterMetric(name string, value float64, labelVa
func (c *DefaultCollector) makeMetric(name string, value float64, valueType prometheus.ValueType, labelValues ...string) prometheus.Metric {
desc := c.GetDescriptor(name)
metric := prometheus.MustNewConstMetric(desc, valueType, value, labelValues...)
if config.GetBool("enable-timestamps") {
if c.timestamps == true {
metric = prometheus.NewMetricWithTimestamp(c.Clock.Now(), metric)
}
return metric
Expand Down
Loading

0 comments on commit 529c6ee

Please sign in to comment.