Skip to content

Commit 95bd428

Browse files
committed
test : create large ca-bundle configmap in che installation namespace before running load tests
Signed-off-by: Rohan Kumar <[email protected]>
1 parent 0ef22dc commit 95bd428

File tree

5 files changed

+428
-10
lines changed

5 files changed

+428
-10
lines changed

test/load/README.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Load Testing for DevWorkspace Operator
2+
3+
This directory contains load testing tools for the DevWorkspace Operator using k6. The tests create multiple DevWorkspaces concurrently to measure the operator's performance under load.
4+
5+
## Prerequisites
6+
7+
- `kubectl` (version >= 1.24.0)
8+
- `curl` (version >= 7.0.0)
9+
- `k6` (version >= 1.1.0) - Required when using `--mode binary`
10+
- Access to a Kubernetes cluster with DevWorkspace Operator installed
11+
- Proper RBAC permissions to create DevWorkspaces, ConfigMaps, Secrets, and Namespaces
12+
13+
## Running Load Tests
14+
15+
The load tests can be run using the `make test_load` target with various arguments. The tests support two modes:
16+
- **binary mode**: Runs k6 locally (default)
17+
- **operator mode**: Runs k6 using the k6-operator in the cluster
18+
19+
### Running with Eclipse Che
20+
21+
When running with Eclipse Che, the script automatically provisions additional ConfigMaps for certificates that are required for Che workspaces to function properly.
22+
23+
```bash
24+
make test_load ARGS=" \
25+
--mode binary \
26+
--run-with-eclipse-che true \
27+
--max-vus ${MAX_VUS} \
28+
--create-automount-resources true \
29+
--max-devworkspaces ${MAX_DEVWORKSPACES} \
30+
--devworkspace-ready-timeout-seconds 3600 \
31+
--delete-devworkspace-after-ready false \
32+
--separate-namespaces false \
33+
--test-duration-minutes 40"
34+
```
35+
36+
**Note**: When `--run-with-eclipse-che true` is set, the script will:
37+
- Provision a workspace namespace compatible with Eclipse Che
38+
- Create additional certificate ConfigMaps required by Che
39+
40+
### Running without Eclipse Che
41+
42+
When running without Eclipse Che, the standard namespace setup is used without additional certificate ConfigMaps.
43+
44+
```bash
45+
make test_load ARGS=" \
46+
--mode binary \
47+
--max-vus ${MAX_VUS} \
48+
--create-automount-resources true \
49+
--max-devworkspaces ${MAX_DEVWORKSPACES} \
50+
--devworkspace-ready-timeout-seconds 3600 \
51+
--delete-devworkspace-after-ready false \
52+
--separate-namespaces false \
53+
--test-duration-minutes 40"
54+
```
55+
56+
## Available Parameters
57+
58+
| Parameter | Description | Default | Example |
59+
|-----------|-------------|---------|---------|
60+
| `--mode` | Execution mode: `binary` or `operator` | `binary` | `--mode binary` |
61+
| `--max-vus` | Maximum number of virtual users (concurrent DevWorkspace creations) | `100` | `--max-vus 50` |
62+
| `--max-devworkspaces` | Maximum number of DevWorkspaces to create (-1 for unlimited) | `-1` | `--max-devworkspaces 200` |
63+
| `--separate-namespaces` | Create each DevWorkspace in its own namespace | `false` | `--separate-namespaces true` |
64+
| `--delete-devworkspace-after-ready` | Delete DevWorkspace once it becomes Ready | `true` | `--delete-devworkspace-after-ready false` |
65+
| `--devworkspace-ready-timeout-seconds` | Timeout in seconds for workspace to become ready | `1200` | `--devworkspace-ready-timeout-seconds 3600` |
66+
| `--devworkspace-link` | URL to external DevWorkspace JSON to use instead of default | (empty) | `--devworkspace-link https://...` |
67+
| `--create-automount-resources` | Create automount ConfigMap and Secret for testing | `false` | `--create-automount-resources true` |
68+
| `--dwo-namespace` | DevWorkspace Operator namespace | `openshift-operators` | `--dwo-namespace devworkspace-controller` |
69+
| `--logs-dir` | Directory for DevWorkspace and event logs | `logs` | `--logs-dir /tmp/test-logs` |
70+
| `--test-duration-minutes` | Duration in minutes for the load test | `25` | `--test-duration-minutes 40` |
71+
| `--run-with-eclipse-che` | Enable Eclipse Che integration (adds certificate ConfigMaps) | `false` | `--run-with-eclipse-che true` |
72+
| `--che-cluster-name` | Eclipse Che cluster name (when using Che) | `eclipse-che` | `--che-cluster-name my-che` |
73+
| `--che-namespace` | Eclipse Che namespace (when using Che) | `eclipse-che` | `--che-namespace my-che-ns` |
74+
75+
## What the Tests Do
76+
77+
1. **Setup**: Creates a test namespace, ServiceAccount, and RBAC resources
78+
2. **Eclipse Che Setup** (if enabled): Provisions Che-compatible namespace and certificate ConfigMaps
79+
3. **Load Generation**: Creates DevWorkspaces concurrently based on `--max-devworkspaces`
80+
4. **Monitoring**:
81+
- Watches DevWorkspace status until Ready
82+
- Monitors operator CPU and memory usage
83+
- Tracks etcd metrics
84+
- Logs events and DevWorkspace state changes
85+
5. **Cleanup**: Removes all created resources and test namespace
86+
87+
## Test Metrics
88+
89+
The tests track the following metrics:
90+
- DevWorkspace creation duration
91+
- DevWorkspace ready duration
92+
- DevWorkspace deletion duration
93+
- Operator CPU and memory usage
94+
- etcd CPU and memory usage
95+
- Success/failure rates
96+
97+
## Output
98+
99+
- **Logs**: Stored in the `logs/` directory (or custom directory specified by `--logs-dir`)
100+
- `{timestamp}_events.log`: Kubernetes events
101+
- `{timestamp}_dw_watch.log`: DevWorkspace watch logs
102+
- `dw_failure_report.csv`: Failed DevWorkspaces report
103+
- **HTML Report**: Generated when running in binary mode (outside cluster)
104+
- **Console Output**: Real-time test progress and summary
105+
106+
## Troubleshooting
107+
108+
- **Permission errors**: Ensure your kubeconfig has sufficient RBAC permissions
109+
- **Timeout errors**: Increase `--devworkspace-ready-timeout-seconds` for slower clusters
110+
- **Resource exhaustion**: Reduce `--max-vus` or `--max-devworkspaces` if cluster resources are limited
111+
- **k6 not found**: Install k6 from https://k6.io/docs/getting-started/installation/
112+
113+
## Additional Notes
114+
115+
- The tests use an opinionated minimal DevWorkspace by default, or you can provide a custom one via `--devworkspace-link`
116+
- When `--separate-namespaces true` is used, each DevWorkspace gets its own namespace
117+
- The `--delete-devworkspace-after-ready false` option is useful for testing sustained load scenarios
118+
- Certificate ConfigMaps are only created when `--run-with-eclipse-che true` is set
119+

