Skip to content

Commit 86c34b6

Browse files
committed
[feat: aga] add accelerator model builder
1 parent fc4e2d1 commit 86c34b6

File tree

11 files changed

+1453
-8
lines changed

11 files changed

+1453
-8
lines changed

controllers/aga/globalaccelerator_controller.go

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,22 @@ import (
3131
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3232

3333
agaapi "sigs.k8s.io/aws-load-balancer-controller/apis/aga/v1beta1"
34+
"sigs.k8s.io/aws-load-balancer-controller/pkg/aga"
3435
"sigs.k8s.io/aws-load-balancer-controller/pkg/config"
36+
"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy"
37+
"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/tracking"
3538
ctrlerrors "sigs.k8s.io/aws-load-balancer-controller/pkg/error"
3639
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
3740
lbcmetrics "sigs.k8s.io/aws-load-balancer-controller/pkg/metrics/lbc"
3841
metricsutil "sigs.k8s.io/aws-load-balancer-controller/pkg/metrics/util"
42+
agamodel "sigs.k8s.io/aws-load-balancer-controller/pkg/model/aga"
43+
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
3944
"sigs.k8s.io/aws-load-balancer-controller/pkg/runtime"
4045
)
4146

4247
const (
4348
controllerName = "globalAccelerator"
49+
agaTagPrefix = "aga.k8s.aws"
4450

4551
// the groupVersion of used GlobalAccelerator resource.
4652
agaResourcesGroupVersion = "aga.k8s.aws/v1beta1"
@@ -49,23 +55,45 @@ const (
4955
// Metric stage constants
5056
MetricStageFetchGlobalAccelerator = "fetch_globalAccelerator"
5157
MetricStageAddFinalizers = "add_finalizers"
58+
MetricStageBuildModel = "build_model"
5259
MetricStageReconcileGlobalAccelerator = "reconcile_globalaccelerator"
5360

5461
// Metric error constants
5562
MetricErrorAddFinalizers = "add_finalizers_error"
5663
MetricErrorRemoveFinalizers = "remove_finalizers_error"
64+
MetricErrorBuildModel = "build_model_error"
5765
MetricErrorReconcileGlobalAccelerator = "reconcile_globalaccelerator_error"
5866
)
5967

6068
// 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 {
69+
func NewGlobalAcceleratorReconciler(k8sClient client.Client, eventRecorder record.EventRecorder, finalizerManager k8s.FinalizerManager, config config.ControllerConfig, logger logr.Logger, metricsCollector lbcmetrics.MetricCollector, reconcileCounters *metricsutil.ReconcileCounters) *globalAcceleratorReconciler {
70+
71+
// Create tracking provider
72+
trackingProvider := tracking.NewDefaultProvider(agaTagPrefix, config.ClusterName)
73+
74+
// Create model builder
75+
agaModelBuilder := aga.NewDefaultModelBuilder(
76+
k8sClient,
77+
eventRecorder,
78+
trackingProvider,
79+
config.FeatureGates,
80+
config.ClusterName,
81+
config.DefaultTags,
82+
config.ExternalManagedTags,
83+
logger.WithName("aga-model-builder"),
84+
metricsCollector,
85+
)
86+
87+
// Create stack marshaller
88+
stackMarshaller := deploy.NewDefaultStackMarshaller()
6389

6490
return &globalAcceleratorReconciler{
6591
k8sClient: k8sClient,
6692
eventRecorder: eventRecorder,
6793
finalizerManager: finalizerManager,
6894
logger: logger,
95+
modelBuilder: agaModelBuilder,
96+
stackMarshaller: stackMarshaller,
6997
metricsCollector: metricsCollector,
7098
reconcileTracker: reconcileCounters.IncrementAGA,
7199

@@ -78,6 +106,8 @@ type globalAcceleratorReconciler struct {
78106
k8sClient client.Client
79107
eventRecorder record.EventRecorder
80108
finalizerManager k8s.FinalizerManager
109+
modelBuilder aga.ModelBuilder
110+
stackMarshaller deploy.StackMarshaller
81111
logger logr.Logger
82112
metricsCollector lbcmetrics.MetricCollector
83113
reconcileTracker func(namespaceName types.NamespacedName)
@@ -157,10 +187,42 @@ func (r *globalAcceleratorReconciler) cleanupGlobalAccelerator(ctx context.Conte
157187
return nil
158188
}
159189

190+
func (r *globalAcceleratorReconciler) buildModel(ctx context.Context, ga *agaapi.GlobalAccelerator) (core.Stack, *agamodel.Accelerator, error) {
191+
stack, accelerator, err := r.modelBuilder.Build(ctx, ga)
192+
if err != nil {
193+
r.eventRecorder.Event(ga, corev1.EventTypeWarning, k8s.GlobalAcceleratorEventReasonFailedBuildModel, fmt.Sprintf("Failed build model due to %v", err))
194+
return nil, nil, err
195+
}
196+
stackJSON, err := r.stackMarshaller.Marshal(stack)
197+
if err != nil {
198+
r.eventRecorder.Event(ga, corev1.EventTypeWarning, k8s.GlobalAcceleratorEventReasonFailedBuildModel, fmt.Sprintf("Failed build model due to %v", err))
199+
return nil, nil, err
200+
}
201+
r.logger.Info("successfully built model", "model", stackJSON)
202+
return stack, accelerator, nil
203+
}
204+
160205
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
163206
r.logger.Info("Reconciling GlobalAccelerator resources", "name", ga.Name, "namespace", ga.Namespace)
207+
var stack core.Stack
208+
var accelerator *agamodel.Accelerator
209+
var err error
210+
buildModelFn := func() {
211+
stack, accelerator, err = r.buildModel(ctx, ga)
212+
}
213+
r.metricsCollector.ObserveControllerReconcileLatency(controllerName, MetricStageBuildModel, buildModelFn)
214+
if err != nil {
215+
return ctrlerrors.NewErrorWithMetrics(controllerName, MetricErrorBuildModel, err, r.metricsCollector)
216+
}
217+
218+
// Log the built model for debugging
219+
r.logger.Info("Built model successfully", "accelerator", accelerator.ID(), "stackID", stack.StackID())
220+
221+
// TODO: Implement the deploy phase
222+
// This would include:
223+
// 1. Deploy the stack to create/update AWS Global Accelerator resources
224+
// 2. Update the GlobalAccelerator status with the created resources
225+
// 3. Handle any deployment errors and update status accordingly
164226

165227
return nil
166228
}

pkg/aga/model_build_accelerator.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package aga
2+
3+
import (
4+
"context"
5+
"crypto/sha256"
6+
"encoding/hex"
7+
"fmt"
8+
awssdk "github.com/aws/aws-sdk-go-v2/aws"
9+
"k8s.io/apimachinery/pkg/util/sets"
10+
"regexp"
11+
agaapi "sigs.k8s.io/aws-load-balancer-controller/apis/aga/v1beta1"
12+
"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/tracking"
13+
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
14+
agamodel "sigs.k8s.io/aws-load-balancer-controller/pkg/model/aga"
15+
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
16+
)
17+
18+
var invalidAcceleratorNamePattern = regexp.MustCompile("[^a-zA-Z0-9_-]")
19+
20+
// acceleratorBuilder builds Accelerator model resources
21+
type acceleratorBuilder interface {
22+
Build(ctx context.Context, stack core.Stack, ga *agaapi.GlobalAccelerator) (*agamodel.Accelerator, error)
23+
}
24+
25+
// NewAcceleratorBuilder constructs new acceleratorBuilder
26+
func NewAcceleratorBuilder(trackingProvider tracking.Provider, clusterName string, defaultTags map[string]string, externalManagedTags []string) acceleratorBuilder {
27+
externalManagedTagsSet := sets.New(externalManagedTags...)
28+
tagHelper := newTagHelper(externalManagedTagsSet, defaultTags)
29+
30+
return &defaultAcceleratorBuilder{
31+
trackingProvider: trackingProvider,
32+
clusterName: clusterName,
33+
tagHelper: tagHelper,
34+
}
35+
}
36+
37+
var _ acceleratorBuilder = &defaultAcceleratorBuilder{}
38+
39+
type defaultAcceleratorBuilder struct {
40+
trackingProvider tracking.Provider
41+
clusterName string
42+
tagHelper tagHelper
43+
}
44+
45+
func (b *defaultAcceleratorBuilder) Build(ctx context.Context, stack core.Stack, ga *agaapi.GlobalAccelerator) (*agamodel.Accelerator, error) {
46+
spec, err := b.buildAcceleratorSpec(ctx, stack, ga)
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
accelerator := agamodel.NewAccelerator(stack, agamodel.ResourceIDAccelerator, spec)
52+
return accelerator, nil
53+
}
54+
55+
func (b *defaultAcceleratorBuilder) buildAcceleratorSpec(ctx context.Context, stack core.Stack, ga *agaapi.GlobalAccelerator) (agamodel.AcceleratorSpec, error) {
56+
ipAddressType := b.buildAcceleratorIPAddressType(ctx, ga)
57+
58+
name, err := b.buildAcceleratorName(ctx, ga, ipAddressType)
59+
if err != nil {
60+
return agamodel.AcceleratorSpec{}, err
61+
}
62+
63+
ipAddresses := b.buildAcceleratorIPAddresses(ctx, ga)
64+
65+
tags, err := b.buildAcceleratorTags(ctx, stack, ga)
66+
if err != nil {
67+
return agamodel.AcceleratorSpec{}, err
68+
}
69+
70+
return agamodel.AcceleratorSpec{
71+
Name: name,
72+
Enabled: awssdk.Bool(true), // Controller always creates enabled accelerator
73+
IpAddresses: ipAddresses,
74+
IPAddressType: ipAddressType,
75+
Tags: tags,
76+
}, nil
77+
}
78+
79+
func (b *defaultAcceleratorBuilder) buildAcceleratorName(_ context.Context, ga *agaapi.GlobalAccelerator, ipAddressType agamodel.IPAddressType) (string, error) {
80+
if ga.Spec.Name != nil {
81+
return *ga.Spec.Name, nil
82+
}
83+
84+
// Generate unique name using SHA256 hash
85+
gaKey := k8s.NamespacedName(ga)
86+
87+
uuidHash := sha256.New()
88+
_, _ = uuidHash.Write([]byte(b.clusterName))
89+
_, _ = uuidHash.Write([]byte(gaKey.Namespace))
90+
_, _ = uuidHash.Write([]byte(gaKey.Name))
91+
_, _ = uuidHash.Write([]byte(string(ipAddressType)))
92+
uuid := hex.EncodeToString(uuidHash.Sum(nil))
93+
94+
sanitizedNamespace := invalidAcceleratorNamePattern.ReplaceAllString(gaKey.Namespace, "")
95+
sanitizedName := invalidAcceleratorNamePattern.ReplaceAllString(gaKey.Name, "")
96+
97+
// AWS Global Accelerator name constraints: 1-64 characters, alphanumeric, underscores, and hyphens
98+
return fmt.Sprintf("k8s_%.16s_%.16s_%.25s", sanitizedNamespace, sanitizedName, uuid), nil
99+
}
100+
101+
func (b *defaultAcceleratorBuilder) buildAcceleratorIPAddresses(_ context.Context, ga *agaapi.GlobalAccelerator) []string {
102+
if ga.Spec.IpAddresses != nil {
103+
return *ga.Spec.IpAddresses
104+
}
105+
106+
// Return nil if not specified (AWS will assign automatically)
107+
return nil
108+
}
109+
110+
func (b *defaultAcceleratorBuilder) buildAcceleratorIPAddressType(_ context.Context, ga *agaapi.GlobalAccelerator) agamodel.IPAddressType {
111+
switch ga.Spec.IPAddressType {
112+
case agaapi.IPAddressTypeIPV4:
113+
return agamodel.IPAddressTypeIPV4
114+
case agaapi.IPAddressTypeDualStack:
115+
return agamodel.IPAddressTypeDualStack
116+
default:
117+
// Default to IPv4
118+
return agamodel.IPAddressTypeIPV4
119+
}
120+
}
121+
122+
func (b *defaultAcceleratorBuilder) buildAcceleratorTags(_ context.Context, stack core.Stack, ga *agaapi.GlobalAccelerator) (map[string]string, error) {
123+
// Get tags from tag helper (includes default tags and user-specified tags)
124+
tags, err := b.tagHelper.getAcceleratorTags(ga)
125+
if err != nil {
126+
return nil, err
127+
}
128+
129+
// Add tracking tags (includes cluster tag and stack tag)
130+
trackingTags := b.trackingProvider.StackTags(stack)
131+
for k, v := range trackingTags {
132+
tags[k] = v
133+
}
134+
135+
// Add resource ID tag manually since we don't have the resource object yet
136+
tags[b.trackingProvider.ResourceIDTagKey()] = agamodel.ResourceIDAccelerator
137+
138+
return tags, nil
139+
}

0 commit comments

Comments
 (0)