Skip to content

Commit 38d4a25

Browse files
committed
Add KernelCI pull-lab poller, translator, and direct KCIDB submission
Wire pullab_cloud into the KernelCI pipeline as a PULL_LABS consumer: poll kernelci-api /events for jobs scheduled to runtime pull-labs-aws-ec2, translate the PULL_LABS job_definition into a per-run test_config, invoke the existing AWS run_pipeline(), and push per-test results directly to KCIDB via kcidb-restd-rs /submit. Pipeline callback (lava_callback.py) is deliberately bypassed; tests attach to existing build_ids resolved from the maestro node tree, so checkouts/builds are not re-emitted. New code is generic Python (stdlib urllib only, no boto3 at module top level) and ships three entry points: CLI, library, and AWS Lambda handler. Configuration is env-var-driven with config.json fallback. The KCIDB_REST=https://<token>@host/submit form is supported for zero-effort reuse of an existing kci-dev configuration; explicit KCIDB_SUBMIT_URL+KCIDB_JWT takes precedence. A new vm-tests/url-kernel-boot scaffold downloads the artifacts.kernel / artifacts.modules / optional artifacts.rootfs URLs from the job definition and emits a BenchmarkAnalyzer-compatible CSV; the deployment-specific boot step is left as a BOOT_HOOK extension point. run_pipeline() now returns its summary dict (was discarded) so the poller can map per-VM pass/fail into KCIDB rows. 76 new pytest cases cover translation edge cases, KCIDB row/revision building, status mapping, KCIDB_REST parsing, credential priority, cursor store, event filtering, and build_id resolution. All 111 tests pass (35 pre-existing + 76 new). See QUICKSTART.md for the full operator walkthrough. This is not completely verified skeleton, just to start things up, so i expect follow up PRs. Signed-off-by: Denys Fedoryshchenko <denys.f@collabora.com>
1 parent fc8c67c commit 38d4a25

13 files changed

Lines changed: 1898 additions & 2 deletions

File tree

Makefile

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
.PHONY: help install install-dev test lint format type-check clean build release docs docs-clean
1+
.PHONY: help install install-dev test lint format type-check clean build release docs docs-clean poller poller-once
2+
3+
CONFIG ?= examples/aws/config.json
24

35
help:
46
@echo "Available targets:"
@@ -13,6 +15,14 @@ help:
1315
@echo " clean - Clean build artifacts"
1416
@echo " build - Build the package"
1517
@echo " release - Build and check the package for release"
18+
@echo " poller - Run the kernelci pull-lab poller (long-lived)"
19+
@echo " poller-once - Run a single poll cycle and exit"
20+
21+
poller:
22+
python -m kernel_ci_cloud_labs.pull_labs_poller --config $(CONFIG)
23+
24+
poller-once:
25+
python -m kernel_ci_cloud_labs.pull_labs_poller --config $(CONFIG) --once
1626

1727
install: build test
1828
pip install -e .

