Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
4 changes: 2 additions & 2 deletions apis/gateway/v1beta1/targetgroupconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// Reference defines how to look up the Target Group configuration for a service.
// Reference defines how to look up the Target Group configuration for a kubernetes object.
type Reference struct {
// Group is the group of the referent. For example, "gateway.networking.k8s.io".
// When unspecified or empty string, core API group is inferred.
Expand Down Expand Up @@ -108,7 +108,7 @@ const (
TargetTypeIP TargetType = "ip"
)

// +kubebuilder:validation:Enum=http;https;tcp
// +kubebuilder:validation:Enum=HTTP;HTTPS;TCP
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This enum validation was incorrect, which made the field unusable as the casing should be all upper case.

type TargetGroupHealthCheckProtocol string

const (
Expand Down
12 changes: 6 additions & 6 deletions config/crd/gateway/gateway-crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -821,9 +821,9 @@ spec:
with the target. The GENEVE, TLS, UDP, and TCP_UDP protocols
are not supported for health checks.
enum:
- http
- https
- tcp
- HTTP
- HTTPS
- TCP
type: string
healthCheckTimeout:
description: healthCheckTimeout The amount of time, in seconds,
Expand Down Expand Up @@ -1014,9 +1014,9 @@ spec:
and TCP_UDP protocols are not supported for health
checks.
enum:
- http
- https
- tcp
- HTTP
- HTTPS
- TCP
type: string
healthCheckTimeout:
description: healthCheckTimeout The amount of time,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ spec:
with the target. The GENEVE, TLS, UDP, and TCP_UDP protocols
are not supported for health checks.
enum:
- http
- https
- tcp
- HTTP
- HTTPS
- TCP
type: string
healthCheckTimeout:
description: healthCheckTimeout The amount of time, in seconds,
Expand Down Expand Up @@ -275,9 +275,9 @@ spec:
and TCP_UDP protocols are not supported for health
checks.
enum:
- http
- https
- tcp
- HTTP
- HTTPS
- TCP
type: string
healthCheckTimeout:
description: healthCheckTimeout The amount of time,
Expand Down
105 changes: 81 additions & 24 deletions controllers/gateway/eventhandlers/target_group_configuration_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1"
Expand All @@ -13,66 +14,122 @@ import (
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
gwalpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
)

// NewEnqueueRequestsForTargetGroupConfigurationEvent creates handler for TargetGroupConfiguration resources
func NewEnqueueRequestsForTargetGroupConfigurationEvent(svcEventChan chan<- event.TypedGenericEvent[*corev1.Service],
func NewEnqueueRequestsForTargetGroupConfigurationEvent(svcEventChan chan<- event.TypedGenericEvent[*corev1.Service], tcpRouteEventChan chan<- event.TypedGenericEvent[*gwalpha2.TCPRoute],
k8sClient client.Client, eventRecorder record.EventRecorder, logger logr.Logger) handler.TypedEventHandler[*elbv2gw.TargetGroupConfiguration, reconcile.Request] {
return &enqueueRequestsForTargetGroupConfigurationEvent{
svcEventChan: svcEventChan,
k8sClient: k8sClient,
eventRecorder: eventRecorder,
logger: logger,
svcEventChan: svcEventChan,
tcpRouteEventChan: tcpRouteEventChan,
k8sClient: k8sClient,
eventRecorder: eventRecorder,
logger: logger,
}
}

var _ handler.TypedEventHandler[*elbv2gw.TargetGroupConfiguration, reconcile.Request] = (*enqueueRequestsForTargetGroupConfigurationEvent)(nil)

// enqueueRequestsForTargetGroupConfigurationEvent handles TargetGroupConfiguration events
type enqueueRequestsForTargetGroupConfigurationEvent struct {
svcEventChan chan<- event.TypedGenericEvent[*corev1.Service]
k8sClient client.Client
eventRecorder record.EventRecorder
logger logr.Logger
svcEventChan chan<- event.TypedGenericEvent[*corev1.Service]
tcpRouteEventChan chan<- event.TypedGenericEvent[*gwalpha2.TCPRoute]
k8sClient client.Client
eventRecorder record.EventRecorder
logger logr.Logger
}

func (h *enqueueRequestsForTargetGroupConfigurationEvent) Create(ctx context.Context, e event.TypedCreateEvent[*elbv2gw.TargetGroupConfiguration], queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
tgconfigNew := e.Object
h.logger.V(1).Info("enqueue targetgroupconfiguration create event", "targetgroupconfiguration", tgconfigNew.Name)
h.enqueueImpactedService(ctx, tgconfigNew, queue)
h.enqueueImpactedObject(ctx, tgconfigNew, queue)
}

func (h *enqueueRequestsForTargetGroupConfigurationEvent) Update(ctx context.Context, e event.TypedUpdateEvent[*elbv2gw.TargetGroupConfiguration], queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
tgconfigNew := e.ObjectNew
h.logger.V(1).Info("enqueue targetgroupconfiguration update event", "targetgroupconfiguration", tgconfigNew.Name)
h.enqueueImpactedService(ctx, tgconfigNew, queue)
h.enqueueImpactedObject(ctx, tgconfigNew, queue)
}

func (h *enqueueRequestsForTargetGroupConfigurationEvent) Delete(ctx context.Context, e event.TypedDeleteEvent[*elbv2gw.TargetGroupConfiguration], queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
tgconfig := e.Object
h.logger.V(1).Info("enqueue targetgroupconfiguration delete event", "targetgroupconfiguration", tgconfig.Name)
h.enqueueImpactedService(ctx, tgconfig, queue)
h.enqueueImpactedObject(ctx, tgconfig, queue)
}

func (h *enqueueRequestsForTargetGroupConfigurationEvent) Generic(ctx context.Context, e event.TypedGenericEvent[*elbv2gw.TargetGroupConfiguration], queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
tgconfig := e.Object
h.logger.V(1).Info("enqueue targetgroupconfiguration generic event", "targetgroupconfiguration", tgconfig.Name)
h.enqueueImpactedService(ctx, tgconfig, queue)
h.enqueueImpactedObject(ctx, tgconfig, queue)
}

func (h *enqueueRequestsForTargetGroupConfigurationEvent) enqueueImpactedService(ctx context.Context, tgconfig *elbv2gw.TargetGroupConfiguration, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
svcName := types.NamespacedName{Namespace: tgconfig.Namespace, Name: tgconfig.Spec.TargetReference.Name}
svc := &corev1.Service{}
if err := h.k8sClient.Get(ctx, svcName, svc); err != nil {
h.logger.V(1).Info("ignoring targetgroupconfiguration event for unknown service",
func (h *enqueueRequestsForTargetGroupConfigurationEvent) enqueueImpactedObject(ctx context.Context, tgconfig *elbv2gw.TargetGroupConfiguration, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) {
objName := types.NamespacedName{Namespace: tgconfig.Namespace, Name: tgconfig.Spec.TargetReference.Name}

if tgconfig.Spec.TargetReference.Kind == nil || *tgconfig.Spec.TargetReference.Kind == "Service" {
svc := &corev1.Service{}
if err := h.k8sClient.Get(ctx, objName, svc); err != nil {
h.logger.V(1).Info("ignoring targetgroupconfiguration event for unknown service",
"targetgroupconfiguration", k8s.NamespacedName(tgconfig),
"service", k8s.NamespacedName(svc))
return
}
h.logger.V(1).Info("enqueue service for targetgroupconfiguration event",
"targetgroupconfiguration", k8s.NamespacedName(tgconfig),
"service", k8s.NamespacedName(svc))
return
h.svcEventChan <- event.TypedGenericEvent[*corev1.Service]{
Object: svc,
}
}

// TODO - We should probably use an indexer here, we have a task to do this.
if tgconfig.Spec.TargetReference.Kind != nil && *tgconfig.Spec.TargetReference.Kind == "Gateway" && h.tcpRouteEventChan != nil {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required to support TG config updates that impact ALB typed target groups. We need to find out all TCP Routes that reference the Gateway which the config references it. See the added tests.

tcpRouteList := &gwalpha2.TCPRouteList{}

if err := h.k8sClient.List(ctx, tcpRouteList); err != nil {
h.logger.V(1).Info("failed to list tcp routes for target group configuration event", "targetgroupconfiguration", k8s.NamespacedName(tgconfig))
return
}

impactedRoutes := getImpactedTCPRoutes(tcpRouteList, tgconfig)
for i := range impactedRoutes {
h.tcpRouteEventChan <- event.TypedGenericEvent[*gwalpha2.TCPRoute]{
Object: impactedRoutes[i],
}
}

}
h.logger.V(1).Info("enqueue service for targetgroupconfiguration event",
"targetgroupconfiguration", k8s.NamespacedName(tgconfig),
"service", k8s.NamespacedName(svc))
h.svcEventChan <- event.TypedGenericEvent[*corev1.Service]{
Object: svc,
}

func getImpactedTCPRoutes(list *gwalpha2.TCPRouteList, tgconfig *elbv2gw.TargetGroupConfiguration) []*gwalpha2.TCPRoute {
seen := sets.Set[types.NamespacedName]{}
res := make([]*gwalpha2.TCPRoute, 0)

for i, route := range list.Items {
nsn := k8s.NamespacedName(&route)
for _, rule := range route.Spec.Rules {
for _, beRef := range rule.BackendRefs {
if beRef.Kind != nil && *beRef.Kind == "Gateway" {
if string(beRef.Name) == tgconfig.Spec.TargetReference.Name {

// The route backend ns
var routeNs string
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to support optional namespace, which mandates that when the backend ns is omitted we should use the route namespace.

if beRef.Namespace == nil {
routeNs = route.Namespace
} else {
routeNs = string(*beRef.Namespace)
}

if routeNs == tgconfig.Namespace && !seen.Has(nsn) {
res = append(res, &list.Items[i])
seen.Insert(nsn)
}

}
}
}
}
}
return res
}
Loading
Loading