Skip to content

feat(tenant_gateway): register TenantGateway kind as raw-spec passthrough - #23

Open
Aleksei Sviridkin (lexfrei) wants to merge 1 commit into
feat/cozystack-1.6from
feat/cozystack-1.6-tenantgateway
Open

feat(tenant_gateway): register TenantGateway kind as raw-spec passthrough#23
Aleksei Sviridkin (lexfrei) wants to merge 1 commit into
feat/cozystack-1.6from
feat/cozystack-1.6-tenantgateway

Conversation

@lexfrei

Copy link
Copy Markdown
Collaborator

Pull Request

Summary

Adds cozystack_tenant_gateway resource and data source for the new gateway.cozystack.io/v1alpha1 TenantGateway kind (namespaced), introduced with Cozystack 1.6 Gateway API support. Spec is raw-JSON passthrough, matching the existing pattern for non-app platform kinds, so upstream spec changes need no provider changes.

Changes

  • Add the gateway.cozystack.io namespaced client resource descriptor
  • Register cozystack_tenant_gateway resource and data source through the existing raw-spec machinery
  • Add examples, regenerated docs and a README resource catalog entry

Testing

  • Unit tests pass locally (make test): the branch inherits 7 spec-coverage failures from the integration base (v1.6.1 pin bump). The sibling field-modeling PRs fix them. This diff adds zero new failures.
  • Linters pass locally (make lint)
  • Generated docs are up to date (make docs produces no diff)
  • Acceptance tests pass against a live cluster (make testacc), if applicable: not run in this pass

Documentation

  • Examples added or updated under examples/
  • Registry docs regenerated (make docs)
  • CHANGELOG updated (if user-facing): a consolidated v1.6 section lands with the integration PR

Checklist

  • Commit messages follow semantic format (type(scope): description)
  • No secrets or credentials in code or state
  • Breaking changes documented (none in this PR)

Additional Notes

Targets the feat/cozystack-1.6 integration branch, not master.

…ough

Cozystack 1.6 adds a new gateway.cozystack.io/v1alpha1 group with a
namespaced TenantGateway kind, declaring a tenant's per-namespace
Gateway API / Cilium Gateway. It is a controller-facing CRD rather
than a packages/apps application, so it follows the same raw-spec
passthrough pattern already used for other platform kinds (spec =
jsonencode(...)) instead of a fully typed model.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e4195279-cb49-4115-92af-0f590d46629c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lexfrei
Aleksei Sviridkin (lexfrei) marked this pull request as ready for review August 7, 2026 11:39

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict

LGTM

Summary

Registers TenantGateway (gateway.cozystack.io) as a namespaced raw-spec passthrough: a new cozystack_tenant_gateway resource and matching data source, plus generated docs, examples, and a README entry. Purely additive — no change to any existing kind.

What I verified

  • GVR is exact. TenantGatewayResource() returns {Group: gateway.cozystack.io, Resource: tenantgateways, Kind: TenantGateway} with no ClusterScoped, and Resource.Version defaults to v1alpha1. Cross-checked against upstream cozystack main: api/gateway/v1alpha1/groupversion_info.go (Group: gateway.cozystack.io, Version: v1alpha1) and the CRD packages/system/cozystack-controller/definitions/gateway.cozystack.io_tenantgateways.yaml (plural: tenantgateways, kind: TenantGateway, scope: Namespaced, v1alpha1 served+storage). Group, version, plural, kind and scope all match.
  • Correct wiring. Registered via newAppResource / newAppDataSource (namespaced) with the shared rawSpecNsResourceModel / rawSpecNsModel and the shared rawSpecNsSchema / rawSpecNsDataSourceSchema — identical to the existing backup and backup_job namespaced passthroughs. expand / flatten are the shared, unit-tested code paths.
  • Docs accuracy. Every spec field named in the docs/README (apex, certMode, issuerName, dns01, wildcardSecretRef, attachedNamespaces, tlsPassthroughServices, gatewayClassName) exists in the CRD schema; apex is the only required field. README table separator formatting is correct.
  • Builds clean. go build ./... and go vet ./internal/provider/ pass; the rawspec unit tests pass.
  • No dangling refs / no secret in state. The DNS-01 credential is referenced by secretRef name only inside the user-authored JSON; no secret value is read into Terraform state.

Caveats (non-blocking)

[MINOR] Raw-spec drift on server-defaulted fields. The TenantGateway CRD server-defaults several top-level fields (certMode: http01, gatewayClassName: cilium, issuerName: letsencrypt-prod, plus nested dns01 defaults). Because spec is Optional+Computed normalized JSON and flatten reads back the full server spec, a config that sets only apex is likely to show a perpetual plan diff as the server populates those defaults. This is inherent to the raw-spec passthrough design (the other passthrough kinds behave the same) and is the deliberate "don't chase the spec evolution" tradeoff, so it is not a regression. A one-line docs note advising users to include the defaulted fields they care about (or to expect the passthrough not to reconcile server-added defaults) would help. Not blocking.

