Skip to content

Commit e72c630

Browse files
committed
test : add metrics to track etcd cpu and memory usage
Signed-off-by: Rohan Kumar <[email protected]>
1 parent 1434d63 commit e72c630

File tree

1 file changed

+80
-1
lines changed

1 file changed

+80
-1
lines changed

test/load/devworkspace_load_test.js

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,41 @@ const devworkspaceReadyDuration = new Trend('devworkspace_ready_duration');
7676
const devworkspaceReadyFailed = new Counter('devworkspace_ready_failed');
7777
const operatorCpu = new Trend('average_operator_cpu'); // in milli cores
7878
const operatorMemory = new Trend('average_operator_memory'); // in Mi
79+
const etcdCpu = new Trend('average_etcd_cpu'); // in milli cores
80+
const etcdMemory = new Trend('average_etcd_memory'); // in Mi
7981
const devworkspacesCreated = new Counter('devworkspace_create_count');
8082
const operatorCpuViolations = new Counter('operator_cpu_violations');
8183
const operatorMemViolations = new Counter('operator_mem_violations');
8284

8385
const maxCpuMillicores = 250;
8486
const maxMemoryBytes = 200 * 1024 * 1024;
8587

88+
let etcdNamespace = 'openshift-etcd';
89+
let etcdPodNamePattern = 'etcd';
90+
91+
function detectClusterType() {
92+
const apiGroupsUrl = `${apiServer}/apis`;
93+
const res = http.get(apiGroupsUrl, {headers});
94+
95+
if (res.status === 200) {
96+
try {
97+
const data = JSON.parse(res.body);
98+
const groups = data.groups || [];
99+
const hasOpenShiftRoutes = groups.some(g => g.name === 'route.openshift.io');
100+
101+
if (!hasOpenShiftRoutes) {
102+
etcdNamespace = __ENV.ETCD_NAMESPACE || 'kube-system';
103+
etcdPodNamePattern = __ENV.ETCD_POD_NAME_PATTERN || 'kube-proxy';
104+
console.log('Detected Kubernetes cluster - using kube-system namespace with kube-proxy');
105+
}
106+
} catch (e) {
107+
console.warn(`Failed to detect cluster type: ${e.message}, using defaults`);
108+
}
109+
}
110+
}
111+
86112
export function setup() {
113+
detectClusterType();
87114
if (shouldCreateAutomountResources) {
88115
createNewAutomountConfigMap();
89116
createNewAutomountSecret();
@@ -155,7 +182,7 @@ export function final_cleanup() {
155182
}
156183

157184
export function handleSummary(data) {
158-
const allowed = ['devworkspace_create_count', 'devworkspace_create_duration', 'devworkspace_delete_duration', 'devworkspace_ready_duration', 'devworkspace_ready', 'devworkspace_ready_failed', 'operator_cpu_violations', 'operator_mem_violations', 'average_operator_cpu', 'average_operator_memory'];
185+
const allowed = ['devworkspace_create_count', 'devworkspace_create_duration', 'devworkspace_delete_duration', 'devworkspace_ready_duration', 'devworkspace_ready', 'devworkspace_ready_failed', 'operator_cpu_violations', 'operator_mem_violations', 'average_operator_cpu', 'average_operator_memory', 'etcd_cpu_violations', 'etcd_mem_violations', 'average_etcd_cpu', 'average_etcd_memory'];
159186

160187
const filteredData = JSON.parse(JSON.stringify(data));
161188
for (const key of Object.keys(filteredData.metrics)) {
@@ -227,6 +254,7 @@ function waitUntilDevWorkspaceIsReady(vuId, crName, namespace) {
227254
}
228255

229256
checkDevWorkspaceOperatorMetrics();
257+
checkEtcdMetrics();
230258
sleep(pollWaitInterval);
231259
attempts++;
232260
}
@@ -295,6 +323,57 @@ function checkDevWorkspaceOperatorMetrics() {
295323
}
296324
}
297325

326+
function checkEtcdMetrics() {
327+
if (!etcdNamespace || !etcdPodNamePattern) {
328+
console.warn(`[ETCD METRICS] Variables not initialized: etcdNamespace=${etcdNamespace}, etcdPodNamePattern=${etcdPodNamePattern}`);
329+
return;
330+
}
331+
332+
const metricsUrl = `${apiServer}/apis/metrics.k8s.io/v1beta1/namespaces/${etcdNamespace}/pods`;
333+
const res = http.get(metricsUrl, {headers});
334+
335+
check(res, {
336+
'Fetched etcd pod metrics successfully': (r) => r.status === 200,
337+
});
338+
339+
if (res.status !== 200) {
340+
return;
341+
}
342+
343+
const data = JSON.parse(res.body);
344+
const etcdPods = data.items.filter(p => p.metadata.name.includes(etcdPodNamePattern));
345+
346+
if (etcdPods.length === 0) {
347+
if (data.items && data.items.length > 0) {
348+
const podNames = data.items.map(p => p.metadata.name).join(', ');
349+
console.warn(`[ETCD METRICS] No pods found matching pattern '${etcdPodNamePattern}' in namespace '${etcdNamespace}'. Available pods: ${podNames}`);
350+
} else {
351+
console.warn(`[ETCD METRICS] No pods found in namespace '${etcdNamespace}'`);
352+
}
353+
return;
354+
}
355+
356+
for (const pod of etcdPods) {
357+
if (!pod.containers || pod.containers.length === 0) {
358+
console.warn(`[ETCD METRICS] Pod ${pod.metadata.name} has no containers`);
359+
continue;
360+
}
361+
const container = pod.containers[0];
362+
const name = pod.metadata.name;
363+
364+
if (!container.usage || !container.usage.cpu || !container.usage.memory) {
365+
console.warn(`[ETCD METRICS] Pod ${name} has no usage data:`, JSON.stringify(container.usage));
366+
continue;
367+
}
368+
369+
const cpu = parseCpuToMillicores(container.usage.cpu);
370+
const memory = parseMemoryToBytes(container.usage.memory);
371+
372+
etcdCpu.add(cpu);
373+
etcdMemory.add(memory / 1024 / 1024);
374+
}
375+
}
376+
298377
function createNewNamespace(namespaceName) {
299378
const url = `${apiServer}/api/v1/namespaces`;
300379

0 commit comments

Comments
 (0)