Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion docs/en/latest/reference/example.md
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,8 @@ spec:
- 10.24.87.13
```

Each entry in `statusAddress` can be an IP address or a hostname. The controller automatically sets the address type on the Gateway status — `IPAddress` for valid IPs and `Hostname` for everything else.

</TabItem>

<TabItem value="ingress">
Expand All @@ -1116,6 +1118,8 @@ spec:
- 10.24.87.13
```

Each entry in `statusAddress` can be an IP address or a hostname. The controller automatically sets the `IP` field for valid IPs and the `Hostname` field for everything else in the Ingress load balancer status.

To configure the `publishService`:

```yaml
Expand All @@ -1133,7 +1137,10 @@ spec:
publishService: apisix-gateway
```

When using `publishService`, make sure your gateway Service is of `LoadBalancer` type the address can be populated. 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 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.

</TabItem>

Expand Down
6 changes: 6 additions & 0 deletions internal/controller/gateway_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"context"
"errors"
"fmt"
"net"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -183,8 +184,13 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
if addr == "" {
continue
}
addrType := gatewayv1.IPAddressType
if net.ParseIP(addr) == nil {
addrType = gatewayv1.HostnameAddressType
}
addrs = append(addrs,
gatewayv1.GatewayStatusAddress{
Type: &addrType,
Value: addr,
},
)
Expand Down
41 changes: 37 additions & 4 deletions internal/controller/ingress_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package controller
import (
"context"
"fmt"
"net"
"reflect"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -694,9 +695,13 @@ func (r *IngressReconciler) updateStatus(ctx context.Context, tctx *provider.Tra
if addr == "" {
continue
}
loadBalancerStatus.Ingress = append(loadBalancerStatus.Ingress, networkingv1.IngressLoadBalancerIngress{
IP: addr,
})
ingress := networkingv1.IngressLoadBalancerIngress{}
if net.ParseIP(addr) != nil {
ingress.IP = addr
} else {
ingress.Hostname = addr
}
loadBalancerStatus.Ingress = append(loadBalancerStatus.Ingress, ingress)
Comment thread
hemanthjk-c marked this conversation as resolved.
Outdated
}
} else {
// 2. if the IngressStatusAddress is not configured, try to use the PublishService
Expand All @@ -717,7 +722,8 @@ func (r *IngressReconciler) updateStatus(ctx context.Context, tctx *provider.Tra
return fmt.Errorf("failed to get publish service %s: %w", publishService, err)
}

if svc.Spec.Type == corev1.ServiceTypeLoadBalancer {
switch svc.Spec.Type {
case corev1.ServiceTypeLoadBalancer:
// get the LoadBalancer IP and Hostname of the service
for _, ip := range svc.Status.LoadBalancer.Ingress {
if ip.IP != "" {
Expand All @@ -731,6 +737,33 @@ func (r *IngressReconciler) updateStatus(ctx context.Context, tctx *provider.Tra
})
}
}
case corev1.ServiceTypeClusterIP:
// for ClusterIP services, find Ingresses that reference this service
// and collect hostnames from their load balancer status
// this is when we run the apisix in ClusterIP mode and enable Ingress
// when deploying in Cloud environments.
ingressList := &networkingv1.IngressList{}
if err := r.List(ctx, ingressList, client.MatchingFields{
indexer.ServiceIndexRef: indexer.GenIndexKey(namespace, name),
}); err != nil {
return fmt.Errorf("failed to list ingresses for ClusterIP service %s/%s: %w", namespace, name, err)
}
for _, ing := range ingressList.Items {
// Skip the current Ingress being reconciled to avoid a
// self-referential loop: updating its own status would trigger
// a new reconcile, which would collect its own (just-written)
// hostname again and potentially repeat indefinitely.
if ing.Namespace == ingress.Namespace && ing.Name == ingress.Name {
continue
}
for _, lb := range ing.Status.LoadBalancer.Ingress {
if lb.Hostname != "" {
loadBalancerStatus.Ingress = append(loadBalancerStatus.Ingress, networkingv1.IngressLoadBalancerIngress{
Hostname: lb.Hostname,
})
}
}
Comment thread
hemanthjk-c marked this conversation as resolved.
}
}
}
}
Expand Down
107 changes: 107 additions & 0 deletions test/e2e/gatewayapi/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,4 +539,111 @@ spec:
Expect(string(getListener.SupportedKinds[0].Kind)).To(Equal("UDPRoute"), "udp listener supported kind content")
})
})