[NIT] No dedicated unit/acceptance test for tenant_gateway. This matches the nearest precedents (backup, backup_job, marketplace_panel — namespaced/dashboard raw-spec passthroughs that also ship without a per-kind test), and CONTRIBUTING only requires the reflection spec-coverage guard "where a typed model is used", which does not apply to a raw-spec kind. SchedulingClass does carry a TestAcc*, so if the team wants parity a small acceptance test could be added later. Not blocking.

Note

Base branch is feat/cozystack-1.6, not main — this is a stacked PR in the 1.6 series; respect the merge order relative to the rest of the 1.6 stack.

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict

LGTM

Registration is a faithful reuse of the existing namespaced raw-spec passthrough machinery: same rawSpecNs{Model,ResourceModel} and rawSpecNs{,DataSource}Schema helpers already exercised by backup / backup_job, wired symmetrically into both Resources() and DataSources(), with a correctly namespaced TenantGatewayResource() (no ClusterScoped, unlike the cluster-scoped MarketplacePanel).

Caveats

  • GVK gateway.cozystack.io/tenantgateways (Kind TenantGateway, namespaced) could not be verified against a live Cozystack 1.6 aggregated API in this static review. If the plural/group does not match the 1.6 ApplicationDefinition, apply/read will fail with NotFound at runtime. This is additive (no effect on existing resources, no silent state loss) and self-evident on first use, so it is not blocking — but an acceptance test against a 1.6 cluster (or cozystack-pr-test) is the authoritative confirmation.
  • No dedicated acceptance test for tenant_gateway; verified this matches the established convention (the backup, backup_job, and marketplace_panel passthrough kinds carry no per-kind *_test.go either — the generic rawSpecNs path is the tested surface). go build ./... and go vet ./internal/provider/ are clean.

@IvanHunters

Copy link
Copy Markdown

Verdict

LGTM with non-blocking notes

Summary

Small, purely-additive PR (+150/-0, 8 files, 0 deletions) that registers the new gateway.cozystack.io/v1alpha1 TenantGateway kind (introduced by Cozystack 1.6) as a raw-JSON-passthrough Terraform resource/data source, following the exact established pattern used for Backup/BackupJob (other namespaced, controller-facing, non-apps.cozystack.io kinds). Verified end-to-end against the real upstream CRD type, the repo's own generic wiring, and independently re-ran the PR's testing claims rather than trusting them.

Checked and correct

  • GVK verified against upstream, not just the PR body. Fetched api/gateway/v1alpha1/tenantgateway_types.go and groupversion_info.go from cozystack/cozystack directly: group gateway.cozystack.io, version v1alpha1, +kubebuilder:resource:scope=Namespaced,shortName=tgw. The provider's TenantGatewayResource() (internal/client/groups.go) correctly omits Version (defaults to v1alpha1, matching) and omits ClusterScoped (correctly namespaced).
  • Spec field list matches exactly, same order: apex, certMode, issuerName, dns01, wildcardSecretRef, attachedNamespaces, tlsPassthroughServices, gatewayClassName in the schema description (internal/provider/rawspec_schema.go) is a verbatim match of TenantGatewaySpec's json-tagged fields in the upstream type.
  • Registration wiring is a byte-identical reuse of the established pattern: newAppResource[rawSpecNsResourceModel, *rawSpecNsResourceModel](client.TenantGatewayResource(), "tenant_gateway", tenantGatewaySchema) / newAppDataSource[rawSpecNsModel, *rawSpecNsModel](...) (internal/provider/provider.go) is structurally identical to the Backup/BackupJob registrations — this is genuine reuse of tested generic machinery (rawSpecNsModel.expand/flatten, rawSpecNsSchema), not a bespoke reimplementation. Schema is genuine raw-spec passthrough (spec as jsontypes.Normalized), not a typed model — matches the PR's stated intent.
  • wait_for_ready/wait_timeout will actually work for this Kind: readyFromConditions (internal/client/application.go) looks for status.conditions[type=Ready,status=True]; the upstream TenantGatewayStatus.Conditions doc explicitly states "Standard condition types: Ready, Programmed" — compatible, and a wait timeout surfaces a legible wrapped error, not a silent hang.
  • Immutability claim in the docs is actually enforced: identityResourceAttributes (used by rawSpecNsSchema) applies stringplanmodifier.RequiresReplace() to both name and namespace, matching the "Immutable" wording in the generated docs.
  • Zero regression risk: the diff has 0 deletions; no existing resource's registration, schema, or model line was modified, only new lines appended. Confirmed no GVR collision against any other client.Resource{} constructor in the tree.
  • Testing claims independently re-verified, not trusted: go build ./..., go vet ./..., golangci-lint run all clean at HEAD. go test -race ./... shows 7 pre-existing failures (spec-coverage-guard mismatches in tenant/postgresql/kafka/kubernetes/nats/qdrant models from the v1.6.1 API pin bump) — reproduced the identical 7 failures at the merge-base commit 583e1a9 (before this diff), confirming the PR body's claim that this diff "adds zero new failures" rather than accepting it on the author's word.
  • Mechanical anti-pattern sweep (fail-open shell patterns, over-broad RBAC verbs) run against the diff: clean, but there is no shell/RBAC surface in this diff at all to trigger it.
  • Commit follows Conventional Commits, carries Signed-off-by + Assisted-By: Claude, matching this repo's own convention; CHANGELOG/docs-index correctly left untouched (both are release-time/tfplugindocs-owned artifacts per this repo's own history, not per-PR).

