Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 13 additions & 1 deletion control-plane/api-gateway/binding/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func (b *Binder) Snapshot() *Snapshot {
for i, listener := range b.config.Gateway.Spec.Listeners {
status.Listeners = append(status.Listeners, gwv1beta1.ListenerStatus{
Name: listener.Name,
SupportedKinds: supportedKindsForProtocol[listener.Protocol],
SupportedKinds: supportedKinds(listener),
AttachedRoutes: int32(boundCounts[listener.Name]),
Conditions: listenerValidation.Conditions(b.config.Gateway.Generation, i),
})
Expand Down Expand Up @@ -374,3 +374,15 @@ func addressesFromPodHosts(pods []corev1.Pod) []gwv1beta1.GatewayAddress {
func isDeleted(object client.Object) bool {
return !object.GetDeletionTimestamp().IsZero()
}

func supportedKinds(listener gwv1beta1.Listener) []gwv1beta1.RouteGroupKind {
if listener.AllowedRoutes != nil && listener.AllowedRoutes.Kinds != nil {
return common.Filter(listener.AllowedRoutes.Kinds, func(kind gwv1beta1.RouteGroupKind) bool {
if _, ok := allSupportedRouteKinds[kind.Kind]; !ok {
return true
}
return !common.NilOrEqual(kind.Group, gwv1beta1.GroupVersion.Group)
})
}
return supportedKindsForProtocol[listener.Protocol]
}
5 changes: 5 additions & 0 deletions control-plane/api-gateway/binding/binder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ func TestBinder_Lifecycle(t *testing.T) {
Status: metav1.ConditionTrue,
Reason: "Accepted",
Message: "listener accepted",
}, {
Type: "Programmed",
Status: metav1.ConditionTrue,
Reason: "Programmed",
Message: "listener programmed",
}, {
Type: "Conflicted",
Status: metav1.ConditionFalse,
Expand Down
49 changes: 46 additions & 3 deletions control-plane/api-gateway/binding/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ func (p parentBindResults) boundSections() mapset.Set {
}

var (
// Each of the below are specified in the Gateway spec under ListenerConditionReason
// the general usage is that each error is specified as errListener* where * corresponds
// Each of the below are specified in the Gateway spec under ListenerConditionReason.
// The general usage is that each error is specified as errListener* where * corresponds
// to the ListenerConditionReason given in the spec. If a reason is overloaded and can
// be used with two different types of things (i.e. something is not found or it's not supported)
// then we distinguish those two usages with errListener*_Usage.
Expand All @@ -225,6 +225,8 @@ var (
errListenerInvalidCertificateRef_NotFound = errors.New("certificate not found")
errListenerInvalidCertificateRef_NotSupported = errors.New("certificate type is not supported")
errListenerInvalidCertificateRef_InvalidData = errors.New("certificate is invalid or does not contain a supported server name")
errListenerInvalidRouteKinds = errors.New("allowed route kind is invalid")
errListenerProgrammed_Invalid = errors.New("listener cannot be programmed because it is invalid")

// Below is where any custom generic listener validation errors should go.
// We map anything under here to a custom ListenerConditionReason of Invalid on
Expand All @@ -243,7 +245,36 @@ type listenerValidationResult struct {
conflictedErr error
// status type: ResolvedRefs
refErr error
// TODO: programmed
// status type: ResolvedRefs (but with internal validation)
routeKindErr error
}

// programmedCondition constructs the condition for the Programmed status type.
// If there are no validation errors for the listener, we mark it as programmed.
// If there are validation errors for the listener, we mark it as invalid.
func (l listenerValidationResult) programmedCondition(generation int64) metav1.Condition {
now := timeFunc()

switch {
case l.acceptedErr != nil, l.conflictedErr != nil, l.refErr != nil, l.routeKindErr != nil:
return metav1.Condition{
Type: "Programmed",
Status: metav1.ConditionFalse,
Reason: "Invalid",
ObservedGeneration: generation,
Message: errListenerProgrammed_Invalid.Error(),
LastTransitionTime: now,
}
default:
return metav1.Condition{
Type: "Programmed",
Status: metav1.ConditionTrue,
Reason: "Programmed",
ObservedGeneration: generation,
Message: "listener programmed",
LastTransitionTime: now,
}
}
}

// acceptedCondition constructs the condition for the Accepted status type.
Expand Down Expand Up @@ -329,6 +360,17 @@ func (l listenerValidationResult) conflictedCondition(generation int64) metav1.C
func (l listenerValidationResult) resolvedRefsCondition(generation int64) metav1.Condition {
now := timeFunc()

if l.routeKindErr != nil {
return metav1.Condition{
Type: "ResolvedRefs",
Status: metav1.ConditionFalse,
Reason: "InvalidRouteKinds",
ObservedGeneration: generation,
Message: l.routeKindErr.Error(),
LastTransitionTime: now,
}
}

switch l.refErr {
case errListenerInvalidCertificateRef_NotFound, errListenerInvalidCertificateRef_NotSupported, errListenerInvalidCertificateRef_InvalidData:
return metav1.Condition{
Expand Down Expand Up @@ -364,6 +406,7 @@ func (l listenerValidationResult) resolvedRefsCondition(generation int64) metav1
func (l listenerValidationResult) Conditions(generation int64) []metav1.Condition {
return []metav1.Condition{
l.acceptedCondition(generation),
l.programmedCondition(generation),
l.conflictedCondition(generation),
l.resolvedRefsCondition(generation),
}
Expand Down
26 changes: 25 additions & 1 deletion control-plane/api-gateway/binding/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ var (
Kind: "TCPRoute",
}},
}
allSupportedRouteKinds = map[gwv1beta1.Kind]struct{}{
gwv1beta1.Kind("HTTPRoute"): {},
gwv1beta1.Kind("TCPRoute"): {},
}
)

// validateRefs validates backend references for a route, determining whether or
Expand Down Expand Up @@ -202,7 +206,10 @@ func validateTLS(gateway gwv1beta1.Gateway, tls *gwv1beta1.GatewayTLSConfig, res

func validateCertificateData(secret corev1.Secret) error {
_, _, err := common.ParseCertificateData(secret)
return err
if err != nil {
return errListenerInvalidCertificateRef_InvalidData
}
return nil
}

// validateListeners validates the given listeners both internally and with respect to each
Expand Down Expand Up @@ -231,6 +238,8 @@ func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener
} else if listener.Port == 20000 { //admin port
result.acceptedErr = errListenerPortUnavailable
}

result.routeKindErr = validateListenerAllowedRouteKinds(listener.AllowedRoutes)
}

if err := merged[listener.Port].validateProtocol(); err != nil {
Expand All @@ -244,6 +253,21 @@ func validateListeners(gateway gwv1beta1.Gateway, listeners []gwv1beta1.Listener
return results
}

func validateListenerAllowedRouteKinds(allowedRoutes *gwv1beta1.AllowedRoutes) error {
if allowedRoutes == nil {
return nil
}
for _, kind := range allowedRoutes.Kinds {
if _, ok := allSupportedRouteKinds[kind.Kind]; !ok {
return errListenerInvalidRouteKinds
}
if !common.NilOrEqual(kind.Group, gwv1beta1.GroupVersion.Group) {
return errListenerInvalidRouteKinds
}
}
return nil
}

// routeAllowedForListenerNamespaces determines whether the route is allowed
// to bind to the Gateway based on the AllowedRoutes namespace selectors.
func routeAllowedForListenerNamespaces(gatewayNamespace string, allowedRoutes *gwv1beta1.AllowedRoutes, namespace corev1.Namespace) bool {
Expand Down