From 4bbdb36da0ef3f4d06009c4767905e9f8448b83f Mon Sep 17 00:00:00 2001 From: Stephen Marshall Date: Thu, 24 May 2018 17:10:16 +0100 Subject: [PATCH 1/2] Skip metrics collect on first pass and more unit tests --- internal/metrics/exporter.go | 27 +++++++---- internal/metrics/exporter_test.go | 78 +++++++++++++++++++++++++++++++ internal/metrics/update_test.go | 40 +++++++++++++++- 3 files changed, 134 insertions(+), 11 deletions(-) diff --git a/internal/metrics/exporter.go b/internal/metrics/exporter.go index 18aced2..bff5ed3 100644 --- a/internal/metrics/exporter.go +++ b/internal/metrics/exporter.go @@ -30,14 +30,16 @@ const ( ) type exporter struct { - qmName string - gaugeMap map[string]*prometheus.GaugeVec + qmName string + gaugeMap map[string]*prometheus.GaugeVec + firstCollect bool } func newExporter(qmName string) *exporter { return &exporter{ - qmName: qmName, - gaugeMap: make(map[string]*prometheus.GaugeVec), + qmName: qmName, + gaugeMap: make(map[string]*prometheus.GaugeVec), + firstCollect: true, } } @@ -71,17 +73,24 @@ func (e *exporter) Collect(ch chan<- prometheus.Metric) { gaugeVec.Reset() // Populate Prometheus Gauge with metric values - for label, value := range metric.values { - if label == qmgrLabelValue { - gaugeVec.WithLabelValues(e.qmName).Set(value) - } else { - gaugeVec.WithLabelValues(label, e.qmName).Set(value) + // - Skip on first collect to avoid build-up of accumulated values + if !e.firstCollect { + for label, value := range metric.values { + if label == qmgrLabelValue { + gaugeVec.WithLabelValues(e.qmName).Set(value) + } else { + gaugeVec.WithLabelValues(label, e.qmName).Set(value) + } } } // Collect metric gaugeVec.Collect(ch) } + + if e.firstCollect { + e.firstCollect = false + } } // createGaugeVec returns a Prometheus GaugeVec populated with metric details diff --git a/internal/metrics/exporter_test.go b/internal/metrics/exporter_test.go index 723ecd3..52faae9 100644 --- a/internal/metrics/exporter_test.go +++ b/internal/metrics/exporter_test.go @@ -17,10 +17,88 @@ package metrics import ( "testing" + "time" "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" ) +func TestDescribe(t *testing.T) { + + teardownTestCase := setupTestCase() + defer teardownTestCase() + + ch := make(chan *prometheus.Desc) + go func() { + exporter := newExporter("qmName") + exporter.Describe(ch) + }() + + collect := <-requestChannel + if collect { + t.Errorf("Received unexpected collect request") + } + + metrics := initialiseMetrics() + responseChannel <- metrics + + select { + case prometheusDesc := <-ch: + expected := "Desc{fqName: \"ibmmq_qmgr_Element1Name\", help: \"Element1Description\", constLabels: {}, variableLabels: [qmgr]}" + actual := prometheusDesc.String() + if actual != expected { + t.Errorf("Expected value=%s; actual %s", expected, actual) + } + case <-time.After(1 * time.Second): + t.Error("Did not receive channel response from describe") + } +} + +func TestCollect(t *testing.T) { + + teardownTestCase := setupTestCase() + defer teardownTestCase() + + exporter := newExporter("qmName") + exporter.gaugeMap["ClassName/Type1Name/Element1Name"] = createGaugeVec("Element1Name", "Element1Description", false) + + for i := 1; i <= 3; i++ { + + ch := make(chan prometheus.Metric) + go func() { + exporter.Collect(ch) + close(ch) + }() + + collect := <-requestChannel + if !collect { + t.Errorf("Received unexpected describe request") + } + + populateTestMetrics(i) + metrics := initialiseMetrics() + updateMetrics(metrics) + responseChannel <- metrics + + select { + case <-ch: + prometheusMetric := dto.Metric{} + exporter.gaugeMap["ClassName/Type1Name/Element1Name"].WithLabelValues("qmName").Write(&prometheusMetric) + actual := prometheusMetric.GetGauge().GetValue() + + if i == 1 { + if actual != float64(0) { + t.Errorf("Expected values to be zero on first collect; actual %f", actual) + } + } else if actual != float64(i) { + t.Errorf("Expected value=%f; actual %f", float64(i), actual) + } + case <-time.After(1 * time.Second): + t.Error("Did not receive channel response from collect") + } + } +} + func TestCreateGaugeVec(t *testing.T) { ch := make(chan *prometheus.Desc) diff --git a/internal/metrics/update_test.go b/internal/metrics/update_test.go index 3156779..d2bb65a 100644 --- a/internal/metrics/update_test.go +++ b/internal/metrics/update_test.go @@ -55,6 +55,39 @@ func TestInitialiseMetrics(t *testing.T) { } } +func TestUpdateMetrics(t *testing.T) { + + teardownTestCase := setupTestCase() + defer teardownTestCase() + + metrics := initialiseMetrics() + updateMetrics(metrics) + + metric, _ := metrics["ClassName/Type1Name/Element1Name"] + actual, ok := metric.values[qmgrLabelValue] + + if !ok { + t.Error("No metric values found for queue manager label") + } else { + if actual != float64(1) { + t.Errorf("Expected metric value=%f; actual %f", float64(1), actual) + } + if len(metric.values) != 1 { + t.Errorf("Expected values-size=%d; actual %d", 1, len(metric.values)) + } + } + + if len(mqmetric.Metrics.Classes[0].Types[0].Elements[0].Values) != 0 { + t.Error("Unexpected cached value; publication data should have been reset") + } + + updateMetrics(metrics) + + if len(metric.values) != 0 { + t.Errorf("Unexpected metric value; data should have been cleared") + } +} + func TestMakeKey(t *testing.T) { teardownTestCase := setupTestCase() @@ -68,13 +101,13 @@ func TestMakeKey(t *testing.T) { } func setupTestCase() func() { - populateTestMetrics() + populateTestMetrics(1) return func() { cleanTestMetrics() } } -func populateTestMetrics() { +func populateTestMetrics(testValue int) { metricClass := new(mqmetric.MonClass) metricType1 := new(mqmetric.MonType) @@ -87,8 +120,11 @@ func populateTestMetrics() { metricType2.Name = "Type2Name" metricElement1.MetricName = "Element1Name" metricElement1.Description = "Element1Description" + metricElement1.Values = make(map[string]int64) + metricElement1.Values[qmgrLabelValue] = int64(testValue) metricElement2.MetricName = "Element2Name" metricElement2.Description = "Element2Description" + metricElement2.Values = make(map[string]int64) metricType1.ObjectTopic = "ObjectTopic" metricType2.ObjectTopic = "%s" metricElement1.Parent = metricType1 From 7f78ba89bfeeadd44c07a530b92fd77645d8d383 Mon Sep 17 00:00:00 2001 From: Stephen Marshall Date: Fri, 25 May 2018 08:54:20 +0100 Subject: [PATCH 2/2] Security upgrade for libprocps4 and procps --- install-mq.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install-mq.sh b/install-mq.sh index 0d520c5..d828349 100644 --- a/install-mq.sh +++ b/install-mq.sh @@ -130,7 +130,7 @@ rm -rf ${DIR_EXTRACT} # Apply any bug fixes not included in base Ubuntu or MQ image. # Don't upgrade everything based on Docker best practices https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#run -$UBUNTU && apt-get upgrade -y perl-base +$UBUNTU && apt-get upgrade -y libprocps4 procps # End of bug fixes # Clean up cached files