Skip to content

Commit f4ebd81

Browse files
committed
promtorture v0.1
1 parent b12cf24 commit f4ebd81

File tree

9 files changed

+320
-0
lines changed

9 files changed

+320
-0
lines changed

testcases/promtorture/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
promtorture

testcases/promtorture/Dockerfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# syntax=docker/dockerfile:1
2+
3+
# docker buildx build -t promtorture .
4+
FROM golang:1.22-alpine AS builder
5+
6+
WORKDIR /app
7+
COPY go.mod go.sum .
8+
RUN --mount=type=cache,target=/go/pkg/mod \
9+
go mod download
10+
11+
COPY . .
12+
RUN --mount=type=cache,target=/go/pkg/mod \
13+
go build -o /app/promtorture .
14+
15+
FROM scratch
16+
COPY --from=builder /app/promtorture /app/promtorture
17+
18+
ENTRYPOINT ["/app/promtorture"]

testcases/promtorture/LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Do what you want

testcases/promtorture/cmd/root.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"strconv"
8+
"strings"
9+
10+
"github.com/ringerc/scrapcode/testcases/promtorture/internal/generator"
11+
"github.com/ringerc/scrapcode/testcases/promtorture/internal/server"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
var rootCmd = &cobra.Command{
16+
Use: "promtorture",
17+
Short: "Generate metrics to make Prometheus sad",
18+
Long: `Generate varied sets of metrics to test
19+
Prometheus (and compatible) memory, CPU, disk consumption,
20+
computed cardinality etc.
21+
22+
This is intended as a torture test for Prometheus, Thanos,
23+
VictoriaMetrics etc resource use.
24+
`,
25+
Run: func(cmd *cobra.Command, args []string) {
26+
var err error
27+
cfg := generator.Config{}
28+
cfg.Port, err = cmd.Flags().GetInt("port")
29+
if err != nil {
30+
log.Fatalf("while parsing --port: %v", err)
31+
}
32+
targetsArg, err := cmd.Flags().GetString("targets")
33+
if err != nil {
34+
log.Fatalf("while reading --targets: %v", err)
35+
}
36+
cfg.Targets, err = parseTargets(targetsArg)
37+
if err != nil {
38+
log.Fatalf("while parsing --targets: %v", err)
39+
}
40+
cfg.InfoMetricsLabels, err = cmd.Flags().GetInt("info-metrics-labels")
41+
if err != nil {
42+
log.Fatalf("while parsing --info-metrics-labels: %v", err)
43+
}
44+
cfg.GaugeMetrics, err = cmd.Flags().GetInt("gauge-metrics")
45+
if err != nil {
46+
log.Fatalf("while parsing --gauge-metrics: %v", err)
47+
}
48+
49+
reg := generator.CreateRegistry(cfg)
50+
server.ServeForever(reg)
51+
},
52+
}
53+
54+
func Execute() {
55+
err := rootCmd.Execute()
56+
if err != nil {
57+
os.Exit(1)
58+
}
59+
}
60+
61+
func init() {
62+
rootCmd.Flags().String("targets", "1", "Number of unique targets to generate. Can be a comma separated list of numbers in which case multiple labels will be used and each number specifies the number of unique values in that label.")
63+
rootCmd.Flags().Int("info-metrics-labels", 0, "Number of labels per info metric. If 0, no info-metric will be generated.")
64+
rootCmd.Flags().Int("gauge-metrics", 1, "Number of gauge metrics to generate for each target.")
65+
rootCmd.Flags().IntP("port", "p", 8080, "Port to listen on for scrape requests")
66+
}
67+
68+
/*
69+
* Parse comma-separated list of target label counts into a slice of integers
70+
*/
71+
func parseTargets(targetsParam string) ([]int, error) {
72+
targets := make([]int, 0)
73+
for i, t := range strings.Split(targetsParam, ",") {
74+
tn, err := strconv.ParseInt(t, 10, 32)
75+
if err != nil {
76+
return nil, fmt.Errorf("\"%v\" element \"%s\" (#%d): %v", targetsParam, t, i, err)
77+
}
78+
if tn < 1 {
79+
return nil, fmt.Errorf("\"%v\" element \"%s\" (#%d): must be >= 1", targetsParam, t, i)
80+
}
81+
targets = append(targets, int(tn))
82+
}
83+
return targets, nil
84+
}

testcases/promtorture/go.mod

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
module github.com/ringerc/scrapcode/testcases/promtorture
2+
3+
go 1.22.0
4+
5+
require (
6+
github.com/prometheus/client_golang v1.19.1
7+
github.com/spf13/cobra v1.8.1
8+
honnef.co/go/tools v0.4.7
9+
)
10+
11+
require (
12+
github.com/BurntSushi/toml v1.2.1 // indirect
13+
github.com/beorn7/perks v1.0.1 // indirect
14+
github.com/cespare/xxhash/v2 v2.2.0 // indirect
15+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
16+
github.com/prometheus/client_model v0.5.0 // indirect
17+
github.com/prometheus/common v0.48.0 // indirect
18+
github.com/prometheus/procfs v0.12.0 // indirect
19+
github.com/spf13/pflag v1.0.5 // indirect
20+
golang.org/x/sys v0.17.0 // indirect
21+
golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5 // indirect
22+
google.golang.org/protobuf v1.33.0 // indirect
23+
)

