From 96bf7614b20bdf6d995cdde3500c7ab0c776ca08 Mon Sep 17 00:00:00 2001 From: Sebastian Widmer Date: Tue, 20 Dec 2022 15:48:25 +0100 Subject: [PATCH] Allow testing PromQL queries --- Makefile.vars.mk | 2 +- go.mod | 3 + go.sum | 12 ++- pkg/db/seeds/promtest/common.libsonnet | 13 +++ pkg/db/seeds/promtest/query.jsonnet | 105 +++++++++++++++++++++++++ pkg/db/seeds/queries_test.go | 55 +++++++++++++ pkg/testsuite/prometheus.go | 6 +- 7 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 pkg/db/seeds/promtest/common.libsonnet create mode 100644 pkg/db/seeds/promtest/query.jsonnet create mode 100644 pkg/db/seeds/queries_test.go diff --git a/Makefile.vars.mk b/Makefile.vars.mk index 8baa94b..682c69e 100644 --- a/Makefile.vars.mk +++ b/Makefile.vars.mk @@ -19,7 +19,7 @@ COMPOSE_CMD ?= docker-compose COMPOSE_DB_URL ?= postgres://reporting:reporting@localhost:55432/reporting-db?sslmode=disable COMPOSE_FILE ?= docker-compose.yml -PROMETHEUS_VERSION ?= 2.32.1 +PROMETHEUS_VERSION ?= 2.40.7 PROMETHEUS_DIST ?= $(shell go env GOOS) PROMETHEUS_ARCH ?= $(shell go env GOARCH) PROMETHEUS_DOWNLOAD_LINK ?= https://github.com/prometheus/prometheus/releases/download/v$(PROMETHEUS_VERSION)/prometheus-$(PROMETHEUS_VERSION).$(PROMETHEUS_DIST)-$(PROMETHEUS_ARCH).tar.gz diff --git a/go.mod b/go.mod index 1791985..0bb5a4b 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( github.com/go-logr/logr v1.2.3 github.com/go-logr/zapr v1.2.3 + github.com/google/go-jsonnet v0.19.1 github.com/google/uuid v1.3.0 github.com/jackc/pgconn v1.13.0 github.com/jackc/pgtype v1.12.0 @@ -38,5 +39,7 @@ require ( go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/text v0.3.7 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + sigs.k8s.io/yaml v1.1.0 // indirect ) diff --git a/go.sum b/go.sum index d02e691..ec74199 100644 --- a/go.sum +++ b/go.sum @@ -69,6 +69,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -130,6 +131,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-jsonnet v0.19.1 h1:MORxkrG0elylUqh36R4AcSPX0oZQa9hvI3lroN+kDhs= +github.com/google/go-jsonnet v0.19.1/go.mod h1:5JVT33JVCoehdTj5Z2KJq1eIdt3Nb8PCmZ+W5D8U350= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -229,6 +232,7 @@ github.com/lopezator/migrator v0.3.1 h1:ZFPT6aC7+nGWkqhleynABZ6ftycSf6hmHHLOaryq github.com/lopezator/migrator v0.3.1/go.mod h1:X+lHDMZ9Ci3/KdbypJcQYFFwipVrJsX4fRCQ4QLauYk= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -283,6 +287,8 @@ github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThC github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -475,7 +481,8 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -631,6 +638,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -649,3 +657,5 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/pkg/db/seeds/promtest/common.libsonnet b/pkg/db/seeds/promtest/common.libsonnet new file mode 100644 index 0000000..0544b44 --- /dev/null +++ b/pkg/db/seeds/promtest/common.libsonnet @@ -0,0 +1,13 @@ +local formatLabels = function(labels) + local lf = std.join(', ', std.map(function(l) '%s="%s"' % [ l, labels[l] ], std.objectFields(labels))); + "{%s}" % [ lf ]; + +local series = function(name, labels, values) { + series: name+formatLabels(labels), + values: values, +}; + +{ + series: series, + formatLabels: formatLabels, +} diff --git a/pkg/db/seeds/promtest/query.jsonnet b/pkg/db/seeds/promtest/query.jsonnet new file mode 100644 index 0000000..94665ac --- /dev/null +++ b/pkg/db/seeds/promtest/query.jsonnet @@ -0,0 +1,105 @@ +local c = import 'common.libsonnet'; + +local query = importstr '../appuio_cloud_memory.promql'; + +local commonLabels = { + cluster_id: 'c-appuio-cloudscale-lpg-2', + tenant_id: 'c-appuio-cloudscale-lpg-2', +}; + +{ + tests: [ + { + interval: '30s', + local runningUID = '35e3a8b1-b46d-496c-b2b7-1b52953bf904', + local succeededUID = '2a7a6e32-0840-4ac3-bab4-52d7e16f4a0a', + input_series: [ + c.series('kube_node_labels', commonLabels { + label_appuio_io_node_class: 'flex', + label_kubernetes_io_hostname: 'flex-x666', + node: 'flex-x666', + }, '1+0x10'), + c.series('kube_namespace_labels', commonLabels { + namespace: 'testproject', + label_appuio_io_organization: 'cherry-pickers-inc', + }, '1+0x10'), + // Phases + c.series('kube_pod_status_phase', commonLabels { + namespace: 'testproject', + phase: 'Succeeded', + pod: 'succeeded-pod', + uid: succeededUID, + }, '1+0x10'), + c.series('kube_pod_status_phase', commonLabels { + namespace: 'testproject', + phase: 'Running', + pod: 'running-pod', + uid: runningUID, + }, '1+0x10'), + // Requests + c.series('kube_pod_container_resource_requests', commonLabels { + namespace: 'testproject', + pod: 'succeeded-pod', + resource: 'memory', + node: 'flex-x666', + uid: succeededUID, + }, '1+0x10'), + c.series('kube_pod_container_resource_requests', commonLabels { + namespace: 'testproject', + pod: 'running-pod', + resource: 'memory', + node: 'flex-x666', + uid: runningUID, + }, '1+0x10'), + c.series('kube_pod_container_resource_requests', commonLabels { + namespace: 'testproject', + pod: 'succeeded-pod', + node: 'flex-x666', + resource: 'cpu', + uid: succeededUID, + }, '0+0x10'), + c.series('kube_pod_container_resource_requests', commonLabels { + namespace: 'testproject', + pod: 'running-pod', + node: 'flex-x666', + resource: 'cpu', + uid: runningUID, + }, '0+0x10'), + // Real usage + c.series('container_memory_working_set_bytes', commonLabels { + image: 'busybox', + namespace: 'testproject', + pod: 'succeeded-pod', + node: 'flex-x666', + uid: succeededUID, + }, '1+0x10'), + c.series('container_memory_working_set_bytes', commonLabels { + image: 'busybox', + namespace: 'testproject', + pod: 'running-pod', + node: 'flex-x666', + uid: runningUID, + }, '1+0x10'), + ], + promql_expr_test: [ + { + expr: query, + eval_time: '1h', + exp_samples: [ + { + labels: c.formatLabels({ + category: 'c-appuio-cloudscale-lpg-2:testproject', + cluster_id: 'c-appuio-cloudscale-lpg-2', + label_appuio_io_node_class: 'flex', + namespace: 'testproject', + product: 'appuio_cloud_memory:c-appuio-cloudscale-lpg-2:cherry-pickers-inc:testproject:flex', + tenant_id: 'cherry-pickers-inc', + }), + value: 128 * 10, + }, + ], + }, + ], + }, + ], +} diff --git a/pkg/db/seeds/queries_test.go b/pkg/db/seeds/queries_test.go new file mode 100644 index 0000000..0042eb9 --- /dev/null +++ b/pkg/db/seeds/queries_test.go @@ -0,0 +1,55 @@ +package seeds_test + +import ( + "fmt" + "io/fs" + "os" + "os/exec" + "path" + "strings" + "testing" + + "github.com/google/go-jsonnet" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/appuio/appuio-cloud-reporting/pkg/testsuite" +) + +func TestQueries(t *testing.T) { + wd := os.DirFS(".") + testFiles, err := fs.Glob(wd, "promtest/*.jsonnet") + require.NoError(t, err) + + for _, tFile := range testFiles { + t.Run(tFile, func(t *testing.T) { + tmp := renderJsonnet(t, tFile) + runPromtool(t, tmp) + }) + } +} + +func runPromtool(t *testing.T, tmp string) { + t.Helper() + + cmd := exec.Command(testsuite.PromtoolBin, "test", "rules", tmp) + var stderr, stdout strings.Builder + cmd.Stderr = &stderr + cmd.Stdout = &stdout + assert.NoError(t, cmd.Run()) + // Not using t.Log to keep formatting sane + fmt.Println("STDERR") + fmt.Println(stderr.String()) + fmt.Println("STDOUT") + fmt.Println(stdout.String()) +} + +func renderJsonnet(t *testing.T, tFile string) string { + t.Helper() + + ev, err := jsonnet.MakeVM().EvaluateFile(tFile) + require.NoError(t, err) + tmp := path.Join(t.TempDir(), "test.json") + require.NoError(t, os.WriteFile(tmp, []byte(ev), 0644)) + return tmp +} diff --git a/pkg/testsuite/prometheus.go b/pkg/testsuite/prometheus.go index 7987309..e551424 100644 --- a/pkg/testsuite/prometheus.go +++ b/pkg/testsuite/prometheus.go @@ -14,8 +14,12 @@ var ( // Root folder of this project Root = filepath.Join(filepath.Dir(b), "..", "..") + // PromDir is the filepath to the prometheus directory + PromDir = filepath.Join(Root, ".cache", "prometheus") // PromBin is the filepath to the Prometheus binary - PromBin = filepath.Join(Root, ".cache", "prometheus", "prometheus") + PromBin = filepath.Join(PromDir, "prometheus") + // PromtoolBin is the filepath to the promtool binary + PromtoolBin = filepath.Join(PromDir, "promtool") ) // StartPrometheus starts a new prometheus instance on the given port.