diff --git a/cmd/collector/collector_test.go b/cmd/collector/collector_test.go index 3450e42..d719d98 100644 --- a/cmd/collector/collector_test.go +++ b/cmd/collector/collector_test.go @@ -1,4 +1,4 @@ -package collector +package main import ( "fmt" @@ -10,12 +10,40 @@ import ( "github.com/stretchr/testify/require" ) -// TODO -func TestMetricsUp() { - // testutil.ScrapeAndCompare() - // "http://localhost:2112/metrics" - // # HELP perfpod_memory_collector_up_metric Test metric to confirm skeleton application functionality. - // # TYPE perfpod_memory_collector_up_metric gauge - // perfpod_memory_collector_up_metric 1 -} +func TestMetricsUp(t *testing.T) { + + // Initialize module/server for standalone integration test + go main() + time.Sleep(1 * time.Second) + + // Set values for standalone integration test + serverURL := "http://localhost:2112" + metricName := "perfpod_memory_collector_up_metric" + metricHelpText := "Test metric to confirm skeleton application functionality." + metricType := "gauge" + expectedMetricValue := 1 + + // Check for test result every 100 milliseconds with a 1 second limit + require.Eventuallyf(t, func() bool { + + // Exact multiline metric template required by testutil.ScrapeAndCompare: + + // # HELP perfpod_memory_collector_up_metric Test metric to confirm skeleton application functionality. + // # TYPE perfpod_memory_collector_up_metric gauge + // perfpod_memory_collector_up_metric 1 + + // Generalized metric template + expected := fmt.Sprintf("\n# HELP %s %s\n# TYPE %s %s\n%s %d\n", metricName, metricHelpText, metricName, metricType, metricName, expectedMetricValue) + + // Check current server metrics against expected string/value + if err := testutil.ScrapeAndCompare(serverURL+"/metrics", strings.NewReader(expected), metricName); err == nil { + return true + + // Returns an error and error message if the expected string/value is not found + } else { + t.Log(err.Error()) + return false + } + }, time.Second, 100*time.Millisecond, "Could not find metric %s with value %d", metricName, expectedMetricValue) +} diff --git a/cmd/collector/go.mod b/cmd/collector/go.mod index 5384a4d..a065b9e 100644 --- a/cmd/collector/go.mod +++ b/cmd/collector/go.mod @@ -5,12 +5,17 @@ go 1.22.9 require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/klauspost/compress v1.17.9 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/stretchr/testify v1.10.0 // indirect golang.org/x/sys v0.22.0 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/cmd/collector/go.sum b/cmd/collector/go.sum index 49cf790..55e39a6 100644 --- a/cmd/collector/go.sum +++ b/cmd/collector/go.sum @@ -2,10 +2,16 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +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 v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= @@ -14,7 +20,12 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cmd/collector/main.go b/cmd/collector/main.go index 2772ae9..ee301c6 100644 --- a/cmd/collector/main.go +++ b/cmd/collector/main.go @@ -2,26 +2,12 @@ package main import ( "net/http" - - "github.com/prometheus/client_golang/prometheus" - // "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" ) -func recordMetrics() { - upMetric := prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: "perfpod", - Subsystem: "memory_collector", - Name: "up_metric", - Help: "Test metric to confirm skeleton application functionality.", - }) - prometheus.MustRegister(upMetric) - - upMetric.Set(1) -} func main() { - recordMetrics() + go recordMetrics() http.Handle("/metrics", promhttp.Handler()) http.ListenAndServe(":2112", nil) diff --git a/cmd/collector/record_metrics.go b/cmd/collector/record_metrics.go new file mode 100644 index 0000000..50c6922 --- /dev/null +++ b/cmd/collector/record_metrics.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/prometheus/client_golang/prometheus" +) + + +func recordMetrics() { + upMetric := prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "perfpod", + Subsystem: "memory_collector", + Name: "up_metric", + Help: "Test metric to confirm skeleton application functionality.", + }) + prometheus.MustRegister(upMetric) + + upMetric.Set(1) +} diff --git a/docs/devlog.md b/docs/devlog.md index dcb4348..e4a77b8 100644 --- a/docs/devlog.md +++ b/docs/devlog.md @@ -1,8 +1,8 @@ Documentation of development steps, environment, and dependencies -Contributors: atimeofday; -Goals: Create skeleton collector with Prometheus endpoint; -Issues: https://github.com/perfpod/memory-collector/issues/19 +- Contributors: atimeofday +- Goals: Create skeleton collector with Prometheus endpoint +- Issues: https://github.com/perfpod/memory-collector/issues/19 Initial environment and tools: ``` @@ -48,7 +48,8 @@ curl http://localhost:2112/metrics Issue 19 objective 3: Expose the `up` metric with value 1 ``` -# Created, registered, and set an 'up' metric in func main() +// Created, registered, and set an 'up' metric in func main() + upMetric := prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: "perfpod", Subsystem: "memory_collector", @@ -73,7 +74,7 @@ perfpod_memory_collector_up_metric 1 Issue 19 objective 5: Move the code into a function (not `main()`) ``` -# Moved Up metric into "func recordMetrics()" and added function call in main() +// Moved Up metric into "func recordMetrics()" and added function call in main() func main() { recordMetrics() @@ -82,10 +83,90 @@ func main() { http.ListenAndServe(":2112", nil) } -# Repeated manual verification endpoint query +// Repeated manual verification endpoint query ``` Issue 19 objective 6: Add an integration test that verifies the metrics are up, using client_golang's testutil - TO DO - May require assistance +--- + +- Issue 19 split into 5/5 done and new Issue 20 +- Issue 19 5/5 PR opened and merged + +--- + +- Contributors: atimeofday +- Goals: Add integration test to Prometheus endpoint +- Issues: https://github.com/perfpod/memory-collector/issues/20 + +Research & references: +``` +https://go.dev/doc/tutorial/add-a-test +https://albertmoreno.dev/posts/testing-prometheus-metrics-in-integration-tests-in-golang/ +https://github.com/prometheus/client_golang/blob/main/prometheus/testutil/testutil.go +https://github.com/prometheus/client_golang/blob/main/prometheus/testutil/testutil_test.go +``` + +``` +go get github.com/prometheus/client_golang/prometheus/testutil +go get github.com/stretchr/testify/require +``` + +Go test format: +``` +[filename]_test.go + +import( + [...] +) + +func [TestFunction](t *testing.T) { + // Set test values + // Perform test +} + +// Perform more tests +``` + +1. Created skeleton test based on examples + +``` +func TestMetricsUp(t *testing.T) { + require.Eventuallyf(t, func() bool { + + // Test values + // ??? expected format + + if err := testutil.ScrapeAndCompare(serverURL+"/metrics", strings.NewReader(expected), metricName); err == nil { + return true + } else { + t.Log(err.Error()) + return false + } + }, time.Second, 100*time.Millisecond, "Could not find metric %s with value %d", metricName, expectedMetricValue) +} +``` + +2. Checked the implementation of the testutil ScrapeAndCompare function, and notably, the implementation of its own integration test. +3. Located and implemented the exact input template required by the function, then implemented generalized code for the template. +4. Researched goroutines to allow automatically initializing the (currently local) remote server to be tested. + +``` +go main() +time.Sleep(1 * time.Second) +``` + +5. Refined logical flow from example code for improved readability. + +--- + +- Issue 20 done + +--- + +- Contributors: +- Goals: +- Issues: +