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