Skip to content

Commit

Permalink
Merge pull request #2 from sapcc/error-test-cases
Browse files Browse the repository at this point in the history
Error test cases for collectors
  • Loading branch information
talal committed Jan 20, 2020
2 parents e355e14 + 0f4a85d commit bce410c
Show file tree
Hide file tree
Showing 31 changed files with 1,551 additions and 894 deletions.
17 changes: 12 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ GO_ALLPKGS := $(shell go list $(GO_BUILDFLAGS) $(PKG)/...)
# which packages to test with `go test`?
GO_TESTPKGS := $(shell go list $(GO_BUILDFLAGS) -f '{{if .TestGoFiles}}{{.ImportPath}}{{end}}' $(PKG)/...)
# which packages to measure coverage for?
GO_COVERPKGS := $(shell go list $(GO_BUILDFLAGS) $(PKG) $(PKG)/collectors)
GO_COVERPKGS := $(shell go list $(GO_BUILDFLAGS) $(PKG) $(PKG)/collector)
# output files from `go test`
GO_COVERFILES := $(patsubst %,build/%.cover.out,$(subst /,_,$(GO_TESTPKGS)))

Expand All @@ -43,19 +43,26 @@ static-check: FORCE
@$(GO) vet $(GO_BUILDFLAGS) $(GO_ALLPKGS)

# detailed unit test run (incl. test coverage)
build/%.cover.out: FORCE build/mock-swift-dispersion-report build/mock-swift-recon
build/%.cover.out: FORCE build/mock-tools
@printf "\e[1;36m>> go test $(subst _,/,$*)\e[0m\n"
$(GO) test $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' -coverprofile=$@ -covermode=count -coverpkg=$(subst $(space),$(comma),$(GO_COVERPKGS)) $(subst _,/,$*)
build/cover.out: $(GO_COVERFILES)
$(GO) run $(GO_BUILDFLAGS) test/cmd/gocovcat/main.go $(GO_COVERFILES) > $@
build/cover.html: build/cover.out
$(GO) tool cover -html $< -o $@

