|
| 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. |
0 commit comments