Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
31 changes: 28 additions & 3 deletions internal/controller/ingress_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import (
"context"
"fmt"
"net"
"reflect"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -694,9 +695,13 @@
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,7 @@
return fmt.Errorf("failed to get publish service %s: %w", publishService, err)
}

if svc.Spec.Type == corev1.ServiceTypeLoadBalancer {

Check failure on line 725 in internal/controller/ingress_controller.go

View workflow job for this annotation

GitHub Actions / lint

QF1003: could use tagged switch on svc.Spec.Type (staticcheck)
// get the LoadBalancer IP and Hostname of the service
for _, ip := range svc.Status.LoadBalancer.Ingress {
if ip.IP != "" {
Expand All @@ -731,6 +736,26 @@
})
}
}
} else if svc.Spec.Type == corev1.ServiceTypeClusterIP {
Comment thread
hemanthjk-c marked this conversation as resolved.
Outdated
// 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 {
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
143 changes: 143 additions & 0 deletions test/e2e/gatewayapi/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,4 +539,147 @@ 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
}

It("sets IPAddress type when statusAddress is an IP", func() {
ipAddr := "192.168.1.100"
gatewayClassName := s.Namespace()

By("create GatewayProxy with IP statusAddress")
gatewayProxy := fmt.Sprintf(gatewayProxyWithStatusAddressYaml,
s.Namespace(), ipAddr, 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 addresses has IPAddress 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 != ipAddr {
return fmt.Errorf("expected address value %s, got %s", ipAddr, addr.Value)
}
if addr.Type == nil {
return fmt.Errorf("expected address type to be set, got nil")
}
if *addr.Type != gatewayv1.IPAddressType {
return fmt.Errorf("expected address type %s, got %s", gatewayv1.IPAddressType, *addr.Type)
}
return nil
}).ShouldNot(HaveOccurred(), "check Gateway IPAddress status address type")
})

It("sets Hostname type when statusAddress is a hostname", func() {
hostname := "mygateway.example.com"
gatewayClassName := s.Namespace()

By("create GatewayProxy with hostname statusAddress")
gatewayProxy := fmt.Sprintf(gatewayProxyWithStatusAddressYaml,
s.Namespace(), hostname, 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 addresses has Hostname 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 != hostname {
return fmt.Errorf("expected address value %s, got %s", hostname, addr.Value)
}
if addr.Type == nil {
return fmt.Errorf("expected address type to be set, got nil")
}
if *addr.Type != gatewayv1.HostnameAddressType {
return fmt.Errorf("expected address type %s, got %s", gatewayv1.HostnameAddressType, *addr.Type)
}
return nil
}).ShouldNot(HaveOccurred(), "check Gateway Hostname status address type")
})
})
})
Loading
Loading