一键导入
poolboy-scoring-overview
Project architecture, repo structure, key patterns, configuration, and API contract. Load when working on any part of the codebase.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Project architecture, repo structure, key patterns, configuration, and API contract. Load when working on any part of the codebase.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Go conventions, controller-runtime patterns, error handling, logging, and testing patterns used in this codebase. Load when writing or reviewing code.
How poolboy-scoring fits in the RHDP ecosystem — Poolboy sort, CRD relationships, cluster-scheduler integration, and provisioning flow. Load when understanding cross-system behavior.
Build, test, debug, and deploy procedures. Load when making code changes, running tests, or deploying.
| name | poolboy-scoring-overview |
| description | Project architecture, repo structure, key patterns, configuration, and API contract. Load when working on any part of the codebase. |
| user-invocable | false |
Go controller that watches Poolboy ResourcePools, collects unbound ResourceHandles, resolves their cluster placement from AnarchySubjects, calls the cluster-scheduler's /evaluate endpoint, and patches spec.preferenceScore on each handle. Poolboy consumes the scores through its existing 7-tier sort with zero code changes. Standalone project — its own repository, Helm chart, container image, and release cycle.
cmd/
main.go Entry point: config, logging, Manager, controller wiring
internal/
config/
config.go Centralized Config struct (envconfig), all env vars
controller/
reconciler.go ResourcePoolReconciler: Reconcile(), SetupWithManager()
metrics.go Prometheus counters/histograms (4 custom metrics)
metrics_test.go
reconciler_test.go
metrics/
auth.go HTTP Basic Auth FilterProvider for /metrics endpoint
auth_test.go
placement/
types.go Placement, ResourceRef, GVK constants, partial CRD types
resourcehandle.go ParseHandleSpec/Status, PlacementFromProvisionData
resourcepool.go ParsePoolStatus
anarchysubject.go ParseAnarchySubjectSpec, ExtractPlacement
lookup.go PlacementLookup (3-tier: cached → provision_data → GET)
*_test.go
scheduler/
types.go Candidate, EvaluateRequest/Response, ScoredCandidate
client.go Scorer interface + HTTP Client implementation
*_test.go
helm/ Full Helm chart (Deployment, RBAC, ConfigMap, Secrets, ServiceMonitor)
skills/ AI agent knowledge base
.github/workflows/ CI (test + validate) and Release (multi-arch image)
┌──────────────────────────────────────────────────────────────┐
│ Kubernetes API Server │
│ ResourcePool watch stream (persistent connection) │
└────────────────────────┬─────────────────────────────────────┘
│ ADDED / MODIFIED / DELETED
▼
┌──────────────────────────────────────────────────────────────┐
│ controller-runtime Manager + Informer Cache │
│ Watches: ResourcePool (unstructured) │
│ Built-in: leader election, health probes, metrics │
│ Resync period: 5 minutes (re-enqueue from cache) │
└────────────────────────┬─────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ ResourcePoolReconciler.Reconcile(req) │
│ 1. Get pool → read status.resourceHandles[] │
│ 2. For each handle: get, skip bound, resolve placement │
│ 3. Collect unique clusters → POST /evaluate (one call) │
│ 4. Map scores → PATCH spec.preferenceScore where changed │
└──────────────────────────────────────────────────────────────┘
The reconciler watches ResourcePool, not individual ResourceHandles. Each pool reconciliation collects all unbound handles, groups by cluster, and sends ONE batch /evaluate call. The cluster-scheduler's most_capacity strategy produces relative scores — multiple candidates are required for differentiated scores.
Entry point: internal/controller/reconciler.go — ResourcePoolReconciler.Reconcile()
internal/placement/lookup.go — PlacementLookup.Lookup() resolves which cluster a handle is placed on:
status.placements — written by the reconciler on previous runs, zero API callsprovision_data shortcut — some catalog items propagate placement in status.summary.provision_datasandbox_openshift_cluster from spec.vars.job_varsFirst match wins. Tier 3 results are cached as status.placements for subsequent reconciliations.
internal/controller/reconciler.go — score comparison before patching
Before patching spec.preferenceScore, the controller compares the new score from the scheduler against the handle's current score. If they match, the patch is skipped. This avoids unnecessary API writes and the MODIFIED events they would generate. Since the controller watches ResourcePool (not ResourceHandle), patching a handle does not trigger a new reconciliation — no infinite loop is possible.
internal/placement/types.go defines partial Go structs (e.g., ResourcePoolStatus, ResourceHandleSpec, AnarchySubjectSpec) with JSON tags. The Parse* functions convert unstructured.Unstructured objects into these typed structs via runtime.DefaultUnstructuredConverter.FromUnstructured. This provides type safety without code generation.
All env vars defined in internal/config/config.go:
| Env Var | Required | Default | Description |
|---|---|---|---|
CLUSTER_SCHEDULER_URL | Yes | -- | Cluster Scheduler service URL |
CLUSTER_SCHEDULER_API_KEY | Yes | -- | API key for X-API-Key header |
METRICS_PASSWORD | Yes | -- | HTTP Basic Auth password for /metrics |
CLUSTER_DOMAIN | No | babydev.dev.open.redhat.com | Prometheus metric label for cluster identity |
RESYNC_INTERVAL | No | 5m | Informer resync period |
SCORE_TIMEOUT | No | 5s | HTTP timeout for /evaluate |
RETRY_INTERVAL | No | 30s | RequeueAfter delay on transient failures |
LEADER_ELECTION | No | true | Enable leader election |
DRY_RUN | No | false | Log score changes without patching |
DEBUG | No | false | Enable V(1) debug logging |
Request:
{
"candidates": [
{ "name": "ocpv05" },
{ "name": "ocpv06" },
{ "name": "ocpv10" }
]
}
Response:
{
"ranked": [
{ "name": "ocpv06", "score": 73.69, "scores": {"cpu": 90.0, "memory": 75.0}, "eligible": true },
{ "name": "ocpv05", "score": 65.58, "scores": {"cpu": 70.0, "memory": 60.0}, "eligible": true },
{ "name": "ocpv10", "score": 34.44, "scores": {"cpu": 40.0, "memory": 30.0}, "eligible": true }
],
"excluded": [],
"strategy": "most_capacity",
"generated_at": "2026-04-26T14:30:00Z"
}
X-API-Key headerhandle_name and handle_namespace are optional (nil omits from JSON)internal/scheduler/types.goClusterRole defined in helm/templates/rbac.yaml:
| Resource | Verbs | Why |
|---|---|---|
resourcepools | get, list, watch | Watch pools to trigger reconciliation |
resourcehandles | get, list, watch, patch | Read handles, patch preferenceScore |
resourcehandles/status | patch | Cache placements in status.placements |
anarchysubjects | get | Read job_vars for placement resolution |
leases | create, delete, get, list, update, watch | Leader election |
events | create, get, list, patch, watch | controller-runtime event recording |
All failures degrade to current FIFO behavior — no failure mode breaks Poolboy.
| Scenario | Effect | Recovery |
|---|---|---|
| Controller is down | Last scores persist, new handles get 0 | Restart; resync re-scores |
| Cluster Scheduler is down | Existing scores persist | Scheduler returns; next resync updates |
| Stale scores (5+ min) | Better than no scores | Resync catches up |
| AnarchySubject not found | Handle skipped, requeued | Subject appears; next resync resolves |
| 409 Conflict on patch | Logged at Info, requeued | Next reconcile succeeds |