Context("Gateway Status Address", func() {
var gatewayProxyWithStatusAddressYaml = `
apiVersion: apisix.apache.org/v1alpha1
kind: GatewayProxy
metadata:
name: apisix-proxy-config
namespace: %s
spec:
statusAddress:
- %s
provider:
type: ControlPlane
controlPlane:
endpoints:
- %s
auth:
type: AdminKey
adminKey:
value: "%s"
`
var defaultGatewayClass = `
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: %s
spec:
controllerName: "%s"
`
var defaultGateway = `
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: %s
spec:
gatewayClassName: %s
listeners:
- name: http
protocol: HTTP
port: 80
infrastructure:
parametersRef:
group: apisix.apache.org
kind: GatewayProxy
name: apisix-proxy-config
`
getGatewayAddresses := func(gatewayName string) ([]gatewayv1.GatewayStatusAddress, error) {
var gateway gatewayv1.Gateway
if err := s.GetKubeClient().Get(context.Background(), k8stypes.NamespacedName{
Name: gatewayName,
Namespace: s.Namespace(),
}, &gateway); err != nil {
return nil, err
}
return gateway.Status.Addresses, nil
}

checkGatewayStatusAddressType := func(addrValue string, expectedType gatewayv1.AddressType) {
gatewayClassName := s.Namespace()

By("create GatewayProxy with statusAddress")
gatewayProxy := fmt.Sprintf(gatewayProxyWithStatusAddressYaml,
s.Namespace(), addrValue, s.Deployer.GetAdminEndpoint(), s.AdminKey())
Expect(s.CreateResourceFromString(gatewayProxy)).NotTo(HaveOccurred(), "creating GatewayProxy")

By("create GatewayClass")
Expect(s.CreateResourceFromStringWithNamespace(
fmt.Sprintf(defaultGatewayClass, gatewayClassName, s.GetControllerName()), ""),
).NotTo(HaveOccurred(), "creating GatewayClass")

By("create Gateway")
gatewayName := s.Namespace()
Expect(s.CreateResourceFromStringWithNamespace(
fmt.Sprintf(defaultGateway, gatewayName, gatewayClassName), s.Namespace()),
).NotTo(HaveOccurred(), "creating Gateway")

By("check Gateway status address type")
s.RetryAssertion(func() error {
addrs, err := getGatewayAddresses(gatewayName)
if err != nil {
return err
}
if len(addrs) == 0 {
return fmt.Errorf("expected at least 1 status address, got 0")
}
addr := addrs[0]
if addr.Value != addrValue {
return fmt.Errorf("expected address value %s, got %s", addrValue, addr.Value)
}
if addr.Type == nil {
return fmt.Errorf("expected address type to be set, got nil")
}
if *addr.Type != expectedType {
return fmt.Errorf("expected address type %s, got %s", expectedType, *addr.Type)
}
return nil
}).ShouldNot(HaveOccurred(), "check Gateway status address type")
}

It("sets IPAddress type when statusAddress is an IP", func() {
checkGatewayStatusAddressType("192.168.1.100", gatewayv1.IPAddressType)
})

It("sets Hostname type when statusAddress is a hostname", func() {
checkGatewayStatusAddressType("mygateway.example.com", gatewayv1.HostnameAddressType)
})
})
})
Loading
Loading