Skip to content

fix: fall back to publishService for Gateway status addresses - #461

Open
janiussyafiq wants to merge 2 commits into
api7:masterfrom
janiussyafiq:fix/gateway-status-publish-service
Open

fix: fall back to publishService for Gateway status addresses#461
janiussyafiq wants to merge 2 commits into
api7:masterfrom
janiussyafiq:fix/gateway-status-publish-service

Conversation

@janiussyafiq

@janiussyafiq janiussyafiq commented Aug 13, 2026

Copy link
Copy Markdown

Type of change:

  • Bugfix
  • Backport patches

What this PR does / why we need it:

Backport of apache/apisix-ingress-controller#2846.

Gateway.status.addresses was only populated from GatewayProxy.spec.statusAddress.
When spec.publishService is set instead, the Gateway published no address.
This makes Gateway.status.addresses fall back to the LoadBalancer address of spec.publishService when statusAddress is empty (statusAddress still wins when both are set), sharing the same publish-Service resolution helpers with the Ingress status path.

This repo was also missing upstream apache/apisix-ingress-controller#2732 (typed/deduplicated status addresses, Ingress ClusterIP status propagation), which the fix builds on, so it is cherry-picked here as the first commit.

Pre-submission checklist:

  • Did you explain what problem does this PR solve? Or what new features have been added?
  • Have you added corresponding test cases?
  • Have you modified the corresponding document?
  • Is this PR backward compatible?

Summary by CodeRabbit

  • New Features

    • Gateway and Ingress status addresses now automatically identify IP addresses versus hostnames.
    • Gateway statuses can use configured addresses or addresses from a LoadBalancer publish Service.
    • Ingress statuses support LoadBalancer and ClusterIP publish Services, including address inheritance.
    • Duplicate and outdated status addresses are removed automatically.
    • Documentation now covers supported address formats and publish Service behavior.
  • Bug Fixes

    • Improved handling of missing, changed, or unresolved publish Service addresses.

@CLAassistant

CLAassistant commented Aug 13, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The controllers now resolve Gateway and Ingress status addresses from configured values or publish Services. They classify IPs and hostnames, deduplicate entries, clear stale status, and support LoadBalancer and ClusterIP publish Services. Unit and end-to-end tests cover these flows.

Changes

Status address resolution

Layer / File(s) Summary
Address resolution foundation
internal/utils/k8s.go, internal/controller/utils.go, internal/controller/utils_publishservice_test.go, docs/en/latest/reference/example.md
The shared key parser now supports name and namespace/name references. Publish-Service helpers resolve Services, extract load-balancer addresses, and deduplicate status entries. Documentation covers address types and supported Service formats.
Gateway status resolution
internal/controller/gateway_controller.go, test/e2e/gatewayapi/gateway.go
Gateway reconciliation resolves configured or LoadBalancer publish-Service addresses, classifies IPs and hostnames, deduplicates entries, updates changed addresses, and clears stale status. End-to-end tests cover fallback and precedence behavior.
Ingress status resolution
internal/controller/ingress_controller.go, test/e2e/ingress/ingress.go
Ingress reconciliation resolves LoadBalancer and ClusterIP publish Services. ClusterIP services inherit addresses from other backend Ingresses. Tests cover address typing, deduplication, and propagation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Mergeability Score: 🟠 High · up to 0376f

The PR adds Gateway status-address fallback through publishService, but lookup failures currently abort Gateway reconciliation before dataplane configuration and status conditions are updated. A missing, invalid, or temporarily unavailable Service can therefore leave affected Gateways unconfigured, so merge should wait until status resolution is decoupled from configuration sync or the behavior is explicitly accepted.

Sequence Diagram(s)

sequenceDiagram
  participant IngressController
  participant PublishService
  participant BackendIngress
  participant IngressStatus
  IngressController->>PublishService: Resolve publishService
  PublishService-->>IngressController: Return Service type and status
  IngressController->>BackendIngress: Read backend Ingress status
  BackendIngress-->>IngressController: Return IP and hostname entries
  IngressController->>IngressStatus: Deduplicate and update status
