Commit d20a576
feat: use v2 deployment read endpoints (#23)
* chore: update upstream OpenAPI spec 2026-05-07
* feat: use v2 deployment read endpoints
Upstream RIG-Cluster shipped GET /v2/projects/{p}/deployments and
GET /v2/projects/{p}/deployments/{d} (closes the structural problem
flagged in RijksICTGilde/RIG-Cluster#51). The CLI no longer has to
fuse /logs and /tasks to reconstruct deployment state.
Client:
- add ZadClient.list_deployments_v2 and get_deployment_v2 wrappers
- list_deployments and describe_deployment now prefer the v2 endpoints
and fall back to the legacy logs+tasks fusion on 404, so older
Operations Manager deployments keep working
- legacy code paths preserved as _list_deployments_legacy and
_describe_deployment_legacy
Models:
- add DeploymentStatus and ErrorCategory string enums
- add DeploymentDetail, DeploymentListResponse, DeploymentComponentDetail,
StatusError pydantic models for the new response shapes
Describe rendering:
- show Status (color-coded), Revision (short SHA), Last sync
- when Status indicates a problem, show an Errors table with
Category/Resource/Message and a deduplicated explanation footer
- hide the K8s Deployment column in the v2 path (the field doesn't
exist there); legacy path still populates it
Tests:
- new respx tests for the v2 happy paths and the 404 fallback
- existing legacy-path tests now mock a 404 on the v2 endpoint to
exercise the same fallback code
* fix: address review on PR #23
- describe_deployment now propagates a real 404 when the v2 list
endpoint works but get-one 404s (deployment is genuinely missing).
Previously the legacy fallback would silently return empty data.
Probe by calling list_deployments_v2 to disambiguate "deployment
missing" from "endpoint not registered on this upstream".
- _status_color: Suspended joins the red tier alongside Degraded,
Missing, OutOfSync per upstream's documented severity ordering.
- backwards-compat baseline: register list_deployments_v2 and
get_deployment_v2 as expected public methods.
* fix: address second review on PR #23
- list_deployments now disambiguates "project not found" from
"v2 endpoint not registered on this upstream", mirroring the
describe_deployment logic. Probes via list_projects (available on
every upstream version) before falling back to the legacy fusion.
- "Last sync" label renamed to "Last sync attempt": per upstream
spec, the timestamp is the most recent reconciliation attempt
regardless of outcome, so a Degraded deployment with a recent
failed sync no longer reads as if it last synced cleanly.
- Move _status_color above describe (helpers before callers).
* fix: only swallow 404 from list_projects probe
The disambiguation probe in list_deployments was catching every
ZadApiError from list_projects, including 401/403/5xx. An expired
API key or transient server error would silently route to the
legacy fusion path and surface a confusing error from /logs
instead of the real cause. Now only 404 means "old upstream";
everything else propagates.
* fix: extend describe_deployment 404 disambiguation
describe_deployment now mirrors list_deployments: when both v2
endpoints 404, it probes list_projects to tell "old upstream" from
"project does not exist on this upstream". The old code silently
fell back to the legacy logs+tasks fusion in both cases, masking
"Project not found" with empty-component results.
Both callers go through a shared _project_exists helper which only
swallows a 404 from list_projects (treating that as "even /projects
is missing, fall back"); 401/403/5xx propagate so the user sees the
real cause.
New test: describe_deployment raises ZadApiError(404) with a clear
"Project '<name>' not found" message when the project is missing.
* fix: address third review on PR #23
Three findings:
1. (Significant) project_status was unconditionally overwriting v2-supplied
URLs with empty dicts on modern upstreams that have no recent completed
tasks. The task probe is now best-effort: only overrides v2 URLs when
it actually finds something.
2. Validate v2 read endpoint responses through pydantic.
list_deployments_v2 and get_deployment_v2 now run the response through
DeploymentListResponse / DeploymentDetail and re-emit a dict. Upstream
schema drift surfaces as a clear pydantic error instead of leaking
malformed data downstream.
3. Tests:
- new test for project_status preserving v2 URLs when /tasks is empty
- new test for the "/projects also 404s" branch (very old upstream)
* fix: v2 URLs are authoritative over task-history URLs
The previous fix inverted the priority — task_urls or dep["urls"] gave
task URLs priority when both were non-empty. v2 is authoritative when
present, so prefer dep["urls"] (the v2-supplied dict) and fall back to
the task probe only when v2 returned nothing (legacy upstreams or
deployments with no public components).
New test: when both v2 and task history carry URLs for the same
deployment, the v2 URLs win.
* fix: address fourth review on PR #23
Four findings:
1. (Significant) ValidationError from pydantic schema mismatch was not
caught by @handle_api_errors and would surface as a raw traceback.
New _parse_v2_response helper translates ValidationError to
ZadApiError(502, "Unexpected API response shape: ...") so the
existing error rendering path handles upstream schema drift cleanly.
This becomes load-bearing as upstream adds new DeploymentStatus or
ErrorCategory enum values without a breaking-change label.
2. (Minor) project_status URL merge now uses presence rather than
truthiness. v2 returning {} legitimately means "no publish-on-web";
stale task URLs no longer leak through after the config is removed.
Legacy rows lack the "urls" key so the test still picks them up.
3. (Minor) New test: describe_deployment propagates non-404 (401/etc)
from the disambiguation probe, mirroring the list_deployments test.
4. (Minor) New parametrized test for _status_color covering every
DeploymentStatus enum value plus the empty-string fallback.
* fix: address fifth review on PR #23
- New CLI integration tests for deployment describe rendering, using
typer's CliRunner with a stubbed ZadClient. Covers Healthy (no
Errors table) and Degraded (Errors table with category, message,
and explanation footer) so future Rich-markup or key-name
regressions get caught at CI.
- Trim multi-paragraph docstrings on _parse_v2_response,
list_deployments_v2, get_deployment_v2, list_deployments,
describe_deployment, _project_exists per CLAUDE.md (one-line
docstrings only). Reasoning lives in the PR description.
- _status_color now uses a dict keyed on DeploymentStatus enum
members instead of bare string literals; if upstream renames a
status the type system surfaces the dependency.
* refactor: drop legacy logs+tasks fallback paths
There is one production upstream and it has been upgraded to ship the
v2 read endpoints, so the fallback chain no longer earns its keep.
Removed:
- _list_deployments_legacy and _describe_deployment_legacy (logs+tasks
fusion to reconstruct deployment state)
- _project_exists disambiguation probe and the layered 404 handling in
list_deployments / describe_deployment
- project_status's /tasks call to recover URLs (v2 supplies them
directly on every deployment)
- The optional `subdomain` plumbing and `k8s_deployment` column
rendering, both vestigial after the legacy path is gone
- All tests covering legacy fallback paths, probe behavior, and stale
task-history URL handling
resolve_namespace now uses get_deployment_v2 directly instead of
walking list_deployments. describe_deployment, list_deployments, and
project_status pass through v2 fields without `.get()` defaults: after
pydantic validation the required fields are guaranteed present and
the optional ones are explicitly None.
Net change: ~150 lines of client code and ~250 lines of tests removed,
no behavior change against the current upstream.
* fix: coerce unknown enum values instead of failing validation
Closed enums were brittle: an additive upstream change (a new
ErrorCategory or DeploymentStatus value) would make pydantic reject
the entire DeploymentListResponse, taking the whole `zad deployment
list` for a project down until the CLI is updated. Upstream's own
`Unknown` catch-all signals they expect callers to handle unknown
values gracefully, not hard-fail.
- StatusError now coerces unknown category strings to UNKNOWN via a
before-validator.
- DeploymentDetail does the same for status.
- Tests cover both coercion paths plus the known-value passthrough.
- Drop vestigial `k8s_deployment` keys from the describe rendering
test fixtures (the column was removed when v2 became the only
source).
* fix: address final review on PR #23
- Restore `k8s_deployment` as a tombstone field in describe_deployment's
component dicts. The v2 endpoint doesn't expose this field, but
removing it from the public describe shape would break consumers
reading `comp["k8s_deployment"]` from the JSON output. Per the
backwards-compat policy, return-shape removals require a deprecation
cycle; an empty-string tombstone is the additive-only path.
- Replace the private `_value2member_map_` access in the enum coercion
validators with a public set comprehension `{e.value for e in Enum}`.
- Use a realistic 40-char SHA in the describe rendering test so the
`[:12]` truncation is actually exercised.
* fix: tighten _STATUS_COLORS type and exercise SHA truncation
- _STATUS_COLORS is now annotated dict[DeploymentStatus, str], so type
checkers flag a new enum value missing from the mapping instead of
it silently falling through to "dim" at runtime.
- The Degraded fixture's sync_revision was exactly 12 characters, so
the [:12] truncation was a no-op there. Pad to 40 chars to match
the Healthy fixture and actually verify truncation.
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>1 parent cb27c51 commit d20a576
9 files changed
Lines changed: 810 additions & 191 deletions
File tree
- api
- src/zad_cli
- api
- commands
- tests
- commands
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
513 | 513 | | |
514 | 514 | | |
515 | 515 | | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
516 | 631 | | |
517 | 632 | | |
518 | 633 | | |
| |||
6261 | 6376 | | |
6262 | 6377 | | |
6263 | 6378 | | |
| 6379 | + | |
| 6380 | + | |
| 6381 | + | |
| 6382 | + | |
| 6383 | + | |
| 6384 | + | |
| 6385 | + | |
| 6386 | + | |
| 6387 | + | |
| 6388 | + | |
| 6389 | + | |
| 6390 | + | |
| 6391 | + | |
| 6392 | + | |
| 6393 | + | |
| 6394 | + | |
| 6395 | + | |
| 6396 | + | |
| 6397 | + | |
| 6398 | + | |
| 6399 | + | |
| 6400 | + | |
| 6401 | + | |
| 6402 | + | |
| 6403 | + | |
| 6404 | + | |
| 6405 | + | |
| 6406 | + | |
| 6407 | + | |
| 6408 | + | |
| 6409 | + | |
| 6410 | + | |
| 6411 | + | |
| 6412 | + | |
| 6413 | + | |
| 6414 | + | |
| 6415 | + | |
| 6416 | + | |
| 6417 | + | |
| 6418 | + | |
| 6419 | + | |
| 6420 | + | |
| 6421 | + | |
| 6422 | + | |
| 6423 | + | |
| 6424 | + | |
| 6425 | + | |
| 6426 | + | |
| 6427 | + | |
| 6428 | + | |
| 6429 | + | |
| 6430 | + | |
| 6431 | + | |
| 6432 | + | |
| 6433 | + | |
| 6434 | + | |
| 6435 | + | |
| 6436 | + | |
| 6437 | + | |
| 6438 | + | |
| 6439 | + | |
| 6440 | + | |
| 6441 | + | |
| 6442 | + | |
| 6443 | + | |
| 6444 | + | |
| 6445 | + | |
| 6446 | + | |
| 6447 | + | |
| 6448 | + | |
| 6449 | + | |
| 6450 | + | |
| 6451 | + | |
| 6452 | + | |
| 6453 | + | |
| 6454 | + | |
| 6455 | + | |
| 6456 | + | |
| 6457 | + | |
| 6458 | + | |
| 6459 | + | |
| 6460 | + | |
| 6461 | + | |
| 6462 | + | |
| 6463 | + | |
| 6464 | + | |
| 6465 | + | |
| 6466 | + | |
| 6467 | + | |
| 6468 | + | |
| 6469 | + | |
| 6470 | + | |
| 6471 | + | |
| 6472 | + | |
| 6473 | + | |
| 6474 | + | |
| 6475 | + | |
| 6476 | + | |
| 6477 | + | |
| 6478 | + | |
| 6479 | + | |
| 6480 | + | |
| 6481 | + | |
| 6482 | + | |
| 6483 | + | |
| 6484 | + | |
| 6485 | + | |
| 6486 | + | |
| 6487 | + | |
| 6488 | + | |
| 6489 | + | |
| 6490 | + | |
| 6491 | + | |
| 6492 | + | |
| 6493 | + | |
| 6494 | + | |
| 6495 | + | |
| 6496 | + | |
| 6497 | + | |
| 6498 | + | |
| 6499 | + | |
| 6500 | + | |
| 6501 | + | |
| 6502 | + | |
| 6503 | + | |
| 6504 | + | |
| 6505 | + | |
| 6506 | + | |
| 6507 | + | |
| 6508 | + | |
| 6509 | + | |
| 6510 | + | |
| 6511 | + | |
| 6512 | + | |
| 6513 | + | |
| 6514 | + | |
| 6515 | + | |
| 6516 | + | |
| 6517 | + | |
| 6518 | + | |
| 6519 | + | |
| 6520 | + | |
| 6521 | + | |
| 6522 | + | |
| 6523 | + | |
6264 | 6524 | | |
6265 | 6525 | | |
6266 | 6526 | | |
| |||
6412 | 6672 | | |
6413 | 6673 | | |
6414 | 6674 | | |
| 6675 | + | |
| 6676 | + | |
| 6677 | + | |
| 6678 | + | |
| 6679 | + | |
| 6680 | + | |
| 6681 | + | |
| 6682 | + | |
| 6683 | + | |
| 6684 | + | |
| 6685 | + | |
| 6686 | + | |
| 6687 | + | |
| 6688 | + | |
| 6689 | + | |
| 6690 | + | |
| 6691 | + | |
| 6692 | + | |
| 6693 | + | |
| 6694 | + | |
| 6695 | + | |
| 6696 | + | |
| 6697 | + | |
| 6698 | + | |
| 6699 | + | |
| 6700 | + | |
| 6701 | + | |
| 6702 | + | |
| 6703 | + | |
| 6704 | + | |
6415 | 6705 | | |
6416 | 6706 | | |
6417 | 6707 | | |
| |||
7029 | 7319 | | |
7030 | 7320 | | |
7031 | 7321 | | |
| 7322 | + | |
| 7323 | + | |
| 7324 | + | |
| 7325 | + | |
| 7326 | + | |
| 7327 | + | |
| 7328 | + | |
| 7329 | + | |
| 7330 | + | |
| 7331 | + | |
| 7332 | + | |
| 7333 | + | |
| 7334 | + | |
| 7335 | + | |
| 7336 | + | |
| 7337 | + | |
| 7338 | + | |
| 7339 | + | |
| 7340 | + | |
| 7341 | + | |
| 7342 | + | |
| 7343 | + | |
| 7344 | + | |
| 7345 | + | |
| 7346 | + | |
| 7347 | + | |
| 7348 | + | |
| 7349 | + | |
| 7350 | + | |
| 7351 | + | |
| 7352 | + | |
| 7353 | + | |
| 7354 | + | |
| 7355 | + | |
| 7356 | + | |
| 7357 | + | |
| 7358 | + | |
| 7359 | + | |
| 7360 | + | |
| 7361 | + | |
| 7362 | + | |
| 7363 | + | |
| 7364 | + | |
| 7365 | + | |
| 7366 | + | |
| 7367 | + | |
| 7368 | + | |
| 7369 | + | |
| 7370 | + | |
| 7371 | + | |
7032 | 7372 | | |
7033 | 7373 | | |
7034 | 7374 | | |
| |||
0 commit comments