Skip to content

feat(gateway): support multiple hosts in ingress#392

Merged
sylr merged 3 commits intomainfrom
feat/ingress-multiple-hosts
Feb 27, 2026
Merged

feat(gateway): support multiple hosts in ingress#392
sylr merged 3 commits intomainfrom
feat/ingress-multiple-hosts

Conversation

@sylr
Copy link
Contributor

@sylr sylr commented Feb 17, 2026

Add a Hosts field to GatewayIngress CRD and a gateway.ingress.hosts setting to allow serving a stack on multiple hostnames. Hosts from both sources are deduplicated and merged. Each host gets its own ingress rule and all hosts are included in TLS configuration.

@sylr sylr requested a review from a team as a code owner February 17, 2026 16:44
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

Important

Review skipped

This PR was authored by the user configured for CodeRabbit reviews. By default, CodeRabbit skips reviewing PRs authored by this user. It's recommended to use a dedicated user account to post CodeRabbit review feedback.

To trigger a single review, invoke the @coderabbitai review command.

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:

  • ✅ Review completed - (🔄 Check again to review again)

Walkthrough

Adds support for multiple gateway hosts by introducing a Hosts field on GatewayIngress, utilities to aggregate and deduplicate hosts (including settings-based hosts with {stack} templating), and refactors ingress/TLS generation and tests to use the aggregated host list.

Changes

Cohort / File(s) Summary
API Types & deepcopy
api/formance.com/v1beta1/gateway_types.go, api/formance.com/v1beta1/zz_generated.deepcopy.go
Added GatewayIngress Hosts []string field, DedupHosts() utility, GetHosts() method, and deep-copy/DeepCopyObject updates for the new field.
Controller implementation
internal/resources/gateways/ingress.go
Added getAllHosts(ctx, gateway) to collect/templating/deduplicate hosts from spec and Settings. Refactored withIngressRules(hosts []string) and withTls(ctx, gateway, hosts []string) and updated createIngress to pass aggregated hosts.
Tests
internal/tests/gateway_controller_test.go
Added test contexts covering multiple spec hosts, additional hosts from Settings, and overlapping hosts deduplication; assertions updated for multiple ingress rules and TLS hosts.
Documentation
docs/04-Modules/02-Gateway.md, docs/09-Configuration reference/01-Settings.md, docs/09-Configuration reference/02-Custom Resource Definitions.md
Documented “Serving on Multiple Hosts”, added gateway.ingress.hosts Settings key (comma-separated, supports {stack}), and added GatewayIngress CRD docs showing new hosts field and examples.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Controller
participant SettingsStore as Settings
participant GatewayCR as Gateway
participant IngressAPI as KubernetesIngress
Controller->>Settings: read gateway.ingress.hosts
Controller->>GatewayCR: read GatewayIngress (host + Hosts)
Controller->>Controller: merge lists, apply {stack} template
Controller->>Controller: DedupHosts(mergedHosts)
Controller->>IngressAPI: create/update Ingress with rules for each host and TLS hosts list

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibble hosts from spec and from config too,
I sort, I trim, I dedupe—then I hop through.
Ingress rules lined up like carrots in a row,
TLS coats ready, and away we go! 🐇✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding support for multiple hosts in the gateway ingress, which is the core feature introduced across the CRD, settings, and implementation files.
Description check ✅ Passed The description is directly related to the changeset, clearly explaining the addition of a Hosts field to GatewayIngress CRD, the new gateway.ingress.hosts setting, and how hosts are deduplicated, merged, and applied to ingress rules and TLS configuration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
internal/resources/gateways/ingress.go (1)

88-95: getAllHosts is called twice per reconciliation — consider computing once.

Both withTls and withIngressRules independently call getAllHosts, which hits the settings store each time. If the settings value were to change between the two calls, TLS hosts and ingress rule hosts could theoretically diverge. Computing the hosts once in createIngress and closing over the result would be both more efficient and more consistent.

