@@ -62,83 +62,64 @@ type NodeAddresses struct {
6262// service and deployment or daemonset state.
6363func UpdateGatewayStatusProgrammedCondition (gw * gwapiv1.Gateway , svc * corev1.Service , envoyObj client.Object , nodeAddresses NodeAddresses ) {
6464 var addresses , hostnames []string
65+ var addressNotUsable bool
66+
67+ // When the Service doesn't exist yet but spec.Addresses is set, populate
68+ // status addresses from spec so they are visible as soon as conditions are
69+ // reconciled. The Programmed condition will indicate AddressNotUsable until
70+ // the backing Service is created.
71+ if svc == nil && len (gw .Spec .Addresses ) > 0 {
72+ addresses , hostnames = specAddressesToSlices (gw .Spec .Addresses )
73+ gw .Status .Addresses = buildGatewayAddresses (addresses , hostnames )
74+ gw .Status .Conditions = MergeConditions (gw .Status .Conditions ,
75+ newCondition (string (gwapiv1 .GatewayConditionProgrammed ), metav1 .ConditionFalse , string (gwapiv1 .GatewayReasonAddressNotUsable ),
76+ messageAddressNotUsable , gw .Generation ))
77+ return
78+ }
79+
6580 // Update the status addresses field.
6681 if svc != nil {
6782 // If the addresses is explicitly set in the Gateway spec by the user, use it
6883 // to populate the Status
6984 if len (gw .Spec .Addresses ) > 0 {
70- // Make sure the addresses have been populated into ExternalIPs/ClusterIPs
71- // and use that value
7285 if len (svc .Spec .ExternalIPs ) > 0 {
7386 addresses = append (addresses , svc .Spec .ExternalIPs ... )
74- } else if len (svc .Spec .ClusterIPs ) > 0 {
75- // Filter out "None" values which represent headless services
76- for _ , ip := range svc .Spec .ClusterIPs {
77- if ip != "" && ip != "None" {
78- addresses = append (addresses , ip )
79- }
80- }
87+ } else {
88+ addresses = filterNoneClusterIPs (svc .Spec .ClusterIPs )
8189 }
90+ // Hostname addresses in spec cannot be assigned via ExternalIPs/LB;
91+ // report AddressNotUsable if any are present.
92+ addressNotUsable = hasHostnameAddress (gw .Spec .Addresses )
8293 } else {
83- if svc .Spec .Type == corev1 .ServiceTypeLoadBalancer {
84- for i := range svc .Status .LoadBalancer .Ingress {
85- switch {
86- case len (svc .Status .LoadBalancer .Ingress [i ].IP ) > 0 :
87- addresses = append (addresses , svc .Status .LoadBalancer .Ingress [i ].IP )
88- case len (svc .Status .LoadBalancer .Ingress [i ].Hostname ) > 0 :
89- // Remove when the following supports the hostname address type:
90- // https://github.com/kubernetes-sigs/gateway-api/blob/v0.5.0/conformance/utils/kubernetes/helpers.go#L201-L207
91- if svc .Status .LoadBalancer .Ingress [i ].Hostname == "localhost" {
92- addresses = append (addresses , "127.0.0.1" )
93- }
94- hostnames = append (hostnames , svc .Status .LoadBalancer .Ingress [i ].Hostname )
95- }
96- }
97- }
98-
99- if svc .Spec .Type == corev1 .ServiceTypeClusterIP {
100- for i := range svc .Spec .ClusterIPs {
101- // Filter out "None" values which represent headless services
102- if svc .Spec .ClusterIPs [i ] != "" && svc .Spec .ClusterIPs [i ] != "None" {
103- addresses = append (addresses , svc .Spec .ClusterIPs [i ])
104- }
105- }
106- }
107-
108- if svc .Spec .Type == corev1 .ServiceTypeNodePort {
109- var relevantAddresses []string
94+ switch svc .Spec .Type {
95+ case corev1 .ServiceTypeLoadBalancer :
96+ addresses , hostnames = collectLoadBalancerAddresses (svc )
97+ case corev1 .ServiceTypeClusterIP :
98+ addresses = filterNoneClusterIPs (svc .Spec .ClusterIPs )
99+ case corev1 .ServiceTypeNodePort :
110100 if slices .Contains (svc .Spec .IPFamilies , corev1 .IPv4Protocol ) {
111- relevantAddresses = append (relevantAddresses , nodeAddresses .IPv4 ... )
101+ addresses = append (addresses , nodeAddresses .IPv4 ... )
112102 }
113103 if slices .Contains (svc .Spec .IPFamilies , corev1 .IPv6Protocol ) {
114- relevantAddresses = append (relevantAddresses , nodeAddresses .IPv6 ... )
104+ addresses = append (addresses , nodeAddresses .IPv6 ... )
115105 }
116- addresses = relevantAddresses
117106 }
118107 }
119108
120- gwAddresses := make ([]gwapiv1.GatewayStatusAddress , 0 , len (addresses )+ len (hostnames ))
121- for i := range addresses {
122- addr := gwapiv1.GatewayStatusAddress {
123- Type : new (gwapiv1.IPAddressType ),
124- Value : addresses [i ],
125- }
126- gwAddresses = append (gwAddresses , addr )
127- }
128-
129- for i := range hostnames {
130- addr := gwapiv1.GatewayStatusAddress {
131- Type : new (gwapiv1.HostnameAddressType ),
132- Value : hostnames [i ],
133- }
134- gwAddresses = append (gwAddresses , addr )
135- }
136-
137- gw .Status .Addresses = gwAddresses
109+ gw .Status .Addresses = buildGatewayAddresses (addresses , hostnames )
138110 } else {
139111 gw .Status .Addresses = nil
140112 }
141113
114+ // If any requested address cannot be used (e.g. hostname addresses),
115+ // report AddressNotUsable before checking deployment readiness.
116+ if addressNotUsable {
117+ gw .Status .Conditions = MergeConditions (gw .Status .Conditions ,
118+ newCondition (string (gwapiv1 .GatewayConditionProgrammed ), metav1 .ConditionFalse , string (gwapiv1 .GatewayReasonAddressNotUsable ),
119+ messageAddressNotUsable , gw .Generation ))
120+ return
121+ }
122+
142123 // Update the programmed condition.
143124 updateGatewayProgrammedCondition (gw , envoyObj )
144125}
@@ -159,6 +140,7 @@ func SetGatewayListenerStatusCondition(gateway *gwapiv1.Gateway, listenerStatusI
159140
160141const (
161142 messageAddressNotAssigned = "No addresses have been assigned to the Gateway"
143+ messageAddressNotUsable = "One or more Gateway addresses cannot be used"
162144 messageFmtTooManyAddresses = "Too many addresses (%d) have been assigned to the Gateway; only the first 16 are included in the status."
163145 messageNoResources = "Envoy replicas unavailable"
164146 messageFmtProgrammed = "Address assigned to the Gateway, %d/%d envoy replicas available"
@@ -217,3 +199,64 @@ func GetGatewayListenerStatusConditions(gateway *gwapiv1.Gateway, listenerStatus
217199 }
218200 return gateway .Status .Listeners [listenerStatusIdx ].Conditions
219201}
202+
203+ // hasHostnameAddress returns true if any address in the slice uses HostnameAddressType.
204+ func hasHostnameAddress (addrs []gwapiv1.GatewaySpecAddress ) bool {
205+ for i := range addrs {
206+ if addrs [i ].Type != nil && * addrs [i ].Type == gwapiv1 .HostnameAddressType {
207+ return true
208+ }
209+ }
210+ return false
211+ }
212+
213+ func specAddressesToSlices (addrs []gwapiv1.GatewaySpecAddress ) (ips , hostnames []string ) {
214+ for i := range addrs {
215+ if addrs [i ].Type != nil && * addrs [i ].Type == gwapiv1 .HostnameAddressType {
216+ hostnames = append (hostnames , addrs [i ].Value )
217+ } else {
218+ ips = append (ips , addrs [i ].Value )
219+ }
220+ }
221+ return ips , hostnames
222+ }
223+
224+ func collectLoadBalancerAddresses (svc * corev1.Service ) (addresses , hostnames []string ) {
225+ for i := range svc .Status .LoadBalancer .Ingress {
226+ switch {
227+ case len (svc .Status .LoadBalancer .Ingress [i ].IP ) > 0 :
228+ addresses = append (addresses , svc .Status .LoadBalancer .Ingress [i ].IP )
229+ case len (svc .Status .LoadBalancer .Ingress [i ].Hostname ) > 0 :
230+ hostnames = append (hostnames , svc .Status .LoadBalancer .Ingress [i ].Hostname )
231+ }
232+ }
233+ return addresses , hostnames
234+ }
235+
236+ func filterNoneClusterIPs (clusterIPs []string ) []string {
237+ var out []string
238+ for _ , ip := range clusterIPs {
239+ if ip != "" && ip != "None" {
240+ out = append (out , ip )
241+ }
242+ }
243+ return out
244+ }
245+
246+ // buildGatewayAddresses creates a status address slice from IP and hostname slices.
247+ func buildGatewayAddresses (ips , hostnames []string ) []gwapiv1.GatewayStatusAddress {
248+ out := make ([]gwapiv1.GatewayStatusAddress , 0 , len (ips )+ len (hostnames ))
249+ for i := range ips {
250+ out = append (out , gwapiv1.GatewayStatusAddress {
251+ Type : new (gwapiv1.IPAddressType ),
252+ Value : ips [i ],
253+ })
254+ }
255+ for i := range hostnames {
256+ out = append (out , gwapiv1.GatewayStatusAddress {
257+ Type : new (gwapiv1.HostnameAddressType ),
258+ Value : hostnames [i ],
259+ })
260+ }
261+ return out
262+ }
0 commit comments