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  }