♻️ Suggested approach
 func createIngress(ctx core.Context, stack *v1beta1.Stack,
 	gateway *v1beta1.Gateway) error {
 	name := types.NamespacedName{
 		Namespace: stack.Name,
 		Name:      "gateway",
 	}
 	if gateway.Spec.Ingress == nil {
 		return core.DeleteIfExists[*v1.Ingress](ctx, name)
 	}
 
+	hosts, err := getAllHosts(ctx, gateway)
+	if err != nil {
+		return err
+	}
+
 	_, _, err := core.CreateOrUpdate(ctx, name,
 		withAnnotations(ctx, stack, gateway),
 		withLabels(ctx, stack, gateway),
 		withIngressClassName(ctx, stack, gateway),
-		withIngressRules(ctx, gateway),
-		withTls(ctx, gateway),
+		withIngressRules(hosts),
+		withTls(ctx, gateway, hosts),
 		core.WithController[*v1.Ingress](ctx.GetScheme(), gateway),
 	)
 
 	return err
 }

Then update withIngressRules and withTls to accept hosts []string directly instead of computing them internally.

Also applies to: 124-127

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/resources/gateways/ingress.go` around lines 88 - 95, Compute hosts
once in createIngress by calling getAllHosts(ctx, gateway) and store the
[]string result, then pass that hosts slice into withTls and withIngressRules
instead of letting those functions call getAllHosts themselves; update the
signatures of withTls and withIngressRules to accept hosts []string (and remove
their internal getAllHosts calls), and ensure createIngress uses the single
hosts value for both TLS (SecretName/Hosts) and ingress rules so both use the
same, cached data.
api/formance.com/v1beta1/gateway_types.go (1)

53-68: Deduplication logic is duplicated with getAllHosts in ingress.go.

The same seen-map dedup loop appears here and in internal/resources/gateways/ingress.go:57-68. Consider extracting a small dedup([]string) []string helper to share between the two call sites.

This is a minor DRY nit — the current code is correct.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/formance.com/v1beta1/gateway_types.go` around lines 53 - 68, The GetHosts
method on GatewayIngress duplicates the deduplication logic used by getAllHosts
in internal/resources/gateways/ingress.go; extract a small helper function (e.g.
dedupStrings or dedup([]string) []string) and use it from both
GatewayIngress.GetHosts and getAllHosts to remove the seen-map loop duplication,
preserving current behavior (skip empty strings and preserve first occurrence
order).
internal/tests/gateway_controller_test.go (1)

167-186: Consider adding a test for host deduplication across spec and settings.

The happy path is well covered. A test where a settings host overlaps with the spec host (e.g., settings value includes "example.net, settings.example.com") would exercise the dedup path in getAllHosts and guard against regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/tests/gateway_controller_test.go` around lines 167 - 186, Add a test
to cover host deduplication by creating a Settings resource whose value includes
the stack/spec host (e.g., "example.net, settings.example.com") and asserting
the resulting Ingress produced by the controller contains no duplicates; update
the existing Context in gateway_controller_test.go to create hostsSetting via
settings.New with a value that includes "example.net" and
"settings.example.com", call Create(hostsSetting) in JustBeforeEach and
Delete(hostsSetting) in AfterEach, then in the It block load the Ingress and
assert ingress.Spec.Rules length and Host entries reflect deduplication
(referencing getAllHosts in the controller logic to ensure this path is
exercised).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@api/formance.com/v1beta1/gateway_types.go`:
- Around line 53-68: The GetHosts method on GatewayIngress duplicates the
deduplication logic used by getAllHosts in
internal/resources/gateways/ingress.go; extract a small helper function (e.g.
dedupStrings or dedup([]string) []string) and use it from both
GatewayIngress.GetHosts and getAllHosts to remove the seen-map loop duplication,
preserving current behavior (skip empty strings and preserve first occurrence
order).

In `@internal/resources/gateways/ingress.go`:
- Around line 88-95: Compute hosts once in createIngress by calling
getAllHosts(ctx, gateway) and store the []string result, then pass that hosts
slice into withTls and withIngressRules instead of letting those functions call
getAllHosts themselves; update the signatures of withTls and withIngressRules to
accept hosts []string (and remove their internal getAllHosts calls), and ensure
createIngress uses the single hosts value for both TLS (SecretName/Hosts) and
ingress rules so both use the same, cached data.

