66 - main
77
88jobs :
9- benchmark :
9+ run- benchmark :
1010 runs-on : ubuntu-latest
1111 steps :
1212 - name : Checkout code
@@ -33,14 +33,228 @@ jobs:
3333 mkdir -p /tmp/artifacts/
3434 ARTIFACT_PATH=/tmp/artifacts make test-benchmark
3535
36- - name : Compare with baseline
36+ - name : Convert Benchmark Output to Prometheus Metrics
3737 run : |
38- go install golang.org/x/perf/cmd/benchstat@latest
39- benchstat benchmarks/baseline.txt /tmp/artifacts/new.txt | tee /tmp/artifacts/output
38+ mkdir -p /tmp/artifacts/prometheus/
39+ cat << 'EOF' > benchmark_to_prometheus.py
40+ import sys
41+ import re
4042
41- - name : Upload benchmark results
43+ def parse_benchmark_output(benchmark_output):
44+ metrics = []
45+ run_id = 0
46+ for line in benchmark_output.split("\n"):
47+ match = re.match(r"Benchmark([\w\d]+)-\d+\s+\d+\s+([\d]+)\s+ns/op\s+([\d]+)\s+B/op\s+([\d]+)\s+allocs/op", line)
48+ if match:
49+ benchmark_name = match.group(1).lower()
50+ time_ns = match.group(2)
51+ memory_bytes = match.group(3)
52+ allocs = match.group(4)
53+
54+ metrics.append(f"benchmark_{benchmark_name}_ns {{run_id=\"{run_id}\"}} {time_ns}")
55+ metrics.append(f"benchmark_{benchmark_name}_allocs {{run_id=\"{run_id}\"}} {allocs}")
56+ metrics.append(f"benchmark_{benchmark_name}_mem_bytes {{run_id=\"{run_id}\"}} {memory_bytes}")
57+ run_id+=1
58+
59+ return "\n".join(metrics)
60+
61+ if __name__ == "__main__":
62+ benchmark_output = sys.stdin.read()
63+ metrics = parse_benchmark_output(benchmark_output)
64+ print(metrics)
65+ EOF
66+
67+ cat /tmp/artifacts/new.txt | python3 benchmark_to_prometheus.py > /tmp/artifacts/prometheus/metrics.txt
68+
69+ # - name: Compare with baseline
70+ # run: |
71+ # go install golang.org/x/perf/cmd/benchstat@latest
72+ # benchstat benchmarks/baseline.txt /tmp/artifacts/new.txt | tee /tmp/artifacts/output
73+
74+ - name : Upload Benchmark Metrics
4275 uses : actions/upload-artifact@v4
4376 with :
44- name : benchmark-artifacts
45- path : /tmp/artifacts/
77+ name : benchmark-metrics
78+ path : /tmp/artifacts/prometheus/
79+
80+ run-prometheus :
81+ needs : run-benchmark
82+ runs-on : ubuntu-latest
83+ steps :
84+ - name : Checkout code
85+ uses : actions/checkout@v4
86+ with :
87+ fetch-depth : 0
4688
89+ # ToDo: use GitHub REST API to download artifact across repos
90+ - name : Download Prometheus Snapshot
91+ run : |
92+ echo "Available Artifacts in this run:"
93+ gh run list --repo operator-framework/operator-controller --limit 5
94+ gh run download --repo operator-framework/operator-controller --name prometheus-snapshot --dir .
95+ ls -lh ./
96+ env :
97+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
98+
99+ # #this step is invalid if download the artifacts in a different job
100+ # - name: Download Prometheus Snapshot2
101+ # uses: actions/download-artifact@v4
102+ # with:
103+ # name: prometheus-snapshot
104+ # path: ./
105+ # env:
106+ # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
107+
108+ - name : Download Benchmark Metrics
109+ uses : actions/download-artifact@v4
110+ with :
111+ name : benchmark-metrics
112+ path : ./
113+
114+ - name : Set Up Prometheus Config
115+ run : |
116+ cat << 'EOF' > prometheus.yml
117+ global:
118+ scrape_interval: 5s
119+ scrape_configs:
120+ - job_name: 'benchmark_metrics'
121+ static_configs:
122+ - targets: ['localhost:9000']
123+ EOF
124+ mkdir -p ${{ github.workspace }}/prometheus-data
125+ sudo chown -R 65534:65534 ${{ github.workspace }}/prometheus-data
126+ sudo chmod -R 777 ${{ github.workspace }}/prometheus-data
127+
128+ - name : Extract and Restore Prometheus Snapshot
129+ run : |
130+ SNAPSHOT_ZIP="${{ github.workspace }}/prometheus-snapshot.zip"
131+ SNAPSHOT_TAR="${{ github.workspace }}/prometheus_snapshot.tar.gz"
132+ SNAPSHOT_DIR="${{ github.workspace }}/prometheus-data/snapshots"
133+
134+ mkdir -p "$SNAPSHOT_DIR"
135+
136+ if [[ -f "$SNAPSHOT_ZIP" ]]; then
137+ echo "📦 Detected ZIP archive: $SNAPSHOT_ZIP"
138+ unzip -o "$SNAPSHOT_ZIP" -d "$SNAPSHOT_DIR"
139+ echo "✅ Successfully extracted ZIP snapshot."
140+ elif [[ -f "$SNAPSHOT_TAR" ]]; then
141+ echo "📦 Detected TAR archive: $SNAPSHOT_TAR"
142+ tar -xzf "$SNAPSHOT_TAR" -C "$SNAPSHOT_DIR"
143+ echo "✅ Successfully extracted TAR snapshot."
144+ else
145+ echo "⚠️ WARNING: No snapshot file found. Skipping extraction."
146+ fi
147+
148+ - name : Run Prometheus
149+ run : |
150+ docker run -d --name prometheus -p 9090:9090 \
151+ -v ${{ github.workspace }}/prometheus.yml:/etc/prometheus/prometheus.yml \
152+ -v ${{ github.workspace }}/prometheus-data:/prometheus \
153+ prom/prometheus --config.file=/etc/prometheus/prometheus.yml \
154+ --storage.tsdb.path=/prometheus \
155+ --storage.tsdb.retention.time=1h \
156+ --web.enable-admin-api
157+
158+ - name : Wait for Prometheus to start
159+ run : sleep 10
160+
161+ - name : Check Prometheus is running
162+ run : curl -s http://localhost:9090/-/ready || (docker logs prometheus && exit 1)
163+
164+ - name : Start HTTP Server to Expose Metrics
165+ run : |
166+ cat << 'EOF' > server.py
167+ from http.server import SimpleHTTPRequestHandler, HTTPServer
168+
169+ class MetricsHandler(SimpleHTTPRequestHandler):
170+ def do_GET(self):
171+ if self.path == "/metrics":
172+ self.send_response(200)
173+ self.send_header("Content-type", "text/plain")
174+ self.end_headers()
175+ with open("metrics.txt", "r") as f:
176+ self.wfile.write(f.read().encode())
177+ else:
178+ self.send_response(404)
179+ self.end_headers()
180+
181+ if __name__ == "__main__":
182+ server = HTTPServer(('0.0.0.0', 9000), MetricsHandler)
183+ print("Serving on port 9000...")
184+ server.serve_forever()
185+ EOF
186+
187+ nohup python3 server.py &
188+
189+ - name : Wait for Prometheus to Collect Data
190+ run : sleep 30
191+
192+ - name : Check Prometheus targets page
193+ run : |
194+ http_status=$(curl -o /dev/null -s -w "%{http_code}" http://localhost:9090/targets)
195+ if [ "$http_status" -eq 200 ]; then
196+ echo "Prometheus targets page is reachable."
197+ else
198+ echo "Error: Prometheus targets page is not reachable. Status code: $http_status"
199+ exit 1
200+ fi
201+
202+ - name : Check Benchmark Metrics Against Threshold
203+ run : |
204+ MAX_TIME_NS=1200000000 # 1.2s
205+ MAX_ALLOCS=4000
206+ MAX_MEM_BYTES=450000
207+
208+ # Query Prometheus Metrics, get the max value
209+ time_ns=$(curl -s "http://localhost:9090/api/v1/query?query=max(benchmark_createclustercatalog_ns)" | jq -r '.data.result[0].value[1]')
210+ allocs=$(curl -s "http://localhost:9090/api/v1/query?query=max(benchmark_createclustercatalog_allocs)" | jq -r '.data.result[0].value[1]')
211+ mem_bytes=$(curl -s "http://localhost:9090/api/v1/query?query=max(benchmark_createclustercatalog_mem_bytes)" | jq -r '.data.result[0].value[1]')
212+
213+ echo "⏳ Benchmark Execution Time: $time_ns ns"
214+ echo "🛠️ Memory Allocations: $allocs"
215+ echo "💾 Memory Usage: $mem_bytes bytes"
216+
217+ # threshold checking
218+ if (( $(echo "$time_ns > $MAX_TIME_NS" | bc -l) )); then
219+ echo "❌ ERROR: Execution time exceeds threshold!"
220+ exit 1
221+ fi
222+
223+ if (( $(echo "$allocs > $MAX_ALLOCS" | bc -l) )); then
224+ echo "❌ ERROR: Too many memory allocations!"
225+ exit 1
226+ fi
227+
228+ if (( $(echo "$mem_bytes > $MAX_MEM_BYTES" | bc -l) )); then
229+ echo "❌ ERROR: Memory usage exceeds threshold!"
230+ exit 1
231+ fi
232+
233+ echo "✅ All benchmarks passed within threshold!"
234+
235+ - name : Trigger Prometheus Snapshot
236+ run : |
237+ curl -X POST http://localhost:9090/api/v1/admin/tsdb/snapshot || (docker logs prometheus && exit 1)
238+
239+ - name : Find and Upload Prometheus Snapshot
240+ run : |
241+ SNAPSHOT_PATH=$(ls -td ${{ github.workspace }}/prometheus-data/snapshots/* 2>/dev/null | head -1 || echo "")
242+ if [[ -z "$SNAPSHOT_PATH" ]]; then
243+ echo "❌ No Prometheus snapshot found!"
244+ docker logs prometheus
245+ exit 1
246+ fi
247+
248+ echo "✅ Prometheus snapshot stored in: $SNAPSHOT_PATH"
249+ tar -czf $GITHUB_WORKSPACE/prometheus_snapshot.tar.gz -C "$SNAPSHOT_PATH" .
250+
251+
252+ - name : Stop Prometheus
253+ run : docker stop prometheus
254+
255+ - name : Upload Prometheus Snapshot
256+ uses : actions/upload-artifact@v4
257+ with :
258+ name : prometheus-snapshot
259+ path : prometheus_snapshot.tar.gz
260+
0 commit comments