Skip to content
Open
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
1 change: 1 addition & 0 deletions apis/aga/v1beta1/globalaccelerator_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const (
)

// PortRange defines the port range for Global Accelerator listeners.
// +kubebuilder:validation:XValidation:rule="self.fromPort <= self.toPort",message="FromPort must be less than or equal to ToPort"
type PortRange struct {
// FromPort is the first port in the range of ports, inclusive.
// +kubebuilder:validation:Minimum=1
Expand Down
3 changes: 3 additions & 0 deletions config/crd/aga/aga-crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ spec:
- fromPort
- toPort
type: object
x-kubernetes-validations:
- message: FromPort must be less than or equal to ToPort
rule: self.fromPort <= self.toPort
maxItems: 10
minItems: 1
type: array
Expand Down
3 changes: 3 additions & 0 deletions config/crd/aga/aga.k8s.aws_globalaccelerators.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ spec:
- fromPort
- toPort
type: object
x-kubernetes-validations:
- message: FromPort must be less than or equal to ToPort
rule: self.fromPort <= self.toPort
maxItems: 10
minItems: 1
type: array
Expand Down
18 changes: 18 additions & 0 deletions config/webhook/globalaccelerator_validator_patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This patch adds the GlobalAccelerator validator webhook configuration to the webhook configurations
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: webhook-configuration
webhooks:
- name: vglobalaccelerator.aga.k8s.aws
rules:
- apiGroups:
- "aga.k8s.aws"
apiVersions:
- v1beta1
operations:
- CREATE
- UPDATE
resources:
- globalaccelerators
scope: "Namespaced"
1 change: 1 addition & 0 deletions config/webhook/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ patchesStrategicMerge:
- pod_mutator_patch.yaml
- service_mutator_patch.yaml
- ingressclassparams_validator_patch.yaml
- globalaccelerator_validator_patch.yaml
21 changes: 21 additions & 0 deletions config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ kind: ValidatingWebhookConfiguration
metadata:
name: webhook
webhooks:
- admissionReviewVersions:
- v1beta1
clientConfig:
service:
name: webhook-service
namespace: system
path: /validate-aga-k8s-aws-v1beta1-globalaccelerator
failurePolicy: Fail
matchPolicy: Equivalent
name: vglobalaccelerator.aga.k8s.aws
rules:
- apiGroups:
- aga.k8s.aws
apiVersions:
- v1beta1
operations:
- CREATE
- UPDATE
resources:
- globalaccelerators
sideEffects: None
- admissionReviewVersions:
- v1beta1
clientConfig:
Expand Down
4 changes: 3 additions & 1 deletion controllers/aga/globalaccelerator_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,9 @@ func (r *globalAcceleratorReconciler) reconcileGlobalAcceleratorResources(ctx co
func (r *globalAcceleratorReconciler) cleanupGlobalAcceleratorResources(ctx context.Context, ga *agaapi.GlobalAccelerator) error {
r.logger.Info("Cleaning up GlobalAccelerator resources", "globalAccelerator", k8s.NamespacedName(ga))

// TODO we will handle cleaning up dependent resources when we implement those
// Our enhanced AcceleratorManager now handles deletion of listeners before accelerator.
// TODO: This will be enhanced to delete endpoint groups and endpoints
// before deleting listeners and accelerator (when those features are implemented)
// 1. Find the accelerator ARN from the CRD status
if ga.Status.AcceleratorARN == nil {
r.logger.Info("No accelerator ARN found in status, nothing to clean up", "globalAccelerator", k8s.NamespacedName(ga))
Expand Down
3 changes: 3 additions & 0 deletions helm/aws-load-balancer-controller/crds/aga-crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ spec:
- fromPort
- toPort
type: object
x-kubernetes-validations:
- message: FromPort must be less than or equal to ToPort
rule: self.fromPort <= self.toPort
maxItems: 10
minItems: 1
type: array
Expand Down
9 changes: 8 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"k8s.io/apimachinery/pkg/util/sets"
"os"
"sigs.k8s.io/aws-load-balancer-controller/pkg/aga"
"sigs.k8s.io/aws-load-balancer-controller/pkg/shared_utils"

elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1"
Expand Down Expand Up @@ -65,6 +66,7 @@ import (
"sigs.k8s.io/aws-load-balancer-controller/pkg/runtime"
"sigs.k8s.io/aws-load-balancer-controller/pkg/targetgroupbinding"
"sigs.k8s.io/aws-load-balancer-controller/pkg/version"
agawebhook "sigs.k8s.io/aws-load-balancer-controller/webhooks/aga"
corewebhook "sigs.k8s.io/aws-load-balancer-controller/webhooks/core"
elbv2webhook "sigs.k8s.io/aws-load-balancer-controller/webhooks/elbv2"
networkingwebhook "sigs.k8s.io/aws-load-balancer-controller/webhooks/networking"
Expand Down Expand Up @@ -236,7 +238,7 @@ func main() {
}

// Setup GlobalAccelerator controller only if enabled
if shared_utils.IsAGAControllerEnabled(controllerCFG.FeatureGates, controllerCFG.AWSConfig.Region) {
if aga.IsAGAControllerEnabled(controllerCFG.FeatureGates, controllerCFG.AWSConfig.Region) {
agaReconciler := agacontroller.NewGlobalAcceleratorReconciler(mgr.GetClient(), mgr.GetEventRecorderFor("globalAccelerator"),
finalizerManager, controllerCFG, cloud, ctrl.Log.WithName("controllers").WithName("globalAccelerator"), lbcMetricsCollector, reconcileCounters)
if err := agaReconciler.SetupWithManager(ctx, mgr, clientSet); err != nil {
Expand Down Expand Up @@ -415,6 +417,11 @@ func main() {
elbv2webhook.NewTargetGroupBindingMutator(cloud.ELBV2(), ctrl.Log, lbcMetricsCollector).SetupWithManager(mgr)
elbv2webhook.NewTargetGroupBindingValidator(mgr.GetClient(), cloud.ELBV2(), cloud.VpcID(), ctrl.Log, lbcMetricsCollector).SetupWithManager(mgr)
networkingwebhook.NewIngressValidator(mgr.GetClient(), controllerCFG.IngressConfig, ctrl.Log, lbcMetricsCollector).SetupWithManager(mgr)

// Setup GlobalAccelerator validator only if enabled
if aga.IsAGAControllerEnabled(controllerCFG.FeatureGates, controllerCFG.AWSConfig.Region) {
agawebhook.NewGlobalAcceleratorValidator(ctrl.Log, lbcMetricsCollector).SetupWithManager(mgr)
}
//+kubebuilder:scaffold:builder

go func() {
Expand Down
128 changes: 128 additions & 0 deletions pkg/aga/model_build_listener.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package aga

import (
"context"
"fmt"
"github.com/pkg/errors"
agaapi "sigs.k8s.io/aws-load-balancer-controller/apis/aga/v1beta1"
agamodel "sigs.k8s.io/aws-load-balancer-controller/pkg/model/aga"
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
)

// listenerBuilder builds Listener model resources
type listenerBuilder interface {
Build(ctx context.Context, stack core.Stack, accelerator *agamodel.Accelerator, listeners []agaapi.GlobalAcceleratorListener) ([]*agamodel.Listener, error)
}

// NewListenerBuilder constructs new listenerBuilder
func NewListenerBuilder() listenerBuilder {
return &defaultListenerBuilder{}
}

var _ listenerBuilder = &defaultListenerBuilder{}

type defaultListenerBuilder struct{}

// Build builds Listener model resources
func (b *defaultListenerBuilder) Build(ctx context.Context, stack core.Stack, accelerator *agamodel.Accelerator, listeners []agaapi.GlobalAcceleratorListener) ([]*agamodel.Listener, error) {
if listeners == nil || len(listeners) == 0 {
return nil, nil
}

var result []*agamodel.Listener
for i, listener := range listeners {
listenerModel, err := buildListener(ctx, stack, accelerator, listener, i)
if err != nil {
return nil, err
}
result = append(result, listenerModel)
}
return result, nil
}

// buildListener builds a single Listener model resource
func buildListener(ctx context.Context, stack core.Stack, accelerator *agamodel.Accelerator, listener agaapi.GlobalAcceleratorListener, index int) (*agamodel.Listener, error) {
spec, err := buildListenerSpec(ctx, accelerator, listener)
if err != nil {
return nil, err
}

resourceID := fmt.Sprintf("Listener-%d", index)
listenerModel := agamodel.NewListener(stack, resourceID, spec, accelerator)
return listenerModel, nil
}

// buildListenerSpec builds the ListenerSpec for a single Listener model resource
func buildListenerSpec(ctx context.Context, accelerator *agamodel.Accelerator, listener agaapi.GlobalAcceleratorListener) (agamodel.ListenerSpec, error) {
protocol, err := buildListenerProtocol(ctx, listener)
if err != nil {
return agamodel.ListenerSpec{}, err
}

portRanges, err := buildListenerPortRanges(ctx, listener)
if err != nil {
return agamodel.ListenerSpec{}, err
}

clientAffinity := buildListenerClientAffinity(ctx, listener)

return agamodel.ListenerSpec{
AcceleratorARN: accelerator.AcceleratorARN(),
Protocol: protocol,
PortRanges: portRanges,
ClientAffinity: clientAffinity,
}, nil
}

// buildListenerProtocol determines the protocol for the listener
func buildListenerProtocol(_ context.Context, listener agaapi.GlobalAcceleratorListener) (agamodel.Protocol, error) {
if listener.Protocol == nil {
// TODO: Auto-discovery feature - Auto-determine protocol from endpoints if nil
// For now, default to TCP
return agamodel.ProtocolTCP, nil
}

switch *listener.Protocol {
case agaapi.GlobalAcceleratorProtocolTCP:
return agamodel.ProtocolTCP, nil
case agaapi.GlobalAcceleratorProtocolUDP:
return agamodel.ProtocolUDP, nil
default:
return "", errors.Errorf("unsupported protocol: %s", *listener.Protocol)
}
}

// buildListenerPortRanges determines the port ranges for the listener
func buildListenerPortRanges(_ context.Context, listener agaapi.GlobalAcceleratorListener) ([]agamodel.PortRange, error) {
if listener.PortRanges == nil {
// TODO: Auto-discovery feature - Auto-determine port ranges from endpoints if nil
// For now, default to port 80
return []agamodel.PortRange{{
FromPort: 80,
ToPort: 80,
}}, nil
}

var portRanges []agamodel.PortRange
for _, pr := range *listener.PortRanges {
// Required validations are already done webhooks and CEL
portRanges = append(portRanges, agamodel.PortRange{
FromPort: pr.FromPort,
ToPort: pr.ToPort,
})
}
return portRanges, nil
}

// buildListenerClientAffinity determines the client affinity for the listener
func buildListenerClientAffinity(_ context.Context, listener agaapi.GlobalAcceleratorListener) agamodel.ClientAffinity {
switch listener.ClientAffinity {
case agaapi.ClientAffinitySourceIP:
return agamodel.ClientAffinitySourceIP
case agaapi.ClientAffinityNone:
return agamodel.ClientAffinityNone
default:
// Default to NONE as per AWS Global Accelerator behavior
return agamodel.ClientAffinityNone
}
}
Loading
Loading