build/mock-swift-dispersion-report: FORCE
$(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-dispersion-report'
# quick unit test run
quick-check: FORCE all build/mock-tools $(addprefix quick-check-,$(subst /,_,$(GO_TESTPKGS)))
quick-check-%:
@printf "\e[1;36m>> go test $(subst _,/,$*)\e[0m\n"
$(GO) test $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' $(subst _,/,$*)
@printf "\e[1;32m>> Unit tests successful.\e[0m\n"

build/mock-swift-recon: FORCE
build/mock-tools: FORCE
$(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-dispersion-report'
$(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-dispersion-report-with-errors'
$(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-recon'
$(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-recon-with-errors'

clean: FORCE
rm -rf -- build
Expand Down
43 changes: 43 additions & 0 deletions collector/collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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 collector

import (
"strings"

"github.com/prometheus/client_golang/prometheus"
)

type typedDesc struct {
desc *prometheus.Desc
valueType prometheus.ValueType
}

func (d *typedDesc) mustNewConstMetric(value float64, labels ...string) prometheus.Metric {
return prometheus.MustNewConstMetric(d.desc, d.valueType, value, labels...)
}

func (d *typedDesc) describe(ch chan<- *prometheus.Desc) {
ch <- d.desc
}

// collectorTask is the interface that a specific collector task must implement.
type collectorTask interface {
describeMetrics(ch chan<- *prometheus.Desc)
collectMetrics(ch chan<- prometheus.Metric, taskExitCodeTypedDesc typedDesc)
}

func cmdArgsToStr(cmdArgs []string) string {
return strings.Join(cmdArgs, " ")
}
194 changes: 194 additions & 0 deletions collector/dispersion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// Copyright 2019 SAP SE
//
// 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 collector

import (
"encoding/json"
"os/exec"

"github.com/prometheus/client_golang/prometheus"
"github.com/sapcc/go-bits/logg"
)

// DispersionCollector implements the prometheus.Collector interface.
type DispersionCollector struct {
taskExitCode typedDesc
dispersionReportDumpTask collectorTask
}

// NewDispersionCollector creates a new DispersionCollector.
func NewDispersionCollector(pathToExecutable string) *DispersionCollector {
return &DispersionCollector{
taskExitCode: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_task_exit_code",
"The exit code for a Swift Dispersion Report query execution.",
[]string{"query"}, nil),
valueType: prometheus.GaugeValue,
},
dispersionReportDumpTask: newDispersionReportDumpTask(pathToExecutable),
}
}

// Describe implements the prometheus.Collector interface.
func (c *DispersionCollector) Describe(ch chan<- *prometheus.Desc) {
c.taskExitCode.describe(ch)
c.dispersionReportDumpTask.describeMetrics(ch)
}

// Collect implements the prometheus.Collector interface.
func (c *DispersionCollector) Collect(ch chan<- prometheus.Metric) {
c.dispersionReportDumpTask.collectMetrics(ch, c.taskExitCode)
}

///////////////////////////////////////////////////////////////////////////////
// Dispersion collector tasks.

// dispersionReportDumpTask implements the collector.collectorTask interface.
type dispersionReportDumpTask struct {
pathToDispersionExecutable string

containerCopiesExpected typedDesc
containerCopiesFound typedDesc
containerCopiesMissing typedDesc
containerOverlapping typedDesc
objectCopiesExpected typedDesc
objectCopiesFound typedDesc
objectCopiesMissing typedDesc
objectOverlapping typedDesc
}

func newDispersionReportDumpTask(pathToDispersionExecutable string) collectorTask {
return &dispersionReportDumpTask{
pathToDispersionExecutable: pathToDispersionExecutable,
containerCopiesExpected: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_container_copies_expected",
"Expected container copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
containerCopiesFound: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_container_copies_found",
"Found container copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
containerCopiesMissing: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_container_copies_missing",
"Missing container copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
containerOverlapping: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_container_overlapping",
"Expected container copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
objectCopiesExpected: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_object_copies_expected",
"Expected object copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
objectCopiesFound: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_object_copies_found",
"Found object copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
objectCopiesMissing: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_object_copies_missing",
"Missing object copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
objectOverlapping: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_object_overlapping",
"Expected object copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
}
}

// dispersionReportDumpTask implements the collector.collectorTask interface.
func (t *dispersionReportDumpTask) describeMetrics(ch chan<- *prometheus.Desc) {
t.containerCopiesExpected.describe(ch)
t.containerCopiesFound.describe(ch)
t.containerCopiesMissing.describe(ch)
t.containerOverlapping.describe(ch)
t.objectCopiesExpected.describe(ch)
t.objectCopiesFound.describe(ch)
t.objectCopiesMissing.describe(ch)
t.objectOverlapping.describe(ch)
}

// dispersionReportDumpTask implements the collector.collectorTask interface.
func (t *dispersionReportDumpTask) collectMetrics(ch chan<- prometheus.Metric, exitCodeTypedDesc typedDesc) {
exitCode := 0
cmdArg := "--dump-json"
out, err := exec.Command(t.pathToDispersionExecutable, cmdArg).CombinedOutput()
if err == nil {
var data struct {
Object struct {
Expected int64 `json:"copies_expected"`
Found int64 `json:"copies_found"`
Overlapping int64 `json:"overlapping"`
Missing int64
} `json:"object"`
Container struct {
Expected int64 `json:"copies_expected"`
Found int64 `json:"copies_found"`
Overlapping int64 `json:"overlapping"`
Missing int64
} `json:"container"`
}
err = json.Unmarshal(out, &data)
if err == nil {
cntr := data.Container
if cntr.Expected > 0 && cntr.Found > 0 {
cntr.Missing = cntr.Expected - cntr.Found
}
ch <- t.containerCopiesExpected.mustNewConstMetric(float64(cntr.Expected))
ch <- t.containerCopiesFound.mustNewConstMetric(float64(cntr.Found))
ch <- t.containerCopiesMissing.mustNewConstMetric(float64(cntr.Missing))
ch <- t.containerOverlapping.mustNewConstMetric(float64(cntr.Overlapping))

obj := data.Object
if obj.Expected > 0 && obj.Found > 0 {
obj.Missing = obj.Expected - obj.Found
}
ch <- t.objectCopiesExpected.mustNewConstMetric(float64(obj.Expected))
ch <- t.objectCopiesFound.mustNewConstMetric(float64(obj.Found))
ch <- t.objectCopiesMissing.mustNewConstMetric(float64(obj.Missing))
ch <- t.objectOverlapping.mustNewConstMetric(float64(obj.Overlapping))
}
}
if err != nil {
exitCode = 1
logg.Error("swift dispersion: %s: %s", cmdArg, err.Error())
}

ch <- exitCodeTypedDesc.mustNewConstMetric(float64(exitCode), cmdArg)
}
20 changes: 18 additions & 2 deletions collectors/dispersion_test.go → collector/dispersion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package collectors
package collector

import (
"path/filepath"
Expand All @@ -35,6 +35,22 @@ func TestDispersionCollector(t *testing.T) {
Method: "GET",
Path: "/metrics",
ExpectStatus: 200,
ExpectBody: assert.FixtureFile("fixtures/dispersion_metrics.prom"),
ExpectBody: assert.FixtureFile("fixtures/dispersion_successful_collect.prom"),
}.Check(t, promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
}

func TestDispersionCollectorWithErrors(t *testing.T) {
pathToExecutable, err := filepath.Abs("../build/mock-swift-dispersion-report-with-errors")
if err != nil {
t.Error(err)
}

registry := prometheus.NewPedanticRegistry()
registry.MustRegister(NewDispersionCollector(pathToExecutable))
assert.HTTPRequest{
Method: "GET",
Path: "/metrics",
ExpectStatus: 200,
ExpectBody: assert.FixtureFile("fixtures/dispersion_failed_collect.prom"),
}.Check(t, promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
}
3 changes: 3 additions & 0 deletions collector/fixtures/dispersion_failed_collect.prom
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# HELP swift_dispersion_task_exit_code The exit code for a Swift Dispersion Report query execution.
# TYPE swift_dispersion_task_exit_code gauge
swift_dispersion_task_exit_code{query="--dump-json"} 1
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ swift_dispersion_container_copies_missing 0
# HELP swift_dispersion_container_overlapping Expected container copies reported by the swift-dispersion-report tool.
# TYPE swift_dispersion_container_overlapping gauge
swift_dispersion_container_overlapping 0
# HELP swift_dispersion_exit_code The exit code for a Swift Dispersion Report query execution.
# TYPE swift_dispersion_exit_code counter
swift_dispersion_exit_code{query="--dump-json"} 0
# HELP swift_dispersion_object_copies_expected Expected object copies reported by the swift-dispersion-report tool.
# TYPE swift_dispersion_object_copies_expected gauge
swift_dispersion_object_copies_expected 1965
Expand All @@ -25,3 +22,6 @@ swift_dispersion_object_copies_missing 0
# HELP swift_dispersion_object_overlapping Expected object copies reported by the swift-dispersion-report tool.
# TYPE swift_dispersion_object_overlapping gauge
swift_dispersion_object_overlapping 0
# HELP swift_dispersion_task_exit_code The exit code for a Swift Dispersion Report query execution.
# TYPE swift_dispersion_task_exit_code gauge
swift_dispersion_task_exit_code{query="--dump-json"} 0
Loading

0 comments on commit bce410c

Please sign in to comment.