In `@internal/tests/gateway_controller_test.go`:
- Around line 167-186: Add a test to cover host deduplication by creating a
Settings resource whose value includes the stack/spec host (e.g., "example.net,
settings.example.com") and asserting the resulting Ingress produced by the
controller contains no duplicates; update the existing Context in
gateway_controller_test.go to create hostsSetting via settings.New with a value
that includes "example.net" and "settings.example.com", call
Create(hostsSetting) in JustBeforeEach and Delete(hostsSetting) in AfterEach,
then in the It block load the Ingress and assert ingress.Spec.Rules length and
Host entries reflect deduplication (referencing getAllHosts in the controller
logic to ensure this path is exercised).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

@sylr sylr force-pushed the feat/ingress-multiple-hosts branch from bfa5250 to 48d331b Compare February 25, 2026 15:31
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
internal/resources/gateways/ingress.go (1)

82-85: ⚠️ Potential issue | 🟠 Major

Empty hosts slice produces a TLS block with no host coverage.

When getAllHosts returns [] (triggered by the empty-Host catch-all scenario already flagged), withTls still creates an IngressTLS entry with Hosts: []. Most ingress controllers will silently ignore or mis-handle a TLS stanza with an empty hosts list. This is a downstream effect of the empty-host filtering in DedupHosts, but the impact here is independently observable: TLS is configured but covers nothing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/resources/gateways/ingress.go` around lines 82 - 85, The TLS stanza
is being added even when hosts is an empty slice, producing a TLS entry with no
host coverage; update the logic in the function that sets t.Spec.TLS (the
withTls / TLS assignment block) to only set t.Spec.TLS when the hosts slice
returned by getAllHosts/DedupHosts is non-empty—if hosts is empty, leave
t.Spec.TLS nil or an empty list so no IngressTLS entry is emitted. Ensure you
reference and gate on the same hosts variable used currently for SecretName to
avoid creating a TLS block with Hosts: [].
api/formance.com/v1beta1/gateway_types.go (1)

54-73: ⚠️ Potential issue | 🟠 Major

DedupHosts filtering empty strings breaks existing catch-all ingresses.

When Host is set to "" (empty) and no Hosts/settings are configured, DedupHosts discards the empty string, GetHosts() returns [], and withIngressRules generates zero rules — replacing what was previously a catch-all rule. Existing users relying on an empty Host for catch-all behavior will have their ingress rules silently cleared.

Consider explicitly handling the empty-host case (e.g., pass it through as-is when it is the only entry) or document the migration path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/formance.com/v1beta1/gateway_types.go` around lines 54 - 73, DedupHosts
currently drops empty strings which removes intended catch-all hosts; update
DedupHosts (used by GatewayIngress.GetHosts) to preserve a single empty host
when that was the only input (or otherwise explicitly present as the intended
host) instead of returning an empty slice. Concretely: when deduplication yields
no non-empty hosts but the original input contained an empty string as the
sole/explicit host, return []string{""}; keep existing dedup behavior for other
cases so duplicates and intermediate empty entries are still filtered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/resources/gateways/ingress.go`:
- Around line 53-64: getAllHosts currently dereferences gateway.Spec.Ingress via
gateway.Spec.Ingress.GetHosts() and will panic if Spec.Ingress is nil; add a
defensive nil check inside getAllHosts: if gateway.Spec.Ingress is nil, treat
ingress hosts as an empty slice, otherwise call gateway.Spec.Ingress.GetHosts(),
then proceed to append settingsHosts and return v1beta1.DedupHosts(...) as
before. Update getAllHosts to reference gateway.Spec.Ingress and use a local
variable like ingressHosts to avoid the nil deref and keep behavior consistent
with createIngress.

---

Duplicate comments:
In `@api/formance.com/v1beta1/gateway_types.go`:
- Around line 54-73: DedupHosts currently drops empty strings which removes
intended catch-all hosts; update DedupHosts (used by GatewayIngress.GetHosts) to
preserve a single empty host when that was the only input (or otherwise
explicitly present as the intended host) instead of returning an empty slice.
Concretely: when deduplication yields no non-empty hosts but the original input
contained an empty string as the sole/explicit host, return []string{""}; keep
existing dedup behavior for other cases so duplicates and intermediate empty
entries are still filtered.

In `@internal/resources/gateways/ingress.go`:
- Around line 82-85: The TLS stanza is being added even when hosts is an empty
slice, producing a TLS entry with no host coverage; update the logic in the
function that sets t.Spec.TLS (the withTls / TLS assignment block) to only set
t.Spec.TLS when the hosts slice returned by getAllHosts/DedupHosts is
non-empty—if hosts is empty, leave t.Spec.TLS nil or an empty list so no
IngressTLS entry is emitted. Ensure you reference and gate on the same hosts
variable used currently for SecretName to avoid creating a TLS block with Hosts:
[].

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bfa5250 and 48d331b.

⛔ Files ignored due to path filters (2)
  • config/crd/bases/formance.com_gateways.yaml is excluded by !**/*.yaml
  • helm/crds/templates/crds/apiextensions.k8s.io_v1_customresourcedefinition_gateways.formance.com.yaml is excluded by !**/*.yaml
📒 Files selected for processing (6)
  • api/formance.com/v1beta1/gateway_types.go
  • api/formance.com/v1beta1/zz_generated.deepcopy.go
  • docs/04-Modules/02-Gateway.md
  • docs/09-Configuration reference/01-Settings.md
  • internal/resources/gateways/ingress.go
  • internal/tests/gateway_controller_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • api/formance.com/v1beta1/zz_generated.deepcopy.go
  • internal/tests/gateway_controller_test.go
  • docs/09-Configuration reference/01-Settings.md

Comment on lines +53 to +64
func getAllHosts(ctx core.Context, gateway *v1beta1.Gateway) ([]string, error) {
settingsHosts, err := settings.GetTrimmedStringSlice(ctx, gateway.Spec.Stack, "gateway", "ingress", "hosts")
if err != nil {
return nil, err
}

for i, h := range settingsHosts {
settingsHosts[i] = strings.ReplaceAll(h, "{stack}", gateway.Spec.Stack)
}

return v1beta1.DedupHosts(append(gateway.Spec.Ingress.GetHosts(), settingsHosts...)), nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

getAllHosts has an implicit nil precondition on gateway.Spec.Ingress.

gateway.Spec.Ingress.GetHosts() on line 63 panics if gateway.Spec.Ingress is nil. The current call-site (createIngress) guards against this, but nothing prevents getAllHosts from being called elsewhere without the guard. A defensive nil check or a documenting comment would make the precondition explicit.

🛡️ Proposed defensive guard
 func getAllHosts(ctx core.Context, gateway *v1beta1.Gateway) ([]string, error) {
+	if gateway.Spec.Ingress == nil {
+		return nil, nil
+	}
 	settingsHosts, err := settings.GetTrimmedStringSlice(ctx, gateway.Spec.Stack, "gateway", "ingress", "hosts")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func getAllHosts(ctx core.Context, gateway *v1beta1.Gateway) ([]string, error) {
settingsHosts, err := settings.GetTrimmedStringSlice(ctx, gateway.Spec.Stack, "gateway", "ingress", "hosts")
if err != nil {
return nil, err
}
for i, h := range settingsHosts {
settingsHosts[i] = strings.ReplaceAll(h, "{stack}", gateway.Spec.Stack)
}
return v1beta1.DedupHosts(append(gateway.Spec.Ingress.GetHosts(), settingsHosts...)), nil
}
func getAllHosts(ctx core.Context, gateway *v1beta1.Gateway) ([]string, error) {
if gateway.Spec.Ingress == nil {
return nil, nil
}
settingsHosts, err := settings.GetTrimmedStringSlice(ctx, gateway.Spec.Stack, "gateway", "ingress", "hosts")
if err != nil {
return nil, err
}
for i, h := range settingsHosts {
settingsHosts[i] = strings.ReplaceAll(h, "{stack}", gateway.Spec.Stack)
}
return v1beta1.DedupHosts(append(gateway.Spec.Ingress.GetHosts(), settingsHosts...)), nil
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/resources/gateways/ingress.go` around lines 53 - 64, getAllHosts
currently dereferences gateway.Spec.Ingress via gateway.Spec.Ingress.GetHosts()
and will panic if Spec.Ingress is nil; add a defensive nil check inside
getAllHosts: if gateway.Spec.Ingress is nil, treat ingress hosts as an empty
slice, otherwise call gateway.Spec.Ingress.GetHosts(), then proceed to append
settingsHosts and return v1beta1.DedupHosts(...) as before. Update getAllHosts
to reference gateway.Spec.Ingress and use a local variable like ingressHosts to
avoid the nil deref and keep behavior consistent with createIngress.

@sylr sylr force-pushed the feat/ingress-multiple-hosts branch from 48d331b to a464c64 Compare February 25, 2026 15:40
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/formance.com/v1beta1/gateway_types.go`:
- Around line 28-31: Remove the kubebuilder root/resource markers from the
nested type GatewayIngress: delete the lines with
"+kubebuilder:object:root=true", "+kubebuilder:subresource:status", and
"+kubebuilder:resource:scope=Cluster" that are placed on the GatewayIngress type
definition so it is no longer treated as a top-level API object; this will
prevent generation of DeepCopyObject()/runtime.Object implementations for
GatewayIngress and stop it from being emitted as a standalone CRD—leave
GatewayIngress defined as a plain struct referenced by GatewaySpec (Ingress
*GatewayIngress) and ensure only the real root type (e.g. Gateway) keeps
root/resource kubebuilder markers.

In `@docs/09-Configuration` reference/02-Custom Resource Definitions.md:
- Line 2118: The table entry links to a non-existent fragment
`#gatewayingresstls` for GatewayIngressTLS; locate the GatewayIngressTLS section
header and either add an explicit HTML anchor or update the table link to match
the actual header fragment (exact hyphenation/lowercasing) used by that section
so the link resolves correctly; ensure the table row referencing
`GatewayIngressTLS` uses the same fragment string as the target header.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 48d331b and a464c64.

📒 Files selected for processing (7)
  • api/formance.com/v1beta1/gateway_types.go
  • api/formance.com/v1beta1/zz_generated.deepcopy.go
  • docs/04-Modules/02-Gateway.md
  • docs/09-Configuration reference/01-Settings.md
  • docs/09-Configuration reference/02-Custom Resource Definitions.md
  • internal/resources/gateways/ingress.go
  • internal/tests/gateway_controller_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/04-Modules/02-Gateway.md

Comment on lines +28 to +31
// GatewayIngress represents the ingress configuration for the gateway.
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Step 1: Check the actual markers on GatewayIngress
echo "=== Checking GatewayIngress markers ==="
rg -n -B5 -A2 'type GatewayIngress struct' api/formance.com/v1beta1/gateway_types.go | head -40

# Step 2: Check the full GatewayIngress definition and context
echo ""
echo "=== Checking full context of GatewayIngress definition ==="
sed -n '20,80p' api/formance.com/v1beta1/gateway_types.go

# Step 3: Check if Gateway type exists and if GatewayIngress is nested
echo ""
echo "=== Checking Gateway type structure ==="
sed -n '1,150p' api/formance.com/v1beta1/gateway_types.go | tail -100

Repository: formancehq/operator

Length of output: 4891


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check for DeepCopyObject generation on GatewayIngress
echo "=== Checking for DeepCopyObject on GatewayIngress ==="
rg -n 'func \(in \*GatewayIngress\) DeepCopyObject' api/formance.com/v1beta1/ || echo "Not found (expected)"

# Check for DeepCopyInto on GatewayIngress (regular nested type)
echo ""
echo "=== Checking for DeepCopyInto on GatewayIngress ==="
rg -n 'func \(in \*GatewayIngress\) DeepCopyInto' api/formance.com/v1beta1/

# Check for any SchemeBuilder registrations of GatewayIngress
echo ""
echo "=== Checking SchemeBuilder registrations ==="
rg -n 'SchemeBuilder\.Register.*GatewayIngress' --type=go api/formance.com/v1beta1/ || echo "Not found (expected)"

# Check if Gateway type is the actual root
echo ""
echo "=== Verifying Gateway is actual root ==="
rg -n -B2 'type Gateway struct' api/formance.com/v1beta1/gateway_types.go | head -20

Repository: formancehq/operator

Length of output: 591


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if GatewayIngress is documented as a standalone CRD (unwanted artifact)
echo "=== Checking for GatewayIngress in docs ==="
find . -name "*.md" -type f -exec rg -l 'GatewayIngress' {} \; | head -5

# Verify the exact lines of the markers to confirm what should be removed
echo ""
echo "=== Exact marker lines on GatewayIngress ==="
sed -n '28,33p' api/formance.com/v1beta1/gateway_types.go

Repository: formancehq/operator

Length of output: 490


Remove root/resource kubebuilder markers from nested GatewayIngress.

GatewayIngress (lines 29–31) is marked with +kubebuilder:object:root=true, +kubebuilder:subresource:status, and +kubebuilder:resource:scope=Cluster, but this type is nested under GatewaySpec as an optional field (Ingress *GatewayIngress), not an API root resource. These markers cause:

  • DeepCopyObject() to be generated in the deepcopy file, implementing runtime.Object unnecessarily
  • GatewayIngress to appear as a standalone CRD in documentation
  • Misleading artifact surface that contradicts its actual nested usage

These markers belong only on root types (like Gateway itself) and should be removed from GatewayIngress.

Proposed fix
 // GatewayIngress represents the ingress configuration for the gateway.
-// +kubebuilder:object:root=true
-// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Cluster
 type GatewayIngress struct {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// GatewayIngress represents the ingress configuration for the gateway.
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
// GatewayIngress represents the ingress configuration for the gateway.
type GatewayIngress struct {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/formance.com/v1beta1/gateway_types.go` around lines 28 - 31, Remove the
kubebuilder root/resource markers from the nested type GatewayIngress: delete
the lines with "+kubebuilder:object:root=true",
"+kubebuilder:subresource:status", and "+kubebuilder:resource:scope=Cluster"
that are placed on the GatewayIngress type definition so it is no longer treated
as a top-level API object; this will prevent generation of
DeepCopyObject()/runtime.Object implementations for GatewayIngress and stop it
from being emitted as a standalone CRD—leave GatewayIngress defined as a plain
struct referenced by GatewaySpec (Ingress *GatewayIngress) and ensure only the
real root type (e.g. Gateway) keeps root/resource kubebuilder markers.

| `scheme` _string_ | Indicate the scheme.<br />Actually, It should be `https` unless you know what you are doing. | https | |
| `ingressClassName` _string_ | Ingress class to use | | |
| `annotations` _object (keys:string, values:string)_ | Custom annotations to add on the ingress | | |
| `tls` _[GatewayIngressTLS](#gatewayingresstls)_ | Allow to customize the tls part of the ingress | | |
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix broken anchor link for GatewayIngressTLS.

Line 2118 links to #gatewayingresstls, but that fragment is not present, so the reference is broken.

📝 Minimal doc fix
-| `tls` _[GatewayIngressTLS](`#gatewayingresstls`)_ | Allow to customize the tls part of the ingress |  |  |
+| `tls` _GatewayIngressTLS_ | Allow to customize the tls part of the ingress |  |  |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| `tls` _[GatewayIngressTLS](#gatewayingresstls)_ | Allow to customize the tls part of the ingress | | |
| `tls` _GatewayIngressTLS_ | Allow to customize the tls part of the ingress | | |
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 2118-2118: Link fragments should be valid

(MD051, link-fragments)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/09-Configuration` reference/02-Custom Resource Definitions.md at line
2118, The table entry links to a non-existent fragment `#gatewayingresstls` for
GatewayIngressTLS; locate the GatewayIngressTLS section header and either add an
explicit HTML anchor or update the table link to match the actual header
fragment (exact hyphenation/lowercasing) used by that section so the link
resolves correctly; ensure the table row referencing `GatewayIngressTLS` uses
the same fragment string as the target header.

@sylr sylr force-pushed the feat/ingress-multiple-hosts branch from a464c64 to 0a6e2bd Compare February 25, 2026 15:49
sylr and others added 3 commits February 27, 2026 13:43
Add a Hosts field to GatewayIngress CRD and a gateway.ingress.hosts
setting to allow serving a stack on multiple hostnames. Hosts from both
sources are deduplicated and merged. Each host gets its own ingress rule
and all hosts are included in TLS configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… dedup test

Address review feedback:
- Extract shared DedupHosts function used by both GetHosts and getAllHosts
- Compute hosts once in createIngress and pass to withIngressRules/withTls
- Add test for host deduplication when spec and settings overlap

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…etting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sylr sylr force-pushed the feat/ingress-multiple-hosts branch from 0a6e2bd to 2c68232 Compare February 27, 2026 12:44
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
api/formance.com/v1beta1/gateway_types.go (1)

71-74: Consider documenting or handling nil receiver.

GetHosts() will panic if called on a nil *GatewayIngress. While the current call site in getAllHosts is guarded by a nil check in createIngress, this method could be called from other contexts without protection.

Consider either adding a nil check or documenting the precondition:

Option 1: Defensive nil check
 // GetHosts returns the deduplicated union of Host and Hosts.
 func (in *GatewayIngress) GetHosts() []string {
+	if in == nil {
+		return nil
+	}
 	return DedupHosts(append([]string{in.Host}, in.Hosts...))
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/formance.com/v1beta1/gateway_types.go` around lines 71 - 74, GetHosts()
on *GatewayIngress can panic when receiver is nil; update the
GatewayIngress.GetHosts method to defensively handle a nil receiver (e.g., if in
== nil return nil or return []string{}) before calling DedupHosts, and ensure
any callers such as createIngress/getAllHosts still behave correctly;
alternatively, if you choose to require a non-nil receiver, add a clear comment
on GatewayIngress.GetHosts documenting that the receiver must be non-nil.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@api/formance.com/v1beta1/gateway_types.go`:
- Around line 71-74: GetHosts() on *GatewayIngress can panic when receiver is
nil; update the GatewayIngress.GetHosts method to defensively handle a nil
receiver (e.g., if in == nil return nil or return []string{}) before calling
DedupHosts, and ensure any callers such as createIngress/getAllHosts still
behave correctly; alternatively, if you choose to require a non-nil receiver,
add a clear comment on GatewayIngress.GetHosts documenting that the receiver
must be non-nil.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a464c64 and 2c68232.

⛔ Files ignored due to path filters (2)
  • config/crd/bases/formance.com_gateways.yaml is excluded by !**/*.yaml
  • helm/crds/templates/crds/apiextensions.k8s.io_v1_customresourcedefinition_gateways.formance.com.yaml is excluded by !**/*.yaml
📒 Files selected for processing (6)
  • api/formance.com/v1beta1/gateway_types.go
  • api/formance.com/v1beta1/zz_generated.deepcopy.go
  • docs/04-Modules/02-Gateway.md
  • docs/09-Configuration reference/01-Settings.md
  • internal/resources/gateways/ingress.go
  • internal/tests/gateway_controller_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/04-Modules/02-Gateway.md
  • api/formance.com/v1beta1/zz_generated.deepcopy.go
  • docs/09-Configuration reference/01-Settings.md

@sylr sylr merged commit 31623e0 into main Feb 27, 2026
6 checks passed
@sylr sylr deleted the feat/ingress-multiple-hosts branch February 27, 2026 13:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants