Skip to content

Commit be51e36

Browse files
authored
Set Accepted condition type on Gateway status (#633)
* Set Accepted condition type on Gateway status We always report the Accepted condition on the Gateway status. Its value depends on the validity of its spec and its Listeners. If the Gateway spec is invalid, we report the Accepted/False/Invalid Gateway condition but do not report Listener statuses. In all other cases, the Listener statuses will be reported. For routes that reference an invalid Gateway, we set the Route condition Accepted/False/InvalidGateway. * Reduce the complexity of bindRouteToListeners The gocyclo linter complained about the complexity of the bindRouteToListeners function. This commit refactors this function to reduce its complexity.
1 parent e4cb606 commit be51e36

18 files changed

+830
-548
lines changed

docs/gateway-api-compatibility.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,13 @@ Fields:
7171
* `name` - supported.
7272
* `supportedKinds` - not supported.
7373
* `attachedRoutes` - supported.
74-
* `conditions` - partially supported.
74+
* `conditions` - Supported (Condition/Status/Reason):
75+
* `Accepted/True/Accepted`
76+
* `Accepted/True/ListenersNotValid`
77+
* `Accepted/False/Invalid`
78+
* `Accepted/False/ListenersNotValid`
79+
* `Accepted/False/UnsupportedValue`: Custom reason for when a value of a field in a Gateway is invalid or not supported.
80+
* `Accepted/False/GatewayConflict`: Custom reason for when the Gateway is ignored due to a conflicting Gateway. NKG only supports a single Gateway.
7581

7682
### HTTPRoute
7783

internal/state/change_processor_test.go

+158-178
Large diffs are not rendered by default.

internal/state/conditions/conditions.go

+161-78
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,32 @@ import (
88
)
99

1010
const (
11-
// RouteReasonInvalidListener is used with the "Accepted" condition when the Route references an invalid listener.
12-
RouteReasonInvalidListener v1beta1.RouteConditionReason = "InvalidListener"
13-
1411
// ListenerReasonUnsupportedValue is used with the "Accepted" condition when a value of a field in a Listener
1512
// is invalid or not supported.
1613
ListenerReasonUnsupportedValue v1beta1.ListenerConditionReason = "UnsupportedValue"
1714

18-
// ListenerReasonNoValidGatewayClass is used with the "Accepted" condition when there is no valid GatewayClass
19-
// in the cluster.
20-
ListenerReasonNoValidGatewayClass v1beta1.ListenerConditionReason = "NoValidGatewayClass"
21-
2215
// RouteReasonBackendRefUnsupportedValue is used with the "ResolvedRefs" condition when one of the
2316
// Route rules has a backendRef with an unsupported value.
2417
RouteReasonBackendRefUnsupportedValue = "UnsupportedValue"
18+
19+
// RouteReasonInvalidGateway is used with the "Accepted" (false) condition when the Gateway the Route
20+
// references is invalid.
21+
RouteReasonInvalidGateway = "InvalidGateway"
22+
23+
// RouteReasonInvalidListener is used with the "Accepted" condition when the Route references an invalid listener.
24+
RouteReasonInvalidListener v1beta1.RouteConditionReason = "InvalidListener"
25+
26+
// GatewayReasonGatewayConflict indicates there are multiple Gateway resources to choose from,
27+
// and we ignored the resource in question and picked another Gateway as the winner.
28+
// This reason is used with GatewayConditionAccepted (false).
29+
GatewayReasonGatewayConflict v1beta1.GatewayConditionReason = "GatewayConflict"
30+
31+
// GatewayMessageGatewayConflict is message that describes GatewayReasonGatewayConflict.
32+
GatewayMessageGatewayConflict = "The resource is ignored due to a conflicting Gateway resource"
33+
34+
// GatewayReasonUnsupportedValue is used with GatewayConditionAccepted (false) when a value of a field in a Gateway
35+
// is invalid or not supported.
36+
GatewayReasonUnsupportedValue v1beta1.GatewayConditionReason = "UnsupportedValue"
2537
)
2638

2739
// Condition defines a condition to be reported in the status of resources.
@@ -64,6 +76,16 @@ func DeduplicateConditions(conds []Condition) []Condition {
6476
return result
6577
}
6678

79+
// NewTODO returns a Condition that can be used as a placeholder for a condition that is not yet implemented.
80+
func NewTODO(msg string) Condition {
81+
return Condition{
82+
Type: "TODO",
83+
Status: metav1.ConditionTrue,
84+
Reason: "TODO",
85+
Message: fmt.Sprintf("The condition for this has not been implemented yet: %s", msg),
86+
}
87+
}
88+
6789
// NewDefaultRouteConditions returns the default conditions that must be present in the status of an HTTPRoute.
6890
func NewDefaultRouteConditions() []Condition {
6991
return []Condition{
@@ -103,16 +125,6 @@ func NewRouteUnsupportedValue(msg string) Condition {
103125
}
104126
}
105127

106-
// NewTODO returns a Condition that can be used as a placeholder for a condition that is not yet implemented.
107-
func NewTODO(msg string) Condition {
108-
return Condition{
109-
Type: "TODO",
110-
Status: metav1.ConditionTrue,
111-
Reason: "TODO",
112-
Message: fmt.Sprintf("The condition for this has not been implemented yet: %s", msg),
113-
}
114-
}
115-
116128
// NewRouteInvalidListener returns a Condition that indicates that the HTTPRoute is not accepted because of an
117129
// invalid listener.
118130
func NewRouteInvalidListener() Condition {
@@ -124,6 +136,80 @@ func NewRouteInvalidListener() Condition {
124136
}
125137
}
126138

139+
// NewRouteResolvedRefs returns a Condition that indicates that all the references on the Route are resolved.
140+
func NewRouteResolvedRefs() Condition {
141+
return Condition{
142+
Type: string(v1beta1.RouteConditionResolvedRefs),
143+
Status: metav1.ConditionTrue,
144+
Reason: string(v1beta1.RouteReasonResolvedRefs),
145+
Message: "All references are resolved",
146+
}
147+
}
148+
149+
// NewRouteBackendRefInvalidKind returns a Condition that indicates that the Route has a backendRef with an
150+
// invalid kind.
151+
func NewRouteBackendRefInvalidKind(msg string) Condition {
152+
return Condition{
153+
Type: string(v1beta1.RouteConditionResolvedRefs),
154+
Status: metav1.ConditionFalse,
155+
Reason: string(v1beta1.RouteReasonInvalidKind),
156+
Message: msg,
157+
}
158+
}
159+
160+
// NewRouteBackendRefRefNotPermitted returns a Condition that indicates that the Route has a backendRef that
161+
// is not permitted.
162+
func NewRouteBackendRefRefNotPermitted(msg string) Condition {
163+
return Condition{
164+
Type: string(v1beta1.RouteConditionResolvedRefs),
165+
Status: metav1.ConditionFalse,
166+
Reason: string(v1beta1.RouteReasonRefNotPermitted),
167+
Message: msg,
168+
}
169+
}
170+
171+
// NewRouteBackendRefRefBackendNotFound returns a Condition that indicates that the Route has a backendRef that
172+
// points to non-existing backend.
173+
func NewRouteBackendRefRefBackendNotFound(msg string) Condition {
174+
return Condition{
175+
Type: string(v1beta1.RouteConditionResolvedRefs),
176+
Status: metav1.ConditionFalse,
177+
Reason: string(v1beta1.RouteReasonBackendNotFound),
178+
Message: msg,
179+
}
180+
}
181+
182+
// NewRouteBackendRefUnsupportedValue returns a Condition that indicates that the Route has a backendRef with
183+
// an unsupported value.
184+
func NewRouteBackendRefUnsupportedValue(msg string) Condition {
185+
return Condition{
186+
Type: string(v1beta1.RouteConditionResolvedRefs),
187+
Status: metav1.ConditionFalse,
188+
Reason: RouteReasonBackendRefUnsupportedValue,
189+
Message: msg,
190+
}
191+
}
192+
193+
// NewRouteInvalidGateway returns a Condition that indicates that the Route is not Accepted because the Gateway it
194+
// references is invalid.
195+
func NewRouteInvalidGateway() Condition {
196+
return Condition{
197+
Type: string(v1beta1.RouteConditionAccepted),
198+
Status: metav1.ConditionFalse,
199+
Reason: RouteReasonInvalidGateway,
200+
Message: "Gateway is invalid",
201+
}
202+
}
203+
204+
// NewDefaultListenerConditions returns the default Conditions that must be present in the status of a Listener.
205+
func NewDefaultListenerConditions() []Condition {
206+
return []Condition{
207+
NewListenerAccepted(),
208+
NewListenerResolvedRefs(),
209+
NewListenerNoConflicts(),
210+
}
211+
}
212+
127213
// NewListenerPortUnavailable returns a Condition that indicates a port is unavailable in a Listener.
128214
func NewListenerPortUnavailable(msg string) Condition {
129215
return Condition{
@@ -164,15 +250,6 @@ func NewListenerNoConflicts() Condition {
164250
}
165251
}
166252

167-
// NewDefaultListenerConditions returns the default Conditions that must be present in the status of a Listener.
168-
func NewDefaultListenerConditions() []Condition {
169-
return []Condition{
170-
NewListenerAccepted(),
171-
NewListenerResolvedRefs(),
172-
NewListenerNoConflicts(),
173-
}
174-
}
175-
176253
// NewListenerUnsupportedValue returns a Condition that indicates that a field of a Listener has an unsupported value.
177254
// Unsupported means that the value is not supported by the implementation or invalid.
178255
func NewListenerUnsupportedValue(msg string) Condition {
@@ -230,89 +307,95 @@ func NewListenerUnsupportedProtocol(msg string) Condition {
230307
}
231308
}
232309

233-
// NewListenerNoValidGatewayClass returns a Condition that indicates that the Listener is not accepted because
234-
// there is no valid GatewayClass.
235-
func NewListenerNoValidGatewayClass(msg string) Condition {
236-
return Condition{
237-
Type: string(v1beta1.ListenerConditionAccepted),
238-
Status: metav1.ConditionFalse,
239-
Reason: string(ListenerReasonNoValidGatewayClass),
240-
Message: msg,
310+
// NewDefaultGatewayClassConditions returns the default Conditions that must be present in the status of a GatewayClass.
311+
func NewDefaultGatewayClassConditions() []Condition {
312+
return []Condition{
313+
{
314+
Type: string(v1beta1.GatewayClassConditionStatusAccepted),
315+
Status: metav1.ConditionTrue,
316+
Reason: string(v1beta1.GatewayClassReasonAccepted),
317+
Message: "GatewayClass is accepted",
318+
},
241319
}
242320
}
243321

244-
// NewRouteBackendRefInvalidKind returns a Condition that indicates that the Route has a backendRef with an
245-
// invalid kind.
246-
func NewRouteBackendRefInvalidKind(msg string) Condition {
322+
// NewGatewayClassInvalidParameters returns a Condition that indicates that the GatewayClass has invalid parameters.
323+
func NewGatewayClassInvalidParameters(msg string) Condition {
247324
return Condition{
248-
Type: string(v1beta1.RouteConditionResolvedRefs),
325+
Type: string(v1beta1.GatewayClassConditionStatusAccepted),
249326
Status: metav1.ConditionFalse,
250-
Reason: string(v1beta1.RouteReasonInvalidKind),
327+
Reason: string(v1beta1.GatewayClassReasonInvalidParameters),
251328
Message: msg,
252329
}
253330
}
254331

255-
// NewRouteBackendRefRefNotPermitted returns a Condition that indicates that the Route has a backendRef that
256-
// is not permitted.
257-
func NewRouteBackendRefRefNotPermitted(msg string) Condition {
258-
return Condition{
259-
Type: string(v1beta1.RouteConditionResolvedRefs),
260-
Status: metav1.ConditionFalse,
261-
Reason: string(v1beta1.RouteReasonRefNotPermitted),
262-
Message: msg,
332+
// NewDefaultGatewayConditions returns the default Condition that must be present in the status of a Gateway.
333+
func NewDefaultGatewayConditions() []Condition {
334+
return []Condition{
335+
NewGatewayAccepted(),
263336
}
264337
}
265338

266-
// NewRouteBackendRefRefBackendNotFound returns a Condition that indicates that the Route has a backendRef that
267-
// points to non-existing backend.
268-
func NewRouteBackendRefRefBackendNotFound(msg string) Condition {
339+
// NewGatewayAccepted returns a Condition that indicates the Gateway is accepted.
340+
func NewGatewayAccepted() Condition {
269341
return Condition{
270-
Type: string(v1beta1.RouteConditionResolvedRefs),
271-
Status: metav1.ConditionFalse,
272-
Reason: string(v1beta1.RouteReasonBackendNotFound),
273-
Message: msg,
342+
Type: string(v1beta1.GatewayConditionAccepted),
343+
Status: metav1.ConditionTrue,
344+
Reason: string(v1beta1.GatewayReasonAccepted),
345+
Message: "Gateway is accepted",
274346
}
275347
}
276348

277-
// NewRouteBackendRefUnsupportedValue returns a Condition that indicates that the Route has a backendRef with
278-
// an unsupported value.
279-
func NewRouteBackendRefUnsupportedValue(msg string) Condition {
349+
// NewGatewayConflict returns a Condition that indicates the Gateway has a conflict with another Gateway.
350+
func NewGatewayConflict() Condition {
280351
return Condition{
281-
Type: string(v1beta1.RouteConditionResolvedRefs),
352+
Type: string(v1beta1.GatewayConditionAccepted),
282353
Status: metav1.ConditionFalse,
283-
Reason: RouteReasonBackendRefUnsupportedValue,
284-
Message: msg,
354+
Reason: string(GatewayReasonGatewayConflict),
355+
Message: GatewayMessageGatewayConflict,
285356
}
286357
}
287358

288-
// NewRouteResolvedRefs returns a Condition that indicates that all the references on the Route are resolved.
289-
func NewRouteResolvedRefs() Condition {
359+
// NewGatewayAcceptedListenersNotValid returns a Condition that indicates the Gateway is accepted,
360+
// but has at least one listener that is invalid.
361+
func NewGatewayAcceptedListenersNotValid() Condition {
290362
return Condition{
291-
Type: string(v1beta1.RouteConditionResolvedRefs),
363+
Type: string(v1beta1.GatewayConditionAccepted),
292364
Status: metav1.ConditionTrue,
293-
Reason: string(v1beta1.RouteReasonResolvedRefs),
294-
Message: "All references are resolved",
365+
Reason: string(v1beta1.GatewayReasonListenersNotValid),
366+
Message: "Gateway has at least one valid listener",
295367
}
296368
}
297369

298-
// NewGatewayClassInvalidParameters returns a Condition that indicates that the GatewayClass has invalid parameters.
299-
func NewGatewayClassInvalidParameters(msg string) Condition {
370+
// NewGatewayNotAcceptedListenersNotValid returns a Condition that indicates the Gateway is not accepted,
371+
// because all listeners are invalid.
372+
func NewGatewayNotAcceptedListenersNotValid() Condition {
300373
return Condition{
301-
Type: string(v1beta1.GatewayClassConditionStatusAccepted),
374+
Type: string(v1beta1.GatewayConditionAccepted),
302375
Status: metav1.ConditionFalse,
303-
Reason: string(v1beta1.GatewayClassReasonInvalidParameters),
376+
Reason: string(v1beta1.GatewayReasonListenersNotValid),
377+
Message: "Gateway has no valid listeners",
378+
}
379+
}
380+
381+
// NewGatewayInvalid returns a Condition that indicates the Gateway is not accepted because it is
382+
// semantically or syntactically invalid. The provided message contains the details of why the Gateway is invalid.
383+
func NewGatewayInvalid(msg string) Condition {
384+
return Condition{
385+
Type: string(v1beta1.GatewayConditionAccepted),
386+
Status: metav1.ConditionFalse,
387+
Reason: string(v1beta1.GatewayReasonInvalid),
304388
Message: msg,
305389
}
306390
}
307391

308-
// NewDefaultGatewayClassConditions returns the default Conditions that must be present in the status of a GatewayClass.
309-
func NewDefaultGatewayClassConditions() []Condition {
310-
return []Condition{
311-
{
312-
Type: string(v1beta1.GatewayClassConditionStatusAccepted),
313-
Status: metav1.ConditionTrue,
314-
Reason: string(v1beta1.GatewayClassReasonAccepted),
315-
Message: "GatewayClass is accepted",
316-
},
392+
// NewGatewayUnsupportedValue returns a Condition that indicates that a field of the Gateway has an unsupported value.
393+
// Unsupported means that the value is not supported by the implementation or invalid.
394+
func NewGatewayUnsupportedValue(msg string) Condition {
395+
return Condition{
396+
Type: string(v1beta1.GatewayConditionAccepted),
397+
Status: metav1.ConditionFalse,
398+
Reason: string(GatewayReasonUnsupportedValue),
399+
Message: msg,
317400
}
318401
}

internal/state/graph/gateway.go

+37-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import (
44
"sort"
55

66
"k8s.io/apimachinery/pkg/types"
7+
"k8s.io/apimachinery/pkg/util/validation/field"
78
"sigs.k8s.io/controller-runtime/pkg/client"
89
"sigs.k8s.io/gateway-api/apis/v1beta1"
910

1011
nkgsort "github.com/nginxinc/nginx-kubernetes-gateway/internal/sort"
12+
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/conditions"
1113
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/secrets"
1214
)
1315

@@ -17,6 +19,10 @@ type Gateway struct {
1719
Source *v1beta1.Gateway
1820
// Listeners include the listeners of the Gateway.
1921
Listeners map[string]*Listener
22+
// Conditions holds the conditions for the Gateway.
23+
Conditions []conditions.Condition
24+
// Valid indicates whether the Gateway Spec is valid.
25+
Valid bool
2026
}
2127

2228
// processedGateways holds the resources that belong to NKG.
@@ -89,8 +95,38 @@ func buildGateway(gw *v1beta1.Gateway, secretMemoryMgr secrets.SecretDiskMemoryM
8995
return nil
9096
}
9197

98+
conds := validateGateway(gw, gc)
99+
100+
if len(conds) > 0 {
101+
return &Gateway{
102+
Source: gw,
103+
Valid: false,
104+
Conditions: conds,
105+
}
106+
}
107+
92108
return &Gateway{
93109
Source: gw,
94-
Listeners: buildListeners(gw, secretMemoryMgr, gc),
110+
Listeners: buildListeners(gw, secretMemoryMgr),
111+
Valid: true,
112+
}
113+
}
114+
115+
func validateGateway(gw *v1beta1.Gateway, gc *GatewayClass) []conditions.Condition {
116+
var conds []conditions.Condition
117+
118+
if gc == nil {
119+
conds = append(conds, conditions.NewGatewayInvalid("GatewayClass doesn't exist"))
120+
} else if !gc.Valid {
121+
conds = append(conds, conditions.NewGatewayInvalid("GatewayClass is invalid"))
122+
}
123+
124+
if len(gw.Spec.Addresses) > 0 {
125+
path := field.NewPath("spec", "addresses")
126+
valErr := field.Forbidden(path, "addresses are not supported")
127+
128+
conds = append(conds, conditions.NewGatewayUnsupportedValue(valErr.Error()))
95129
}
130+
131+
return conds
96132
}

0 commit comments

Comments
 (0)