testcases/promtorture/go.sum

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
2+
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
3+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
4+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
5+
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
6+
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
7+
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
8+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
11+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
12+
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
13+
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
14+
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
15+
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
16+
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
17+
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
18+
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
19+
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
20+
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
21+
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
22+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
23+
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
24+
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
25+
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
26+
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
27+
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
28+
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
29+
golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5 h1:Vk4mysSz+GqQK2eqgWbo4zEO89wkeAjJiFIr9bpqa8k=
30+
golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
31+
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
32+
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
33+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
34+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
35+
honnef.co/go/tools v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs=
36+
honnef.co/go/tools v0.4.7/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0=
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Generate varied prometheus metrics
3+
*/
4+
package generator
5+
6+
import (
7+
"fmt"
8+
"log"
9+
"strconv"
10+
11+
"github.com/prometheus/client_golang/prometheus"
12+
)
13+
14+
type Config struct {
15+
Port int
16+
Targets []int
17+
InfoMetricsLabels int
18+
GaugeMetrics int
19+
}
20+
21+
type target struct {
22+
targetNumber int
23+
targetIndexes []int
24+
targetLabels prometheus.Labels
25+
infoMetric *prometheus.Gauge
26+
gaugeMetrics []prometheus.Gauge
27+
}
28+
29+
// string representation of the target labels
30+
func (t *target) indexesString() string {
31+
s := ""
32+
for i, v := range t.targetIndexes {
33+
if i > 0 {
34+
s += ","
35+
}
36+
s += strconv.Itoa(v)
37+
}
38+
return s
39+
}
40+
41+
func createTarget(cfg Config, targetNumber int, targetIndexes []int) target {
42+
t := target{
43+
targetNumber: targetNumber,
44+
targetIndexes: targetIndexes,
45+
}
46+
t.targetLabels = prometheus.Labels{}
47+
for i, v := range targetIndexes {
48+
t.targetLabels["target_label_"+strconv.Itoa(i)] = strconv.Itoa(v)
49+
}
50+
51+
log.Printf("Creating target %d with indexes %v, labels %v", targetNumber, targetIndexes, t.targetLabels)
52+
53+
indexesStr := t.indexesString()
54+
55+
if cfg.InfoMetricsLabels > 0 {
56+
infoMetricLabels := prometheus.Labels{}
57+
// copy the target labels to the info metric label set
58+
for k, v := range t.targetLabels {
59+
infoMetricLabels[k] = v
60+
}
61+
// add info labels. The names must be consistent for each
62+
// target, and the values should differ since we presume the
63+
// targets are unique. So the target indexes will be used for
64+
// the info-metric values.
65+
for i := 0; i < cfg.InfoMetricsLabels; i++ {
66+
infoMetricLabels["info_label_"+strconv.Itoa(i)] = indexesStr
67+
}
68+
infoMetric := prometheus.NewGauge(prometheus.GaugeOpts{
69+
Name: "info_metric",
70+
Help: "Info metric",
71+
ConstLabels: infoMetricLabels,
72+
})
73+
t.infoMetric = &infoMetric
74+
//log.Printf("Created info-metric %s", (*t.infoMetric).Desc().String())
75+
}
76+
77+
t.gaugeMetrics = make([]prometheus.Gauge, cfg.GaugeMetrics)
78+
for i := 0; i < cfg.GaugeMetrics; i++ {
79+
t.gaugeMetrics[i] = prometheus.NewGauge(prometheus.GaugeOpts{
80+
Name: fmt.Sprintf("gauge_metric_%d", i),
81+
Help: fmt.Sprintf("Gauge metric %d", i),
82+
ConstLabels: t.targetLabels,
83+
})
84+
//log.Printf("Created gauge metric %s", t.gaugeMetrics[i].Desc().String())
85+
}
86+
87+
return t
88+
}
89+
90+
func (t *target) register(reg *prometheus.Registry) {
91+
if t.infoMetric != nil {
92+
reg.MustRegister(*t.infoMetric)
93+
}
94+
for _, m := range t.gaugeMetrics {
95+
reg.MustRegister(m)
96+
}
97+
}
98+
99+
func CreateRegistry(cfg Config) *prometheus.Registry {
100+
101+
// The number of targets to generate is the array product of
102+
// cfg.Targets. The number of labels can vary, so we'll use a counter
103+
// array, incrementing each value until it reaches the limit then reset
104+
// to 0 and roll over to increment the next counter. Dumb, but easy.
105+
totalTargets := 1
106+
targetCounters := make([]int, len(cfg.Targets))
107+
for i := 0; i < len(cfg.Targets); i++ {
108+
targetCounters[i] = 0
109+
totalTargets *= cfg.Targets[i]
110+
}
111+
112+
log.Printf("creating registry for %d targets: %v", totalTargets, cfg.Targets)
113+
114+
reg := prometheus.NewRegistry()
115+
116+
for targetNumber := 0; targetNumber < totalTargets; targetNumber++ {
117+
remainder := targetNumber
118+
for i := 0; i < len(targetCounters); i++ {
119+
targetCounters[i] = remainder % cfg.Targets[i]
120+
remainder = remainder / cfg.Targets[i]
121+
}
122+
123+
t := createTarget(cfg, targetNumber, targetCounters)
124+
t.register(reg)
125+
}
126+
127+
return reg
128+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* prometheus metrics server endpoint
3+
*/
4+
package server
5+
6+
import (
7+
"log"
8+
"net/http"
9+
10+
"github.com/prometheus/client_golang/prometheus"
11+
"github.com/prometheus/client_golang/prometheus/promhttp"
12+
)
13+
14+
func ServeForever(reg *prometheus.Registry) {
15+
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}))
16+
log.Printf("Listening on :8080 for /metrics")
17+
log.Fatal(http.ListenAndServe(":8080", nil))
18+
}

testcases/promtorture/main.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
3+
4+
*/
5+
package main
6+
7+
import "github.com/ringerc/scrapcode/testcases/promtorture/cmd"
8+
9+
func main() {
10+
cmd.Execute()
11+
}

0 commit comments

Comments
 (0)