Skip to content
This repository was archived by the owner on Jul 13, 2026. It is now read-only.

Commit 4e81399

Browse files
committed
Add KUTTL test suite for operator validation
Add comprehensive KUTTL tests for OpenStack Lightspeed operator with two primary test scenarios: 1. Deploy basic OpenStackLightspeed instance and verify OLSConfig is properly configured 2. Update OpenStackLightspeed instance and verify OLSConfig reflects changes Tests mock required dependencies (certificates, LLM API tokens, and /chat/completions endpoint) to enable full deployment of OLS. New Makefile targets: - kuttl-test: Run KUTTL tests only - kuttl-tests-run: Deploy operator, run tests, cleanup - kuttl: Install KUTTL binary For locall testing it is recommeded to use make kuttl-tests-run as it does all the required steps automatically. Depends-On: openshift/release#71750
1 parent 42361b6 commit 4e81399

30 files changed

Lines changed: 451 additions & 0 deletions

Makefile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ openstack-lightspeed-deploy: ## Deploy using a catalog image.
224224
oc apply -f $(OUTPUT_DIR)/catalog
225225
bash scripts/gen-rhosls.sh $(CATALOG_NAME) $(CATALOG_CHANNEL)
226226
oc apply -f $(OUTPUT_DIR)/rhosls
227+
bash scripts/confirm-rhosls-running.sh
227228

228229
# Deploy using the catalog image.
229230
.PHONY: openstack-lightspeed-undeploy
@@ -247,12 +248,14 @@ KUSTOMIZE ?= $(LOCALBIN)/kustomize
247248
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
248249
ENVTEST ?= $(LOCALBIN)/setup-envtest
249250
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
251+
KUTTL ?= $(LOCALBIN)/kubectl-kuttl
250252

251253
## Tool Versions
252254
KUSTOMIZE_VERSION ?= v5.4.2
253255
CONTROLLER_TOOLS_VERSION ?= v0.16.5
254256
ENVTEST_VERSION ?= release-0.18
255257
GOLANGCI_LINT_VERSION ?= v2.6.0
258+
KUTTL_VERSION ?= 0.22.0
256259

257260
.PHONY: kustomize
258261
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
@@ -274,6 +277,19 @@ golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
274277
$(GOLANGCI_LINT): $(LOCALBIN)
275278
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(LOCALBIN) $(GOLANGCI_LINT_VERSION)
276279

280+
.PHONY: kuttl
281+
kuttl: $(KUTTL) ## Download kubectl-kuttl locally if necessary.
282+
$(KUTTL): $(LOCALBIN)
283+
test -s $(LOCALBIN)/kubectl-kuttl || curl -L -o $(LOCALBIN)/kubectl-kuttl https://github.com/kudobuilder/kuttl/releases/download/v$(KUTTL_VERSION)/kubectl-kuttl_$(KUTTL_VERSION)_linux_x86_64
284+
chmod +x $(LOCALBIN)/kubectl-kuttl
285+
286+
.PHONY: kuttl-test
287+
kuttl-test: kuttl ## Run kuttl tests
288+
$(LOCALBIN)/kubectl-kuttl test --config kuttl-test.yaml test/kuttl/tests $(KUTTL_ARGS)
289+
290+
.PHONY: kuttl-test-run
291+
kuttl-test-run: kuttl openstack-lightspeed-deploy kuttl-test openstack-lightspeed-undeploy
292+
277293
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
278294
# $1 - target path with name of binary
279295
# $2 - package url which can be installed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,27 @@ Run all hooks manually:
220220
```bash
221221
pre-commit run --all-files
222222
```
223+
224+
### Running KUTTL Tests
225+
226+
KUTTL (KUbernetes Test TooL) tests validate the operator's behavior in a real
227+
OpenShift environment.
228+
229+
Before running the tests ensure that:
230+
- `oc` CLI tool is available in your PATH and you can access an OpenShift cluster
231+
(e.g., deployed with `crc`) with it
232+
- The `openshift-lightspeed` namespace is empty or non-existing to prevent collisions
233+
234+
Once you are ready you can run the KUTTL tests using:
235+
236+
```bash
237+
make kuttl-test-run
238+
```
239+
240+
**Important Notes:**
241+
- The tests use the `openshift-lightspeed` namespace to test in the exact namespace
242+
where the OLS operator is expected to operate.
243+
- The correct behavior of the OLS operator is not guaranteed outside of the
244+
`openshift-lightspeed` namespace.
245+
- Ensure the namespace is clean before running tests to avoid resource conflicts
246+
or test failures.

kuttl-test.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: kuttl.dev/v1beta1
2+
kind: TestSuite
3+
reportFormat: xml
4+
reportName: kuttl-report-openstack-lightspeed
5+
reportGranularity: test
6+
namespace: openshift-lightspeed
7+
timeout: 600
8+
parallel: 1
9+
suppress:
10+
- events

scripts/confirm-rhosls-running.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
while true; do
4+
csv=$(oc get subscription openstack-lightspeed-operator -n openshift-lightspeed -o jsonpath='{.status.installedCSV}' 2>/dev/null)
5+
if [ -n "$csv" ]; then
6+
echo "Found installedCSV: $csv"
7+
break
8+
fi
9+
echo "Waiting for openstack-lightspeed-operator Subscription installedCSV to be populated ..."
10+
sleep 5
11+
done
12+
13+
# Wait for the CSV to succeed
14+
oc wait csv $csv --for=jsonpath='{.status.phase}'=Succeeded --timeout=300s -n openshift-lightspeed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
apiVersion: v1
3+
kind: Namespace
4+
metadata:
5+
name: openshift-lightspeed
6+
---
7+
apiVersion: v1
8+
kind: Secret
9+
metadata:
10+
name: openstack-lightspeed-apitoken
11+
namespace: openshift-lightspeed
12+
---
13+
apiVersion: v1
14+
kind: ConfigMap
15+
metadata:
16+
name: openstack-lightspeed-cert
17+
namespace: openshift-lightspeed
18+
---
19+
apiVersion: v1
20+
kind: Pod
21+
metadata:
22+
name: mock-llm-api-server-pod
23+
namespace: openshift-lightspeed
24+
status:
25+
phase: Running
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestStep
4+
delete:
5+
- apiVersion: v1
6+
kind: Pod
7+
name: mock-llm-api-server-pod
8+
namespace: openshift-lightspeed
9+
- apiVersion: v1
10+
kind: Service
11+
name: mock-llm-api-server-pod
12+
namespace: openshift-lightspeed
13+
- apiVersion: v1
14+
kind: ConfigMap
15+
name: mock-llm-code
16+
namespace: openshift-lightspeed
17+
- apiVersion: v1
18+
kind: Secret
19+
name: openstack-lightspeed-apitoken
20+
namespace: openshift-lightspeed
21+
- apiVersion: v1
22+
kind: ConfigMap
23+
name: openstack-lightspeed-cert
24+
namespace: openshift-lightspeed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
apiVersion: v1
3+
kind: Pod
4+
metadata:
5+
name: mock-llm-api-server-pod
6+
namespace: openshift-lightspeed
7+
---
8+
apiVersion: v1
9+
kind: Service
10+
metadata:
11+
name: mock-llm-api-server-pod
12+
namespace: openshift-lightspeed
13+
---
14+
apiVersion: v1
15+
kind: ConfigMap
16+
metadata:
17+
name: mock-llm-code
18+
namespace: openshift-lightspeed
19+
---
20+
apiVersion: v1
21+
kind: Secret
22+
metadata:
23+
name: openstack-lightspeed-apitoken
24+
namespace: openshift-lightspeed
25+
---
26+
apiVersion: v1
27+
kind: ConfigMap
28+
metadata:
29+
name: openstack-lightspeed-cert
30+
namespace: openshift-lightspeed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
##############################################################################
2+
# Mock API tokens and certificates required for OLSConfig tests #
3+
##############################################################################
4+
---
5+
apiVersion: v1
6+
kind: Secret
7+
type: Opaque
8+
metadata:
9+
name: openstack-lightspeed-apitoken
10+
namespace: openshift-lightspeed
11+
stringData:
12+
apitoken: secret
13+
---
14+
apiVersion: v1
15+
kind: ConfigMap
16+
type: Opaque
17+
metadata:
18+
name: openstack-lightspeed-cert
19+
namespace: openshift-lightspeed
20+
data:
21+
cert: |
22+
-----BEGIN CERTIFICATE-----
23+
MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYD
24+
VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk
25+
MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U
26+
cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29y
27+
IFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkxMjMxMTcyMzE2WjCB
28+
pDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFuYW1h
29+
IENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUG
30+
A1UECwweVHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZU
31+
cnVzdENvciBSb290Q2VydCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
32+
CgKCAQEAv463leLCJhJrMxnHQFgKq1mqjQCj/IDHUHuO1CAmujIS2CNUSSUQIpid
33+
RtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4pQa81QBeCQryJ3pS/C3V
34+
seq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0JEsq1pme
35+
9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CV
36+
EY4hgLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorW
37+
hnAbJN7+KIor0Gqw/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/
38+
DeOxCbeKyKsZn3MzUOcwHwYDVR0jBBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcw
39+
DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
40+
ggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5mDo4Nvu7Zp5I
41+
/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf
42+
ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZ
43+
yonnMlo2HD6CqFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djts
44+
L1Ac59v2Z3kf9YKVmgenFK+P3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdN
45+
zl/HHk484IkzlQsPpTLWPFp5LBk=
46+
-----END CERTIFICATE-----
47+
48+
##############################################################################
49+
# Mock Pod to simulate OpenAI /chat/completions API endpoints #
50+
# Used by OpenShiftLightspeed for LLM connection verification #
51+
##############################################################################
52+
---
53+
apiVersion: v1
54+
kind: Pod
55+
metadata:
56+
name: mock-llm-api-server-pod
57+
labels:
58+
app: mock-llm-api-server-pod
59+
spec:
60+
containers:
61+
- name: mock-llm-api-server
62+
image: registry.redhat.io/ubi8/python-311:latest
63+
ports:
64+
- containerPort: 8000
65+
volumeMounts:
66+
- name: app-code
67+
mountPath: /app
68+
workingDir: /app
69+
command:
70+
- sh
71+
- -c
72+
- |
73+
pip install "fastapi[standard]" && \
74+
uvicorn app:app --host 0.0.0.0 --port 8000
75+
volumes:
76+
- name: app-code
77+
configMap:
78+
name: mock-llm-code
79+
---
80+
apiVersion: v1
81+
kind: Service
82+
metadata:
83+
name: mock-llm-api-server-pod
84+
spec:
85+
selector:
86+
app: mock-llm-api-server-pod
87+
ports:
88+
- protocol: TCP
89+
port: 8000
90+
targetPort: 8000
91+
---
92+
apiVersion: v1
93+
kind: ConfigMap
94+
metadata:
95+
name: mock-llm-code
96+
data:
97+
app.py: |
98+
from fastapi import FastAPI, Request
99+
from fastapi.responses import JSONResponse
100+
101+
app = FastAPI()
102+
@app.post("/v1/chat/completions")
103+
async def completions_post(request: Request):
104+
try:
105+
body = await request.json()
106+
except Exception:
107+
body = {}
108+
109+
# Always return a valid OpenAI-like response, handle missing/None body gracefully
110+
model = body.get("model", "gpt-3.5-turbo") if isinstance(body, dict) else "gpt-3.5-turbo"
111+
112+
# The OpenAI API expects 'messages' (for chat) or 'prompt' (for completions), but for mock, we will accept either
113+
response = {
114+
"id": "cmpl-123",
115+
"object": "chat.completion",
116+
"created": 1234567890,
117+
"model": model,
118+
"choices": [
119+
{
120+
"index": 0,
121+
"message": {
122+
"role": "assistant",
123+
"content": "Hello, this is a dummy chat completion."
124+
},
125+
"finish_reason": "stop"
126+
}
127+
],
128+
"usage": {
129+
"prompt_tokens": 5,
130+
"completion_tokens": 7,
131+
"total_tokens": 12
132+
}
133+
}
134+
return JSONResponse(content=response)
135+
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
apiVersion: ols.openshift.io/v1alpha1
3+
kind: OLSConfig
4+
metadata:
5+
name: cluster
6+
spec:
7+
llm:
8+
providers:
9+
- name: openstack-lightspeed-provider
10+
type: openai
11+
url: http://mock-llm-api-server-pod:8000/v1
12+
credentialsSecretRef:
13+
name: openstack-lightspeed-apitoken
14+
models:
15+
- name: ibm-granite/granite-3.1-8b-instruct
16+
parameters:
17+
maxTokensForResponse: 2048
18+
ols:
19+
defaultProvider: openstack-lightspeed-provider
20+
defaultModel: ibm-granite/granite-3.1-8b-instruct
21+
byokRAGOnly: true
22+
logLevel: INFO
23+
additionalCAConfigMapRef:
24+
name: openstack-lightspeed-cert
25+
rag:
26+
- image: quay.io/openstack-lightspeed/rag-content:os-docs-2025.2
27+
indexID: ""
28+
indexPath: /rag/vector_db/os_product_docs
29+
status:
30+
conditions:
31+
- type: ConsolePluginReady
32+
status: "True"
33+
reason: Reconciling
34+
- type: CacheReady
35+
status: "True"
36+
reason: Reconciling
37+
- type: ApiReady
38+
status: "True"
39+
reason: Reconciling
40+
- type: Reconciled
41+
status: "True"
42+
reason: Reconciling
43+
---
44+
apiVersion: lightspeed.openstack.org/v1beta1
45+
kind: OpenStackLightspeed
46+
metadata:
47+
name: openstack-lightspeed
48+
namespace: openshift-lightspeed
49+
spec:
50+
catalogSourceName: redhat-operators
51+
catalogSourceNamespace: openshift-marketplace
52+
llmCredentials: openstack-lightspeed-apitoken
53+
llmEndpoint: http://mock-llm-api-server-pod:8000/v1
54+
llmEndpointType: openai
55+
modelName: ibm-granite/granite-3.1-8b-instruct
56+
tlsCACertBundle: openstack-lightspeed-cert
57+
status:
58+
conditions:
59+
- type: Ready
60+
status: "True"
61+
reason: Ready
62+
message: Setup complete
63+
- type: OpenShiftLightspeedOperatorReady
64+
status: "True"
65+
reason: Ready
66+
message: OpenShift Lightspeed operator is ready.
67+
- type: OpenStackLightspeedReady
68+
status: "True"
69+
reason: Ready
70+
message: OpenStack Lightspeed created
71+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestStep
4+
delete:
5+
- apiVersion: lightspeed.openstack.org/v1beta1
6+
kind: OpenStackLightspeed
7+
name: openstack-lightspeed
8+
namespace: openshift-lightspeed
9+

0 commit comments

Comments
 (0)