sigs.k8s.io/cluster-api-provider-aws@v1.5.5/test/e2e/shared/aws_helpers.go (about) 1 //go:build e2e 2 // +build e2e 3 4 /* 5 Copyright 2022 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package shared 21 22 import ( 23 "errors" 24 "fmt" 25 "strings" 26 27 "github.com/aws/aws-sdk-go/aws" 28 "github.com/aws/aws-sdk-go/aws/arn" 29 "github.com/aws/aws-sdk-go/aws/client" 30 rgapi "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" 31 . "github.com/onsi/ginkgo" 32 . "github.com/onsi/gomega" 33 34 infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1" 35 ) 36 37 type LoadBalancerType string 38 39 var ( 40 LoadBalancerTypeELB = LoadBalancerType("elb") 41 LoadBalancerTypeALB = LoadBalancerType("alb") 42 LoadBalancerTypeNLB = LoadBalancerType("nlb") 43 ) 44 45 type WaitForLoadBalancerToExistForServiceInput struct { 46 AWSSession client.ConfigProvider 47 ServiceName string 48 ServiceNamespace string 49 ClusterName string 50 Type LoadBalancerType 51 } 52 53 func WaitForLoadBalancerToExistForService(input WaitForLoadBalancerToExistForServiceInput, intervals ...interface{}) { 54 By(fmt.Sprintf("Waiting for AWS load balancer of type %s to exist for service %s/%s", input.Type, input.ServiceNamespace, input.ServiceName)) 55 56 Eventually(func() bool { 57 arns, err := GetLoadBalancerARNs(GetLoadBalancerARNsInput{ //nolint: gosimple 58 AWSSession: input.AWSSession, 59 ServiceName: input.ServiceName, 60 ServiceNamespace: input.ServiceNamespace, 61 ClusterName: input.ClusterName, 62 Type: input.Type, 63 }) 64 if err != nil { 65 fmt.Fprintf(GinkgoWriter, "error getting loadbalancer arns: %v\n", err) 66 67 return false 68 } 69 if len(arns) == 0 { 70 return false 71 } 72 73 return true 74 }, intervals...).Should(BeTrue(), "failed to wait for loadbalancer") 75 } 76 77 type GetLoadBalancerARNsInput struct { 78 AWSSession client.ConfigProvider 79 ServiceName string 80 ServiceNamespace string 81 ClusterName string 82 Type LoadBalancerType 83 } 84 85 func GetLoadBalancerARNs(input GetLoadBalancerARNsInput) ([]string, error) { 86 By(fmt.Sprintf("Getting AWS load balancer ARNs of type %s for service %s/%s", input.Type, input.ServiceNamespace, input.ServiceName)) 87 88 serviceTag := infrav1.ClusterAWSCloudProviderTagKey(input.ClusterName) 89 tags := map[string][]string{ 90 "kubernetes.io/service-name": {fmt.Sprintf("%s/%s", input.ServiceNamespace, input.ServiceName)}, 91 serviceTag: {string(infrav1.ResourceLifecycleOwned)}, 92 } 93 descInput := &DescribeResourcesByTagsInput{ 94 AWSSession: input.AWSSession, 95 Tags: tags, 96 } 97 98 descOutput, err := DescribeResourcesByTags(*descInput) 99 if err != nil { 100 fmt.Fprintf(GinkgoWriter, "error querying resources by tags: %v\n", err) 101 return nil, fmt.Errorf("describing resource tags: %w", err) 102 } 103 104 matchingARNs := []string{} 105 for _, resARN := range descOutput.ARNs { 106 parsedArn, err := arn.Parse(resARN) 107 if err != nil { 108 fmt.Fprintf(GinkgoWriter, "error parsing arn %s: %v\n", resARN, err) 109 return nil, fmt.Errorf("parsing resource arn %s: %w", resARN, err) 110 } 111 112 if parsedArn.Service != "elasticloadbalancing" { 113 continue 114 } 115 116 switch input.Type { 117 case LoadBalancerTypeALB: 118 if strings.HasPrefix(parsedArn.Resource, "loadbalancer/app/") { 119 matchingARNs = append(matchingARNs, resARN) 120 } 121 case LoadBalancerTypeNLB: 122 if strings.HasPrefix(parsedArn.Resource, "loadbalancer/net/") { 123 matchingARNs = append(matchingARNs, resARN) 124 } 125 case LoadBalancerTypeELB: 126 if strings.HasPrefix(parsedArn.Resource, "loadbalancer/") { 127 matchingARNs = append(matchingARNs, resARN) 128 } 129 } 130 } 131 132 return matchingARNs, nil 133 } 134 135 type DescribeResourcesByTagsInput struct { 136 AWSSession client.ConfigProvider 137 Tags map[string][]string 138 } 139 140 type DescribeResourcesByTagsOutput struct { 141 ARNs []string 142 } 143 144 func DescribeResourcesByTags(input DescribeResourcesByTagsInput) (*DescribeResourcesByTagsOutput, error) { 145 if len(input.Tags) == 0 { 146 return nil, errors.New("you must supply tags") 147 } 148 149 awsInput := rgapi.GetResourcesInput{ 150 TagFilters: []*rgapi.TagFilter{}, 151 } 152 153 for k, v := range input.Tags { 154 awsInput.TagFilters = append(awsInput.TagFilters, &rgapi.TagFilter{ 155 Key: aws.String(k), 156 Values: aws.StringSlice(v), 157 }) 158 } 159 160 rgSvc := rgapi.New(input.AWSSession) 161 awsOutput, err := rgSvc.GetResources(&awsInput) 162 if err != nil { 163 return nil, fmt.Errorf("getting resources by tags: %w", err) 164 } 165 166 output := &DescribeResourcesByTagsOutput{ 167 ARNs: []string{}, 168 } 169 for _, res := range awsOutput.ResourceTagMappingList { 170 output.ARNs = append(output.ARNs, *res.ResourceARN) 171 } 172 173 return output, nil 174 }