diff --git a/apis/gateway/v1beta1/loadbalancerconfig_types.go b/apis/gateway/v1beta1/loadbalancerconfig_types.go index 076561bc59..c9a3e164a5 100644 --- a/apis/gateway/v1beta1/loadbalancerconfig_types.go +++ b/apis/gateway/v1beta1/loadbalancerconfig_types.go @@ -44,8 +44,8 @@ type ListenerAttribute struct { Value string `json:"value"` } -// Tag defines a AWS Tag on resources. -type LoadBalancerTag struct { +// AWSTag defines a AWS Tag on resources. +type AWSTag struct { // The key of the tag. Key string `json:"key"` @@ -183,10 +183,16 @@ type LoadBalancerConfigurationSpec struct { // +optional EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic *string `json:"enforceSecurityGroupInboundRulesOnPrivateLinkTraffic,omitempty"` - // customerOwnedIpv4Pool is the ID of the customer-owned address for Application Load Balancers on Outposts pool. + // customerOwnedIpv4Pool [Application LoadBalancer] + // is the ID of the customer-owned address for Application Load Balancers on Outposts pool. // +optional CustomerOwnedIpv4Pool *string `json:"customerOwnedIpv4Pool,omitempty"` + // IPv4IPAMPoolId [Application LoadBalancer] + // defines the IPAM pool ID used for IPv4 Addresses on the ALB. + // +optional + IPv4IPAMPoolId *string `json:"ipv4IPAMPoolId,omitempty"` + // loadBalancerSubnets is an optional list of subnet configurations to be used in the LB // This value takes precedence over loadBalancerSubnetsSelector if both are selected. // +optional @@ -224,7 +230,19 @@ type LoadBalancerConfigurationSpec struct { // Tags defines list of Tags on LB. // +optional - Tags []LoadBalancerTag `json:"tags,omitempty"` + Tags []AWSTag `json:"tags,omitempty"` + + // EnableICMP [Network LoadBalancer] + // enables the creation of security group rules to the managed security group + // to allow explicit ICMP traffic for Path MTU discovery for IPv4 and dual-stack VPCs + // +optional + EnableICMP bool `json:"enableICMP,omitempty"` + + // ManageBackendSecurityGroupRules [Application / Network LoadBalancer] + // specifies whether you want the controller to configure security group rules on Node/Pod for traffic access + // when you specify securityGroups + // +optional + ManageBackendSecurityGroupRules bool `json:"manageBackendSecurityGroupRules,omitempty"` } // TODO -- these can be used to set what generation the gateway is currently on to track progress on reconcile. diff --git a/apis/gateway/v1beta1/zz_generated.deepcopy.go b/apis/gateway/v1beta1/zz_generated.deepcopy.go index 41bb66a02f..d4c9a325ab 100644 --- a/apis/gateway/v1beta1/zz_generated.deepcopy.go +++ b/apis/gateway/v1beta1/zz_generated.deepcopy.go @@ -25,6 +25,21 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSTag) DeepCopyInto(out *AWSTag) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSTag. +func (in *AWSTag) DeepCopy() *AWSTag { + if in == nil { + return nil + } + out := new(AWSTag) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HealthCheckConfiguration) DeepCopyInto(out *HealthCheckConfiguration) { *out = *in @@ -273,6 +288,11 @@ func (in *LoadBalancerConfigurationSpec) DeepCopyInto(out *LoadBalancerConfigura *out = new(string) **out = **in } + if in.IPv4IPAMPoolId != nil { + in, out := &in.IPv4IPAMPoolId, &out.IPv4IPAMPoolId + *out = new(string) + **out = **in + } if in.LoadBalancerSubnets != nil { in, out := &in.LoadBalancerSubnets, &out.LoadBalancerSubnets *out = new([]SubnetConfiguration) @@ -354,7 +374,7 @@ func (in *LoadBalancerConfigurationSpec) DeepCopyInto(out *LoadBalancerConfigura } if in.Tags != nil { in, out := &in.Tags, &out.Tags - *out = make([]LoadBalancerTag, len(*in)) + *out = make([]AWSTag, len(*in)) copy(*out, *in) } } @@ -394,21 +414,6 @@ func (in *LoadBalancerConfigurationStatus) DeepCopy() *LoadBalancerConfiguration return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LoadBalancerTag) DeepCopyInto(out *LoadBalancerTag) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerTag. -func (in *LoadBalancerTag) DeepCopy() *LoadBalancerTag { - if in == nil { - return nil - } - out := new(LoadBalancerTag) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MutualAuthenticationAttributes) DeepCopyInto(out *MutualAuthenticationAttributes) { *out = *in diff --git a/config/crd/gateway/gateway-crds.yaml b/config/crd/gateway/gateway-crds.yaml index 735b106b5e..846f287ce9 100644 --- a/config/crd/gateway/gateway-crds.yaml +++ b/config/crd/gateway/gateway-crds.yaml @@ -45,9 +45,16 @@ spec: LoadBalancerConfiguration properties: customerOwnedIpv4Pool: - description: customerOwnedIpv4Pool is the ID of the customer-owned - address for Application Load Balancers on Outposts pool. + description: |- + customerOwnedIpv4Pool [Application LoadBalancer] + is the ID of the customer-owned address for Application Load Balancers on Outposts pool. type: string + enableICMP: + description: |- + EnableICMP [Network LoadBalancer] + enables the creation of security group rules to the managed security group + to allow explicit ICMP traffic for Path MTU discovery for IPv4 and dual-stack VPCs + type: boolean enforceSecurityGroupInboundRulesOnPrivateLinkTraffic: description: enforceSecurityGroupInboundRulesOnPrivateLinkTraffic Indicates whether to evaluate inbound security group rules for traffic @@ -61,6 +68,11 @@ spec: - dualstack - dualstack-without-public-ipv4 type: string + ipv4IPAMPoolId: + description: |- + IPv4IPAMPoolId [Application LoadBalancer] + defines the IPAM pool ID used for IPv4 Addresses on the ALB. + type: string listenerConfigurations: description: listenerConfigurations is an optional list of configurations for each listener on LB @@ -212,6 +224,12 @@ spec: tag specified in the map key contains one of the values in the corresponding value list. type: object + manageBackendSecurityGroupRules: + description: |- + ManageBackendSecurityGroupRules [Application / Network LoadBalancer] + specifies whether you want the controller to configure security group rules on Node/Pod for traffic access + when you specify securityGroups + type: boolean scheme: description: scheme defines the type of LB to provision. If unspecified, it will be automatically inferred. @@ -240,7 +258,7 @@ spec: tags: description: Tags defines list of Tags on LB. items: - description: Tag defines a AWS Tag on resources. + description: AWSTag defines a AWS Tag on resources. properties: key: description: The key of the tag. diff --git a/config/crd/gateway/gateway.k8s.aws_loadbalancerconfigurations.yaml b/config/crd/gateway/gateway.k8s.aws_loadbalancerconfigurations.yaml index c858e905ce..f146a9eb99 100644 --- a/config/crd/gateway/gateway.k8s.aws_loadbalancerconfigurations.yaml +++ b/config/crd/gateway/gateway.k8s.aws_loadbalancerconfigurations.yaml @@ -46,9 +46,16 @@ spec: LoadBalancerConfiguration properties: customerOwnedIpv4Pool: - description: customerOwnedIpv4Pool is the ID of the customer-owned - address for Application Load Balancers on Outposts pool. + description: |- + customerOwnedIpv4Pool [Application LoadBalancer] + is the ID of the customer-owned address for Application Load Balancers on Outposts pool. type: string + enableICMP: + description: |- + EnableICMP [Network LoadBalancer] + enables the creation of security group rules to the managed security group + to allow explicit ICMP traffic for Path MTU discovery for IPv4 and dual-stack VPCs + type: boolean enforceSecurityGroupInboundRulesOnPrivateLinkTraffic: description: enforceSecurityGroupInboundRulesOnPrivateLinkTraffic Indicates whether to evaluate inbound security group rules for traffic @@ -62,6 +69,11 @@ spec: - dualstack - dualstack-without-public-ipv4 type: string + ipv4IPAMPoolId: + description: |- + IPv4IPAMPoolId [Application LoadBalancer] + defines the IPAM pool ID used for IPv4 Addresses on the ALB. + type: string listenerConfigurations: description: listenerConfigurations is an optional list of configurations for each listener on LB @@ -213,6 +225,12 @@ spec: tag specified in the map key contains one of the values in the corresponding value list. type: object + manageBackendSecurityGroupRules: + description: |- + ManageBackendSecurityGroupRules [Application / Network LoadBalancer] + specifies whether you want the controller to configure security group rules on Node/Pod for traffic access + when you specify securityGroups + type: boolean scheme: description: scheme defines the type of LB to provision. If unspecified, it will be automatically inferred. @@ -241,7 +259,7 @@ spec: tags: description: Tags defines list of Tags on LB. items: - description: Tag defines a AWS Tag on resources. + description: AWSTag defines a AWS Tag on resources. properties: key: description: The key of the tag. diff --git a/controllers/gateway/gateway_controller.go b/controllers/gateway/gateway_controller.go index 6c91111124..01b4abbbbc 100644 --- a/controllers/gateway/gateway_controller.go +++ b/controllers/gateway/gateway_controller.go @@ -26,6 +26,7 @@ import ( elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2" "sigs.k8s.io/aws-load-balancer-controller/pkg/networking" "sigs.k8s.io/aws-load-balancer-controller/pkg/runtime" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -46,19 +47,19 @@ var _ Reconciler = &gatewayReconciler{} // NewNLBGatewayReconciler constructs a gateway reconciler to handle specifically for NLB gateways func NewNLBGatewayReconciler(routeLoader routeutils.Loader, cloud services.Cloud, k8sClient client.Client, eventRecorder record.EventRecorder, controllerConfig config.ControllerConfig, finalizerManager k8s.FinalizerManager, networkingSGReconciler networking.SecurityGroupReconciler, networkingSGManager networking.SecurityGroupManager, elbv2TaggingManager elbv2deploy.TaggingManager, subnetResolver networking.SubnetsResolver, vpcInfoProvider networking.VPCInfoProvider, backendSGProvider networking.BackendSGProvider, sgResolver networking.SecurityGroupResolver, logger logr.Logger, metricsCollector lbcmetrics.MetricCollector, reconcileCounters *metricsutil.ReconcileCounters) Reconciler { - return newGatewayReconciler(constants.NLBGatewayController, elbv2model.LoadBalancerTypeNetwork, controllerConfig.NLBGatewayMaxConcurrentReconciles, constants.NLBGatewayTagPrefix, constants.NLBGatewayFinalizer, routeLoader, routeutils.L4RouteFilter, cloud, k8sClient, eventRecorder, controllerConfig, finalizerManager, networkingSGReconciler, networkingSGManager, elbv2TaggingManager, subnetResolver, vpcInfoProvider, backendSGProvider, sgResolver, logger, metricsCollector, reconcileCounters.IncrementNLBGateway) + return newGatewayReconciler(constants.NLBGatewayController, elbv2model.LoadBalancerTypeNetwork, controllerConfig.NLBGatewayMaxConcurrentReconciles, constants.NLBGatewayTagPrefix, shared_constants.NLBGatewayFinalizer, routeLoader, routeutils.L4RouteFilter, cloud, k8sClient, eventRecorder, controllerConfig, finalizerManager, networkingSGReconciler, networkingSGManager, elbv2TaggingManager, subnetResolver, vpcInfoProvider, backendSGProvider, sgResolver, logger, metricsCollector, reconcileCounters.IncrementNLBGateway) } // NewALBGatewayReconciler constructs a gateway reconciler to handle specifically for ALB gateways func NewALBGatewayReconciler(routeLoader routeutils.Loader, cloud services.Cloud, k8sClient client.Client, eventRecorder record.EventRecorder, controllerConfig config.ControllerConfig, finalizerManager k8s.FinalizerManager, networkingSGReconciler networking.SecurityGroupReconciler, networkingSGManager networking.SecurityGroupManager, elbv2TaggingManager elbv2deploy.TaggingManager, subnetResolver networking.SubnetsResolver, vpcInfoProvider networking.VPCInfoProvider, backendSGProvider networking.BackendSGProvider, sgResolver networking.SecurityGroupResolver, logger logr.Logger, metricsCollector lbcmetrics.MetricCollector, reconcileCounters *metricsutil.ReconcileCounters) Reconciler { - return newGatewayReconciler(constants.ALBGatewayController, elbv2model.LoadBalancerTypeApplication, controllerConfig.ALBGatewayMaxConcurrentReconciles, constants.ALBGatewayTagPrefix, constants.ALBGatewayFinalizer, routeLoader, routeutils.L7RouteFilter, cloud, k8sClient, eventRecorder, controllerConfig, finalizerManager, networkingSGReconciler, networkingSGManager, elbv2TaggingManager, subnetResolver, vpcInfoProvider, backendSGProvider, sgResolver, logger, metricsCollector, reconcileCounters.IncrementALBGateway) + return newGatewayReconciler(constants.ALBGatewayController, elbv2model.LoadBalancerTypeApplication, controllerConfig.ALBGatewayMaxConcurrentReconciles, constants.ALBGatewayTagPrefix, shared_constants.ALBGatewayFinalizer, routeLoader, routeutils.L7RouteFilter, cloud, k8sClient, eventRecorder, controllerConfig, finalizerManager, networkingSGReconciler, networkingSGManager, elbv2TaggingManager, subnetResolver, vpcInfoProvider, backendSGProvider, sgResolver, logger, metricsCollector, reconcileCounters.IncrementALBGateway) } // newGatewayReconciler constructs a reconciler that responds to gateway object changes func newGatewayReconciler(controllerName string, lbType elbv2model.LoadBalancerType, maxConcurrentReconciles int, gatewayTagPrefix string, finalizer string, routeLoader routeutils.Loader, routeFilter routeutils.LoadRouteFilter, cloud services.Cloud, k8sClient client.Client, eventRecorder record.EventRecorder, controllerConfig config.ControllerConfig, finalizerManager k8s.FinalizerManager, networkingSGReconciler networking.SecurityGroupReconciler, networkingSGManager networking.SecurityGroupManager, elbv2TaggingManager elbv2deploy.TaggingManager, subnetResolver networking.SubnetsResolver, vpcInfoProvider networking.VPCInfoProvider, backendSGProvider networking.BackendSGProvider, sgResolver networking.SecurityGroupResolver, logger logr.Logger, metricsCollector lbcmetrics.MetricCollector, reconcileTracker func(namespaceName types.NamespacedName)) Reconciler { trackingProvider := tracking.NewDefaultProvider(gatewayTagPrefix, controllerConfig.ClusterName) - modelBuilder := gatewaymodel.NewModelBuilder(subnetResolver, vpcInfoProvider, cloud.VpcID(), lbType, trackingProvider, elbv2TaggingManager, cloud.EC2(), controllerConfig.FeatureGates, controllerConfig.ClusterName, controllerConfig.DefaultTags, sets.New(controllerConfig.ExternalManagedTags...), controllerConfig.DefaultSSLPolicy, controllerConfig.DefaultTargetType, controllerConfig.DefaultLoadBalancerScheme, backendSGProvider, sgResolver, controllerConfig.EnableBackendSecurityGroup, controllerConfig.DisableRestrictedSGRules, logger) + modelBuilder := gatewaymodel.NewModelBuilder(subnetResolver, vpcInfoProvider, cloud.VpcID(), lbType, trackingProvider, elbv2TaggingManager, controllerConfig, cloud.EC2(), controllerConfig.FeatureGates, controllerConfig.ClusterName, controllerConfig.DefaultTags, sets.New(controllerConfig.ExternalManagedTags...), controllerConfig.DefaultSSLPolicy, controllerConfig.DefaultTargetType, controllerConfig.DefaultLoadBalancerScheme, backendSGProvider, sgResolver, controllerConfig.EnableBackendSecurityGroup, controllerConfig.DisableRestrictedSGRules, logger) stackMarshaller := deploy.NewDefaultStackMarshaller() stackDeployer := deploy.NewDefaultStackDeployer(cloud, k8sClient, networkingSGManager, networkingSGReconciler, elbv2TaggingManager, controllerConfig, gatewayTagPrefix, logger, metricsCollector, controllerName) diff --git a/controllers/service/service_controller.go b/controllers/service/service_controller.go index 6aa7383dfd..2357ba4deb 100644 --- a/controllers/service/service_controller.go +++ b/controllers/service/service_controller.go @@ -3,6 +3,7 @@ package service import ( "context" "fmt" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -33,7 +34,6 @@ import ( ) const ( - serviceFinalizer = "service.k8s.aws/resources" serviceTagPrefix = "service.k8s.aws" serviceAnnotationPrefix = "service.beta.kubernetes.io" controllerName = "service" @@ -47,7 +47,7 @@ func NewServiceReconciler(cloud services.Cloud, k8sClient client.Client, eventRe annotationParser := annotations.NewSuffixAnnotationParser(serviceAnnotationPrefix) trackingProvider := tracking.NewDefaultProvider(serviceTagPrefix, controllerConfig.ClusterName) - serviceUtils := service.NewServiceUtils(annotationParser, serviceFinalizer, controllerConfig.ServiceConfig.LoadBalancerClass, controllerConfig.FeatureGates) + serviceUtils := service.NewServiceUtils(annotationParser, shared_constants.ServiceFinalizer, controllerConfig.ServiceConfig.LoadBalancerClass, controllerConfig.FeatureGates) modelBuilder := service.NewDefaultModelBuilder(annotationParser, subnetsResolver, vpcInfoProvider, cloud.VpcID(), trackingProvider, elbv2TaggingManager, cloud.EC2(), controllerConfig.FeatureGates, controllerConfig.ClusterName, controllerConfig.DefaultTags, controllerConfig.ExternalManagedTags, controllerConfig.DefaultSSLPolicy, controllerConfig.DefaultTargetType, controllerConfig.DefaultLoadBalancerScheme, controllerConfig.FeatureGates.Enabled(config.EnableIPTargetType), serviceUtils, @@ -170,7 +170,7 @@ func (r *serviceReconciler) reconcileLoadBalancerResources(ctx context.Context, var err error addFinalizersFn := func() { - err = r.finalizerManager.AddFinalizers(ctx, svc, serviceFinalizer) + err = r.finalizerManager.AddFinalizers(ctx, svc, shared_constants.ServiceFinalizer) } r.metricsCollector.ObserveControllerReconcileLatency(controllerName, "add_finalizers", addFinalizersFn) if err != nil { @@ -214,7 +214,7 @@ func (r *serviceReconciler) reconcileLoadBalancerResources(ctx context.Context, } func (r *serviceReconciler) cleanupLoadBalancerResources(ctx context.Context, svc *corev1.Service, stack core.Stack) error { - if k8s.HasFinalizer(svc, serviceFinalizer) { + if k8s.HasFinalizer(svc, shared_constants.ServiceFinalizer) { err := r.deployModel(ctx, svc, stack) if err != nil { return err @@ -226,7 +226,7 @@ func (r *serviceReconciler) cleanupLoadBalancerResources(ctx context.Context, sv r.eventRecorder.Event(svc, corev1.EventTypeWarning, k8s.ServiceEventReasonFailedCleanupStatus, fmt.Sprintf("Failed update status due to %v", err)) return err } - if err := r.finalizerManager.RemoveFinalizers(ctx, svc, serviceFinalizer); err != nil { + if err := r.finalizerManager.RemoveFinalizers(ctx, svc, shared_constants.ServiceFinalizer); err != nil { r.eventRecorder.Event(svc, corev1.EventTypeWarning, k8s.ServiceEventReasonFailedRemoveFinalizer, fmt.Sprintf("Failed remove finalizer due to %v", err)) return err } diff --git a/main.go b/main.go index f0f1250dfc..e57c640037 100644 --- a/main.go +++ b/main.go @@ -148,6 +148,8 @@ func main() { os.Exit(1) } + nlbGatewayEnabled := controllerCFG.FeatureGates.Enabled(config.NLBGatewayAPI) + albGatewayEnabled := controllerCFG.FeatureGates.Enabled(config.ALBGatewayAPI) podInfoRepo := k8s.NewDefaultPodInfoRepo(clientSet.CoreV1().RESTClient(), controllerCFG.RuntimeConfig.WatchNamespace, ctrl.Log) finalizerManager := k8s.NewDefaultFinalizerManager(mgr.GetClient(), ctrl.Log) sgManager := networking.NewDefaultSecurityGroupManager(cloud.EC2(), ctrl.Log) @@ -165,7 +167,7 @@ func main() { cloud.VpcID(), controllerCFG.ClusterName, controllerCFG.FeatureGates.Enabled(config.EndpointsFailOpen), controllerCFG.EnableEndpointSlices, controllerCFG.DisableRestrictedSGRules, controllerCFG.ServiceTargetENISGTags, mgr.GetEventRecorderFor("targetGroupBinding"), ctrl.Log) backendSGProvider := networking.NewBackendSGProvider(controllerCFG.ClusterName, controllerCFG.BackendSecurityGroup, - cloud.VpcID(), cloud.EC2(), mgr.GetClient(), controllerCFG.DefaultTags, ctrl.Log.WithName("backend-sg-provider")) + cloud.VpcID(), cloud.EC2(), mgr.GetClient(), controllerCFG.DefaultTags, nlbGatewayEnabled || albGatewayEnabled, ctrl.Log.WithName("backend-sg-provider")) sgResolver := networking.NewDefaultSecurityGroupResolver(cloud.EC2(), cloud.VpcID()) elbv2TaggingManager := elbv2deploy.NewDefaultTaggingManager(cloud.ELBV2(), cloud.VpcID(), controllerCFG.FeatureGates, cloud.RGT(), ctrl.Log) ingGroupReconciler := ingress.NewGroupReconciler(cloud, mgr.GetClient(), mgr.GetEventRecorderFor("ingress"), @@ -221,7 +223,7 @@ func main() { } // Setup NLB Gateway controller if enabled - if controllerCFG.FeatureGates.Enabled(config.NLBGatewayAPI) { + if nlbGatewayEnabled { gwControllerConfig.routeLoader = routeutils.NewLoader(mgr.GetClient()) if err := setupGatewayController(ctx, mgr, gwControllerConfig, constants.NLBGatewayController); err != nil { setupLog.Error(err, "failed to setup NLB Gateway controller") @@ -230,7 +232,7 @@ func main() { } // Setup ALB Gateway controller if enabled - if controllerCFG.FeatureGates.Enabled(config.ALBGatewayAPI) { + if albGatewayEnabled { if gwControllerConfig.routeLoader == nil { gwControllerConfig.routeLoader = routeutils.NewLoader(mgr.GetClient()) } diff --git a/pkg/config/controller_config.go b/pkg/config/controller_config.go index 1f0903af8a..3aa091c4fc 100644 --- a/pkg/config/controller_config.go +++ b/pkg/config/controller_config.go @@ -1,6 +1,7 @@ package config import ( + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "strings" "time" @@ -45,12 +46,16 @@ const ( var ( trackingTagKeys = sets.NewString( - "elbv2.k8s.aws/cluster", - "elbv2.k8s.aws/resource", + shared_constants.TagKeyK8sCluster, + shared_constants.TagKeyResource, "ingress.k8s.aws/stack", "ingress.k8s.aws/resource", "service.k8s.aws/stack", "service.k8s.aws/resource", + "gateway.k8s.aws.nlb/resource", + "gateway.k8s.aws.alb/resource", + "gateway.k8s.aws.nlb/stack", + "gateway.k8s.aws.alb/stack", ) ) diff --git a/pkg/config/controller_config_test.go b/pkg/config/controller_config_test.go index 0351ded53f..74ec7ae382 100644 --- a/pkg/config/controller_config_test.go +++ b/pkg/config/controller_config_test.go @@ -3,6 +3,7 @@ package config import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "testing" ) @@ -28,7 +29,7 @@ func TestControllerConfig_validateDefaultTagsCollisionWithTrackingTags(t *testin name: "default tags and tracking tags have collision", fields: fields{ DefaultTags: map[string]string{ - "elbv2.k8s.aws/cluster": "value-a", + shared_constants.TagKeyK8sCluster: "value-a", }, }, wantErr: errors.New("tag key elbv2.k8s.aws/cluster cannot be specified in default-tags flag"), @@ -75,7 +76,7 @@ func TestControllerConfig_validateExternalManagedTagsCollisionWithTrackingTags(t { name: "external managed tags and tracking tags have collision", fields: fields{ - ExternalManagedTags: []string{"elbv2.k8s.aws/cluster"}, + ExternalManagedTags: []string{shared_constants.TagKeyK8sCluster}, }, wantErr: errors.New("tag key elbv2.k8s.aws/cluster cannot be specified in external-managed-tags flag"), }, diff --git a/pkg/deploy/elbv2/load_balancer_synthesizer.go b/pkg/deploy/elbv2/load_balancer_synthesizer.go index 629071cce8..aee7c696fb 100644 --- a/pkg/deploy/elbv2/load_balancer_synthesizer.go +++ b/pkg/deploy/elbv2/load_balancer_synthesizer.go @@ -14,13 +14,10 @@ import ( "sigs.k8s.io/aws-load-balancer-controller/pkg/model/core" elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2" "sigs.k8s.io/aws-load-balancer-controller/pkg/runtime" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "strings" ) -const ( - lbAttrsDeletionProtectionEnabled = "deletion_protection.enabled" -) - // NewLoadBalancerSynthesizer constructs loadBalancerSynthesizer func NewLoadBalancerSynthesizer(elbv2Client services.ELBV2, trackingProvider tracking.Provider, taggingManager TaggingManager, lbManager LoadBalancerManager, logger logr.Logger, featureGates config.FeatureGates, controllerConfig config.ControllerConfig, stack core.Stack) *loadBalancerSynthesizer { @@ -116,7 +113,7 @@ func (s *loadBalancerSynthesizer) disableDeletionProtection(ctx context.Context, input := &elbv2sdk.ModifyLoadBalancerAttributesInput{ Attributes: []elbv2types.LoadBalancerAttribute{ { - Key: awssdk.String(lbAttrsDeletionProtectionEnabled), + Key: awssdk.String(shared_constants.LBAttributeDeletionProtection), Value: awssdk.String("false"), }, }, diff --git a/pkg/deploy/tracking/provider.go b/pkg/deploy/tracking/provider.go index 32194e5b3d..7545fabfc4 100644 --- a/pkg/deploy/tracking/provider.go +++ b/pkg/deploy/tracking/provider.go @@ -4,8 +4,11 @@ import ( "fmt" "sigs.k8s.io/aws-load-balancer-controller/pkg/algorithm" "sigs.k8s.io/aws-load-balancer-controller/pkg/model/core" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" ) +// TODO(ztn) - Add Gateway documentation here? + //we use AWS tags and K8s labels to track resources we have created. // //For AWS resources created by this controller, the tagging strategy is as follows: @@ -32,9 +35,6 @@ import ( // * `service.k8s.aws/stack-namespace: namespace` // * `service.k8s.aws/stack-name: serviceName` -// AWS TagKey for cluster resources. -const clusterNameTagKey = "elbv2.k8s.aws/cluster" - // Legacy AWS TagKey for cluster resources, which is used by AWSALBIngressController(v1.1.3+) const clusterNameTagKeyLegacy = "ingress.k8s.aws/cluster" @@ -85,8 +85,8 @@ func (p *defaultProvider) ResourceIDTagKey() string { func (p *defaultProvider) StackTags(stack core.Stack) map[string]string { stackID := stack.StackID() return map[string]string{ - clusterNameTagKey: p.clusterName, - p.prefixedTrackingKey("stack"): stackID.String(), + shared_constants.TagKeyK8sCluster: p.clusterName, + p.prefixedTrackingKey("stack"): stackID.String(), } } diff --git a/pkg/deploy/tracking/provider_test.go b/pkg/deploy/tracking/provider_test.go index 2d1e926682..4ce75e6d11 100644 --- a/pkg/deploy/tracking/provider_test.go +++ b/pkg/deploy/tracking/provider_test.go @@ -3,6 +3,7 @@ package tracking import ( "github.com/stretchr/testify/assert" "sigs.k8s.io/aws-load-balancer-controller/pkg/model/core" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "testing" ) @@ -46,8 +47,8 @@ func Test_defaultProvider_StackTags(t *testing.T) { provider: NewDefaultProvider("ingress.k8s.aws", "cluster-name"), args: args{stack: core.NewDefaultStack(core.StackID{Namespace: "", Name: "awesome-group"})}, want: map[string]string{ - "elbv2.k8s.aws/cluster": "cluster-name", - "ingress.k8s.aws/stack": "awesome-group", + shared_constants.TagKeyK8sCluster: "cluster-name", + "ingress.k8s.aws/stack": "awesome-group", }, }, { @@ -55,8 +56,8 @@ func Test_defaultProvider_StackTags(t *testing.T) { provider: NewDefaultProvider("ingress.k8s.aws", "cluster-name"), args: args{stack: core.NewDefaultStack(core.StackID{Namespace: "namespace", Name: "ingressName"})}, want: map[string]string{ - "elbv2.k8s.aws/cluster": "cluster-name", - "ingress.k8s.aws/stack": "namespace/ingressName", + shared_constants.TagKeyK8sCluster: "cluster-name", + "ingress.k8s.aws/stack": "namespace/ingressName", }, }, { @@ -64,8 +65,8 @@ func Test_defaultProvider_StackTags(t *testing.T) { provider: NewDefaultProvider("service.k8s.aws", "cluster-name"), args: args{stack: core.NewDefaultStack(core.StackID{Namespace: "namespace", Name: "serviceName"})}, want: map[string]string{ - "elbv2.k8s.aws/cluster": "cluster-name", - "service.k8s.aws/stack": "namespace/serviceName", + shared_constants.TagKeyK8sCluster: "cluster-name", + "service.k8s.aws/stack": "namespace/serviceName", }, }, } @@ -100,9 +101,9 @@ func Test_defaultProvider_ResourceTags(t *testing.T) { res: fakeRes, }, want: map[string]string{ - "elbv2.k8s.aws/cluster": "cluster-name", - "ingress.k8s.aws/stack": "namespace/ingressName", - "ingress.k8s.aws/resource": "fake-id", + shared_constants.TagKeyK8sCluster: "cluster-name", + "ingress.k8s.aws/stack": "namespace/ingressName", + "ingress.k8s.aws/resource": "fake-id", }, }, } diff --git a/pkg/gateway/constants/controller_constants.go b/pkg/gateway/constants/controller_constants.go index 194a0373fa..cac44acb3b 100644 --- a/pkg/gateway/constants/controller_constants.go +++ b/pkg/gateway/constants/controller_constants.go @@ -25,9 +25,6 @@ const ( // NLBRouteResourceGroupVersion the groupVersion used by TCPRoute and UDPRoute NLBRouteResourceGroupVersion = "gateway.networking.k8s.io/v1alpha2" - - // NLBGatewayFinalizer the finalizer we attach the NLB Gateway object - NLBGatewayFinalizer = "gateway.k8s.aws/nlb-finalizer" ) /* @@ -43,7 +40,4 @@ const ( // ALBRouteResourceGroupVersion the groupVersion used by HTTPRoute and GRPCRoute ALBRouteResourceGroupVersion = "gateway.networking.k8s.io/v1" - - // ALBGatewayFinalizer the finalizer we attach the ALB Gateway object - ALBGatewayFinalizer = "gateway.k8s.aws/alb-finalizer" ) diff --git a/pkg/gateway/model/base_model_builder.go b/pkg/gateway/model/base_model_builder.go index 83f22b266e..78245db66d 100644 --- a/pkg/gateway/model/base_model_builder.go +++ b/pkg/gateway/model/base_model_builder.go @@ -15,6 +15,7 @@ import ( "sigs.k8s.io/aws-load-balancer-controller/pkg/model/core" elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2" "sigs.k8s.io/aws-load-balancer-controller/pkg/networking" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" gwv1 "sigs.k8s.io/gateway-api/apis/v1" "strconv" ) @@ -28,15 +29,24 @@ type Builder interface { // NewModelBuilder construct a new baseModelBuilder func NewModelBuilder(subnetsResolver networking.SubnetsResolver, vpcInfoProvider networking.VPCInfoProvider, vpcID string, loadBalancerType elbv2model.LoadBalancerType, trackingProvider tracking.Provider, - elbv2TaggingManager elbv2deploy.TaggingManager, ec2Client services.EC2, featureGates config.FeatureGates, clusterName string, defaultTags map[string]string, + elbv2TaggingManager elbv2deploy.TaggingManager, lbcConfig config.ControllerConfig, ec2Client services.EC2, featureGates config.FeatureGates, clusterName string, defaultTags map[string]string, externalManagedTags sets.Set[string], defaultSSLPolicy string, defaultTargetType string, defaultLoadBalancerScheme string, backendSGProvider networking.BackendSGProvider, sgResolver networking.SecurityGroupResolver, enableBackendSG bool, disableRestrictedSGRules bool, logger logr.Logger) Builder { + gwTagHelper := newTagHelper(sets.New(lbcConfig.ExternalManagedTags...), lbcConfig.DefaultTags) subnetBuilder := newSubnetModelBuilder(loadBalancerType, trackingProvider, subnetsResolver, elbv2TaggingManager) + sgBuilder := newSecurityGroupBuilder(gwTagHelper, clusterName, enableBackendSG, sgResolver, backendSGProvider, logger) + lbBuilder := newLoadBalancerBuilder(loadBalancerType, gwTagHelper, clusterName) return &baseModelBuilder{ - lbBuilder: newLoadBalancerBuilder(subnetBuilder, defaultLoadBalancerScheme), + subnetBuilder: subnetBuilder, + securityGroupBuilder: sgBuilder, + lbBuilder: lbBuilder, + logger: logger, + + defaultLoadBalancerScheme: elbv2model.LoadBalancerScheme(defaultLoadBalancerScheme), + defaultIPType: elbv2model.IPAddressTypeIPV4, } } @@ -45,25 +55,62 @@ var _ Builder = &baseModelBuilder{} type baseModelBuilder struct { lbBuilder loadBalancerBuilder logger logr.Logger + + subnetBuilder subnetModelBuilder + securityGroupBuilder securityGroupBuilder + + defaultLoadBalancerScheme elbv2model.LoadBalancerScheme + defaultIPType elbv2model.IPAddressType } func (baseBuilder *baseModelBuilder) Build(ctx context.Context, gw *gwv1.Gateway, lbConf *elbv2gw.LoadBalancerConfiguration, routes map[int][]routeutils.RouteDescriptor) (core.Stack, *elbv2model.LoadBalancer, bool, error) { + stack := core.NewDefaultStack(core.StackID(k8s.NamespacedName(gw))) if gw.DeletionTimestamp != nil && !gw.DeletionTimestamp.IsZero() { if baseBuilder.isDeleteProtected(lbConf) { return nil, nil, false, errors.Errorf("Unable to delete gateway %+v because deletion protection is enabled.", k8s.NamespacedName(gw)) } + return stack, nil, false, nil } - stack := core.NewDefaultStack(core.StackID(k8s.NamespacedName(gw))) + /* Basic LB stuff (Scheme, IP Address Type) */ + scheme, err := baseBuilder.buildLoadBalancerScheme(lbConf) + + if err != nil { + return nil, nil, false, err + } + + ipAddressType, err := baseBuilder.buildLoadBalancerIPAddressType(lbConf) + + if err != nil { + return nil, nil, false, err + } + + /* Subnets */ + + subnets, err := baseBuilder.subnetBuilder.buildLoadBalancerSubnets(ctx, lbConf.Spec.LoadBalancerSubnets, lbConf.Spec.LoadBalancerSubnetsSelector, scheme, ipAddressType, stack) + + if err != nil { + return nil, nil, false, err + } + + /* Security Groups */ + + securityGroups, err := baseBuilder.securityGroupBuilder.buildSecurityGroups(ctx, stack, lbConf, gw, routes, ipAddressType) + + if err != nil { + return nil, nil, false, err + } - // TODO - Fix - _, err := baseBuilder.lbBuilder.buildLoadBalancerSpec(ctx, gw, stack, lbConf, routes) + /* Combine everything to form a LoadBalancer */ + spec, err := baseBuilder.lbBuilder.buildLoadBalancerSpec(scheme, ipAddressType, gw, lbConf, subnets, securityGroups.securityGroupTokens) if err != nil { return nil, nil, false, err } - return stack, nil, false, nil + lb := elbv2model.NewLoadBalancer(stack, resourceIDLoadBalancer, spec) + + return stack, lb, securityGroups.backendSecurityGroupAllocated, nil } func (baseBuilder *baseModelBuilder) isDeleteProtected(lbConf *elbv2gw.LoadBalancerConfiguration) bool { @@ -72,7 +119,7 @@ func (baseBuilder *baseModelBuilder) isDeleteProtected(lbConf *elbv2gw.LoadBalan } for _, attr := range lbConf.Spec.LoadBalancerAttributes { - if attr.Key == deletionProtectionAttributeKey { + if attr.Key == shared_constants.LBAttributeDeletionProtection { deletionProtectionEnabled, err := strconv.ParseBool(attr.Value) if err != nil { @@ -86,3 +133,38 @@ func (baseBuilder *baseModelBuilder) isDeleteProtected(lbConf *elbv2gw.LoadBalan return false } + +func (baseBuilder *baseModelBuilder) buildLoadBalancerScheme(lbConf *elbv2gw.LoadBalancerConfiguration) (elbv2model.LoadBalancerScheme, error) { + scheme := lbConf.Spec.Scheme + + if scheme == nil { + return baseBuilder.defaultLoadBalancerScheme, nil + } + switch *scheme { + case elbv2gw.LoadBalancerScheme(elbv2model.LoadBalancerSchemeInternetFacing): + return elbv2model.LoadBalancerSchemeInternetFacing, nil + case elbv2gw.LoadBalancerScheme(elbv2model.LoadBalancerSchemeInternal): + return elbv2model.LoadBalancerSchemeInternal, nil + default: + return "", errors.Errorf("unknown scheme: %v", *scheme) + } +} + +// buildLoadBalancerIPAddressType builds the LoadBalancer IPAddressType. +func (baseBuilder *baseModelBuilder) buildLoadBalancerIPAddressType(lbConf *elbv2gw.LoadBalancerConfiguration) (elbv2model.IPAddressType, error) { + + if lbConf.Spec.IpAddressType == nil { + return baseBuilder.defaultIPType, nil + } + + switch *lbConf.Spec.IpAddressType { + case elbv2gw.LoadBalancerIpAddressType(elbv2model.IPAddressTypeIPV4): + return elbv2model.IPAddressTypeIPV4, nil + case elbv2gw.LoadBalancerIpAddressType(elbv2model.IPAddressTypeDualStack): + return elbv2model.IPAddressTypeDualStack, nil + case elbv2gw.LoadBalancerIpAddressType(elbv2model.IPAddressTypeDualStackWithoutPublicIPV4): + return elbv2model.IPAddressTypeDualStackWithoutPublicIPV4, nil + default: + return "", errors.Errorf("unknown IPAddressType: %v", *lbConf.Spec.IpAddressType) + } +} diff --git a/pkg/gateway/model/constants.go b/pkg/gateway/model/constants.go deleted file mode 100644 index 327cd6f28a..0000000000 --- a/pkg/gateway/model/constants.go +++ /dev/null @@ -1,5 +0,0 @@ -package model - -const ( - deletionProtectionAttributeKey = "deletion_protection.enabled" -) diff --git a/pkg/gateway/model/gateway_tag_helper.go b/pkg/gateway/model/gateway_tag_helper.go new file mode 100644 index 0000000000..892abdd899 --- /dev/null +++ b/pkg/gateway/model/gateway_tag_helper.go @@ -0,0 +1,56 @@ +package model + +import ( + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/util/sets" + elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1" + "sigs.k8s.io/aws-load-balancer-controller/pkg/algorithm" +) + +type tagHelper interface { + getGatewayTags(lbConf *elbv2gw.LoadBalancerConfiguration) (map[string]string, error) +} + +type tagHelperImpl struct { + externalManagedTags sets.Set[string] + defaultTags map[string]string +} + +func newTagHelper(externalManagedTags sets.Set[string], defaultTags map[string]string) tagHelper { + return &tagHelperImpl{ + externalManagedTags: externalManagedTags, + defaultTags: defaultTags, + } +} + +func (t *tagHelperImpl) getGatewayTags(lbConf *elbv2gw.LoadBalancerConfiguration) (map[string]string, error) { + var annotationTags map[string]string + + if lbConf != nil { + annotationTags = t.convertTagsToMap(lbConf.Spec.Tags) + } + + if err := t.validateTagCollisionWithExternalManagedTags(annotationTags); err != nil { + return nil, err + } + + return algorithm.MergeStringMap(t.defaultTags, annotationTags), nil +} + +func (t *tagHelperImpl) validateTagCollisionWithExternalManagedTags(tags map[string]string) error { + for tagKey := range tags { + if t.externalManagedTags.Has(tagKey) { + return errors.Errorf("external managed tag key %v cannot be specified", tagKey) + } + } + return nil +} + +func (t *tagHelperImpl) convertTagsToMap(tags []elbv2gw.AWSTag) map[string]string { + m := make(map[string]string) + + for _, tag := range tags { + m[tag.Key] = tag.Value + } + return m +} diff --git a/pkg/gateway/model/mock_gateway_tag_helper.go b/pkg/gateway/model/mock_gateway_tag_helper.go new file mode 100644 index 0000000000..fe063ff75a --- /dev/null +++ b/pkg/gateway/model/mock_gateway_tag_helper.go @@ -0,0 +1,14 @@ +package model + +import elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1" + +type mockTagHelper struct { + tags map[string]string + err error +} + +func (m *mockTagHelper) getGatewayTags(lbConf *elbv2gw.LoadBalancerConfiguration) (map[string]string, error) { + return m.tags, m.err +} + +var _ tagHelper = &mockTagHelper{} diff --git a/pkg/gateway/model/model_build_loadbalancer.go b/pkg/gateway/model/model_build_loadbalancer.go index 98945e4d8d..3eddc0016b 100644 --- a/pkg/gateway/model/model_build_loadbalancer.go +++ b/pkg/gateway/model/model_build_loadbalancer.go @@ -1,97 +1,123 @@ package model import ( - "context" - "github.com/pkg/errors" + "crypto/sha256" + "encoding/hex" + "fmt" + "regexp" elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1" - "sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/routeutils" "sigs.k8s.io/aws-load-balancer-controller/pkg/model/core" elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2" gwv1 "sigs.k8s.io/gateway-api/apis/v1" ) +var invalidLoadBalancerNamePattern = regexp.MustCompile("[[:^alnum:]]") + +const ( + resourceIDLoadBalancer = "LoadBalancer" +) + type loadBalancerBuilder interface { - buildLoadBalancerSpec(ctx context.Context, gw *gwv1.Gateway, stack core.Stack, lbConf *elbv2gw.LoadBalancerConfiguration, routes map[int][]routeutils.RouteDescriptor) (elbv2model.LoadBalancerSpec, error) + buildLoadBalancerSpec(scheme elbv2model.LoadBalancerScheme, ipAddressType elbv2model.IPAddressType, gw *gwv1.Gateway, lbConf *elbv2gw.LoadBalancerConfiguration, subnets buildLoadBalancerSubnetsOutput, securityGroupTokens []core.StringToken) (elbv2model.LoadBalancerSpec, error) } type loadBalancerBuilderImpl struct { - subnetBuilder subnetModelBuilder - - defaultLoadBalancerScheme elbv2model.LoadBalancerScheme - defaultIPType elbv2model.IPAddressType + loadBalancerType elbv2model.LoadBalancerType + clusterName string + tagHelper tagHelper } -func newLoadBalancerBuilder(subnetBuilder subnetModelBuilder, defaultLoadBalancerScheme string) loadBalancerBuilder { - +func newLoadBalancerBuilder(loadBalancerType elbv2model.LoadBalancerType, tagHelper tagHelper, clusterName string) loadBalancerBuilder { return &loadBalancerBuilderImpl{ - subnetBuilder: subnetBuilder, - defaultLoadBalancerScheme: elbv2model.LoadBalancerScheme(defaultLoadBalancerScheme), - defaultIPType: elbv2model.IPAddressTypeIPV4, + loadBalancerType: loadBalancerType, + clusterName: clusterName, + tagHelper: tagHelper, } } -func (lbModelBuilder *loadBalancerBuilderImpl) buildLoadBalancerSpec(ctx context.Context, gw *gwv1.Gateway, stack core.Stack, lbConf *elbv2gw.LoadBalancerConfiguration, routes map[int][]routeutils.RouteDescriptor) (elbv2model.LoadBalancerSpec, error) { - scheme, err := lbModelBuilder.buildLoadBalancerScheme(lbConf) +func (lbModelBuilder *loadBalancerBuilderImpl) buildLoadBalancerSpec(scheme elbv2model.LoadBalancerScheme, ipAddressType elbv2model.IPAddressType, gw *gwv1.Gateway, lbConf *elbv2gw.LoadBalancerConfiguration, subnets buildLoadBalancerSubnetsOutput, securityGroupTokens []core.StringToken) (elbv2model.LoadBalancerSpec, error) { + + name, err := lbModelBuilder.buildLoadBalancerName(lbConf, gw, scheme) if err != nil { return elbv2model.LoadBalancerSpec{}, err } - ipAddressType, err := lbModelBuilder.buildLoadBalancerIPAddressType(lbConf) + + tags, err := lbModelBuilder.tagHelper.getGatewayTags(lbConf) if err != nil { return elbv2model.LoadBalancerSpec{}, err } - configuredSubnets, sourcePrefixEnabled, err := lbModelBuilder.subnetBuilder.buildLoadBalancerSubnets(ctx, lbConf.Spec.LoadBalancerSubnets, lbConf.Spec.LoadBalancerSubnetsSelector, scheme, ipAddressType, stack) - if err != nil { - return elbv2model.LoadBalancerSpec{}, err + + spec := elbv2model.LoadBalancerSpec{ + Name: name, + Type: lbModelBuilder.loadBalancerType, + Scheme: scheme, + IPAddressType: ipAddressType, + SubnetMappings: subnets.subnets, + SecurityGroups: securityGroupTokens, + LoadBalancerAttributes: lbModelBuilder.buildLoadBalancerAttributes(lbConf), + MinimumLoadBalancerCapacity: lbModelBuilder.buildLoadBalancerMinimumCapacity(lbConf), + Tags: tags, } - return elbv2model.LoadBalancerSpec{ - Type: elbv2model.LoadBalancerTypeApplication, - Scheme: scheme, - IPAddressType: ipAddressType, - SubnetMappings: configuredSubnets, - EnablePrefixForIpv6SourceNat: lbModelBuilder.translateSourcePrefixEnabled(sourcePrefixEnabled), - }, nil -} + if lbModelBuilder.loadBalancerType == elbv2model.LoadBalancerTypeNetwork { + lbModelBuilder.addL4Fields(&spec, lbConf, subnets) + } -func (lbModelBuilder *loadBalancerBuilderImpl) translateSourcePrefixEnabled(b bool) elbv2model.EnablePrefixForIpv6SourceNat { - if b { - return elbv2model.EnablePrefixForIpv6SourceNatOn + if lbModelBuilder.loadBalancerType == elbv2model.LoadBalancerTypeApplication { + lbModelBuilder.addL7Fields(&spec, lbConf) } - return elbv2model.EnablePrefixForIpv6SourceNatOff + return spec, nil } -func (lbModelBuilder *loadBalancerBuilderImpl) buildLoadBalancerScheme(lbConf *elbv2gw.LoadBalancerConfiguration) (elbv2model.LoadBalancerScheme, error) { - scheme := lbConf.Spec.Scheme +func (lbModelBuilder *loadBalancerBuilderImpl) addL4Fields(spec *elbv2model.LoadBalancerSpec, lbConf *elbv2gw.LoadBalancerConfiguration, subnets buildLoadBalancerSubnetsOutput) { + spec.EnablePrefixForIpv6SourceNat = lbModelBuilder.translateSourcePrefixEnabled(subnets.sourceIPv6NatEnabled) - if scheme == nil { - return lbModelBuilder.defaultLoadBalancerScheme, nil - } - switch *scheme { - case elbv2gw.LoadBalancerScheme(elbv2model.LoadBalancerSchemeInternetFacing): - return elbv2model.LoadBalancerSchemeInternetFacing, nil - case elbv2gw.LoadBalancerScheme(elbv2model.LoadBalancerSchemeInternal): - return elbv2model.LoadBalancerSchemeInternal, nil - default: - return "", errors.Errorf("unknown scheme: %v", *scheme) + if lbConf.Spec.EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic != nil { + spec.SecurityGroupsInboundRulesOnPrivateLink = (*elbv2model.SecurityGroupsInboundRulesOnPrivateLinkStatus)(lbConf.Spec.EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic) } } -// buildLoadBalancerIPAddressType builds the LoadBalancer IPAddressType. -func (lbModelBuilder *loadBalancerBuilderImpl) buildLoadBalancerIPAddressType(lbConf *elbv2gw.LoadBalancerConfiguration) (elbv2model.IPAddressType, error) { +func (lbModelBuilder *loadBalancerBuilderImpl) addL7Fields(spec *elbv2model.LoadBalancerSpec, lbConf *elbv2gw.LoadBalancerConfiguration) { + spec.CustomerOwnedIPv4Pool = lbConf.Spec.CustomerOwnedIpv4Pool + spec.IPv4IPAMPool = lbConf.Spec.IPv4IPAMPoolId +} + +func (lbModelBuilder *loadBalancerBuilderImpl) translateSourcePrefixEnabled(sourceNATEnabled bool) elbv2model.EnablePrefixForIpv6SourceNat { + if sourceNATEnabled { + return elbv2model.EnablePrefixForIpv6SourceNatOn + } + return elbv2model.EnablePrefixForIpv6SourceNatOff +} - if lbConf.Spec.IpAddressType == nil { - return lbModelBuilder.defaultIPType, nil +func (lbModelBuilder *loadBalancerBuilderImpl) buildLoadBalancerName(lbConf *elbv2gw.LoadBalancerConfiguration, gw *gwv1.Gateway, scheme elbv2model.LoadBalancerScheme) (string, error) { + if lbConf.Spec.LoadBalancerName != nil { + name := *lbConf.Spec.LoadBalancerName + return name, nil } + uuidHash := sha256.New() + _, _ = uuidHash.Write([]byte(lbModelBuilder.clusterName)) + _, _ = uuidHash.Write([]byte(gw.UID)) + _, _ = uuidHash.Write([]byte(scheme)) + uuid := hex.EncodeToString(uuidHash.Sum(nil)) + + sanitizedNamespace := invalidLoadBalancerNamePattern.ReplaceAllString(gw.Namespace, "") + sanitizedName := invalidLoadBalancerNamePattern.ReplaceAllString(gw.Name, "") + return fmt.Sprintf("k8s-%.8s-%.8s-%.10s", sanitizedNamespace, sanitizedName, uuid), nil +} - switch *lbConf.Spec.IpAddressType { - case elbv2gw.LoadBalancerIpAddressType(elbv2model.IPAddressTypeIPV4): - return elbv2model.IPAddressTypeIPV4, nil - case elbv2gw.LoadBalancerIpAddressType(elbv2model.IPAddressTypeDualStack): - return elbv2model.IPAddressTypeDualStack, nil - case elbv2gw.LoadBalancerIpAddressType(elbv2model.IPAddressTypeDualStackWithoutPublicIPV4): - return elbv2model.IPAddressTypeDualStackWithoutPublicIPV4, nil - default: - return "", errors.Errorf("unknown IPAddressType: %v", *lbConf.Spec.IpAddressType) +func (lbModelBuilder *loadBalancerBuilderImpl) buildLoadBalancerAttributes(lbConf *elbv2gw.LoadBalancerConfiguration) []elbv2model.LoadBalancerAttribute { + var attributes []elbv2model.LoadBalancerAttribute + for _, attr := range lbConf.Spec.LoadBalancerAttributes { + attributes = append(attributes, elbv2model.LoadBalancerAttribute{ + Key: attr.Key, + Value: attr.Value, + }) } + return attributes +} + +// TODO -- Fill this in at a later time. +func (lbModelBuilder *loadBalancerBuilderImpl) buildLoadBalancerMinimumCapacity(lbConf *elbv2gw.LoadBalancerConfiguration) *elbv2model.MinimumLoadBalancerCapacity { + return nil } diff --git a/pkg/gateway/model/model_build_security_group.go b/pkg/gateway/model/model_build_security_group.go new file mode 100644 index 0000000000..d657398383 --- /dev/null +++ b/pkg/gateway/model/model_build_security_group.go @@ -0,0 +1,311 @@ +package model + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "fmt" + awssdk "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/go-logr/logr" + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" + "regexp" + elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1" + "sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/routeutils" + "sigs.k8s.io/aws-load-balancer-controller/pkg/k8s" + "sigs.k8s.io/aws-load-balancer-controller/pkg/model/core" + ec2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/ec2" + elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2" + "sigs.k8s.io/aws-load-balancer-controller/pkg/networking" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" + "strings" +) + +var ( + invalidSecurityGroupNamePtn = regexp.MustCompile("[[:^alnum:]]") +) + +const ( + resourceIDManagedSecurityGroup = "ManagedLBSecurityGroup" + + managedSGDescription = "[k8s] Managed SecurityGroup for LoadBalancer" +) + +type securityGroupOutput struct { + securityGroupTokens []core.StringToken + backendSecurityGroupToken core.StringToken + backendSecurityGroupAllocated bool +} + +type securityGroupBuilder interface { + buildSecurityGroups(ctx context.Context, stack core.Stack, lbConf *elbv2gw.LoadBalancerConfiguration, gw *gwv1.Gateway, routes map[int][]routeutils.RouteDescriptor, ipAddressType elbv2model.IPAddressType) (securityGroupOutput, error) +} + +type securityGroupBuilderImpl struct { + tagHelper tagHelper + clusterName string + sgResolver networking.SecurityGroupResolver + backendSGProvider networking.BackendSGProvider + + enableBackendSG bool + logger logr.Logger +} + +func newSecurityGroupBuilder(tagHelper tagHelper, clusterName string, enableBackendSG bool, sgResolver networking.SecurityGroupResolver, backendSGProvider networking.BackendSGProvider, logger logr.Logger) securityGroupBuilder { + return &securityGroupBuilderImpl{ + tagHelper: tagHelper, + clusterName: clusterName, + logger: logger, + enableBackendSG: enableBackendSG, + sgResolver: sgResolver, + backendSGProvider: backendSGProvider, + } +} + +func (builder *securityGroupBuilderImpl) buildSecurityGroups(ctx context.Context, stack core.Stack, lbConf *elbv2gw.LoadBalancerConfiguration, gw *gwv1.Gateway, routes map[int][]routeutils.RouteDescriptor, ipAddressType elbv2model.IPAddressType) (securityGroupOutput, error) { + var sgNameOrIds []string + if lbConf != nil && lbConf.Spec.SecurityGroups != nil { + sgNameOrIds = *lbConf.Spec.SecurityGroups + } + + if len(sgNameOrIds) == 0 { + return builder.handleManagedSecurityGroup(ctx, stack, lbConf, gw, routes, ipAddressType) + } + + return builder.handleExplicitSecurityGroups(ctx, lbConf, gw, sgNameOrIds) +} + +func (builder *securityGroupBuilderImpl) handleManagedSecurityGroup(ctx context.Context, stack core.Stack, lbConf *elbv2gw.LoadBalancerConfiguration, gw *gwv1.Gateway, routes map[int][]routeutils.RouteDescriptor, ipAddressType elbv2model.IPAddressType) (securityGroupOutput, error) { + var lbSGTokens []core.StringToken + managedSG, err := builder.buildManagedSecurityGroup(stack, lbConf, gw, routes, ipAddressType) + if err != nil { + return securityGroupOutput{}, err + } + lbSGTokens = append(lbSGTokens, managedSG.GroupID()) + var backendSecurityGroupToken core.StringToken + var backendSGAllocated bool + if !builder.enableBackendSG { + backendSecurityGroupToken = managedSG.GroupID() + } else { + backendSecurityGroupToken, err = builder.getBackendSecurityGroup(ctx, gw) + if err != nil { + return securityGroupOutput{}, err + } + backendSGAllocated = true + lbSGTokens = append(lbSGTokens, backendSecurityGroupToken) + } + builder.logger.Info("Auto Create SG", "LB SGs", lbSGTokens, "backend SG", backendSecurityGroupToken) + return securityGroupOutput{ + securityGroupTokens: lbSGTokens, + backendSecurityGroupToken: backendSecurityGroupToken, + backendSecurityGroupAllocated: backendSGAllocated, + }, nil +} + +func (builder *securityGroupBuilderImpl) handleExplicitSecurityGroups(ctx context.Context, lbConf *elbv2gw.LoadBalancerConfiguration, gw *gwv1.Gateway, sgNameOrIds []string) (securityGroupOutput, error) { + var lbSGTokens []core.StringToken + manageBackendSGRules := lbConf.Spec.ManageBackendSecurityGroupRules + frontendSGIDs, err := builder.sgResolver.ResolveViaNameOrID(ctx, sgNameOrIds) + if err != nil { + return securityGroupOutput{}, err + } + for _, sgID := range frontendSGIDs { + lbSGTokens = append(lbSGTokens, core.LiteralStringToken(sgID)) + } + + var backendSecurityGroupToken core.StringToken + var backendSGAllocated bool + if manageBackendSGRules { + if !builder.enableBackendSG { + return securityGroupOutput{}, errors.New("backendSG feature is required to manage worker node SG rules when frontendSG manually specified") + } + backendSecurityGroupToken, err = builder.getBackendSecurityGroup(ctx, gw) + if err != nil { + return securityGroupOutput{}, err + } + backendSGAllocated = true + lbSGTokens = append(lbSGTokens, backendSecurityGroupToken) + } + builder.logger.Info("SG configured via annotation", "LB SGs", lbSGTokens, "backend SG", backendSecurityGroupToken) + return securityGroupOutput{ + securityGroupTokens: lbSGTokens, + backendSecurityGroupToken: backendSecurityGroupToken, + backendSecurityGroupAllocated: backendSGAllocated, + }, nil +} + +func (builder *securityGroupBuilderImpl) getBackendSecurityGroup(ctx context.Context, gw *gwv1.Gateway) (core.StringToken, error) { + backendSGID, err := builder.backendSGProvider.Get(ctx, networking.ResourceTypeGateway, []types.NamespacedName{k8s.NamespacedName(gw)}) + if err != nil { + return nil, err + } + return core.LiteralStringToken(backendSGID), nil +} + +func (builder *securityGroupBuilderImpl) buildManagedSecurityGroup(stack core.Stack, lbConf *elbv2gw.LoadBalancerConfiguration, gw *gwv1.Gateway, routes map[int][]routeutils.RouteDescriptor, ipAddressType elbv2model.IPAddressType) (*ec2model.SecurityGroup, error) { + name := builder.buildManagedSecurityGroupName(gw) + tags, err := builder.tagHelper.getGatewayTags(lbConf) + if err != nil { + return nil, err + } + + ingressPermissions := builder.buildManagedSecurityGroupIngressPermissions(lbConf, routes, ipAddressType) + return ec2model.NewSecurityGroup(stack, resourceIDManagedSecurityGroup, ec2model.SecurityGroupSpec{ + GroupName: name, + Description: managedSGDescription, + Tags: tags, + Ingress: ingressPermissions, + }), nil +} + +func (builder *securityGroupBuilderImpl) buildManagedSecurityGroupName(gw *gwv1.Gateway) string { + uuidHash := sha256.New() + _, _ = uuidHash.Write([]byte(builder.clusterName)) + _, _ = uuidHash.Write([]byte(gw.Name)) + _, _ = uuidHash.Write([]byte(gw.Namespace)) + _, _ = uuidHash.Write([]byte(gw.UID)) + uuid := hex.EncodeToString(uuidHash.Sum(nil)) + + sanitizedNamespace := invalidSecurityGroupNamePtn.ReplaceAllString(gw.Namespace, "") + sanitizedName := invalidSecurityGroupNamePtn.ReplaceAllString(gw.Name, "") + return fmt.Sprintf("k8s-%.8s-%.8s-%.10s", sanitizedNamespace, sanitizedName, uuid) +} + +func (builder *securityGroupBuilderImpl) buildManagedSecurityGroupIngressPermissions(lbConf *elbv2gw.LoadBalancerConfiguration, routes map[int][]routeutils.RouteDescriptor, ipAddressType elbv2model.IPAddressType) []ec2model.IPPermission { + var permissions []ec2model.IPPermission + + // Default to 0.0.0.0/0 and ::/0 + // If user specified actual ranges, then these values will be overridden. + // TODO - Document this + sourceRanges := []string{ + "0.0.0.0/0", + "::/0", + } + var prefixes []string + var enableICMP bool + + if lbConf != nil && lbConf.Spec.SourceRanges != nil { + sourceRanges = *lbConf.Spec.SourceRanges + } + + if lbConf != nil && lbConf.Spec.SecurityGroupPrefixes != nil { + prefixes = *lbConf.Spec.SecurityGroupPrefixes + } + + if lbConf != nil && lbConf.Spec.EnableICMP { + enableICMP = true + } + + includeIPv6 := isIPv6Supported(ipAddressType) + + // Port Loop + for port, cfg := range routes { + // Protocol Loop + for _, protocol := range generateProtocolListFromRoutes(cfg) { + // CIDR Loop + for _, cidr := range sourceRanges { + isIPv6 := isIPv6CIDR(cidr) + + if !isIPv6 { + permissions = append(permissions, ec2model.IPPermission{ + IPProtocol: protocol, + FromPort: awssdk.Int32(int32(port)), + ToPort: awssdk.Int32(int32(port)), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: cidr, + }, + }, + }) + + if enableICMP { + permissions = append(permissions, ec2model.IPPermission{ + IPProtocol: shared_constants.ICMPV4Protocol, + FromPort: awssdk.Int32(shared_constants.ICMPV4TypeForPathMtu), + ToPort: awssdk.Int32(shared_constants.ICMPV4CodeForPathMtu), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: cidr, + }, + }, + }) + } + + } else if includeIPv6 { + permissions = append(permissions, ec2model.IPPermission{ + IPProtocol: protocol, + FromPort: awssdk.Int32(int32(port)), + ToPort: awssdk.Int32(int32(port)), + IPv6Range: []ec2model.IPv6Range{ + { + CIDRIPv6: cidr, + }, + }, + }) + + if enableICMP { + permissions = append(permissions, ec2model.IPPermission{ + IPProtocol: shared_constants.ICMPV6Protocol, + FromPort: awssdk.Int32(shared_constants.ICMPV6TypeForPathMtu), + ToPort: awssdk.Int32(shared_constants.ICMPV6CodeForPathMtu), + IPv6Range: []ec2model.IPv6Range{ + { + CIDRIPv6: cidr, + }, + }, + }) + } + } + } // CIDR Loop + // PL loop + for _, prefixID := range prefixes { + permissions = append(permissions, ec2model.IPPermission{ + IPProtocol: protocol, + FromPort: awssdk.Int32(int32(port)), + ToPort: awssdk.Int32(int32(port)), + PrefixLists: []ec2model.PrefixList{ + { + ListID: prefixID, + }, + }, + }) + } // PL Loop + } // Protocol Loop + } // Port Loop + return permissions +} + +func generateProtocolListFromRoutes(routes []routeutils.RouteDescriptor) []string { + protocolSet := sets.New[string]() + + for _, route := range routes { + switch route.GetRouteKind() { + case routeutils.HTTPRouteKind, routeutils.GRPCRouteKind, routeutils.TCPRouteKind, routeutils.TLSRouteKind: + protocolSet.Insert(string(ec2types.ProtocolTcp)) + break + case routeutils.UDPRouteKind: + protocolSet = protocolSet.Insert(string(ec2types.ProtocolUdp)) + break + default: + // Ignore? Throw error? + } + } + return protocolSet.UnsortedList() +} + +func isIPv6Supported(ipAddressType elbv2model.IPAddressType) bool { + switch ipAddressType { + case elbv2model.IPAddressTypeDualStack, elbv2model.IPAddressTypeDualStackWithoutPublicIPV4: + return true + default: + return false + } +} + +// TODO - Refactor? +func isIPv6CIDR(cidr string) bool { + return strings.Contains(cidr, ":") +} diff --git a/pkg/gateway/model/model_build_security_group_test.go b/pkg/gateway/model/model_build_security_group_test.go new file mode 100644 index 0000000000..a7bfacbed7 --- /dev/null +++ b/pkg/gateway/model/model_build_security_group_test.go @@ -0,0 +1,918 @@ +package model + +import ( + "context" + "fmt" + awssdk "github.com/aws/aws-sdk-go-v2/aws" + "github.com/go-logr/logr" + "github.com/golang/mock/gomock" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/types" + elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1" + "sigs.k8s.io/aws-load-balancer-controller/pkg/gateway/routeutils" + "sigs.k8s.io/aws-load-balancer-controller/pkg/k8s" + coremodel "sigs.k8s.io/aws-load-balancer-controller/pkg/model/core" + ec2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/ec2" + elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2" + "sigs.k8s.io/aws-load-balancer-controller/pkg/networking" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" + "testing" +) + +func Test_BuildSecurityGroups_Specified(t *testing.T) { + const clusterName = "my-cluster" + + type resolveSgCall struct { + securityGroups []string + err error + } + + type backendSgProviderCall struct { + sgId string + err error + } + + testCases := []struct { + name string + lbConf *elbv2gw.LoadBalancerConfiguration + ipAddressType elbv2model.IPAddressType + expectedTags map[string]string + tagErr error + enableBackendSg bool + + resolveSg *resolveSgCall + providerCall *backendSgProviderCall + + expectErr bool + expectedBackendSgToken coremodel.StringToken + expectedSgTokens []coremodel.StringToken + backendSgAllocated bool + }{ + { + name: "sg specified - no backend sg", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + SecurityGroups: &[]string{ + "sg1", + "sg2", + }, + }, + }, + resolveSg: &resolveSgCall{ + securityGroups: []string{ + "sg1", + "sg2", + }, + }, + expectedSgTokens: []coremodel.StringToken{ + coremodel.LiteralStringToken("sg1"), + coremodel.LiteralStringToken("sg2"), + }, + }, + { + name: "sg specified - with backend sg", + enableBackendSg: true, + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + ManageBackendSecurityGroupRules: true, + SecurityGroups: &[]string{ + "sg1", + "sg2", + }, + }, + }, + resolveSg: &resolveSgCall{ + securityGroups: []string{ + "sg1", + "sg2", + }, + }, + providerCall: &backendSgProviderCall{ + sgId: "auto-allocated", + }, + expectedSgTokens: []coremodel.StringToken{ + coremodel.LiteralStringToken("sg1"), + coremodel.LiteralStringToken("sg2"), + coremodel.LiteralStringToken("auto-allocated"), + }, + expectedBackendSgToken: coremodel.LiteralStringToken("auto-allocated"), + backendSgAllocated: true, + }, + { + name: "sg specified - with backend sg - error - backendsg not enabled", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + ManageBackendSecurityGroupRules: true, + SecurityGroups: &[]string{ + "sg1", + "sg2", + }, + }, + }, + resolveSg: &resolveSgCall{ + securityGroups: []string{ + "sg1", + "sg2", + }, + }, + expectErr: true, + }, + { + name: "sg specified - with backend sg - error - resolve sg error", + enableBackendSg: true, + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + ManageBackendSecurityGroupRules: true, + SecurityGroups: &[]string{ + "sg1", + "sg2", + }, + }, + }, + resolveSg: &resolveSgCall{ + err: errors.New("bad thing"), + }, + expectErr: true, + }, + { + name: "sg specified - with backend sg - error - resolve sg error", + enableBackendSg: true, + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + ManageBackendSecurityGroupRules: true, + SecurityGroups: &[]string{ + "sg1", + "sg2", + }, + }, + }, + resolveSg: &resolveSgCall{ + securityGroups: []string{ + "sg1", + "sg2", + }, + }, + providerCall: &backendSgProviderCall{ + err: errors.New("bad thing"), + }, + expectErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockTagger := &mockTagHelper{ + tags: tc.expectedTags, + err: tc.tagErr, + } + + gw := &gwv1.Gateway{} + gw.Name = "my-gw" + gw.Namespace = "my-namespace" + + mockSgProvider := networking.NewMockBackendSGProvider(ctrl) + mockSgResolver := networking.NewMockSecurityGroupResolver(ctrl) + + if tc.resolveSg != nil { + mockSgResolver.EXPECT().ResolveViaNameOrID(gomock.Any(), gomock.Eq(*tc.lbConf.Spec.SecurityGroups)).Return(tc.resolveSg.securityGroups, tc.resolveSg.err).Times(1) + } + + if tc.providerCall != nil { + mockSgProvider.EXPECT().Get(gomock.Any(), gomock.Eq(networking.ResourceType(networking.ResourceTypeGateway)), gomock.Eq([]types.NamespacedName{k8s.NamespacedName(gw)})).Return(tc.providerCall.sgId, tc.providerCall.err).Times(1) + } + + stack := coremodel.NewDefaultStack(coremodel.StackID{Namespace: "namespace", Name: "name"}) + builder := newSecurityGroupBuilder(mockTagger, clusterName, tc.enableBackendSg, mockSgResolver, mockSgProvider, logr.Discard()) + + out, err := builder.buildSecurityGroups(context.Background(), stack, tc.lbConf, gw, make(map[int][]routeutils.RouteDescriptor), tc.ipAddressType) + + if tc.expectErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tc.expectedBackendSgToken, out.backendSecurityGroupToken) + assert.Equal(t, tc.expectedSgTokens, out.securityGroupTokens) + assert.Equal(t, tc.backendSgAllocated, out.backendSecurityGroupAllocated) + }) + } +} + +func Test_BuildSecurityGroups_Allocate(t *testing.T) { + const clusterName = "my-cluster" + + type backendSgProviderCall struct { + sgId string + err error + } + + testCases := []struct { + name string + lbConf *elbv2gw.LoadBalancerConfiguration + ipAddressType elbv2model.IPAddressType + expectedTags map[string]string + tagErr error + enableBackendSg bool + + providerCall *backendSgProviderCall + + expectErr bool + hasBackendSg bool + backendSgAllocated bool + expectedStackResources int + }{ + { + name: "sg allocate - no backend sg", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{}, + }, + expectedStackResources: 1, + }, + { + name: "sg allocate - with backend sg", + enableBackendSg: true, + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + ManageBackendSecurityGroupRules: true, + }, + }, + providerCall: &backendSgProviderCall{ + sgId: "auto-allocated", + }, + backendSgAllocated: true, + expectedStackResources: 1, + }, + { + name: "sg allocate - provider error", + enableBackendSg: true, + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + ManageBackendSecurityGroupRules: true, + }, + }, + providerCall: &backendSgProviderCall{ + err: errors.New("bad thing"), + }, + expectErr: true, + }, + { + name: "sg allocate - tag error", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{}, + }, + expectErr: true, + tagErr: errors.New("bad thing"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockTagger := &mockTagHelper{ + tags: tc.expectedTags, + err: tc.tagErr, + } + + gw := &gwv1.Gateway{} + gw.Name = "my-gw" + gw.Namespace = "my-namespace" + + mockSgProvider := networking.NewMockBackendSGProvider(ctrl) + mockSgResolver := networking.NewMockSecurityGroupResolver(ctrl) + + if tc.providerCall != nil { + mockSgProvider.EXPECT().Get(gomock.Any(), gomock.Eq(networking.ResourceType(networking.ResourceTypeGateway)), gomock.Eq([]types.NamespacedName{k8s.NamespacedName(gw)})).Return(tc.providerCall.sgId, tc.providerCall.err).Times(1) + } + + stack := coremodel.NewDefaultStack(coremodel.StackID{Namespace: "namespace", Name: "name"}) + builder := newSecurityGroupBuilder(mockTagger, clusterName, tc.enableBackendSg, mockSgResolver, mockSgProvider, logr.Discard()) + + out, err := builder.buildSecurityGroups(context.Background(), stack, tc.lbConf, gw, make(map[int][]routeutils.RouteDescriptor), tc.ipAddressType) + + if tc.expectErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tc.backendSgAllocated, out.backendSecurityGroupAllocated) + var resSGs []*ec2model.SecurityGroup + listErr := stack.ListResources(&resSGs) + assert.NoError(t, listErr) + assert.Equal(t, tc.expectedStackResources, len(resSGs)) + if tc.hasBackendSg { + assert.NotNil(t, out.backendSecurityGroupToken) + } + }) + } +} + +func Test_BuildSecurityGroups_BuildManagedSecurityGroupIngressPermissions(t *testing.T) { + testCases := []struct { + name string + lbConf *elbv2gw.LoadBalancerConfiguration + ipAddressType elbv2model.IPAddressType + routes map[int][]routeutils.RouteDescriptor + expected []ec2model.IPPermission + }{ + { + name: "no routes", + lbConf: &elbv2gw.LoadBalancerConfiguration{}, + expected: make([]ec2model.IPPermission, 0), + }, + { + name: "ipv4 - tcp - with default source ranges", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{}, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "0.0.0.0/0", + }, + }, + }, + }, + }, + { + name: "ipv4 - tcp - with source range", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + SourceRanges: &[]string{ + "127.0.0.1/24", + "127.100.0.1/24", + "127.200.0.1/24", + }, + }, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.100.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.200.0.1/24", + }, + }, + }, + }, + }, + { + name: "ipv4 - udp - with source range", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + SourceRanges: &[]string{ + "127.0.0.1/24", + "127.100.0.1/24", + "127.200.0.1/24", + }, + }, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.UDPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "udp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + { + IPProtocol: "udp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.100.0.1/24", + }, + }, + }, + { + IPProtocol: "udp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.200.0.1/24", + }, + }, + }, + }, + }, + { + name: "ipv4 - udp - with source range - icmp enabled", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + SourceRanges: &[]string{ + "127.0.0.1/24", + }, + EnableICMP: true, + }, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.UDPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "udp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + { + IPProtocol: "icmp", + FromPort: awssdk.Int32(2), + ToPort: awssdk.Int32(3), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + }, + }, + { + name: "ipv4 - with duplicated route type - with source range", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + SourceRanges: &[]string{ + "127.0.0.1/24", + "127.100.0.1/24", + "127.200.0.1/24", + }, + }, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + &routeutils.MockRoute{ + Kind: routeutils.HTTPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.100.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.200.0.1/24", + }, + }, + }, + }, + }, + { + name: "ipv4 - with duplicated route type - with source range - multiple ports", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + SourceRanges: &[]string{ + "127.0.0.1/24", + "127.100.0.1/24", + "127.200.0.1/24", + }, + }, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + &routeutils.MockRoute{ + Kind: routeutils.HTTPRouteKind, + }, + }, + 85: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + &routeutils.MockRoute{ + Kind: routeutils.HTTPRouteKind, + }, + }, + 90: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + &routeutils.MockRoute{ + Kind: routeutils.HTTPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.100.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.200.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(85), + ToPort: awssdk.Int32(85), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(85), + ToPort: awssdk.Int32(85), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.100.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(85), + ToPort: awssdk.Int32(85), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.200.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(90), + ToPort: awssdk.Int32(90), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(90), + ToPort: awssdk.Int32(90), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.100.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(90), + ToPort: awssdk.Int32(90), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.200.0.1/24", + }, + }, + }, + }, + }, + { + name: "ipv6 - with default source ranges", + ipAddressType: elbv2model.IPAddressTypeDualStack, + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{}, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + &routeutils.MockRoute{ + Kind: routeutils.HTTPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "0.0.0.0/0", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPv6Range: []ec2model.IPv6Range{ + { + CIDRIPv6: "::/0", + }, + }, + }, + }, + }, + { + name: "ipv6 - with source range", + ipAddressType: elbv2model.IPAddressTypeDualStack, + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + SourceRanges: &[]string{ + "2001:db8::/32", + }, + }, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + &routeutils.MockRoute{ + Kind: routeutils.HTTPRouteKind, + }, + }, + 85: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + &routeutils.MockRoute{ + Kind: routeutils.HTTPRouteKind, + }, + }, + 90: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + &routeutils.MockRoute{ + Kind: routeutils.HTTPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPv6Range: []ec2model.IPv6Range{ + { + CIDRIPv6: "2001:db8::/32", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(85), + ToPort: awssdk.Int32(85), + IPv6Range: []ec2model.IPv6Range{ + { + CIDRIPv6: "2001:db8::/32", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(90), + ToPort: awssdk.Int32(90), + IPv6Range: []ec2model.IPv6Range{ + { + CIDRIPv6: "2001:db8::/32", + }, + }, + }, + }, + }, + { + name: "ipv6 + ipv4 - with source range", + ipAddressType: elbv2model.IPAddressTypeDualStack, + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + SourceRanges: &[]string{ + "2001:db8::/32", + "127.0.0.1/24", + }, + }, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPv6Range: []ec2model.IPv6Range{ + { + CIDRIPv6: "2001:db8::/32", + }, + }, + }, + }, + }, + { + name: "ipv6 + ipv4 - with source range - but lb type doesnt support ipv6", + ipAddressType: elbv2model.IPAddressTypeIPV4, + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + SourceRanges: &[]string{ + "2001:db8::/32", + "127.0.0.1/24", + }, + }, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + }, + }, + { + name: "prefix list", + lbConf: &elbv2gw.LoadBalancerConfiguration{ + Spec: elbv2gw.LoadBalancerConfigurationSpec{ + SourceRanges: &[]string{ + "127.0.0.1/24", + }, + SecurityGroupPrefixes: &[]string{"pl1", "pl2"}, + }, + }, + routes: map[int][]routeutils.RouteDescriptor{ + 80: { + &routeutils.MockRoute{ + Kind: routeutils.TCPRouteKind, + }, + }, + }, + expected: []ec2model.IPPermission{ + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + IPRanges: []ec2model.IPRange{ + { + CIDRIP: "127.0.0.1/24", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + PrefixLists: []ec2model.PrefixList{ + { + ListID: "pl1", + }, + }, + }, + { + IPProtocol: "tcp", + FromPort: awssdk.Int32(80), + ToPort: awssdk.Int32(80), + PrefixLists: []ec2model.PrefixList{ + { + ListID: "pl2", + }, + }, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + builder := &securityGroupBuilderImpl{} + permissions := builder.buildManagedSecurityGroupIngressPermissions(tc.lbConf, tc.routes, tc.ipAddressType) + assert.ElementsMatch(t, tc.expected, permissions, fmt.Sprintf("%+v", permissions)) + }) + } +} diff --git a/pkg/gateway/model/model_build_subnet.go b/pkg/gateway/model/model_build_subnet.go index 9b2c13301d..adfce1aa93 100644 --- a/pkg/gateway/model/model_build_subnet.go +++ b/pkg/gateway/model/model_build_subnet.go @@ -15,8 +15,13 @@ import ( "sigs.k8s.io/aws-load-balancer-controller/pkg/networking" ) +type buildLoadBalancerSubnetsOutput struct { + subnets []elbv2model.SubnetMapping + sourceIPv6NatEnabled bool +} + type subnetModelBuilder interface { - buildLoadBalancerSubnets(ctx context.Context, gwSubnetConfig *[]elbv2gw.SubnetConfiguration, gwSubnetTagSelectors *map[string][]string, scheme elbv2model.LoadBalancerScheme, ipAddressType elbv2model.IPAddressType, stack core.Stack) ([]elbv2model.SubnetMapping, bool, error) + buildLoadBalancerSubnets(ctx context.Context, gwSubnetConfig *[]elbv2gw.SubnetConfiguration, gwSubnetTagSelectors *map[string][]string, scheme elbv2model.LoadBalancerScheme, ipAddressType elbv2model.IPAddressType, stack core.Stack) (buildLoadBalancerSubnetsOutput, error) } type subnetModelBuilderImpl struct { @@ -50,17 +55,17 @@ func newSubnetModelBuilder(loadBalancerType elbv2model.LoadBalancerType, trackin } } -func (subnetBuilder *subnetModelBuilderImpl) buildLoadBalancerSubnets(ctx context.Context, gwSubnetConfig *[]elbv2gw.SubnetConfiguration, gwSubnetTagSelectors *map[string][]string, scheme elbv2model.LoadBalancerScheme, ipAddressType elbv2model.IPAddressType, stack core.Stack) ([]elbv2model.SubnetMapping, bool, error) { +func (subnetBuilder *subnetModelBuilderImpl) buildLoadBalancerSubnets(ctx context.Context, gwSubnetConfig *[]elbv2gw.SubnetConfiguration, gwSubnetTagSelectors *map[string][]string, scheme elbv2model.LoadBalancerScheme, ipAddressType elbv2model.IPAddressType, stack core.Stack) (buildLoadBalancerSubnetsOutput, error) { sourceNATEnabled, err := subnetBuilder.validateSubnetsInput(gwSubnetConfig, scheme, ipAddressType) if err != nil { - return nil, false, err + return buildLoadBalancerSubnetsOutput{}, err } resolvedEC2Subnets, err := subnetBuilder.resolveEC2Subnets(ctx, stack, gwSubnetConfig, gwSubnetTagSelectors, scheme) if err != nil { - return nil, false, err + return buildLoadBalancerSubnetsOutput{}, err } resultPtrs := make([]*elbv2model.SubnetMapping, 0) @@ -80,7 +85,7 @@ func (subnetBuilder *subnetModelBuilderImpl) buildLoadBalancerSubnets(ctx contex for _, mutator := range subnetBuilder.subnetMutatorChain { err := mutator.Mutate(resultPtrs, resolvedEC2Subnets, subnetConfig) if err != nil { - return nil, false, err + return buildLoadBalancerSubnetsOutput{}, err } } @@ -90,7 +95,10 @@ func (subnetBuilder *subnetModelBuilderImpl) buildLoadBalancerSubnets(ctx contex result = append(result, *v) } - return result, sourceNATEnabled, nil + return buildLoadBalancerSubnetsOutput{ + subnets: result, + sourceIPv6NatEnabled: sourceNATEnabled, + }, nil } func (subnetBuilder *subnetModelBuilderImpl) validateSubnetsInput(subnetConfigsPtr *[]elbv2gw.SubnetConfiguration, scheme elbv2model.LoadBalancerScheme, ipAddressType elbv2model.IPAddressType) (bool, error) { diff --git a/pkg/gateway/model/model_build_subnet_test.go b/pkg/gateway/model/model_build_subnet_test.go index d59fce9513..b7fdeba931 100644 --- a/pkg/gateway/model/model_build_subnet_test.go +++ b/pkg/gateway/model/model_build_subnet_test.go @@ -97,11 +97,11 @@ func Test_BuildLoadBalancerSubnets(t *testing.T) { }, } - subnetMappings, ipv6SourceNatEnabled, err := builder.buildLoadBalancerSubnets(context.Background(), &gwSubnetConfig, nil, elbv2model.LoadBalancerSchemeInternal, elbv2model.IPAddressTypeIPV4, nil) + output, err := builder.buildLoadBalancerSubnets(context.Background(), &gwSubnetConfig, nil, elbv2model.LoadBalancerSchemeInternal, elbv2model.IPAddressTypeIPV4, nil) assert.NoError(t, err) - assert.Equal(t, expectedMappings, subnetMappings) - assert.False(t, ipv6SourceNatEnabled) + assert.Equal(t, expectedMappings, output.subnets) + assert.False(t, output.sourceIPv6NatEnabled) assert.Equal(t, 1, mm.called) } diff --git a/pkg/gateway/routeutils/mock_route.go b/pkg/gateway/routeutils/mock_route.go new file mode 100644 index 0000000000..13b438c58e --- /dev/null +++ b/pkg/gateway/routeutils/mock_route.go @@ -0,0 +1,46 @@ +package routeutils + +import ( + "k8s.io/apimachinery/pkg/types" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +type MockRoute struct { + Kind string +} + +func (m *MockRoute) GetBackendRefs() []gwv1.BackendRef { + //TODO implement me + panic("implement me") +} + +func (m *MockRoute) GetRouteNamespacedName() types.NamespacedName { + //TODO implement me + panic("implement me") +} + +func (m *MockRoute) GetRouteKind() string { + return m.Kind +} + +func (m *MockRoute) GetHostnames() []gwv1.Hostname { + //TODO implement me + panic("implement me") +} + +func (m *MockRoute) GetParentRefs() []gwv1.ParentReference { + //TODO implement me + panic("implement me") +} + +func (m *MockRoute) GetRawRoute() interface{} { + //TODO implement me + panic("implement me") +} + +func (m *MockRoute) GetAttachedRules() []RouteRule { + //TODO implement me + panic("implement me") +} + +var _ RouteDescriptor = &MockRoute{} diff --git a/pkg/ingress/finalizer.go b/pkg/ingress/finalizer.go index c1c0cac67c..4b4d73e06b 100644 --- a/pkg/ingress/finalizer.go +++ b/pkg/ingress/finalizer.go @@ -5,11 +5,7 @@ import ( "fmt" networking "k8s.io/api/networking/v1" "sigs.k8s.io/aws-load-balancer-controller/pkg/k8s" -) - -const ( - explicitGroupFinalizerPrefix = "group.ingress.k8s.aws/" - implicitGroupFinalizer = "ingress.k8s.aws/resources" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" ) // FinalizerManager manages finalizer for ingresses. @@ -62,7 +58,7 @@ func (m *defaultFinalizerManager) RemoveGroupFinalizer(ctx context.Context, grou // for implicit group, the format is "ingress.k8s.aws/resources" func buildGroupFinalizer(groupID GroupID) string { if groupID.IsExplicit() { - return fmt.Sprintf("%s%s", explicitGroupFinalizerPrefix, groupID.Name) + return fmt.Sprintf("%s%s", shared_constants.ExplicitGroupFinalizerPrefix, groupID.Name) } - return implicitGroupFinalizer + return shared_constants.ImplicitGroupFinalizer } diff --git a/pkg/ingress/group_loader.go b/pkg/ingress/group_loader.go index 2bd98ab61a..0aad20eeea 100644 --- a/pkg/ingress/group_loader.go +++ b/pkg/ingress/group_loader.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "regexp" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "sort" "strings" @@ -117,10 +118,10 @@ func (m *defaultGroupLoader) LoadGroupIDIfAny(ctx context.Context, ing *networki func (m *defaultGroupLoader) LoadGroupIDsPendingFinalization(_ context.Context, ing *networking.Ingress) []GroupID { var groupIDs []GroupID for _, finalizer := range ing.GetFinalizers() { - if finalizer == implicitGroupFinalizer { + if finalizer == shared_constants.ImplicitGroupFinalizer { groupIDs = append(groupIDs, NewGroupIDForImplicitGroup(k8s.NamespacedName(ing))) - } else if strings.HasPrefix(finalizer, explicitGroupFinalizerPrefix) { - groupName := finalizer[len(explicitGroupFinalizerPrefix):] + } else if strings.HasPrefix(finalizer, shared_constants.ExplicitGroupFinalizerPrefix) { + groupName := finalizer[len(shared_constants.ExplicitGroupFinalizerPrefix):] groupIDs = append(groupIDs, NewGroupIDForExplicitGroup(groupName)) } } diff --git a/pkg/ingress/model_builder.go b/pkg/ingress/model_builder.go index 1e341f24c1..049c10b3f9 100644 --- a/pkg/ingress/model_builder.go +++ b/pkg/ingress/model_builder.go @@ -3,6 +3,7 @@ package ingress import ( "context" "reflect" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "strconv" elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" @@ -30,8 +31,7 @@ import ( ) const ( - lbAttrsDeletionProtectionEnabled = "deletion_protection.enabled" - controllerName = "ingress" + controllerName = "ingress" ) // ModelBuilder is responsible for build mode stack for a IngressGroup. @@ -443,8 +443,8 @@ func (t *defaultModelBuildTask) getDeletionProtectionViaAnnotation(ing *networki if err != nil { return false, err } - if _, deletionProtectionSpecified := lbAttributes[lbAttrsDeletionProtectionEnabled]; deletionProtectionSpecified { - deletionProtectionEnabled, err := strconv.ParseBool(lbAttributes[lbAttrsDeletionProtectionEnabled]) + if _, deletionProtectionSpecified := lbAttributes[shared_constants.LBAttributeDeletionProtection]; deletionProtectionSpecified { + deletionProtectionEnabled, err := strconv.ParseBool(lbAttributes[shared_constants.LBAttributeDeletionProtection]) if err != nil { return false, err } diff --git a/pkg/ingress/model_builder_test.go b/pkg/ingress/model_builder_test.go index 0c27e4743c..95a24c5b8d 100644 --- a/pkg/ingress/model_builder_test.go +++ b/pkg/ingress/model_builder_test.go @@ -3,6 +3,7 @@ package ingress import ( "context" "encoding/json" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "testing" "time" @@ -2169,8 +2170,8 @@ func Test_defaultModelBuilder_Build(t *testing.T) { Scheme: elbv2types.LoadBalancerSchemeEnumInternal, }, Tags: map[string]string{ - "elbv2.k8s.aws/cluster": "cluster-name", - "ingress.k8s.aws/stack": "ns-1/ing-1", + shared_constants.TagKeyK8sCluster: "cluster-name", + "ingress.k8s.aws/stack": "ns-1/ing-1", }, }, { diff --git a/pkg/metrics/util/reconcile_counter.go b/pkg/metrics/util/reconcile_counter.go index 25910aa654..9b6c62c6d1 100644 --- a/pkg/metrics/util/reconcile_counter.go +++ b/pkg/metrics/util/reconcile_counter.go @@ -26,8 +26,8 @@ func NewReconcileCounters() *ReconcileCounters { serviceReconciles: make(map[types.NamespacedName]int), ingressReconciles: make(map[types.NamespacedName]int), tgbReconciles: make(map[types.NamespacedName]int), - nlbGatewayReconciles: make(map[types.NamespacedName]int), albGatewayReconciles: make(map[types.NamespacedName]int), + nlbGatewayReconciles: make(map[types.NamespacedName]int), mutex: sync.Mutex{}, } } diff --git a/pkg/networking/backend_sg_provider.go b/pkg/networking/backend_sg_provider.go index 8b3900e523..d48332c321 100644 --- a/pkg/networking/backend_sg_provider.go +++ b/pkg/networking/backend_sg_provider.go @@ -8,6 +8,7 @@ import ( ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/smithy-go" "regexp" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "sort" "strings" "sync" @@ -24,6 +25,7 @@ import ( "sigs.k8s.io/aws-load-balancer-controller/pkg/k8s" "sigs.k8s.io/aws-load-balancer-controller/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" ) const ( @@ -31,14 +33,8 @@ const ( defaultSGDeletionTimeout = 2 * time.Minute resourceTypeSecurityGroup = "security-group" - tagKeyK8sCluster = "elbv2.k8s.aws/cluster" - tagKeyResource = "elbv2.k8s.aws/resource" tagValueBackend = "backend-sg" - explicitGroupFinalizerPrefix = "group.ingress.k8s.aws/" - implicitGroupFinalizer = "ingress.k8s.aws/resources" - serviceFinalizer = "service.k8s.aws/resources" - sgDescription = "[k8s] Shared Backend SecurityGroup for LoadBalancer" ) @@ -47,6 +43,7 @@ type ResourceType string const ( ResourceTypeIngress = "ingress" ResourceTypeService = "service" + ResourceTypeGateway = "gateway" ) // BackendSGProvider is responsible for providing backend security groups @@ -59,7 +56,7 @@ type BackendSGProvider interface { // NewBackendSGProvider constructs a new defaultBackendSGProvider func NewBackendSGProvider(clusterName string, backendSG string, vpcID string, - ec2Client services.EC2, k8sClient client.Client, defaultTags map[string]string, logger logr.Logger) *defaultBackendSGProvider { + ec2Client services.EC2, k8sClient client.Client, defaultTags map[string]string, enableGatewayCheck bool, logger logr.Logger) *defaultBackendSGProvider { return &defaultBackendSGProvider{ vpcID: vpcID, clusterName: clusterName, @@ -70,9 +67,11 @@ func NewBackendSGProvider(clusterName string, backendSG string, vpcID string, logger: logger, mutex: sync.Mutex{}, + enableGatewayCheck: enableGatewayCheck, + checkIngressFinalizersFunc: func(finalizers []string) bool { for _, fin := range finalizers { - if fin == implicitGroupFinalizer || strings.HasPrefix(fin, explicitGroupFinalizerPrefix) { + if fin == shared_constants.ImplicitGroupFinalizer || strings.HasPrefix(fin, shared_constants.ExplicitGroupFinalizerPrefix) { return true } } @@ -81,7 +80,16 @@ func NewBackendSGProvider(clusterName string, backendSG string, vpcID string, checkServiceFinalizersFunc: func(finalizers []string) bool { for _, fin := range finalizers { - if fin == serviceFinalizer { + if fin == shared_constants.ServiceFinalizer { + return true + } + } + return false + }, + + checkGatewayFinalizersFunc: func(finalizers []string) bool { + for _, fin := range finalizers { + if fin == shared_constants.ALBGatewayFinalizer || fin == shared_constants.NLBGatewayFinalizer { return true } } @@ -113,8 +121,11 @@ type defaultBackendSGProvider struct { // controller deletes the backend SG. objectsMap sync.Map + enableGatewayCheck bool + checkServiceFinalizersFunc func([]string) bool checkIngressFinalizersFunc func([]string) bool + checkGatewayFinalizersFunc func([]string) bool defaultDeletionPollInterval time.Duration defaultDeletionTimeout time.Duration @@ -175,6 +186,9 @@ func (p *defaultBackendSGProvider) isBackendSGRequired(ctx context.Context) (boo if required, err := p.checkServiceListForUnmapped(ctx); required || err != nil { return required, err } + if required, err := p.checkGatewayListForUnmapped(ctx); required || err != nil { + return required, err + } return false, nil } @@ -210,6 +224,26 @@ func (p *defaultBackendSGProvider) checkServiceListForUnmapped(ctx context.Conte return false, nil } +func (p *defaultBackendSGProvider) checkGatewayListForUnmapped(ctx context.Context) (bool, error) { + if !p.enableGatewayCheck { + return false, nil + } + + gwList := &gwv1.GatewayList{} + if err := p.k8sClient.List(ctx, gwList); err != nil { + return true, errors.Wrapf(err, "unable to list gateways") + } + for _, gw := range gwList.Items { + if !p.checkGatewayFinalizersFunc(gw.GetFinalizers()) { + continue + } + if !p.existsInObjectMap(ResourceTypeGateway, k8s.NamespacedName(&gw)) { + return true, nil + } + } + return false, nil +} + func (p *defaultBackendSGProvider) existsInObjectMap(resourceType ResourceType, resource types.NamespacedName) bool { if _, exists := p.objectsMap.Load(getObjectKey(resourceType, resource)); exists { return true @@ -269,11 +303,11 @@ func (p *defaultBackendSGProvider) buildBackendSGTags(_ context.Context) []ec2ty ResourceType: resourceTypeSecurityGroup, Tags: append(defaultTags, []ec2types.Tag{ { - Key: awssdk.String(tagKeyK8sCluster), + Key: awssdk.String(shared_constants.TagKeyK8sCluster), Value: awssdk.String(p.clusterName), }, { - Key: awssdk.String(tagKeyResource), + Key: awssdk.String(shared_constants.TagKeyResource), Value: awssdk.String(tagValueBackend), }, }...), @@ -289,16 +323,16 @@ func (p *defaultBackendSGProvider) getBackendSGFromEC2(ctx context.Context, sgNa Values: []string{vpcID}, }, { - Name: awssdk.String(fmt.Sprintf("tag:%v", tagKeyK8sCluster)), + Name: awssdk.String(fmt.Sprintf("tag:%v", shared_constants.TagKeyK8sCluster)), Values: []string{p.clusterName}, }, { - Name: awssdk.String(fmt.Sprintf("tag:%v", tagKeyResource)), + Name: awssdk.String(fmt.Sprintf("tag:%v", shared_constants.TagKeyResource)), Values: []string{tagValueBackend}, }, }, } - p.logger.V(1).Info("Queriying existing SG", "vpc-id", vpcID, "name", sgName) + p.logger.V(1).Info("Querying existing SG", "vpc-id", vpcID, "name", sgName) sgs, err := p.ec2Client.DescribeSecurityGroupsAsList(ctx, req) if err != nil && !isEC2SecurityGroupNotFoundError(err) { return "", err diff --git a/pkg/networking/backend_sg_provider_test.go b/pkg/networking/backend_sg_provider_test.go index 4850d7d30d..839b0d2ee7 100644 --- a/pkg/networking/backend_sg_provider_test.go +++ b/pkg/networking/backend_sg_provider_test.go @@ -7,6 +7,7 @@ import ( "k8s.io/apimachinery/pkg/types" "reflect" "sigs.k8s.io/aws-load-balancer-controller/pkg/k8s" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" "testing" "github.com/go-logr/logr" @@ -42,12 +43,13 @@ func Test_defaultBackendSGProvider_Get(t *testing.T) { err error } type fields struct { - backendSG string - ingResources []*networking.Ingress - svcResource *corev1.Service - defaultTags map[string]string - describeSGCalls []describeSecurityGroupsAsListCall - createSGCalls []createSecurityGroupWithContexCall + backendSG string + ingResources []*networking.Ingress + svcResource *corev1.Service + enableGatewayCheck bool + defaultTags map[string]string + describeSGCalls []describeSecurityGroupsAsListCall + createSGCalls []createSecurityGroupWithContexCall } defaultEC2Filters := []ec2types.Filter{ { @@ -285,7 +287,7 @@ func Test_defaultBackendSGProvider_Get(t *testing.T) { } k8sClient := mock_client.NewMockClient(ctrl) sgProvider := NewBackendSGProvider(defaultClusterName, tt.fields.backendSG, - defaultVPCID, ec2Client, k8sClient, tt.fields.defaultTags, logr.New(&log.NullLogSink{})) + defaultVPCID, ec2Client, k8sClient, tt.fields.defaultTags, tt.fields.enableGatewayCheck, logr.New(&log.NullLogSink{})) resourceType := ResourceTypeIngress var activeResources []types.NamespacedName @@ -317,6 +319,11 @@ func Test_defaultBackendSGProvider_Release(t *testing.T) { services []*corev1.Service err error } + type listGatewaysCall struct { + gateways []*gwv1.Gateway + err error + } + type deleteSecurityGroupWithContextCall struct { req *ec2sdk.DeleteSecurityGroupInput resp *ec2sdk.DeleteSecurityGroupOutput @@ -333,11 +340,13 @@ func Test_defaultBackendSGProvider_Release(t *testing.T) { listIngressCalls []listIngressCall deleteSGCalls []deleteSecurityGroupWithContextCall listServicesCalls []listServicesCall + listGatewaysCall []listGatewaysCall activeIngresses []*networking.Ingress inactiveIngresses []*networking.Ingress svcResource *corev1.Service resourceMapItems []mapItem backendSGRequiredForActive bool + enableGatewayCheck bool } ing := &networking.Ingress{ ObjectMeta: metav1.ObjectMeta{ @@ -545,6 +554,94 @@ func Test_defaultBackendSGProvider_Release(t *testing.T) { inactiveIngresses: []*networking.Ingress{ing}, }, }, + { + name: "backend sg required for gw - nlb", + fields: fields{ + autogenSG: "sg-autogen", + listIngressCalls: []listIngressCall{ + {}, + }, + listServicesCalls: []listServicesCall{ + {}, + }, + listGatewaysCall: []listGatewaysCall{ + { + gateways: []*gwv1.Gateway{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "awesome-ns", + Name: "gw-1", + Finalizers: []string{"gateway.k8s.aws/nlb"}, + }, + }, + }, + }, + }, + enableGatewayCheck: true, + inactiveIngresses: []*networking.Ingress{ing}, + }, + }, + { + name: "backend sg required for gw - alb", + fields: fields{ + autogenSG: "sg-autogen", + listIngressCalls: []listIngressCall{ + {}, + }, + listServicesCalls: []listServicesCall{ + {}, + }, + listGatewaysCall: []listGatewaysCall{ + { + gateways: []*gwv1.Gateway{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "awesome-ns", + Name: "gw-1", + Finalizers: []string{"gateway.k8s.aws/alb"}, + }, + }, + }, + }, + }, + enableGatewayCheck: true, + inactiveIngresses: []*networking.Ingress{ing}, + }, + }, + { + name: "backend sg required for gw - alb but gw not enabled.", + fields: fields{ + autogenSG: "sg-autogen", + listIngressCalls: []listIngressCall{ + {}, + }, + listServicesCalls: []listServicesCall{ + {}, + }, + listGatewaysCall: []listGatewaysCall{ + { + gateways: []*gwv1.Gateway{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "awesome-ns", + Name: "gw-1", + Finalizers: []string{"gateway.k8s.aws/alb"}, + }, + }, + }, + }, + }, + inactiveIngresses: []*networking.Ingress{ing}, + deleteSGCalls: []deleteSecurityGroupWithContextCall{ + { + req: &ec2sdk.DeleteSecurityGroupInput{ + GroupId: awssdk.String("sg-autogen"), + }, + resp: &ec2sdk.DeleteSecurityGroupOutput{}, + }, + }, + }, + }, { name: "backend sg requirement for service already known", fields: fields{ @@ -732,7 +829,7 @@ func Test_defaultBackendSGProvider_Release(t *testing.T) { ec2Client := services.NewMockEC2(ctrl) k8sClient := mock_client.NewMockClient(ctrl) sgProvider := NewBackendSGProvider(defaultClusterName, tt.fields.backendSG, - defaultVPCID, ec2Client, k8sClient, tt.fields.defaultTags, logr.New(&log.NullLogSink{})) + defaultVPCID, ec2Client, k8sClient, tt.fields.defaultTags, tt.fields.enableGatewayCheck, logr.New(&log.NullLogSink{})) if len(tt.fields.autogenSG) > 0 { sgProvider.backendSG = "" sgProvider.autoGeneratedSG = tt.fields.autogenSG @@ -771,6 +868,21 @@ func Test_defaultBackendSGProvider_Release(t *testing.T) { }, ).AnyTimes() } + + for _, call := range tt.fields.listGatewaysCall { + if !tt.fields.enableGatewayCheck { + break + } + k8sClient.EXPECT().List(gomock.Any(), &gwv1.GatewayList{}, gomock.Any()).DoAndReturn( + func(ctx context.Context, gatewayList *gwv1.GatewayList, opts ...client.ListOption) error { + for _, gw := range call.gateways { + gatewayList.Items = append(gatewayList.Items, *(gw.DeepCopy())) + } + return call.err + }, + ).AnyTimes() + } + for _, ing := range tt.env.ingresses { assert.NoError(t, k8sClient.Create(context.Background(), ing.DeepCopy())) } diff --git a/pkg/service/model_build_load_balancer_test.go b/pkg/service/model_build_load_balancer_test.go index 02a6cdfbb0..edc838f6c3 100644 --- a/pkg/service/model_build_load_balancer_test.go +++ b/pkg/service/model_build_load_balancer_test.go @@ -6,6 +6,7 @@ import ( ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" "github.com/aws/aws-sdk-go/service/ec2" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "testing" "k8s.io/apimachinery/pkg/util/sets" @@ -24,8 +25,6 @@ import ( "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2" ) -const lbAttrsDeletionProtectionEnabled = "deletion_protection.enabled" - func Test_defaultModelBuilderTask_buildLBAttributes(t *testing.T) { tests := []struct { testName string @@ -78,7 +77,7 @@ func Test_defaultModelBuilderTask_buildLBAttributes(t *testing.T) { Value: "true", }, { - Key: lbAttrsDeletionProtectionEnabled, + Key: shared_constants.LBAttributeDeletionProtection, Value: "true", }, }, @@ -112,7 +111,7 @@ func Test_defaultModelBuilderTask_buildLBAttributes(t *testing.T) { Value: "true", }, { - Key: lbAttrsDeletionProtectionEnabled, + Key: shared_constants.LBAttributeDeletionProtection, Value: "true", }, { @@ -1265,8 +1264,8 @@ func Test_defaultModelBuilderTask_buildLoadBalancerSubnets(t *testing.T) { Scheme: elbv2types.LoadBalancerSchemeEnumInternal, }, Tags: map[string]string{ - "elbv2.k8s.aws/cluster": "cluster-name", - "service.k8s.aws/stack": "namespace/serviceName", + shared_constants.TagKeyK8sCluster: "cluster-name", + "service.k8s.aws/stack": "namespace/serviceName", }, }, }, diff --git a/pkg/service/model_build_managed_sg.go b/pkg/service/model_build_managed_sg.go index fd66a21304..0416468cfe 100644 --- a/pkg/service/model_build_managed_sg.go +++ b/pkg/service/model_build_managed_sg.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "fmt" "regexp" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "strings" awssdk "github.com/aws/aws-sdk-go-v2/aws" @@ -17,15 +18,6 @@ import ( ) const ( - icmpv4Protocol = "icmp" - icmpv6Protocol = "icmpv6" - - icmpv4TypeForPathMtu = 3 // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-3 - icmpv4CodeForPathMtu = 4 - - icmpv6TypeForPathMtu = 2 // https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-2 - icmpv6CodeForPathMtu = 0 - resourceIDManagedSecurityGroup = "ManagedLBSecurityGroup" ) @@ -99,9 +91,9 @@ func (t *defaultModelBuildTask) buildManagedSecurityGroupIngressPermissions(ctx }) if icmpForPathMtuConfigured && icmpForPathMtuConfiguredFlag == "on" { permissions = append(permissions, ec2model.IPPermission{ - IPProtocol: string(icmpv4Protocol), - FromPort: awssdk.Int32(icmpv4TypeForPathMtu), - ToPort: awssdk.Int32(icmpv4CodeForPathMtu), + IPProtocol: shared_constants.ICMPV4Protocol, + FromPort: awssdk.Int32(shared_constants.ICMPV4TypeForPathMtu), + ToPort: awssdk.Int32(shared_constants.ICMPV4CodeForPathMtu), IPRanges: []ec2model.IPRange{ { CIDRIP: cidr, @@ -122,9 +114,9 @@ func (t *defaultModelBuildTask) buildManagedSecurityGroupIngressPermissions(ctx }) if icmpForPathMtuConfigured && icmpForPathMtuConfiguredFlag == "on" { permissions = append(permissions, ec2model.IPPermission{ - IPProtocol: string(icmpv6Protocol), - FromPort: awssdk.Int32(icmpv6TypeForPathMtu), - ToPort: awssdk.Int32(icmpv6CodeForPathMtu), + IPProtocol: shared_constants.ICMPV6Protocol, + FromPort: awssdk.Int32(shared_constants.ICMPV6TypeForPathMtu), + ToPort: awssdk.Int32(shared_constants.ICMPV6CodeForPathMtu), IPv6Range: []ec2model.IPv6Range{ { CIDRIPv6: cidr, diff --git a/pkg/service/model_builder.go b/pkg/service/model_builder.go index 759a957554..4b12004aeb 100644 --- a/pkg/service/model_builder.go +++ b/pkg/service/model_builder.go @@ -2,6 +2,7 @@ package service import ( "context" + "sigs.k8s.io/aws-load-balancer-controller/pkg/shared_constants" "strconv" "sync" @@ -29,7 +30,6 @@ const ( LoadBalancerTypeExternal = "external" LoadBalancerTargetTypeIP = "ip" LoadBalancerTargetTypeInstance = "instance" - lbAttrsDeletionProtection = "deletion_protection.enabled" controllerName = "service" ) @@ -274,8 +274,8 @@ func (t *defaultModelBuildTask) getDeletionProtectionViaAnnotation(svc corev1.Se if err != nil { return false, err } - if _, deletionProtectionSpecified := lbAttributes[lbAttrsDeletionProtection]; deletionProtectionSpecified { - deletionProtectionEnabled, err := strconv.ParseBool(lbAttributes[lbAttrsDeletionProtection]) + if _, deletionProtectionSpecified := lbAttributes[shared_constants.LBAttributeDeletionProtection]; deletionProtectionSpecified { + deletionProtectionEnabled, err := strconv.ParseBool(lbAttributes[shared_constants.LBAttributeDeletionProtection]) if err != nil { return false, err } diff --git a/pkg/shared_constants/attributes.go b/pkg/shared_constants/attributes.go new file mode 100644 index 0000000000..73092d7746 --- /dev/null +++ b/pkg/shared_constants/attributes.go @@ -0,0 +1,6 @@ +package shared_constants + +const ( + // LBAttributeDeletionProtection deletion protection attribute name + LBAttributeDeletionProtection = "deletion_protection.enabled" +) diff --git a/pkg/shared_constants/finalizers.go b/pkg/shared_constants/finalizers.go new file mode 100644 index 0000000000..7f90abf231 --- /dev/null +++ b/pkg/shared_constants/finalizers.go @@ -0,0 +1,18 @@ +package shared_constants + +const ( + // ExplicitGroupFinalizerPrefix the prefix for finalizers applied to an ingress group + ExplicitGroupFinalizerPrefix = "group.ingress.k8s.aws/" + + // ImplicitGroupFinalizer the finalizer used on an ingress resource + ImplicitGroupFinalizer = "ingress.k8s.aws/resources" + + // ServiceFinalizer the finalizer used on service resources + ServiceFinalizer = "service.k8s.aws/resources" + + // NLBGatewayFinalizer the finalizer we attach to an NLB Gateway resource + NLBGatewayFinalizer = "gateway.k8s.aws/nlb" + + // ALBGatewayFinalizer the finalizer we attach to an ALB Gateway resource + ALBGatewayFinalizer = "gateway.k8s.aws/alb" +) diff --git a/pkg/shared_constants/icmp.go b/pkg/shared_constants/icmp.go new file mode 100644 index 0000000000..f156a552b1 --- /dev/null +++ b/pkg/shared_constants/icmp.go @@ -0,0 +1,12 @@ +package shared_constants + +const ( + ICMPV4Protocol = "icmp" + ICMPV6Protocol = "icmpv6" + + ICMPV4CodeForPathMtu = 3 // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-3 + ICMPV6CodeForPathMtu = 4 + + ICMPV4TypeForPathMtu = 2 // https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-2 + ICMPV6TypeForPathMtu = 0 +) diff --git a/pkg/shared_constants/tag_keys.go b/pkg/shared_constants/tag_keys.go new file mode 100644 index 0000000000..cc6a370112 --- /dev/null +++ b/pkg/shared_constants/tag_keys.go @@ -0,0 +1,9 @@ +package shared_constants + +const ( + // TagKeyK8sCluster AWS TagKey for cluster resources. + TagKeyK8sCluster = "elbv2.k8s.aws/cluster" + + // TagKeyResource AWS TagKey to denote what resource is being represented. + TagKeyResource = "elbv2.k8s.aws/resource" +)