Skip to content

fix(k8s): cross-check provided contract hosts against rendered Services (E150) (#1035) - #1038

Open
RonaldHensbergen wants to merge 3 commits into
mainfrom
fix/1035-k8s-e150-contract-hosts
Open

RonaldHensbergen wants to merge 3 commits into
mainfrom
fix/1035-k8s-e150-contract-hosts

Conversation

@RonaldHensbergen

Copy link
Copy Markdown
Owner

Closes #1035.

The E150 provide-side cross-check from #1028 only ran in the compose renderer (cli/renderer.py); the Kubernetes renderer had just the consume-side E084 check, so a provided contract advertising a host that no rendered k8s Service matched did not fail closed on the helm target.

Changes

  • cli/k8s_renderer.py: render_helm now runs the new _check_provided_contract_services right after _check_binding_services. Every provided http-service, sql-database, cache-service, and object-storage contract's advertised network host (the host field, the endpoint's hostname part, or a serviceName resolved through the renderer's service-naming rule to the serving workload's Service name -- honoring workload-level serviceName overrides) is checked against the Services the render produces for that module; a mismatch fails closed with E150 (same code as the compose check, matching how E071-E080 are shared across render targets). External hosts (dots, ports, credentials) are skipped, and modules whose workloads render no Service at all are left to their own diagnostics -- mirroring the compose check.
  • tests/test_k8s_renderer.py: 7 new tests covering the failing host, passing host, serviceName mapping (hit and miss), sql-database/object-storage endpoints, external-host skipping, the no-rendered-services skip, and workload serviceName overrides.
  • README.md / docs/modules.md / CHANGELOG.md: the E150 documentation now covers both render targets.

Verification

  • tests/test_k8s_renderer.py: 33 passed (incl. the golden chart hashes -- the real profile is unaffected).
  • Full suite: 1973 tests, only pre-existing unrelated failure (test_security.py rule-set version, caused by in-progress working-tree changes not part of this branch).
  • ruff check and markdownlint clean on the touched files.

…es (E150) (#1035)

The E150 provide-side cross-check from #1028 only ran in the compose
renderer; the Kubernetes renderer had just the consume-side E084 check,
so a provided contract advertising a host no rendered k8s Service
matched did not fail closed on the helm target.

render_helm now runs _check_provided_contract_services next to
_check_binding_services: every provided http-service, sql-database,
cache-service, and object-storage contract's advertised network host
(the host field, the endpoint's hostname part, or a serviceName resolved
to the serving workload's Service name, honoring workload-level
serviceName overrides) is checked against the Services the render
produces for that module, and a mismatch fails closed with E150.
External hosts (dots, ports) are skipped and modules rendering no Service
are left to their own diagnostics, mirroring the compose check. README's
E150 row, docs/modules.md, and the CHANGELOG cover both render targets.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>

@RonaldHensbergen RonaldHensbergen left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review (contributor == reviewer), all angles checked:

  • Functioneel / fail-closed: _check_provided_contract_services runs in render_helm right after _check_binding_services, before the error gate, so an E150 mismatch returns {} files — the chart is not written. The "renders green but breaks at runtime" scenario (provide host no k8s Service answers) is now a render-time error. Verified against the real profile (still renders clean, golden hashes unchanged) and against broken plans (superset host nowhere, dagster serviceName no-such-workload, workload serviceName override cases) — all fail closed with E150 at the expected paths.
  • serviceName semantics: a provide serviceName names a workload and is resolved through _compose_service_name to the workload's rendered Service name, honoring workload-level serviceName overrides (host must then be the Service name, not the workload name) — consistent with what E084 consume-side bindings and _release_scope_binding_hosts expect.
  • E/W-codes: E150 is reused deliberately — codes are shared across render targets already (E071–E080 in both renderers); no new code allocated, so no collision with main or open PRs (fresh fetch checked; E150 is the highest code in use).
  • Tests: 7 new tests in tests/test_k8s_renderer.py (fail host, pass host, serviceName hit/miss, sql-database + object-storage endpoints, external hosts skipped, no-rendered-services skip, serviceName override). tests/test_k8s_renderer.py: 33 passed; with test_contract_host_resolution.py and test_main.py: 205 passed; full suite 1973 tests with only the pre-existing, unrelated test_security.py rule-set-version failure (working-tree-only changes, not on this branch).
  • Docs/conventies: README E150 row now covers both targets, docs/modules.md describes the helm cross-check, CHANGELOG [Unreleased] has a Fixed entry; ruff and markdownlint clean on touched files.

Geen openstaande punten meer; PR is naar mijn oordeel merge-klaar.

RonaldHensbergen and others added 2 commits October 30, 2026 23:22
…renderers

SonarCloud's quality gate on PR #1038 failed with 10.9% duplication on
new code (required <= 3%): _check_provided_contract_services in
cli/k8s_renderer.py duplicated the candidate extraction and diagnostic
construction of the compose renderer's _validate_provided_contract_hosts.

The shared logic now lives once in cli/renderer.py:
- _provided_contract_host_candidates: per-kind (field, host) pairs a
  provided contract spec advertises;
- _validate_provided_contract_hosts_against: the E150 loop, parameterized
  by the target wording (service_kind/fix_hint) and an optional
  resolve_service_name hook for the helm target's workload-level
  serviceName overrides.

_validate_provided_contract_hosts (compose) and
_check_provided_contract_services (helm) are now thin wrappers that only
supply their rendered-service names. Behavior is unchanged: all 1973
tests pass, ruff clean.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
@sonarqubecloud

Copy link
Copy Markdown

Copy link
Copy Markdown
Owner Author

CI fix pushed (fadbabb): the SonarCloud quality gate failed on the first push with 10.9% duplication on new code (limit 3%) -- _check_provided_contract_services duplicated the compose E150 check's candidate/diagnostic logic.

The E150 core is now shared once in cli/renderer.py (_provided_contract_host_candidates + _validate_provided_contract_hosts_against, parameterized by target wording and a resolve_service_name hook for the helm target's workload-level serviceName overrides); both renderers are thin wrappers. Also merged current main (#75) into the branch.

Result: new_duplicated_lines_density 10.85% -> 0.0%, all 15 checks pass (incl. SonarCloud Code Analysis), 1996 tests OK locally, ruff clean.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

k8s renderer: no provide-side cross-check of advertised contract hosts (E150 is compose-only)

1 participant