diff --git a/control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go b/control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go index ed9f5f373170..365fef17d500 100644 --- a/control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go +++ b/control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go @@ -170,7 +170,7 @@ func (r *PrivateServiceObserver) Reconcile(ctx context.Context, req ctrl.Request Namespace: r.HCPNamespace, }, } - lbName := strings.Split(strings.Split(svc.Status.LoadBalancer.Ingress[0].Hostname, ".")[0], "-")[0] + lbName := extractNLBName(svc.Status.LoadBalancer.Ingress[0].Hostname) if _, err := r.CreateOrUpdate(ctx, r, awsEndpointService, func() error { awsEndpointService.Spec.NetworkLoadBalancerName = lbName if hcp.Spec.Platform.AWS != nil { @@ -184,6 +184,31 @@ func (r *PrivateServiceObserver) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{}, nil } +// extractNLBName extracts the NLB name from its DNS hostname. +// +// AWS NLB DNS format is "{name}-{id}.elb.{region}.amazonaws.com" +// where {name} is the value passed to CreateLoadBalancer and {id} is an +// AWS-assigned hex suffix. +// Ref: https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#dns-name +// +// The in-tree cloud provider generates hyphen-free names ("a" + UID), +// but the AWS LB Controller (EKS Auto Mode) uses "k8s-{ns}-{svc}-{hash}". +// We strip only the last dash-delimited segment (the AWS-assigned ID) +// because {id} is always hex (no hyphens), as shown in every AWS API +// example and required structurally — since {name} may contain hyphens, +// a hyphenated {id} would make the format ambiguous. +// +// In-tree name generation: https://github.com/kubernetes/cloud-provider/blob/v0.32.3/cloud.go#L89-L98 +// AWS LB Controller name generation: https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/v2.12.0/pkg/service/model_build_load_balancer.go#L591-L608 +func extractNLBName(hostname string) string { + firstLabel := strings.Split(hostname, ".")[0] + lastDash := strings.LastIndex(firstLabel, "-") + if lastDash == -1 { + return firstLabel + } + return firstLabel[:lastDash] +} + // errDependencyViolation is returned when AWS reports a DependencyViolation, // indicating the VPC endpoint is still being deleted. The caller translates // this into a controlled requeue rather than an error-driven requeue. diff --git a/control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go b/control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go index 971655e63b20..539a96f3248d 100644 --- a/control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go +++ b/control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller_test.go @@ -910,3 +910,33 @@ func TestReconcileDeletionSharedVPC(t *testing.T) { }) } } + +func TestExtractNLBName(t *testing.T) { + testCases := []struct { + name string + hostname string + expected string + }{ + { + name: "When standard NLB hostname it should extract name without hyphens", + hostname: "a1b2c3d4e5f6g7-1234567890abcdef.elb.us-east-1.amazonaws.com", + expected: "a1b2c3d4e5f6g7", + }, + { + name: "When EKS Auto Mode NLB hostname it should extract full name with hyphens", + hostname: "k8s-clusters-kubeapis-db6fee3a62-8008741421d14306.elb.us-east-1.amazonaws.com", + expected: "k8s-clusters-kubeapis-db6fee3a62", + }, + { + name: "When hostname has no hyphens it should return the first label as-is", + hostname: "somename.elb.us-east-1.amazonaws.com", + expected: "somename", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + g.Expect(extractNLBName(tc.hostname)).To(Equal(tc.expected)) + }) + } +}