Loading

Suggested reviewers: alinsran

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning The added IngressClass cleanup discards Delete errors with _ = s.K8sClient.Delete(...) and ignores non-nil Get errors, violating the blocking error-handling criterion. Handle cleanup errors explicitly, allowing only NotFound; report unexpected Get and Delete failures through the test framework.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: using publishService as a fallback for Gateway status addresses.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Check ✅ Passed No issues found in categories 1-7: the PR adds no secret logging/storage, mutating endpoints, TLS changes, or secret-reference handling; publishService reads are explicit and status-only.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🧹 Nitpick comments (6)
docs/en/latest/reference/example.md (1)

1259-1260: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the new publishService fallback in the Gateway tab.

This PR adds publishService support to the Gateway status path. The Gateway tab still documents statusAddress only. Add the publishService behavior for Gateway, including these two rules that the code implements:

  • statusAddress takes precedence when both fields are set.
  • Only a LoadBalancer publish Service produces Gateway status addresses. A ClusterIP publish Service produces none for Gateway.

I can draft the Gateway tab section if you want.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/en/latest/reference/example.md` around lines 1259 - 1260, Update the
Gateway documentation section near the statusAddress description to document
publishService fallback behavior: statusAddress takes precedence when both are
configured, and only a LoadBalancer publish Service generates Gateway status
addresses; a ClusterIP publish Service generates none.
internal/controller/utils_publishservice_test.go (1)

39-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add unit tests for the dedup helpers.

deduplicateLoadBalancerIngress and deduplicateGatewayStatusAddresses are pure functions in the same package. Unit tests pin the duplicate-removal result and the output order, so a later change to the ordering strategy is caught without an end-to-end run.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/utils_publishservice_test.go` around lines 39 - 41, Add
focused unit tests in the existing utility test suite for
deduplicateLoadBalancerIngress and deduplicateGatewayStatusAddresses. Cover
repeated addresses and assert both duplicate removal and preservation of the
helpers’ current first-seen output order.
test/e2e/gatewayapi/gateway.go (2)

855-914: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reuse the fixtures from the previous context instead of redefining them.

defaultGatewayClass, defaultGateway, and createGatewayClassAndGateway here are identical to the definitions on lines 670-694 and 729-739. Both copies are new in this PR, so they will drift. Move them to the Describe scope and let both contexts use them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/gatewayapi/gateway.go` around lines 855 - 914, Move the duplicate
defaultGatewayClass, defaultGateway, and createGatewayClassAndGateway
definitions into the enclosing Describe scope, reusing the existing fixtures
from the earlier context. Remove these local redefinitions so both contexts
reference the shared fixtures and remain consistent.

944-963: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a case for a publish Service that cannot be resolved.

The suite covers a LoadBalancer publish Service and the statusAddress precedence. It does not cover the two paths where resolution yields nothing:

  • publishService names a Service that does not exist.
  • publishService names a ClusterIP Service, which the Gateway path skips.

The first case matters most. resolveStatusAddresses returns an error there, and Reconcile currently returns before r.Provider.Update, so route configuration stops for that Gateway. A test that creates a Gateway with a dangling publishService and then asserts that traffic still routes would pin that behavior. See my comment on internal/controller/gateway_controller.go lines 197-203.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/gatewayapi/gateway.go` around lines 944 - 963, The gateway
end-to-end tests need a case for an unresolved publishService that verifies
reconciliation still updates the provider and traffic routes. Add a test
alongside the existing “falls back to publishService” case that creates a
GatewayProxy referencing a nonexistent Service, creates the Gateway resources,
and asserts successful routing; cover the ClusterIP publishService resolution
path as well if consistent with the existing helpers.
internal/controller/ingress_controller.go (1)

760-770: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Map the Service load-balancer entries directly instead of round-tripping through strings.

serviceLoadBalancerAddresses discards the IP and Hostname distinction that corev1.LoadBalancerIngress already carries. The code then rebuilds that distinction with net.ParseIP. A Service whose Hostname field holds a literal IP string is reclassified as IP.