QUICKSTART.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# QUICKSTART — KernelCI pull-lab integration
2+
3+
This guide gets pullab_cloud polling KernelCI for pull-lab jobs and pushing
4+
results to KCIDB. The flow is:
5+
6+
1. kernelci-api->poll (/events)
7+
2. pull_labs_poller translates the job definition
8+
3. pull_labs_poller calls the existing AWS pipeline with the translated config
9+
4. pull_labs_poller submits tests-only revisions to kcidb-restd-rs
10+
11+
For the underlying AWS setup (IAM roles, S3 buckets, ECS cluster, ECR
12+
image, etc.) see the main `README.md`. This file only covers the
13+
KernelCI/KCIDB wiring on top of an already-working AWS pipeline.
14+
15+
## 1. Set up the AWS pipeline first
16+
17+
The KernelCI poller drives the *existing* AWS pipeline — it does not
18+
provision AWS resources on its own. Before continuing, make sure the
19+
pipeline can run a job end-to-end. The full walkthrough lives in
20+
[`README.md`](README.md); the minimum steps are:
21+
22+
1. **Install the package** in a venv — see
23+
[README → Installation](README.md#installation):
24+
```bash
25+
python3 -m venv .venv && source .venv/bin/activate
26+
pip install -e .
27+
```
28+
2. **Configure AWS credentials** — see
29+
[README 1. Configure AWS Credentials](README.md#1-configure-aws-credentials).
30+
Either `aws configure` / an IAM role, or drop
31+
`examples/aws/credentials.json` in place.
32+
3. **Generate the pipeline config** — see
33+
[README 2. Configure the project](README.md#2-configure-the-project):
34+
```bash
35+
kernel-ci-cloud-runner aws setup configure \
36+
--prefix kernel-ci-$USER- --region us-west-2
37+
```
38+
This populates `examples/aws/config.json` with unique S3/IAM/ECS/ECR
39+
names. Use `--output my-config.json` to write to a different path
40+
(then pass `PULLAB_BASE_CONFIG=my-config.json` to the poller).
41+
4. **Verify the pipeline works** — see
42+
[README 3. Run integration test to verify setup](README.md#3-run-integration-test-to-verify-setup):
43+
```bash
44+
pytest tests/integration/ -v -m integration
45+
```
46+
Look for `VMs: 2/2 spawned, 2 successful, 0 failed`. If this passes,
47+
the AWS side is ready and you can proceed below.
48+
49+
If your jobs install custom kernels, also follow
50+
[README 4. Upload kernel RPMs](README.md#4-upload-kernel-rpms-required-for-kernel-install-tests).
51+
52+
To tear everything down afterwards, see
53+
[README 7. Clean up resources](README.md#7-clean-up-resources).
54+
55+
## Prerequisites
56+
57+
- A working AWS pipeline — `kernel-ci-cloud-runner aws setup configure`
58+
has been run and `examples/aws/config.json` is populated (see
59+
section 1 above).
60+
- A reachable kernelci-api with at least one pull-lab job scheduled to
61+
`pull-labs-aws-ec2` (or whatever runtime name you use).
62+
- A reachable `kcidb-restd-rs` `/submit` endpoint.
63+
- A JWT signed with the kcidb-restd-rs `unified_secret` carrying the
64+
origin you'll use for these rows.
65+
66+
## 2. Configure the kernelci section
67+
68+
Open `examples/aws/config.json` and edit the `kernelci` block that was
69+
added alongside `test_config`:
70+
71+
```json
72+
"kernelci": {
73+
"api_base_uri": "https://staging.kernelci.org:9000/latest",
74+
"api_token": null,
75+
"runtime_name": "pull-labs-aws-ec2",
76+
"poll_interval_sec": 30,
77+
"cursor_file": "/tmp/pullab_cloud_cursor.json",
78+
"kcidb_submit_url":"https://kcidb-restd.example.org/submit",
79+
"kcidb_origin": "pullab_cloud_aws",
80+
"kcidb_jwt": null
81+
}
82+
```
83+
84+
Secrets (`api_token`, `kcidb_jwt`) are normally injected via environment
85+
variables, not committed to the file:
86+
87+
| Env var | Falls back to | Purpose |
88+
| --- | --- | --- |
89+
| `KERNELCI_API_BASE_URI` | `kernelci.api_base_uri` | API URL |
90+
| `KERNELCI_API_TOKEN` | `kernelci.api_token` | Bearer token for the API (optional for public endpoints) |
91+
| `KERNELCI_RUNTIME_NAME` | `kernelci.runtime_name` | Lab/runtime to consume jobs for |
92+
| `KCIDB_SUBMIT_URL` | `kernelci.kcidb_submit_url` | kcidb-restd-rs submit URL |
93+
| `KCIDB_JWT` | `kernelci.kcidb_jwt` | JWT bearer token |
94+
| `KCIDB_REST` | (alternative to the two above) | `https://<token>@host[/path]` — kci-dev-compatible single URL carrying both endpoint and token |
95+
| `KCIDB_ORIGIN` | `kernelci.kcidb_origin` | Origin string in submitted rows |
96+
| `PULLAB_CURSOR_FILE` | `kernelci.cursor_file` | Where to persist the poll cursor |
97+
| `PULLAB_POLL_INTERVAL_SEC` | `kernelci.poll_interval_sec` | Sleep between empty polls |
98+
| `PULLAB_BASE_CONFIG` | `examples/aws/config.json` | Path to base config |
99+
100+
## 3. Run a single poll cycle (dry test)
101+
102+
```bash
103+
export KCIDB_JWT="eyJ...your.token..."
104+
make poller-once
105+
# or:
106+
python -m kernel_ci_cloud_labs.pull_labs_poller --config examples/aws/config.json --once
107+
```
108+
109+
What happens:
110+
111+
1. Fetches `/events?state=available&kind=job&recursive=true&from=<cursor>`.
112+
2. Skips events whose `node.data.data.runtime``runtime_name`.
113+
3. For each matching event, downloads `node.artifacts.job_definition` JSON.
114+
4. Walks `node.parent` to find the `kbuild` ancestor and builds
115+
`build_id = "<kcidb_origin>:<kbuild_node_id>"`.
116+
5. Translates the job → `test_config.vms[*]` and calls `run_pipeline()`.
117+
6. Submits one tests-only KCIDB revision per job.
118+
7. Persists the latest event `timestamp` to the cursor file.
119+
120+
## 4. Run as a long-lived service
121+
122+
```bash
123+
make poller
124+
# or:
125+
python -m kernel_ci_cloud_labs.pull_labs_poller --config examples/aws/config.json
126+
```
127+
128+
Sleep interval between polls when there is nothing to do is
129+
`PULLAB_POLL_INTERVAL_SEC` (default 30s).
130+
131+
## 5. Run in AWS Lambda
132+
133+
The same module exposes `lambda_handler(event, context)` that runs one
134+
poll cycle per invocation. Wire it to an EventBridge schedule (e.g.
135+
every minute) and set the env vars on the Lambda function. The cursor
136+
file lives on `/tmp` by default — fine for steady polling, but configure
137+
`PULLAB_CURSOR_FILE` to a persistent path (or write a custom
138+
`CursorStore` backed by S3/DynamoDB) if you need true cross-cold-start
139+
deduplication.
140+
141+
Lambda handler entry point:
142+
`kernel_ci_cloud_labs.pull_labs_poller.lambda_handler`
143+
144+
## 6. Run in a container
145+
146+
The poller has no AWS-specific imports at the top level (other than the
147+
default executor which calls into the existing AWS pipeline). For a
148+
custom executor, instantiate `PullLabsPoller` directly:
149+
150+
```python
151+
from kernel_ci_cloud_labs.pull_labs_poller import PullLabsPoller
152+
poller = PullLabsPoller(config, job_executor=my_executor)
153+
poller.run_forever()
154+
```
155+
156+
`my_executor(run_config) -> (test_rows, log_url)` is called once per
157+
job; `test_rows` is a list of `{"name": str, "status": str,
158+
"duration_ms": Optional[int]}` dicts.
159+
160+
## 7. Verify
161+
162+
- **Events reach the poller:** run `--once` with `--log-level DEBUG` and
163+
confirm the poll URL and event count are logged.
164+
- **Cursor advances:** inspect `cat /tmp/pullab_cloud_cursor.json` after
165+
a cycle.
166+
- **KCIDB receives rows:** if `kcidb-restd-rs` is local, check its
167+
`spool_directory` for a `submission-*.json`. Open one and confirm
168+
`tests[*]` rows have your `origin`, a `build_id` of the form
169+
`<origin>:<kbuild_node_id>`, and statuses in
170+
`{PASS, FAIL, SKIP, ERROR, MISS, DONE}`.
171+
- **AWS run actually ran:** the existing pipeline logs land under
172+
`logs/run_*/` and `s3://<results-bucket>/run_pulllab-*/`.
173+
- **Payload shape (optional):** if you have `kci-dev` installed, you can
174+
sanity-check our submission shape by capturing one payload (with
175+
logging) and piping it through:
176+
```bash
177+
kci-dev submit build --from-json <captured.json> --origin <kcidb_origin> --dry-run
178+
```
179+
Our poller speaks the same KCIDB v5.3 schema, so this should
180+
round-trip cleanly.
181+
182+
## Troubleshooting
183+
184+
| Symptom | Likely cause |
185+
| --- | --- |
186+
| `Missing required configuration: kernelci.kcidb_jwt` | env var not set and config value is null |
187+
| Events come back but none are processed | `runtime_name` mismatch with what the scheduler set; check `node.data.data.runtime` in a raw event |
188+
| `Could not resolve build_id` warning | Job node has no `kbuild` ancestor reachable within 8 hops, or `api_token` is missing for a protected `/node/{id}` endpoint |
189+
| `Translation failed … missing required artifacts.kernel` | The KernelCI build that produced this job did not upload a kernel image |
190+
| HTTP 401 from KCIDB submit | JWT not signed by the kcidb-restd-rs `unified_secret`, expired, or origin claim mismatch |
191+
| Same job processed repeatedly | Cursor file path not persistent across restarts (Lambda `/tmp` is ephemeral across cold starts) |
192+
193+
## TODO:
194+
195+
- We do no change yet job state from available. We have deduplication via the cursor, but if the poller restarts it may re-process some events.

examples/aws/config.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,17 @@
200200
},
201201
"metrics_namespace": "kernel-ci-exampleuser-metrics"
202202
},
203+
"kernelci": {
204+
"api_base_uri": "https://staging.kernelci.org:9000/latest",
205+
"api_token": null,
206+
"runtime_name": "pull-labs-aws-ec2",
207+
"poll_interval_sec": 30,
208+
"cursor_file": "/tmp/pullab_cloud_cursor.json",
209+
"kcidb_submit_url": "https://kcidb-restd.example.org/submit",
210+
"kcidb_origin": "pullab_cloud_aws",
211+
"kcidb_jwt": null,
212+
"comment": "kcidb_jwt and api_token are normally provided via the KCIDB_JWT / KERNELCI_API_TOKEN env vars; leave null in the file and inject at runtime."
213+
},
203214
"test_config": {
204215
"test_id": "test-all-tests",
205216
"role_name": "kernel-ci-exampleuser-ecs-role",

src/kernel_ci_cloud_labs/core/pipeline.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ def run_pipeline(
577577
"region": provider.config.get("region", "") if "provider" in locals() else "",
578578
"s3_client": s3_client,
579579
}
580-
create_summary(
580+
return create_summary(
581581
run_dir,
582582
start_time,
583583
task_arn if "task_arn" in locals() else None,

0 commit comments

Comments
 (0)