@@ -400,6 +400,14 @@ func (c *Cloud) ensureLoadBalancerv2(ctx context.Context, namespacedName types.N
400400 loadBalancer = & loadBalancers .LoadBalancers [0 ]
401401 }
402402 }
403+
404+ // Reconcile target group attributes.
405+ if _ , present := annotations [ServiceAnnotationLoadBalancerTargetGroupAttributes ]; present {
406+ if err := c .reconcileTargetGroupsAttributes (ctx , aws .ToString (loadBalancer .LoadBalancerArn ), annotations ); err != nil {
407+ return nil , fmt .Errorf ("error reconciling target group attributes: %q" , err )
408+ }
409+ }
410+
403411 return loadBalancer , nil
404412}
405413
@@ -493,9 +501,156 @@ func (c *Cloud) reconcileLBAttributes(ctx context.Context, loadBalancerArn strin
493501 return fmt .Errorf ("unable to update load balancer attributes during attribute sync: %q" , err )
494502 }
495503 }
504+
505+ return nil
506+ }
507+
508+ // reconcileTargetGroupsAttributes reconciles the target group attributes for all target groups
509+ // associated with a load balancer to match the desired state specified in service annotations.
510+ // Only supported attributes by controller are reconciled.
511+ //
512+ // Parameters:
513+ // - ctx: context for AWS API calls with timeout and cancellation support
514+ // - lbARN: AWS load balancer ARN to identify which target groups to process
515+ // - annotations: service annotations containing desired target group attribute configuration
516+ //
517+ // Returns:
518+ // - error: validation errors, AWS API errors, or target group attribute update failures
519+ //
520+ // Documentation generated by Cursor AI
521+ func (c * Cloud ) reconcileTargetGroupsAttributes (ctx context.Context , lbARN string , annotations map [string ]string ) error {
522+ if len (lbARN ) == 0 {
523+ return fmt .Errorf ("error updating target groups attributes: load balancer ARN is empty" )
524+ }
525+
526+ describeTargetGroupsOutput , err := c .elbv2 .DescribeTargetGroups (ctx , & elbv2.DescribeTargetGroupsInput {
527+ LoadBalancerArn : aws .String (lbARN ),
528+ })
529+ if err != nil {
530+ return fmt .Errorf ("error updating target groups attributes from load balancer %q: %w" , lbARN , err )
531+ }
532+
533+ var errs []error
534+ for _ , tg := range describeTargetGroupsOutput .TargetGroups {
535+ err := c .ensureTargetGroupAttributes (ctx , & tg , annotations )
536+ if err != nil {
537+ errs = append (errs , fmt .Errorf ("error updating target group attributes for target group %q: %w" , aws .ToString (tg .TargetGroupArn ), err ))
538+ }
539+ }
540+ if len (errs ) > 0 {
541+ return fmt .Errorf ("one or more errors occurred while updating target group attributes: %v" , errs )
542+ }
543+ return nil
544+ }
545+
546+ // ensureTargetGroupAttributes ensures that the target group attributes for a specific
547+ // target group match the desired state specified in service annotations.
548+ //
549+ // Parameters:
550+ // - ctx: context for AWS API calls and cancellation
551+ // - tg: target group object containing ARN, protocol, and type information
552+ // - annotations: service annotations containing desired target group attributes
553+ //
554+ // Returns:
555+ // - error: validation errors, AWS API errors, or attribute building errors
556+ //
557+ // Documentation generated by Cursor AI
558+ func (c * Cloud ) ensureTargetGroupAttributes (ctx context.Context , tg * elbv2types.TargetGroup , annotations map [string ]string ) error {
559+ if tg == nil {
560+ return fmt .Errorf ("unable to reconcile target group attributes: target group is required" )
561+ }
562+
563+ tgAttributes , err := c .elbv2 .DescribeTargetGroupAttributes (ctx , & elbv2.DescribeTargetGroupAttributesInput {
564+ TargetGroupArn : tg .TargetGroupArn ,
565+ })
566+ if err != nil {
567+ return fmt .Errorf ("unable to retrieve target group attributes during attribute sync: %w" , err )
568+ }
569+
570+ desiredTargetGroupAttributes , err := c .buildTargetGroupAttributes (tg , tgAttributes .Attributes , annotations )
571+ if err != nil {
572+ return fmt .Errorf ("unable to build target group attributes: %w" , err )
573+ }
574+
575+ if len (desiredTargetGroupAttributes ) == 0 {
576+ return nil
577+ }
578+ klog .Infof ("Updating attributes for target group %q" , aws .ToString (tg .TargetGroupArn ))
579+
580+ if _ , err = c .elbv2 .ModifyTargetGroupAttributes (ctx , & elbv2.ModifyTargetGroupAttributesInput {
581+ TargetGroupArn : tg .TargetGroupArn ,
582+ Attributes : desiredTargetGroupAttributes ,
583+ }); err != nil {
584+ return fmt .Errorf ("unable to modify target group attributes during attribute sync: %w" , err )
585+ }
586+ klog .Infof ("Successfully updated target group attributes for %q" , aws .ToString (tg .TargetGroupArn ))
587+
496588 return nil
497589}
498590
591+ // buildTargetGroupAttributes builds the list of target group attributes that need to be modified
592+ // based on the Service annotation, and current attribute values, calculating only the attributes
593+ // to be changed.
594+ //
595+ // Supported values to annotation ServiceAnnotationLoadBalancerTargetGroupAttributes:
596+ // - preserve_client_ip.enabled=true|false - whether to preserve client IP addresses
597+ // - proxy_protocol_v2.enabled=true|false - whether to enable proxy protocol v2
598+
599+ // Behavior when no annotations provided or removed:
600+ // - Target groups preserves the last set values, and skips any changes.
601+ //
602+ // Parameters:
603+ // - tg: target group object
604+ // - tgAttributes: current target group attributes from AWS resource
605+ // - annotations: service annotations containing desired attribute values
606+ //
607+ // Returns:
608+ // - []elbv2types.TargetGroupAttribute: list of attributes that need to be modified
609+ // - error: validation errors, parsing errors, or AWS restrictions
610+ //
611+ // Documentation generated by Cursor AI
612+ func (c * Cloud ) buildTargetGroupAttributes (tg * elbv2types.TargetGroup , tgAttributes []elbv2types.TargetGroupAttribute , annotations map [string ]string ) ([]elbv2types.TargetGroupAttribute , error ) {
613+ errPrefix := "error building target group attributes"
614+ if tg == nil {
615+ return nil , fmt .Errorf ("%s: target group is nil" , errPrefix )
616+ }
617+ if tgAttributes == nil {
618+ return nil , fmt .Errorf ("%s: target group attributes are nil" , errPrefix )
619+ }
620+
621+ // existingAttributes are current target group attributes from AWS.
622+ existingAttributes := make (map [string ]string , len (tgAttributes ))
623+ for _ , attr := range tgAttributes {
624+ existingAttributes [aws .ToString (attr .Key )] = aws .ToString (attr .Value )
625+ }
626+
627+ // annotationAttributes are the user-defined attributes set through annotations.
628+ annotationAttributes := getKeyValuePropertiesFromAnnotation (annotations , ServiceAnnotationLoadBalancerTargetGroupAttributes )
629+
630+ // Calculate attribute difference between current and desired state.
631+ var diff []elbv2types.TargetGroupAttribute
632+ for attrKey , attrValue := range annotationAttributes {
633+ // Skip non-supported attributes by controller.
634+ if _ , ok := existingAttributes [attrKey ]; ! ok {
635+ klog .V (2 ).Infof ("Skipping non-supported target group attribute %q" , attrKey )
636+ continue
637+ }
638+
639+ // Calculate the target value: annotation override > current value.
640+ if attrValue == existingAttributes [attrKey ] {
641+ klog .V (2 ).Infof ("Skipping changes to target group attribute %q, values are the same: %q" , attrKey , attrValue )
642+ continue
643+ }
644+ klog .V (2 ).Infof ("Setting from annotation the target group attribute %q value from %q to %q" , attrKey , existingAttributes [attrKey ], attrValue )
645+
646+ diff = append (diff , elbv2types.TargetGroupAttribute {
647+ Key : aws .String (attrKey ),
648+ Value : aws .String (attrValue ),
649+ })
650+ }
651+ return diff , nil
652+ }
653+
499654var invalidELBV2NameRegex = regexp .MustCompile ("[^[:alnum:]]" )
500655
501656// buildTargetGroupName will build unique name for targetGroup of service & port.
0 commit comments