test/load/che-cert-bundle-utils.sh

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
log_info() { echo -e "ℹ️ $*" >&2; }
5+
log_success() { echo -e "$*" >&2; }
6+
log_error() { echo -e "$*" >&2; }
7+
8+
9+
run_che_ca_bundle_e2e() {
10+
local che_ns="$1"
11+
local dw_ns="$2"
12+
local dw_name="$3"
13+
local cert_count="${4:-500}"
14+
local bundle_file="${5:-custom-ca-certificates.pem}"
15+
16+
check_namespaces "${che_ns}" "${dw_ns}"
17+
generate_dummy_certs "${cert_count}" "${bundle_file}"
18+
create_che_ca_configmap "${che_ns}" "${bundle_file}"
19+
patch_checluster_disable_pki_mount "${che_ns}"
20+
restart_che "${che_ns}"
21+
create_devworkspace "${dw_ns}" "${dw_name}"
22+
23+
local pod
24+
pod=$(wait_for_workspace_pod "${dw_ns}" "${dw_name}")
25+
26+
verify_ca_bundle_in_workspace "${pod}" "${dw_ns}" "${cert_count}"
27+
cleanup_resources "${dw_ns}" "${dw_name}"
28+
}
29+
30+
check_namespaces() {
31+
local che_ns="$1"
32+
local dw_ns="$2"
33+
34+
log_info "Checking namespaces..."
35+
kubectl get ns "${che_ns}" >/dev/null
36+
kubectl get ns "${dw_ns}" >/dev/null
37+
}
38+
39+
generate_dummy_certs() {
40+
local cert_count="$1"
41+
local bundle_file="$2"
42+
43+
log_info "Generating ${cert_count} dummy CA certificates..."
44+
rm -f "${bundle_file}"
45+
46+
for i in $(seq 1 "${cert_count}"); do
47+
openssl req -x509 -newkey rsa:2048 -nodes -days 1 \
48+
-subj "/CN=dummy-ca-${i}" \
49+
-keyout "dummy-ca-${i}.key" \
50+
-out "dummy-ca-${i}.pem" \
51+
>/dev/null 2>&1
52+
53+
cat "dummy-ca-${i}.pem" >> "${bundle_file}"
54+
done
55+
56+
log_success "Created CA bundle: $(du -h "${bundle_file}" | cut -f1)"
57+
}
58+
59+
create_che_ca_configmap() {
60+
local che_ns="$1"
61+
local bundle_file="$2"
62+
63+
log_info "Creating Che CA bundle ConfigMap..."
64+
65+
kubectl create configmap custom-ca-certificates \
66+
--from-file=custom-ca-certificates.pem="${bundle_file}" \
67+
-n "${che_ns}" \
68+
--dry-run=client -o yaml \
69+
| kubectl apply --server-side -f -
70+
71+
kubectl label configmap custom-ca-certificates \
72+
app.kubernetes.io/component=ca-bundle \
73+
app.kubernetes.io/part-of=che.eclipse.org \
74+
-n "${che_ns}" \
75+
--overwrite
76+
}
77+
78+
patch_checluster_disable_pki_mount() {
79+
local che_ns="$1"
80+
81+
log_info "Configuring CheCluster..."
82+
local checluster
83+
checluster=$(kubectl get checluster -n "${che_ns}" -o jsonpath='{.items[0].metadata.name}')
84+
85+
kubectl patch checluster "${checluster}" \
86+
-n "${che_ns}" \
87+
--type=merge \
88+
-p '{
89+
"spec": {
90+
"devEnvironments": {
91+
"trustedCerts": {
92+
"disableWorkspaceCaBundleMount": true
93+
}
94+
}
95+
}
96+
}'
97+
}
98+
99+
restart_che() {
100+
local che_ns="$1"
101+
102+
log_info "Restarting Che..."
103+
kubectl rollout status deploy/che -n "${che_ns}" --timeout=5m
104+
kubectl wait pod -n "${che_ns}" -l app=che --for=condition=Ready --timeout=5m
105+
106+
log_success "Che restarted"
107+
}
108+
109+
create_devworkspace() {
110+
local dw_ns="$1"
111+
local dw_name="$2"
112+
113+
log_info "Creating DevWorkspace '${dw_name}'..."
114+
cat <<EOF | kubectl apply -n "${dw_ns}" -f -
115+
kind: DevWorkspace
116+
apiVersion: workspace.devfile.io/v1alpha2
117+
metadata:
118+
name: ${dw_name}
119+
annotations:
120+
che.eclipse.org/che-editor: che-incubator/che-code/latest
121+
che.eclipse.org/devfile: |
122+
schemaVersion: 2.2.0
123+
metadata:
124+
generateName: ${dw_name}
125+
che.eclipse.org/devfile-source: |
126+
url:
127+
location: https://github.com/che-samples/web-nodejs-sample.git
128+
factory:
129+
params: che-editor=che-incubator/che-code/latest
130+
spec:
131+
started: true
132+
template:
133+
projects:
134+
- name: web-nodejs-sample
135+
git:
136+
remotes:
137+
origin: "https://github.com/che-samples/web-nodejs-sample.git"
138+
components:
139+
- name: dev
140+
container:
141+
image: quay.io/devfile/universal-developer-image:latest
142+
memoryLimit: 512Mi
143+
memoryRequest: 256Mi
144+
cpuRequest: 1000m
145+
commands:
146+
- id: say-hello
147+
exec:
148+
component: dev
149+
commandLine: echo "Hello from \$(pwd)"
150+
workingDir: \${PROJECT_SOURCE}/app
151+
contributions:
152+
- name: che-code
153+
uri: https://eclipse-che.github.io/che-plugin-registry/main/v3/plugins/che-incubator/che-code/latest/devfile.yaml
154+
components:
155+
- name: che-code-runtime-description
156+
container:
157+
env:
158+
- name: CODE_HOST
159+
value: 0.0.0.0
160+
EOF
161+
162+
163+
kubectl wait devworkspace/"${dw_name}" \
164+
-n "${dw_ns}" \
165+
--for=condition=Ready \
166+
--timeout=5m
167+
}
168+
169+
wait_for_workspace_pod() {
170+
local dw_ns="$1"
171+
local dw_name="$2"
172+
local pod_name
173+
174+
log_info "Waiting for workspace pod..."
175+
176+
kubectl wait pod \
177+
-n "${dw_ns}" \
178+
-l controller.devfile.io/devworkspace_name="${dw_name}" \
179+
--for=condition=Ready \
180+
--timeout=5m \
181+
>/dev/stderr
182+
183+
pod_name=$(kubectl get pod \
184+
-n "${dw_ns}" \
185+
-l controller.devfile.io/devworkspace_name="${dw_name}" \
186+
-o jsonpath='{.items[0].metadata.name}')
187+
188+
echo "${pod_name}"
189+
}
190+
191+
verify_ca_bundle_in_workspace() {
192+
local pod_name="$1"
193+
local dw_ns="$2"
194+
local expected_count="$3"
195+
local cert_path="/public-certs/tls-ca-bundle.pem"
196+
197+
log_info "Verifying CA bundle in workspace..."
198+
199+
kubectl exec "${pod_name}" -n "${dw_ns}" -- test -f "${cert_path}"
200+
201+
local mounted_count
202+
mounted_count=$(kubectl exec "${pod_name}" -n "${dw_ns}" -- \
203+
sh -c "grep -c 'BEGIN CERTIFICATE' ${cert_path}")
204+
205+
log_info "Generated certificates : ${expected_count}"
206+
log_info "Mounted certificates : ${mounted_count}"
207+
208+
if [[ "${mounted_count}" -le "${expected_count}" ]]; then
209+
log_error "Mounted certificate count validation failed"
210+
return 1
211+
fi
212+
213+
log_success "CA bundle verification passed"
214+
}
215+
216+
cleanup_resources() {
217+
local dw_ns="$1"
218+
local dw_name="$2"
219+
220+
log_info "Cleaning up..."
221+
kubectl delete dw "${dw_name}" -n "${dw_ns}" --ignore-not-found
222+
rm -f *.pem *.key
223+
}

