diff --git a/pkg/cloud/services/eks/securitygroup.go b/pkg/cloud/services/eks/securitygroup.go index 829de2fcab..7bf36a972d 100644 --- a/pkg/cloud/services/eks/securitygroup.go +++ b/pkg/cloud/services/eks/securitygroup.go @@ -19,6 +19,8 @@ package eks import ( "context" "fmt" + "reflect" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -73,11 +75,84 @@ func (s *Service) reconcileSecurityGroups(cluster *eks.Cluster) error { return fmt.Errorf("describing EKS cluster security group: %w", err) } - s.scope.ControlPlane.Status.Network.SecurityGroups[ekscontrolplanev1.SecurityGroupCluster] = infrav1.SecurityGroup{ + clusterSecurityGroup := infrav1.SecurityGroup{ ID: aws.StringValue(cluster.ResourcesVpcConfig.ClusterSecurityGroupId), Name: *output.SecurityGroups[0].GroupName, Tags: converters.TagsToMap(output.SecurityGroups[0].Tags), } + s.scope.ControlPlane.Status.Network.SecurityGroups[ekscontrolplanev1.SecurityGroupCluster] = clusterSecurityGroup + + additionalTags := s.scope.ControlPlane.Spec.AdditionalTags + if !reflect.DeepEqual(sg.Tags, desiredTags(sg.Tags, additionalTags)) { + if err = s.updateTagsForEKSManagedSecurityGroup(&sg.ID, sg.Tags, desiredTags(sg.Tags, additionalTags)); err != nil { + return err + } + } + + if !reflect.DeepEqual(clusterSecurityGroup.Tags, desiredTags(clusterSecurityGroup.Tags, additionalTags)) { + if err = s.updateTagsForEKSManagedSecurityGroup(&clusterSecurityGroup.ID, clusterSecurityGroup.Tags, desiredTags(clusterSecurityGroup.Tags, additionalTags)); err != nil { + return err + } + } + + return nil +} + +// desiredTags will return the default tags of EKS cluster with the spec.additionalTags from AWSManagedControlPlane +func desiredTags(existingTags, additionalTags infrav1.Tags) infrav1.Tags { + merged := make(infrav1.Tags) + + for key, value := range existingTags { + // since the cluster is created/managed by CAPA, existing tags will contain the default EKS security group tags + if key == "aws:eks:cluster-name" || key == "Name" || strings.Contains(key, infrav1.NameKubernetesAWSCloudProviderPrefix) { + merged[key] = value + } + } + + // additional tags from spec.additionalTags of AWSManagedControlPlane will be added/updated to desired tags considering them as source of truth + for key, value := range additionalTags { + merged[key] = value + } + + return merged +} + +// updateTagsForEKSManagedSecurityGroup will update the tags in the EKS security group with the desired tags via create/update/delete operations +func (s *Service) updateTagsForEKSManagedSecurityGroup(securityGroupID *string, existingTags, desiredTags infrav1.Tags) error { + tagsToDelete, newTags := getTagUpdates(existingTags, desiredTags) + + // Create tags for updating or adding + if len(newTags) > 0 { + desiredTags := converters.MapToTags(newTags) + _, err := s.EC2Client.CreateTags(&ec2.CreateTagsInput{ + Resources: []*string{securityGroupID}, + Tags: desiredTags, + }) + if err != nil { + return fmt.Errorf("failed to create/update tags: %v", err) + } + } + + // Delete tags + if len(tagsToDelete) > 0 { + var ec2TagKeys []*ec2.Tag + for _, key := range tagsToDelete { + // the default tags added to EKS cluster via AWS will not be deleted + if key != "aws:eks:cluster-name" && key != "Name" && !strings.Contains(key, infrav1.NameKubernetesAWSCloudProviderPrefix) { + ec2TagKeys = append(ec2TagKeys, &ec2.Tag{ + Key: aws.String(key), + }) + } + } + + _, err := s.EC2Client.DeleteTags(&ec2.DeleteTagsInput{ + Resources: []*string{securityGroupID}, + Tags: ec2TagKeys, + }) + if err != nil { + return fmt.Errorf("failed to delete tags: %v", err) + } + } return nil }