Read the Service status fields directly in this branch. The net.ParseIP classification is still needed for the statusAddress branch, where the input is a plain string.

♻️ Direct mapping
 			case corev1.ServiceTypeLoadBalancer:
-				for _, addr := range serviceLoadBalancerAddresses(svc) {
-					lbIngress := networkingv1.IngressLoadBalancerIngress{}
-					if net.ParseIP(addr) != nil {
-						lbIngress.IP = addr
-					} else {
-						lbIngress.Hostname = addr
-					}
-					loadBalancerStatus.Ingress = append(loadBalancerStatus.Ingress, lbIngress)
-				}
+				for _, lb := range svc.Status.LoadBalancer.Ingress {
+					if lb.IP != "" {
+						loadBalancerStatus.Ingress = append(loadBalancerStatus.Ingress,
+							networkingv1.IngressLoadBalancerIngress{IP: lb.IP})
+					}
+					if lb.Hostname != "" {
+						loadBalancerStatus.Ingress = append(loadBalancerStatus.Ingress,
+							networkingv1.IngressLoadBalancerIngress{Hostname: lb.Hostname})
+					}
+				}

This also matches the shape of the ClusterIP branch below.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/ingress_controller.go` around lines 760 - 770, Update the
ServiceTypeLoadBalancer branch to iterate over svc.Status.LoadBalancer.Ingress
directly and map each corev1.LoadBalancerIngress IP to the networking ingress IP
field and Hostname to the hostname field. Remove use of
serviceLoadBalancerAddresses and net.ParseIP in this branch; retain net.ParseIP
for the statusAddress branch.
internal/controller/utils.go (1)

2125-2148: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

The dedup helpers sort the input, so they change the published address order.

Both helpers sort before slices.CompactFunc. The published order therefore becomes lexicographic instead of the configured or Service order. Two effects follow:

  • For statusAddress, the order in GatewayProxy.spec.statusAddress is not preserved.
  • For deduplicateLoadBalancerIngress, a hostname-only entry has an empty IP, so it sorts before any IP entry. A Service that reports [{IP: a}, {Hostname: b}] produces status [{Hostname: b}, {IP: a}].

An order-preserving dedup keeps the status stable and matches the Service and config order. The result is still deterministic, so the change-detection comparison stays correct.

♻️ Order-preserving dedup
-func deduplicateLoadBalancerIngress(entries []networkingv1.IngressLoadBalancerIngress) []networkingv1.IngressLoadBalancerIngress {
-	slices.SortFunc(entries, func(a, b networkingv1.IngressLoadBalancerIngress) int {
-		if c := strings.Compare(a.IP, b.IP); c != 0 {
-			return c
-		}
-		return strings.Compare(a.Hostname, b.Hostname)
-	})
-	return slices.CompactFunc(entries, func(a, b networkingv1.IngressLoadBalancerIngress) bool {
-		return a.IP == b.IP && a.Hostname == b.Hostname
-	})
-}
+func deduplicateLoadBalancerIngress(entries []networkingv1.IngressLoadBalancerIngress) []networkingv1.IngressLoadBalancerIngress {
+	if len(entries) == 0 {
+		return entries
+	}
+	type key struct{ ip, hostname string }
+	seen := make(map[key]struct{}, len(entries))
+	out := entries[:0]
+	for _, e := range entries {
+		k := key{e.IP, e.Hostname}
+		if _, ok := seen[k]; ok {
+			continue
+		}
+		seen[k] = struct{}{}
+		out = append(out, e)
+	}
+	return out
+}

Apply the same pattern to deduplicateGatewayStatusAddresses, keyed on Value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/utils.go` around lines 2125 - 2148, Update
deduplicateLoadBalancerIngress and deduplicateGatewayStatusAddresses to remove
duplicates without sorting their input slices, preserving the first-seen Service
or configured order while keeping the existing deduplication keys (IP plus
Hostname, and Value respectively). Use an order-preserving seen-key approach and
return the entries in their original order so downstream status comparison
remains deterministic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/en/latest/reference/example.md`:
- Around line 1312-1315: Update the publishService documentation to state that a
bare service name is resolved in the reconciled resource’s namespace—Ingress
namespace for the Ingress path and GatewayProxy namespace for the Gateway
path—rather than implying the Kubernetes default namespace. Also revise the
ClusterIP behavior to say the controller propagates both IP and hostname entries
from referencing Ingress resources.

In `@internal/controller/gateway_controller.go`:
- Around line 197-203: Update Reconcile around resolveStatusAddresses to capture
the error in an addressResolveErr variable, log it, and continue with an empty
status address list instead of returning early. Preserve dataplane
configuration, listener status, and condition updates, then return
addressResolveErr from the end of Reconcile after the status update has been
queued so lookup failures retry.

In `@test/e2e/ingress/ingress.go`:
- Around line 1503-1526: Make the source Ingress in the test explicitly target a
foreign IngressClass by adding an ingressClassName referencing a class managed
by another controller, and update the nearby comment to describe this isolation.
Preserve the existing sourceIngressYaml creation and ensure the selected class
cannot be claimed by this controller or any default class.

---

Nitpick comments:
In `@docs/en/latest/reference/example.md`:
- Around line 1259-1260: Update the Gateway documentation section near the
statusAddress description to document publishService fallback behavior:
statusAddress takes precedence when both are configured, and only a LoadBalancer
publish Service generates Gateway status addresses; a ClusterIP publish Service
generates none.

In `@internal/controller/ingress_controller.go`:
- Around line 760-770: Update the ServiceTypeLoadBalancer branch to iterate over
svc.Status.LoadBalancer.Ingress directly and map each corev1.LoadBalancerIngress
IP to the networking ingress IP field and Hostname to the hostname field. Remove
use of serviceLoadBalancerAddresses and net.ParseIP in this branch; retain
net.ParseIP for the statusAddress branch.

In `@internal/controller/utils_publishservice_test.go`:
- Around line 39-41: Add focused unit tests in the existing utility test suite
for deduplicateLoadBalancerIngress and deduplicateGatewayStatusAddresses. Cover
repeated addresses and assert both duplicate removal and preservation of the
helpers’ current first-seen output order.

In `@internal/controller/utils.go`:
- Around line 2125-2148: Update deduplicateLoadBalancerIngress and
deduplicateGatewayStatusAddresses to remove duplicates without sorting their
input slices, preserving the first-seen Service or configured order while
keeping the existing deduplication keys (IP plus Hostname, and Value
respectively). Use an order-preserving seen-key approach and return the entries
in their original order so downstream status comparison remains deterministic.

In `@test/e2e/gatewayapi/gateway.go`:
- Around line 855-914: Move the duplicate defaultGatewayClass, defaultGateway,
and createGatewayClassAndGateway definitions into the enclosing Describe scope,
reusing the existing fixtures from the earlier context. Remove these local
redefinitions so both contexts reference the shared fixtures and remain
consistent.
- Around line 944-963: The gateway end-to-end tests need a case for an
unresolved publishService that verifies reconciliation still updates the
provider and traffic routes. Add a test alongside the existing “falls back to
publishService” case that creates a GatewayProxy referencing a nonexistent
Service, creates the Gateway resources, and asserts successful routing; cover
the ClusterIP publishService resolution path as well if consistent with the
existing helpers.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a1691bf6-68e5-4839-bed8-b6b1c99397cb

📥 Commits

Reviewing files that changed from the base of the PR and between 42befec and 0376feb.

📒 Files selected for processing (8)
  • docs/en/latest/reference/example.md
  • internal/controller/gateway_controller.go
  • internal/controller/ingress_controller.go
  • internal/controller/utils.go
  • internal/controller/utils_publishservice_test.go
  • internal/utils/k8s.go
  • test/e2e/gatewayapi/gateway.go
  • test/e2e/ingress/ingress.go

Comment on lines +1312 to +1315
When using `publishService`, the controller will use the endpoint of this Service to update the status information of the Ingress resource. The format can be either `namespace/svc-name` or simply `svc-name` if the default namespace is correctly set.

- If the Service is of `LoadBalancer` type, the controller uses its external IP or hostname.
- If the Service is of `ClusterIP` type, the controller propagates the hostname from any Ingress resources that reference that Service.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct two statements about publishService.

  1. Line 1312 says a bare name works "if the default namespace is correctly set". The controller resolves a bare name against the namespace of the resource being reconciled: the Ingress namespace for the Ingress path and the GatewayProxy namespace for the Gateway path. The current wording can be read as the Kubernetes default namespace.
  2. Line 1315 says the controller propagates "the hostname" for a ClusterIP Service. The controller propagates both IP and hostname entries from the referencing Ingress resources.
📝 Suggested wording
-When using `publishService`, the controller will use the endpoint of this Service to update the status information of the Ingress resource. The format can be either `namespace/svc-name` or simply `svc-name` if the default namespace is correctly set.
+When using `publishService`, the controller will use the endpoint of this Service to update the status information of the Ingress resource. The format can be either `namespace/svc-name` or `svc-name`. If you omit the namespace, the controller resolves the name in the namespace of the Ingress resource.
 
 - If the Service is of `LoadBalancer` type, the controller uses its external IP or hostname.
-- If the Service is of `ClusterIP` type, the controller propagates the hostname from any Ingress resources that reference that Service.
+- If the Service is of `ClusterIP` type, the controller propagates the IP and hostname entries from the status of any other Ingress resource that references that Service.
📝 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
When using `publishService`, the controller will use the endpoint of this Service to update the status information of the Ingress resource. The format can be either `namespace/svc-name` or simply `svc-name` if the default namespace is correctly set.
- If the Service is of `LoadBalancer` type, the controller uses its external IP or hostname.
- If the Service is of `ClusterIP` type, the controller propagates the hostname from any Ingress resources that reference that Service.
When using `publishService`, the controller will use the endpoint of this Service to update the status information of the Ingress resource. The format can be either `namespace/svc-name` or `svc-name`. If you omit the namespace, the controller resolves the name in the namespace of the Ingress resource.
- If the Service is of `LoadBalancer` type, the controller uses its external IP or hostname.
- If the Service is of `ClusterIP` type, the controller propagates the IP and hostname entries from the status of any other Ingress resource that references that Service.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/en/latest/reference/example.md` around lines 1312 - 1315, Update the
publishService documentation to state that a bare service name is resolved in
the reconciled resource’s namespace—Ingress namespace for the Ingress path and
GatewayProxy namespace for the Gateway path—rather than implying the Kubernetes
default namespace. Also revise the ClusterIP behavior to say the controller
propagates both IP and hostname entries from referencing Ingress resources.

