mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 15:05:27 +00:00
additional subnet configuration for AWS ELB (#97431)
* support subnets without cluster tag for auto-discovery * add support for manual subnet configuration via service annotation
This commit is contained in:
parent
11a05eb9af
commit
f48972e671
@ -233,6 +233,16 @@ const ServiceAnnotationLoadBalancerEIPAllocations = "service.beta.kubernetes.io/
|
||||
// For example: "Key1=Val1,Key2=Val2,KeyNoVal1=,KeyNoVal2"
|
||||
const ServiceAnnotationLoadBalancerTargetNodeLabels = "service.beta.kubernetes.io/aws-load-balancer-target-node-labels"
|
||||
|
||||
// ServiceAnnotationLoadBalancerSubnets is the annotation used on the service to specify the
|
||||
// Availability Zone configuration for the load balancer. The values are comma separated list of
|
||||
// subnetID or subnetName from different AZs
|
||||
// By default, the controller will auto-discover the subnets. If there are multiple subnets per AZ, auto-discovery
|
||||
// will break the tie in the following order -
|
||||
// 1. prefer the subnet with the correct role tag. kubernetes.io/role/elb for public and kubernetes.io/role/internal-elb for private access
|
||||
// 2. prefer the subnet with the cluster tag kubernetes.io/cluster/<Cluster Name>
|
||||
// 3. prefer the subnet that is first in lexicographic order
|
||||
const ServiceAnnotationLoadBalancerSubnets = "service.beta.kubernetes.io/aws-load-balancer-subnets"
|
||||
|
||||
// Event key when a volume is stuck on attaching state when being attached to a volume
|
||||
const volumeAttachmentStuck = "VolumeAttachmentStuck"
|
||||
|
||||
@ -3368,7 +3378,7 @@ func findTag(tags []*ec2.Tag, key string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Finds the subnets associated with the cluster, by matching tags.
|
||||
// Finds the subnets associated with the cluster, by matching cluster tags if present.
|
||||
// For maximal backwards compatibility, if no subnets are tagged, it will fall-back to the current subnet.
|
||||
// However, in future this will likely be treated as an error.
|
||||
func (c *Cloud) findSubnets() ([]*ec2.Subnet, error) {
|
||||
@ -3384,6 +3394,8 @@ func (c *Cloud) findSubnets() ([]*ec2.Subnet, error) {
|
||||
for _, subnet := range subnets {
|
||||
if c.tagging.hasClusterTag(subnet.Tags) {
|
||||
matches = append(matches, subnet)
|
||||
} else if c.tagging.hasNoClusterPrefixTag(subnet.Tags) {
|
||||
matches = append(matches, subnet)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3447,7 +3459,7 @@ func (c *Cloud) findELBSubnets(internalELB bool) ([]string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Try to break the tie using a tag
|
||||
// Try to break the tie using the role tag
|
||||
var tagName string
|
||||
if internalELB {
|
||||
tagName = TagNameSubnetInternalELB
|
||||
@ -3465,8 +3477,17 @@ func (c *Cloud) findELBSubnets(internalELB bool) ([]string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Prefer the one with the cluster Tag
|
||||
existingHasClusterTag := c.tagging.hasClusterTag(existing.Tags)
|
||||
subnetHasClusterTag := c.tagging.hasClusterTag(subnet.Tags)
|
||||
if existingHasClusterTag != subnetHasClusterTag {
|
||||
if subnetHasClusterTag {
|
||||
subnetsByAZ[az] = subnet
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// If we have two subnets for the same AZ we arbitrarily choose the one that is first lexicographically.
|
||||
// TODO: Should this be an error.
|
||||
if strings.Compare(*existing.SubnetId, *subnet.SubnetId) > 0 {
|
||||
klog.Warningf("Found multiple subnets in AZ %q; choosing %q between subnets %q and %q", az, *subnet.SubnetId, *existing.SubnetId, *subnet.SubnetId)
|
||||
subnetsByAZ[az] = subnet
|
||||
@ -3492,6 +3513,90 @@ func (c *Cloud) findELBSubnets(internalELB bool) ([]string, error) {
|
||||
return subnetIDs, nil
|
||||
}
|
||||
|
||||
func splitCommaSeparatedString(commaSeparatedString string) []string {
|
||||
var result []string
|
||||
parts := strings.Split(commaSeparatedString, ",")
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
if len(part) == 0 {
|
||||
continue
|
||||
}
|
||||
result = append(result, part)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// parses comma separated values from annotation into string slice, returns true if annotation exists
|
||||
func parseStringSliceAnnotation(annotations map[string]string, annotation string, value *[]string) bool {
|
||||
rawValue := ""
|
||||
if exists := parseStringAnnotation(annotations, annotation, &rawValue); !exists {
|
||||
return false
|
||||
}
|
||||
*value = splitCommaSeparatedString(rawValue)
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Cloud) getLoadBalancerSubnets(service *v1.Service, internalELB bool) ([]string, error) {
|
||||
var rawSubnetNameOrIDs []string
|
||||
if exists := parseStringSliceAnnotation(service.Annotations, ServiceAnnotationLoadBalancerSubnets, &rawSubnetNameOrIDs); exists {
|
||||
return c.resolveSubnetNameOrIDs(rawSubnetNameOrIDs)
|
||||
}
|
||||
return c.findELBSubnets(internalELB)
|
||||
}
|
||||
|
||||
func (c *Cloud) resolveSubnetNameOrIDs(subnetNameOrIDs []string) ([]string, error) {
|
||||
var subnetIDs []string
|
||||
var subnetNames []string
|
||||
if len(subnetNameOrIDs) == 0 {
|
||||
return []string{}, fmt.Errorf("unable to resolve empty subnet slice")
|
||||
}
|
||||
for _, nameOrID := range subnetNameOrIDs {
|
||||
if strings.HasPrefix(nameOrID, "subnet-") {
|
||||
subnetIDs = append(subnetIDs, nameOrID)
|
||||
} else {
|
||||
subnetNames = append(subnetNames, nameOrID)
|
||||
}
|
||||
}
|
||||
var resolvedSubnets []*ec2.Subnet
|
||||
if len(subnetIDs) > 0 {
|
||||
req := &ec2.DescribeSubnetsInput{
|
||||
SubnetIds: aws.StringSlice(subnetIDs),
|
||||
}
|
||||
subnets, err := c.ec2.DescribeSubnets(req)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
resolvedSubnets = append(resolvedSubnets, subnets...)
|
||||
}
|
||||
if len(subnetNames) > 0 {
|
||||
req := &ec2.DescribeSubnetsInput{
|
||||
Filters: []*ec2.Filter{
|
||||
{
|
||||
Name: aws.String("tag:Name"),
|
||||
Values: aws.StringSlice(subnetNames),
|
||||
},
|
||||
{
|
||||
Name: aws.String("vpc-id"),
|
||||
Values: aws.StringSlice([]string{c.vpcID}),
|
||||
},
|
||||
},
|
||||
}
|
||||
subnets, err := c.ec2.DescribeSubnets(req)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
resolvedSubnets = append(resolvedSubnets, subnets...)
|
||||
}
|
||||
if len(resolvedSubnets) != len(subnetNameOrIDs) {
|
||||
return []string{}, fmt.Errorf("expected to find %v, but found %v subnets", len(subnetNameOrIDs), len(resolvedSubnets))
|
||||
}
|
||||
var subnets []string
|
||||
for _, subnet := range resolvedSubnets {
|
||||
subnets = append(subnets, aws.StringValue(subnet.SubnetId))
|
||||
}
|
||||
return subnets, nil
|
||||
}
|
||||
|
||||
func isSubnetPublic(rt []*ec2.RouteTable, subnetID string) (bool, error) {
|
||||
var subnetTable *ec2.RouteTable
|
||||
for _, table := range rt {
|
||||
@ -3869,7 +3974,7 @@ func (c *Cloud) EnsureLoadBalancer(ctx context.Context, clusterName string, apiS
|
||||
|
||||
if isNLB(annotations) {
|
||||
// Find the subnets that the ELB will live in
|
||||
subnetIDs, err := c.findELBSubnets(internalELB)
|
||||
subnetIDs, err := c.getLoadBalancerSubnets(apiService, internalELB)
|
||||
if err != nil {
|
||||
klog.Errorf("Error listing subnets in VPC: %q", err)
|
||||
return nil, err
|
||||
@ -4036,7 +4141,7 @@ func (c *Cloud) EnsureLoadBalancer(ctx context.Context, clusterName string, apiS
|
||||
}
|
||||
|
||||
// Find the subnets that the ELB will live in
|
||||
subnetIDs, err := c.findELBSubnets(internalELB)
|
||||
subnetIDs, err := c.getLoadBalancerSubnets(apiService, internalELB)
|
||||
if err != nil {
|
||||
klog.Errorf("Error listing subnets in VPC: %q", err)
|
||||
return nil, err
|
||||
|
@ -20,6 +20,7 @@ package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
@ -808,6 +809,356 @@ func constructRouteTable(subnetID string, public bool) *ec2.RouteTable {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_findELBSubnets(t *testing.T) {
|
||||
awsServices := newMockedFakeAWSServices(TestClusterID)
|
||||
c, err := newAWSCloud(CloudConfig{}, awsServices)
|
||||
if err != nil {
|
||||
t.Errorf("Error building aws cloud: %v", err)
|
||||
return
|
||||
}
|
||||
subnetA0000001 := &ec2.Subnet{
|
||||
AvailabilityZone: aws.String("us-west-2a"),
|
||||
SubnetId: aws.String("subnet-a0000001"),
|
||||
Tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String(TagNameSubnetPublicELB),
|
||||
Value: aws.String("1"),
|
||||
},
|
||||
},
|
||||
}
|
||||
subnetA0000002 := &ec2.Subnet{
|
||||
AvailabilityZone: aws.String("us-west-2a"),
|
||||
SubnetId: aws.String("subnet-a0000002"),
|
||||
Tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String(TagNameSubnetPublicELB),
|
||||
Value: aws.String("1"),
|
||||
},
|
||||
},
|
||||
}
|
||||
subnetA0000003 := &ec2.Subnet{
|
||||
AvailabilityZone: aws.String("us-west-2a"),
|
||||
SubnetId: aws.String("subnet-a0000003"),
|
||||
Tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String(c.tagging.clusterTagKey()),
|
||||
Value: aws.String("owned"),
|
||||
},
|
||||
{
|
||||
Key: aws.String(TagNameSubnetInternalELB),
|
||||
Value: aws.String("1"),
|
||||
},
|
||||
},
|
||||
}
|
||||
subnetB0000001 := &ec2.Subnet{
|
||||
AvailabilityZone: aws.String("us-west-2b"),
|
||||
SubnetId: aws.String("subnet-b0000001"),
|
||||
Tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String(c.tagging.clusterTagKey()),
|
||||
Value: aws.String("owned"),
|
||||
},
|
||||
{
|
||||
Key: aws.String(TagNameSubnetPublicELB),
|
||||
Value: aws.String("1"),
|
||||
},
|
||||
},
|
||||
}
|
||||
subnetB0000002 := &ec2.Subnet{
|
||||
AvailabilityZone: aws.String("us-west-2b"),
|
||||
SubnetId: aws.String("subnet-b0000002"),
|
||||
Tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String(c.tagging.clusterTagKey()),
|
||||
Value: aws.String("owned"),
|
||||
},
|
||||
{
|
||||
Key: aws.String(TagNameSubnetInternalELB),
|
||||
Value: aws.String("1"),
|
||||
},
|
||||
},
|
||||
}
|
||||
subnetC0000001 := &ec2.Subnet{
|
||||
AvailabilityZone: aws.String("us-west-2c"),
|
||||
SubnetId: aws.String("subnet-c0000001"),
|
||||
Tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String(c.tagging.clusterTagKey()),
|
||||
Value: aws.String("owned"),
|
||||
},
|
||||
{
|
||||
Key: aws.String(TagNameSubnetInternalELB),
|
||||
Value: aws.String("1"),
|
||||
},
|
||||
},
|
||||
}
|
||||
subnetOther := &ec2.Subnet{
|
||||
AvailabilityZone: aws.String("us-west-2c"),
|
||||
SubnetId: aws.String("subnet-other"),
|
||||
Tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String(TagNameKubernetesClusterPrefix + "clusterid.other"),
|
||||
Value: aws.String("owned"),
|
||||
},
|
||||
{
|
||||
Key: aws.String(TagNameSubnetInternalELB),
|
||||
Value: aws.String("1"),
|
||||
},
|
||||
},
|
||||
}
|
||||
subnetNoTag := &ec2.Subnet{
|
||||
AvailabilityZone: aws.String("us-west-2c"),
|
||||
SubnetId: aws.String("subnet-notag"),
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
subnets []*ec2.Subnet
|
||||
routeTables map[string]bool
|
||||
internal bool
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "no subnets",
|
||||
},
|
||||
{
|
||||
name: "single tagged subnet",
|
||||
subnets: []*ec2.Subnet{
|
||||
subnetA0000001,
|
||||
},
|
||||
routeTables: map[string]bool{
|
||||
"subnet-a0000001": true,
|
||||
},
|
||||
internal: false,
|
||||
want: []string{"subnet-a0000001"},
|
||||
},
|
||||
{
|
||||
name: "no matching public subnet",
|
||||
subnets: []*ec2.Subnet{
|
||||
subnetA0000002,
|
||||
},
|
||||
routeTables: map[string]bool{
|
||||
"subnet-a0000002": false,
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "prefer role over cluster tag",
|
||||
subnets: []*ec2.Subnet{
|
||||
subnetA0000001,
|
||||
subnetA0000003,
|
||||
},
|
||||
routeTables: map[string]bool{
|
||||
"subnet-a0000001": true,
|
||||
"subnet-a0000003": true,
|
||||
},
|
||||
want: []string{"subnet-a0000001"},
|
||||
},
|
||||
{
|
||||
name: "prefer cluster tag",
|
||||
subnets: []*ec2.Subnet{
|
||||
subnetC0000001,
|
||||
subnetNoTag,
|
||||
},
|
||||
want: []string{"subnet-c0000001"},
|
||||
},
|
||||
{
|
||||
name: "include untagged",
|
||||
subnets: []*ec2.Subnet{
|
||||
subnetA0000001,
|
||||
subnetNoTag,
|
||||
},
|
||||
routeTables: map[string]bool{
|
||||
"subnet-a0000001": true,
|
||||
"subnet-notag": true,
|
||||
},
|
||||
want: []string{"subnet-a0000001", "subnet-notag"},
|
||||
},
|
||||
{
|
||||
name: "ignore some other cluster owned subnet",
|
||||
subnets: []*ec2.Subnet{
|
||||
subnetB0000001,
|
||||
subnetOther,
|
||||
},
|
||||
routeTables: map[string]bool{
|
||||
"subnet-b0000001": true,
|
||||
"subnet-other": true,
|
||||
},
|
||||
want: []string{"subnet-b0000001"},
|
||||
},
|
||||
{
|
||||
name: "prefer matching role",
|
||||
subnets: []*ec2.Subnet{
|
||||
subnetB0000001,
|
||||
subnetB0000002,
|
||||
},
|
||||
routeTables: map[string]bool{
|
||||
"subnet-b0000001": false,
|
||||
"subnet-b0000002": false,
|
||||
},
|
||||
want: []string{"subnet-b0000002"},
|
||||
internal: true,
|
||||
},
|
||||
{
|
||||
name: "choose lexicographic order",
|
||||
subnets: []*ec2.Subnet{
|
||||
subnetA0000001,
|
||||
subnetA0000002,
|
||||
},
|
||||
routeTables: map[string]bool{
|
||||
"subnet-a0000001": true,
|
||||
"subnet-a0000002": true,
|
||||
},
|
||||
want: []string{"subnet-a0000001"},
|
||||
},
|
||||
{
|
||||
name: "everything",
|
||||
subnets: []*ec2.Subnet{
|
||||
subnetA0000001,
|
||||
subnetA0000002,
|
||||
subnetB0000001,
|
||||
subnetB0000002,
|
||||
subnetC0000001,
|
||||
subnetNoTag,
|
||||
subnetOther,
|
||||
},
|
||||
routeTables: map[string]bool{
|
||||
"subnet-a0000001": true,
|
||||
"subnet-a0000002": true,
|
||||
"subnet-b0000001": true,
|
||||
"subnet-b0000002": true,
|
||||
"subnet-c0000001": true,
|
||||
"subnet-notag": true,
|
||||
"subnet-other": true,
|
||||
},
|
||||
want: []string{"subnet-a0000001", "subnet-b0000001", "subnet-c0000001"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
awsServices.ec2.RemoveSubnets()
|
||||
awsServices.ec2.RemoveRouteTables()
|
||||
for _, subnet := range tt.subnets {
|
||||
awsServices.ec2.CreateSubnet(subnet)
|
||||
}
|
||||
routeTables := constructRouteTables(tt.routeTables)
|
||||
for _, rt := range routeTables {
|
||||
awsServices.ec2.CreateRouteTable(rt)
|
||||
}
|
||||
got, _ := c.findELBSubnets(tt.internal)
|
||||
sort.Strings(tt.want)
|
||||
sort.Strings(got)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getLoadBalancerSubnets(t *testing.T) {
|
||||
awsServices := newMockedFakeAWSServices(TestClusterID)
|
||||
c, err := newAWSCloud(CloudConfig{}, awsServices)
|
||||
if err != nil {
|
||||
t.Errorf("Error building aws cloud: %v", err)
|
||||
return
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
service *v1.Service
|
||||
subnets []*ec2.Subnet
|
||||
internalELB bool
|
||||
want []string
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "no annotation",
|
||||
service: &v1.Service{},
|
||||
},
|
||||
{
|
||||
name: "annotation with no subnets",
|
||||
service: &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"service.beta.kubernetes.io/aws-load-balancer-subnets": "\t",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: errors.New("unable to resolve empty subnet slice"),
|
||||
},
|
||||
{
|
||||
name: "subnet ids",
|
||||
subnets: []*ec2.Subnet{
|
||||
{
|
||||
AvailabilityZone: aws.String("us-west-2c"),
|
||||
SubnetId: aws.String("subnet-a000001"),
|
||||
},
|
||||
{
|
||||
AvailabilityZone: aws.String("us-west-2b"),
|
||||
SubnetId: aws.String("subnet-a000002"),
|
||||
},
|
||||
},
|
||||
service: &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"service.beta.kubernetes.io/aws-load-balancer-subnets": "subnet-a000001, subnet-a000002",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []string{"subnet-a000001", "subnet-a000002"},
|
||||
},
|
||||
{
|
||||
name: "subnet names",
|
||||
subnets: []*ec2.Subnet{
|
||||
{
|
||||
AvailabilityZone: aws.String("us-west-2c"),
|
||||
SubnetId: aws.String("subnet-a000001"),
|
||||
},
|
||||
{
|
||||
AvailabilityZone: aws.String("us-west-2b"),
|
||||
SubnetId: aws.String("subnet-a000002"),
|
||||
},
|
||||
},
|
||||
service: &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"service.beta.kubernetes.io/aws-load-balancer-subnets": "My Subnet 1, My Subnet 2 ",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []string{"subnet-a000001", "subnet-a000002"},
|
||||
},
|
||||
{
|
||||
name: "unable to find all subnets",
|
||||
subnets: []*ec2.Subnet{
|
||||
{
|
||||
AvailabilityZone: aws.String("us-west-2c"),
|
||||
SubnetId: aws.String("subnet-a000001"),
|
||||
},
|
||||
},
|
||||
service: &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"service.beta.kubernetes.io/aws-load-balancer-subnets": "My Subnet 1, My Subnet 2, Test Subnet ",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: errors.New("expected to find 3, but found 1 subnets"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
awsServices.ec2.RemoveSubnets()
|
||||
for _, subnet := range tt.subnets {
|
||||
awsServices.ec2.CreateSubnet(subnet)
|
||||
}
|
||||
got, err := c.getLoadBalancerSubnets(tt.service, tt.internalELB)
|
||||
if tt.wantErr != nil {
|
||||
assert.EqualError(t, err, tt.wantErr.Error())
|
||||
} else {
|
||||
assert.Equal(t, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubnetIDsinVPC(t *testing.T) {
|
||||
awsServices := newMockedFakeAWSServices(TestClusterID)
|
||||
c, err := newAWSCloud(CloudConfig{}, awsServices)
|
||||
@ -3064,3 +3415,54 @@ func TestCloud_buildNLBHealthCheckConfiguration(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseStringSliceAnnotation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
annotation string
|
||||
annotations map[string]string
|
||||
want []string
|
||||
wantExist bool
|
||||
}{
|
||||
{
|
||||
name: "empty annotation",
|
||||
annotation: "test.annotation",
|
||||
wantExist: false,
|
||||
},
|
||||
{
|
||||
name: "empty value",
|
||||
annotation: "a1",
|
||||
annotations: map[string]string{
|
||||
"a1": "\t, ,,",
|
||||
},
|
||||
want: nil,
|
||||
wantExist: true,
|
||||
},
|
||||
{
|
||||
name: "single value",
|
||||
annotation: "a1",
|
||||
annotations: map[string]string{
|
||||
"a1": " value 1 ",
|
||||
},
|
||||
want: []string{"value 1"},
|
||||
wantExist: true,
|
||||
},
|
||||
{
|
||||
name: "multiple values",
|
||||
annotation: "a1",
|
||||
annotations: map[string]string{
|
||||
"a1": "subnet-1, subnet-2, My Subnet ",
|
||||
},
|
||||
want: []string{"subnet-1", "subnet-2", "My Subnet"},
|
||||
wantExist: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var gotValue []string
|
||||
gotExist := parseStringSliceAnnotation(tt.annotations, tt.annotation, &gotValue)
|
||||
assert.Equal(t, tt.wantExist, gotExist)
|
||||
assert.Equal(t, tt.want, gotValue)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -152,6 +152,15 @@ func (t *awsTagging) hasClusterTag(tags []*ec2.Tag) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *awsTagging) hasNoClusterPrefixTag(tags []*ec2.Tag) bool {
|
||||
for _, tag := range tags {
|
||||
if strings.HasPrefix(aws.StringValue(tag.Key), TagNameKubernetesClusterPrefix) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Ensure that a resource has the correct tags
|
||||
// If it has no tags, we assume that this was a problem caused by an error in between creation and tagging,
|
||||
// and we add the tags. If it has a different cluster's tags, that is an error.
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFilterTags(t *testing.T) {
|
||||
@ -182,3 +183,52 @@ func TestHasClusterTag(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasNoClusterPrefixTag(t *testing.T) {
|
||||
awsServices := NewFakeAWSServices(TestClusterID)
|
||||
c, err := newAWSCloud(CloudConfig{}, awsServices)
|
||||
if err != nil {
|
||||
t.Errorf("Error building aws cloud: %v", err)
|
||||
return
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
tags []*ec2.Tag
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "no tags",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "no cluster tags",
|
||||
tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String("not a cluster tag"),
|
||||
Value: aws.String("true"),
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "contains cluster tags",
|
||||
tags: []*ec2.Tag{
|
||||
{
|
||||
Key: aws.String("tag1"),
|
||||
Value: aws.String("value1"),
|
||||
},
|
||||
{
|
||||
Key: aws.String("kubernetes.io/cluster/test.cluster"),
|
||||
Value: aws.String("owned"),
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, c.tagging.hasNoClusterPrefixTag(tt.tags))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user