Skip to content

Commit f9a006c

Browse files
committed
[feat: aga] add basic framework for AGA controller
1 parent 1dd68b7 commit f9a006c

File tree

11 files changed

+281
-23
lines changed

11 files changed

+281
-23
lines changed
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/*
2+
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package controllers
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"github.com/go-logr/logr"
23+
corev1 "k8s.io/api/core/v1"
24+
"k8s.io/apimachinery/pkg/types"
25+
"k8s.io/client-go/kubernetes"
26+
"k8s.io/client-go/tools/record"
27+
"sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants"
28+
ctrl "sigs.k8s.io/controller-runtime"
29+
"sigs.k8s.io/controller-runtime/pkg/client"
30+
"sigs.k8s.io/controller-runtime/pkg/controller"
31+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
32+
33+
agaapi "sigs.k8s.io/aws-load-balancer-controller/apis/aga/v1beta1"
34+
"sigs.k8s.io/aws-load-balancer-controller/pkg/config"
35+
ctrlerrors "sigs.k8s.io/aws-load-balancer-controller/pkg/error"
36+
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
37+
lbcmetrics "sigs.k8s.io/aws-load-balancer-controller/pkg/metrics/lbc"
38+
metricsutil "sigs.k8s.io/aws-load-balancer-controller/pkg/metrics/util"
39+
"sigs.k8s.io/aws-load-balancer-controller/pkg/runtime"
40+
)
41+
42+
const (
43+
controllerName = "globalAccelerator"
44+
45+
// the groupVersion of used GlobalAccelerator resource.
46+
agaResourcesGroupVersion = "aga.k8s.aws/v1beta1"
47+
globalAcceleratorKind = "GlobalAccelerator"
48+
49+
// Metric stage constants
50+
MetricStageFetchGlobalAccelerator = "fetch_globalAccelerator"
51+
MetricStageAddFinalizers = "add_finalizers"
52+
MetricStageReconcileGlobalAccelerator = "reconcile_globalaccelerator"
53+
54+
// Metric error constants
55+
MetricErrorAddFinalizers = "add_finalizers_error"
56+
MetricErrorRemoveFinalizers = "remove_finalizers_error"
57+
MetricErrorReconcileGlobalAccelerator = "reconcile_globalaccelerator_error"
58+
)
59+
60+
// NewGlobalAcceleratorReconciler constructs new globalAcceleratorReconciler
61+
func NewGlobalAcceleratorReconciler(k8sClient client.Client, eventRecorder record.EventRecorder, finalizerManager k8s.FinalizerManager,
62+
config config.ControllerConfig, logger logr.Logger, metricsCollector lbcmetrics.MetricCollector, reconcileCounters *metricsutil.ReconcileCounters) *globalAcceleratorReconciler {
63+
64+
return &globalAcceleratorReconciler{
65+
k8sClient: k8sClient,
66+
eventRecorder: eventRecorder,
67+
finalizerManager: finalizerManager,
68+
logger: logger,
69+
metricsCollector: metricsCollector,
70+
reconcileTracker: reconcileCounters.IncrementAGA,
71+
72+
maxConcurrentReconciles: config.GlobalAcceleratorMaxConcurrentReconciles,
73+
}
74+
}
75+
76+
// globalAcceleratorReconciler reconciles a GlobalAccelerator object
77+
type globalAcceleratorReconciler struct {
78+
k8sClient client.Client
79+
eventRecorder record.EventRecorder
80+
finalizerManager k8s.FinalizerManager
81+
logger logr.Logger
82+
metricsCollector lbcmetrics.MetricCollector
83+
reconcileTracker func(namespaceName types.NamespacedName)
84+
85+
maxConcurrentReconciles int
86+
}
87+
88+
// +kubebuilder:rbac:groups=aga.k8s.aws,resources=globalaccelerators,verbs=get;list;watch;patch
89+
// +kubebuilder:rbac:groups=aga.k8s.aws,resources=globalaccelerators/status,verbs=update;patch
90+
// +kubebuilder:rbac:groups=aga.k8s.aws,resources=globalaccelerators/finalizers,verbs=update;patch
91+
func (r *globalAcceleratorReconciler) Reconcile(ctx context.Context, req reconcile.Request) (ctrl.Result, error) {
92+
r.reconcileTracker(req.NamespacedName)
93+
r.logger.V(1).Info("Reconcile request", "name", req.Name)
94+
err := r.reconcile(ctx, req)
95+
return runtime.HandleReconcileError(err, r.logger)
96+
}
97+
98+
func (r *globalAcceleratorReconciler) reconcile(ctx context.Context, req reconcile.Request) error {
99+
ga := &agaapi.GlobalAccelerator{}
100+
var err error
101+
fetchGlobalAcceleratorFn := func() {
102+
err = r.k8sClient.Get(ctx, req.NamespacedName, ga)
103+
}
104+
r.metricsCollector.ObserveControllerReconcileLatency(controllerName, MetricStageFetchGlobalAccelerator, fetchGlobalAcceleratorFn)
105+
if err != nil {
106+
return client.IgnoreNotFound(err)
107+
}
108+
109+
if ga.DeletionTimestamp != nil && !ga.DeletionTimestamp.IsZero() {
110+
return r.cleanupGlobalAccelerator(ctx, ga)
111+
}
112+
return r.reconcileGlobalAccelerator(ctx, ga)
113+
}
114+
115+
func (r *globalAcceleratorReconciler) reconcileGlobalAccelerator(ctx context.Context, ga *agaapi.GlobalAccelerator) error {
116+
var err error
117+
finalizerFn := func() {
118+
if !k8s.HasFinalizer(ga, shared_constants.GlobalAcceleratorFinalizer) {
119+
err = r.finalizerManager.AddFinalizers(ctx, ga, shared_constants.GlobalAcceleratorFinalizer)
120+
}
121+
}
122+
r.metricsCollector.ObserveControllerReconcileLatency(controllerName, MetricStageAddFinalizers, finalizerFn)
123+
if err != nil {
124+
r.eventRecorder.Event(ga, corev1.EventTypeWarning, k8s.GlobalAcceleratorEventReasonFailedAddFinalizer, fmt.Sprintf("Failed add finalizer due to %v", err))
125+
return ctrlerrors.NewErrorWithMetrics(controllerName, MetricErrorAddFinalizers, err, r.metricsCollector)
126+
}
127+
128+
// TODO: Implement GlobalAccelerator resource management
129+
// This would include:
130+
// 1. Creating/updating AWS Global Accelerator
131+
// 2. Managing listeners and endpoint groups
132+
// 3. Handling endpoint discovery from Services/Ingresses/Gateways
133+
reconcileResourceFn := func() {
134+
err = r.reconcileGlobalAcceleratorResources(ctx, ga)
135+
}
136+
r.metricsCollector.ObserveControllerReconcileLatency(controllerName, MetricStageReconcileGlobalAccelerator, reconcileResourceFn)
137+
if err != nil {
138+
return ctrlerrors.NewErrorWithMetrics(controllerName, MetricErrorReconcileGlobalAccelerator, err, r.metricsCollector)
139+
}
140+
141+
r.eventRecorder.Event(ga, corev1.EventTypeNormal, k8s.GlobalAcceleratorEventReasonSuccessfullyReconciled, "Successfully reconciled")
142+
return nil
143+
}
144+
145+
func (r *globalAcceleratorReconciler) cleanupGlobalAccelerator(ctx context.Context, ga *agaapi.GlobalAccelerator) error {
146+
if k8s.HasFinalizer(ga, shared_constants.GlobalAcceleratorFinalizer) {
147+
// TODO: Implement cleanup logic for AWS Global Accelerator resources
148+
if err := r.cleanupGlobalAcceleratorResources(ctx, ga); err != nil {
149+
r.eventRecorder.Event(ga, corev1.EventTypeWarning, k8s.GlobalAcceleratorEventReasonFailedCleanup, fmt.Sprintf("Failed cleanup due to %v", err))
150+
return err
151+
}
152+
if err := r.finalizerManager.RemoveFinalizers(ctx, ga, shared_constants.GlobalAcceleratorFinalizer); err != nil {
153+
r.eventRecorder.Event(ga, corev1.EventTypeWarning, k8s.GlobalAcceleratorEventReasonFailedRemoveFinalizer, fmt.Sprintf("Failed remove finalizer due to %v", err))
154+
return ctrlerrors.NewErrorWithMetrics(controllerName, MetricErrorRemoveFinalizers, err, r.metricsCollector)
155+
}
156+
}
157+
return nil
158+
}
159+
160+
func (r *globalAcceleratorReconciler) reconcileGlobalAcceleratorResources(ctx context.Context, ga *agaapi.GlobalAccelerator) error {
161+
// TODO: Implement the actual AWS Global Accelerator resource management
162+
// This is a placeholder implementation
163+
r.logger.Info("Reconciling GlobalAccelerator resources", "name", ga.Name, "namespace", ga.Namespace)
164+
165+
return nil
166+
}
167+
168+
func (r *globalAcceleratorReconciler) cleanupGlobalAcceleratorResources(ctx context.Context, ga *agaapi.GlobalAccelerator) error {
169+
// TODO: Implement the actual AWS Global Accelerator resource cleanup
170+
// This is a placeholder implementation
171+
r.logger.Info("Cleaning up GlobalAccelerator resources", "name", ga.Name, "namespace", ga.Namespace)
172+
return nil
173+
}
174+
175+
func (r *globalAcceleratorReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, clientSet *kubernetes.Clientset) error {
176+
// Check if GlobalAccelerator CRD is available
177+
resList, err := clientSet.ServerResourcesForGroupVersion(agaResourcesGroupVersion)
178+
if err != nil {
179+
r.logger.Info("GlobalAccelerator CRD is not available, skipping controller setup")
180+
return nil
181+
}
182+
globalAcceleratorResourceAvailable := k8s.IsResourceKindAvailable(resList, globalAcceleratorKind)
183+
if !globalAcceleratorResourceAvailable {
184+
r.logger.Info("GlobalAccelerator CRD is not available, skipping controller setup")
185+
return nil
186+
}
187+
188+
if err := r.setupIndexes(ctx, mgr.GetFieldIndexer()); err != nil {
189+
return err
190+
}
191+
192+
// TODO: Add event handlers for Services, Ingresses, and Gateways
193+
// that are referenced by GlobalAccelerator endpoints
194+
195+
return ctrl.NewControllerManagedBy(mgr).
196+
For(&agaapi.GlobalAccelerator{}).
197+
Named(controllerName).
198+
WithOptions(controller.Options{
199+
MaxConcurrentReconciles: r.maxConcurrentReconciles,
200+
}).
201+
Complete(r)
202+
}
203+
204+
func (r *globalAcceleratorReconciler) setupIndexes(ctx context.Context, fieldIndexer client.FieldIndexer) error {
205+
// TODO: Add field indexes for efficient lookups
206+
return nil
207+
}

controllers/ingress/group_controller.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/pkg/errors"
1212
corev1 "k8s.io/api/core/v1"
1313
networking "k8s.io/api/networking/v1"
14-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1514
"k8s.io/apimachinery/pkg/types"
1615
"k8s.io/client-go/kubernetes"
1716
"k8s.io/client-go/tools/record"
@@ -305,7 +304,7 @@ func (r *groupReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager
305304
if err != nil {
306305
return err
307306
}
308-
ingressClassResourceAvailable := isResourceKindAvailable(resList, ingressClassKind)
307+
ingressClassResourceAvailable := k8s.IsResourceKindAvailable(resList, ingressClassKind)
309308
if err := r.setupIndexes(ctx, mgr.GetFieldIndexer(), ingressClassResourceAvailable); err != nil {
310309
return err
311310
}
@@ -401,15 +400,6 @@ func (r *groupReconciler) setupWatches(_ context.Context, c controller.Controlle
401400
return nil
402401
}
403402

404-
// isResourceKindAvailable checks whether specific kind is available.
405-
func isResourceKindAvailable(resList *metav1.APIResourceList, kind string) bool {
406-
for _, res := range resList.APIResources {
407-
if res.Kind == kind {
408-
return true
409-
}
410-
}
411-
return false
412-
}
413403

414404
func isIngressStatusEqual(a, b []networking.IngressLoadBalancerIngress) bool {
415405
if len(a) != len(b) {

helm/aws-load-balancer-controller/templates/rbac.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ rules:
119119
- apiGroups: ["gateway.networking.k8s.io"]
120120
resources: [grpcroutes/status, httproutes/status, tcproutes/status, tlsroutes/status, udproutes/status]
121121
verbs: [get, patch, update]
122+
- apiGroups: ["aga.k8s.aws"]
123+
resources: [globalaccelerators]
124+
verbs: [get, list, patch, watch]
125+
- apiGroups: ["aga.k8s.aws"]
126+
resources: [globalaccelerators/finalizers]
127+
verbs: [patch, update]
128+
- apiGroups: ["aga.k8s.aws"]
129+
resources: [globalaccelerators/status]
130+
verbs: [patch, update]
122131
---
123132
apiVersion: rbac.authorization.k8s.io/v1
124133
kind: ClusterRoleBinding

helm/aws-load-balancer-controller/values.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ controllerConfig:
375375
# NLBHealthCheckAdvancedConfig: true
376376
# ALBSingleSubnet: false
377377
# LBCapacityReservation: true
378+
# AGAController: true
378379
# EnhancedDefaultBehavior: false
379380

380381
certDiscovery:

main.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ import (
4545
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
4646
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
4747
"k8s.io/klog/v2"
48+
agaapi "sigs.k8s.io/aws-load-balancer-controller/apis/aga/v1beta1"
4849
elbv2api "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1beta1"
50+
agacontroller "sigs.k8s.io/aws-load-balancer-controller/controllers/aga"
4951
elbv2controller "sigs.k8s.io/aws-load-balancer-controller/controllers/elbv2"
5052
"sigs.k8s.io/aws-load-balancer-controller/controllers/ingress"
5153
"sigs.k8s.io/aws-load-balancer-controller/controllers/service"
@@ -79,6 +81,7 @@ var (
7981
func init() {
8082
_ = clientgoscheme.AddToScheme(scheme)
8183

84+
_ = agaapi.AddToScheme(scheme)
8285
_ = elbv2api.AddToScheme(scheme)
8386
_ = elbv2gw.AddToScheme(scheme)
8487
_ = gwv1.AddToScheme(scheme)
@@ -226,6 +229,16 @@ func main() {
226229
os.Exit(1)
227230
}
228231

232+
// Setup GlobalAccelerator controller only if enabled
233+
if controllerCFG.FeatureGates.Enabled(config.AGAController) {
234+
agaReconciler := agacontroller.NewGlobalAcceleratorReconciler(mgr.GetClient(), mgr.GetEventRecorderFor("globalAccelerator"),
235+
finalizerManager, controllerCFG, ctrl.Log.WithName("controllers").WithName("globalAccelerator"), lbcMetricsCollector, reconcileCounters)
236+
if err := agaReconciler.SetupWithManager(ctx, mgr, clientSet); err != nil {
237+
setupLog.Error(err, "unable to create controller", "controller", "GlobalAccelerator")
238+
os.Exit(1)
239+
}
240+
}
241+
229242
// Initialize common gateway configuration
230243
if controllerCFG.FeatureGates.Enabled(config.NLBGatewayAPI) || controllerCFG.FeatureGates.Enabled(config.ALBGatewayAPI) {
231244

pkg/config/controller_config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const (
2626
flagGatewayClassMaxConcurrentReconciles = "gateway-class-max-concurrent-reconciles"
2727
flagALBGatewayMaxConcurrentReconciles = "alb-gateway-max-concurrent-reconciles"
2828
flagNLBGatewayMaxConcurrentReconciles = "nlb-gateway-max-concurrent-reconciles"
29+
flagGlobalAcceleratorMaxConcurrentReconciles = "globalaccelerator-max-concurrent-reconciles"
2930
flagTargetGroupBindingMaxExponentialBackoffDelay = "targetgroupbinding-max-exponential-backoff-delay"
3031
flagLbStabilizationMonitorInterval = "lb-stabilization-monitor-interval"
3132
flagDefaultSSLPolicy = "default-ssl-policy"
@@ -119,6 +120,9 @@ type ControllerConfig struct {
119120
// NLBGatewayMaxConcurrentReconciles Max concurrent reconcile loops for NLB Gateway objects
120121
NLBGatewayMaxConcurrentReconciles int
121122

123+
// GlobalAcceleratorMaxConcurrentReconciles Max concurrent reconcile loops for GlobalAccelerator objects
124+
GlobalAcceleratorMaxConcurrentReconciles int
125+
122126
// EnableBackendSecurityGroup specifies whether to use optimized security group rules
123127
EnableBackendSecurityGroup bool
124128

@@ -164,6 +168,8 @@ func (cfg *ControllerConfig) BindFlags(fs *pflag.FlagSet) {
164168
"Maximum number of concurrently running reconcile loops for alb gateway")
165169
fs.IntVar(&cfg.NLBGatewayMaxConcurrentReconciles, flagNLBGatewayMaxConcurrentReconciles, defaultMaxConcurrentReconciles,
166170
"Maximum number of concurrently running reconcile loops for nlb gateway")
171+
fs.IntVar(&cfg.GlobalAcceleratorMaxConcurrentReconciles, flagGlobalAcceleratorMaxConcurrentReconciles, defaultMaxConcurrentReconciles,
172+
"Maximum number of concurrently running reconcile loops for globalAccelerator")
167173
fs.DurationVar(&cfg.TargetGroupBindingMaxExponentialBackoffDelay, flagTargetGroupBindingMaxExponentialBackoffDelay, defaultMaxExponentialBackoffDelay,
168174
"Maximum duration of exponential backoff for targetGroupBinding reconcile failures")
169175
fs.DurationVar(&cfg.LBStabilizationMonitorInterval, flagLbStabilizationMonitorInterval, defaultLbStabilizationMonitorInterval,

pkg/config/feature_gates.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const (
2727
SubnetDiscoveryByReachability Feature = "SubnetDiscoveryByReachability"
2828
NLBGatewayAPI Feature = "NLBGatewayAPI"
2929
ALBGatewayAPI Feature = "ALBGatewayAPI"
30+
AGAController Feature = "AGAController"
3031
EnhancedDefaultBehavior Feature = "EnhancedDefaultBehavior"
3132
)
3233

@@ -70,6 +71,7 @@ func NewFeatureGates() FeatureGates {
7071
LBCapacityReservation: true,
7172
NLBGatewayAPI: false,
7273
ALBGatewayAPI: false,
74+
AGAController: true,
7375
EnableTCPUDPListenerType: false,
7476
EnhancedDefaultBehavior: false,
7577
},

pkg/k8s/events.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,11 @@ const (
4545
// Load Balancer Configuration events
4646
LoadBalancerConfigurationEventReasonFailedAddFinalizer = "FailedAddFinalizer"
4747
LoadBalancerConfigurationEventReasonFailedRemoveFinalizer = "FailedRemoveFinalizer"
48+
49+
// GlobalAccelerator events
50+
GlobalAcceleratorEventReasonFailedAddFinalizer = "FailedAddFinalizer"
51+
GlobalAcceleratorEventReasonFailedRemoveFinalizer = "FailedRemoveFinalizer"
52+
GlobalAcceleratorEventReasonFailedUpdateStatus = "FailedUpdateStatus"
53+
GlobalAcceleratorEventReasonFailedCleanup = "FailedCleanup"
54+
GlobalAcceleratorEventReasonSuccessfullyReconciled = "SuccessfullyReconciled"
4855
)

pkg/k8s/utils.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,13 @@ func ToSliceOfNamespacedNames[T metav1.ObjectMetaAccessor](s []T) []types.Namesp
2121
}
2222
return result
2323
}
24+
25+
// IsResourceKindAvailable checks whether specific kind is available.
26+
func IsResourceKindAvailable(resList *metav1.APIResourceList, kind string) bool {
27+
for _, res := range resList.APIResources {
28+
if res.Kind == kind {
29+
return true
30+
}
31+
}
32+
return false
33+
}

0 commit comments

Comments
 (0)