Comment on lines +197 to +203
statusAddresses, err := r.resolveStatusAddresses(ctx, &gatewayProxy)
if err != nil {
// fail the reconcile so a missing or invalid publish Service retries
// with backoff, mirroring the Ingress status path
r.Log.Error(err, "failed to resolve gateway status addresses", "gateway", req.NamespacedName)
return ctrl.Result{}, err
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

A publish Service lookup failure now blocks dataplane configuration for the Gateway.

The early return ctrl.Result{}, err happens before r.Provider.Update(ctx, tctx, gateway) on line 227, before getListenerStatus, and before the Accepted and Programmed conditions are written. A misspelled publishService, a Service that does not exist yet, or a transient API error therefore stops route and listener configuration for this Gateway, and the Gateway keeps no condition update. The failure mode is much wider than the status field the value feeds.

The Ingress path does not have this coupling: updateStatus in internal/controller/ingress_controller.go runs after translation, so a publish Service error there does not block config sync.

Resolve the addresses without aborting the reconcile. Record the failure, continue with no addresses, and requeue at the end so the lookup retries.

🛠️ Decouple status resolution from config sync
-		statusAddresses, err := r.resolveStatusAddresses(ctx, &gatewayProxy)
-		if err != nil {
-			// fail the reconcile so a missing or invalid publish Service retries
-			// with backoff, mirroring the Ingress status path
-			r.Log.Error(err, "failed to resolve gateway status addresses", "gateway", req.NamespacedName)
-			return ctrl.Result{}, err
-		}
-		for _, addr := range statusAddresses {
+		statusAddresses, resolveErr := r.resolveStatusAddresses(ctx, &gatewayProxy)
+		if resolveErr != nil {
+			// Do not abort the reconcile: the provider update and the listener
+			// statuses below must not depend on the publish Service lookup.
+			r.Log.Error(resolveErr, "failed to resolve gateway status addresses", "gateway", req.NamespacedName)
+			addressResolveErr = resolveErr
+		}
+		for _, addr := range statusAddresses {

Declare var addressResolveErr error next to var addrs []gatewayv1.GatewayStatusAddress on line 186, and return it from the end of Reconcile so controller-runtime retries with backoff after the status update is queued.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/gateway_controller.go` around lines 197 - 203, Update
Reconcile around resolveStatusAddresses to capture the error in an
addressResolveErr variable, log it, and continue with an empty status address
list instead of returning early. Preserve dataplane configuration, listener
status, and condition updates, then return addressResolveErr from the end of
Reconcile after the status update has been queued so lookup failures retry.

Comment on lines +1503 to +1526
By("create source Ingress referencing the ClusterIP service")
sourceIngressName := s.Namespace() + "-source"
// The source Ingress intentionally has no ingressClassName — it simulates a
// cloud ALB Ingress managed by a different controller. Without ingressClassName
// our controller will not reconcile it and will not overwrite its status.
sourceIngressYaml := fmt.Sprintf(`
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: %s
spec:
rules:
- host: clusterip.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: %s
port:
number: 80
`, sourceIngressName, clusterIPSvcName)
Expect(s.CreateResourceFromStringWithNamespace(sourceIngressYaml, s.Namespace())).NotTo(HaveOccurred(), "creating source Ingress")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

The source Ingress can be reconciled by this controller, which makes the test flaky.

The comment states that the controller will not reconcile the source Ingress because it has no ingressClassName. That holds only when no default IngressClass for this controller exists. This same file contains Context("IngressClass Selection", Serial), which creates an IngressClass annotated with ingressclass.kubernetes.io/is-default-class: "true" for this controller precisely so that class-less Ingress resources are reconciled.

If a default IngressClass for this controller is present when this test runs, the controller reconciles the source Ingress and overwrites the hostname that the test writes on line 1536. The assertion on line 1577 then fails.

Make the isolation explicit. Two options:

  • Set an ingressClassName on the source Ingress that points at a foreign controller, so no default class can ever claim it.
  • Mark this It (or the context) Serial and assert that no default IngressClass for this controller exists.

The first option is more robust because it does not depend on ordering.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/ingress/ingress.go` around lines 1503 - 1526, Make the source
Ingress in the test explicitly target a foreign IngressClass by adding an
ingressClassName referencing a class managed by another controller, and update
the nearby comment to describe this isolation. Preserve the existing
sourceIngressYaml creation and ensure the selected class cannot be claimed by
this controller or any default class.

… controller files (backport apache/apisix-ingress-controller#2732)

Cherry-picked unmodified from apache/apisix-ingress-controller#2732
(originally apache/apisix-ingress-controller#2730), authored upstream by
Hemanth Chebrolu. This fork was missing it, and
apache/apisix-ingress-controller#2846 builds on it.
Gateway.status.addresses was only populated from GatewayProxy.spec.statusAddress.
When spec.publishService is set instead, the Gateway published no address, even
though the Ingress status path already falls back to the publish Service's
LoadBalancer address.

Extract shared publish-Service resolution helpers, make Gateway.status.addresses
fall back to the LoadBalancer address of spec.publishService when statusAddress
is empty (statusAddress still wins when both are set), and refactor the Ingress
LoadBalancer status branch onto the same helpers.
@janiussyafiq
janiussyafiq force-pushed the fix/gateway-status-publish-service branch from 0376feb to 8c0c339 Compare August 13, 2026 15:59
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