Findings

[MINOR] Namespaced raw-spec model (rawSpecNsModel/rawSpecNsResourceModel) has zero dedicated unit test coverage, and this PR is the third Kind to rely on that untested path without closing the gap.
This repo's own CLAUDE.md lists "add a unit test" as a step of "Adding a Kind," unconditional on typed vs. rawspec vs. marker. rawspec_model_test.go covers only the cluster-scoped rawSpecModel (TestRawSpecExpand_ParsesJSON, TestRawSpecFlatten_RoundTrip); a repo-wide grep for rawSpecNsModel/rawSpecNsResourceModel in *_test.go returns nothing. This is a pre-existing gap (the commit that introduced the namespaced-rawspec pattern for Backup/BackupJob, bf15f66, added no test files either), so this PR is following precedent rather than introducing a new hole — non-blocking. Recommend a follow-up adding TestRawSpecNsExpand_ParsesJSON / TestRawSpecNsFlatten_RoundTrip analogous to the cluster-scoped tests, since three Kinds (soon possibly more) now share this code path with no direct regression coverage.

Caveats

  • DNS01Config discriminated-union validation gap (inherited, not introduced by this PR). Upstream DNS01Config.Provider selects which of Cloudflare/Route53/DigitalOcean/RFC2136 is read ("Only the field corresponding to Provider is read; others are ignored"), and each block's doc comment says "Required when Provider=X" but is +optional at the struct level (no CRD-schema or CEL enforcement visible in the fetched type). The raw-JSON passthrough this PR adds does not, and by design cannot, validate that constraint at the Terraform layer — a user can legally submit provider: cloudflare with the cloudflare block empty/absent, apply successfully, and the resulting TenantGateway would (per the upstream comment) simply fail to issue certs, surfacing only via status.conditions if the controller sets a reason there (not verified as part of this review — controller code is out of scope for a provider-registration PR). This is the accepted tradeoff of the rawspec-passthrough choice, shared with Backup/BackupJob/PackageSource, and not specific to or worsened by this PR.
  • make docs "no diff" claim not independently executed. tfplugindocs was not available locally and installing it would require a network fetch outside this review's hermetic, local-only execution boundary. Verified instead by manually cross-checking the new docs/resources/tenant_gateway.md / docs/data-sources/tenant_gateway.md content against the schema-generating functions and description strings in rawspec_schema.go — consistent, no contradiction found, but this is "reasoned, not executed," not a ground-truth confirmation of make docs being diff-free.
  • No acceptance test (make testacc) for cozystack_tenant_gateway. Requires a live Cozystack 1.6 cluster, out of scope for this static review per the Execution boundary. Matches established precedent: Backup, BackupJob, MarketplacePanel, PackageSource, ApplicationDefinition, BackupClass all likewise have zero acceptance-test coverage in acc_test.go; only SchedulingClass is an outlier with one. Non-blocking.

What would change my mind

If a future Kind (or this one, on live-cluster testing via cozystack-pr-test) showed the dns01 provider/block mismatch actually reaches a customer as a silently-broken TenantGateway with no Ready=False reason surfaced, that would upgrade the DNS01 Caveat to a MAJOR finding against the upstream controller (out of scope for this repo/PR, but worth a note to the author to verify server-side). Nothing found in this review changes the LGTM verdict for the provider-registration change itself.

Recommended follow-ups

  • Add a TestRawSpecNsExpand_ParsesJSON/TestRawSpecNsFlatten_RoundTrip pair for rawSpecNsModel (covers Backup, BackupJob, and now TenantGateway in one shared test).
  • When a live Cozystack 1.6 dev cluster is available, run make testacc -run TestAccTenantGateway (once/if such a test is added) or a manual terraform apply smoke test to confirm the dns01/certMode combinations behave as documented server-side.

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.

2 participants