test/load/devworkspace_load_test.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,6 @@ export const options = {
5050
iterations: maxDevWorkspaces,
5151
maxDuration: '3h',
5252
},
53-
final_cleanup: {
54-
executor: 'per-vu-iterations',
55-
vus: 1,
56-
iterations: 1,
57-
exec: 'final_cleanup',
58-
startTime: '0s',
59-
maxDuration: '3h',
60-
},
6153
}, thresholds: {
6254
'checks': ['rate>0.95'],
6355
'devworkspace_create_duration': ['p(95)<15000'],
@@ -203,6 +195,11 @@ export function handleSummary(data) {
203195
return loadTestSummaryReport;
204196
}
205197

198+
export function teardown(data) {
199+
console.log("Running final cleanup after all DevWorkspace creation finished...");
200+
final_cleanup();
201+
}
202+
206203
function createNewDevWorkspace(namespace, vuId, iteration) {
207204
const baseUrl = `${apiServer}/apis/workspace.devfile.io/v1alpha2/namespaces/${namespace}/devworkspaces`;
208205

@@ -259,6 +256,13 @@ function waitUntilDevWorkspaceIsReady(vuId, crName, namespace) {
259256
attempts++;
260257
}
261258

259+
if (!isReady && attempts >= maxAttempts) {
260+
console.error(
261+
`GET [VU ${vuId}] Timed out waiting for DevWorkspace '${crName}' in namespace '${namespace}' ` +
262+
`after ${attempts} attempts (${devWorkspaceReadyTimeout}s). Last known phase: '${lastPhase}'`
263+
);
264+
}
265+
262266
if (res.status === 200) {
263267
if (isReady) {
264268
devworkspaceReady.add(1);

0 commit comments

Comments
 (0)