2014-09-09 21:25:35 +00:00
|
|
|
/*
|
2016-06-03 00:25:58 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors.
|
2014-09-09 21:25:35 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2015-10-10 08:18:12 +00:00
|
|
|
package aws
|
2014-09-09 21:25:35 +00:00
|
|
|
|
|
|
|
import (
|
2017-06-13 19:12:07 +00:00
|
|
|
"fmt"
|
2015-03-27 14:31:16 +00:00
|
|
|
"io"
|
2014-09-09 21:25:35 +00:00
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2015-06-04 05:54:59 +00:00
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
"github.com/aws/aws-sdk-go/service/ec2"
|
|
|
|
"github.com/aws/aws-sdk-go/service/elb"
|
2017-06-22 18:24:23 +00:00
|
|
|
"k8s.io/api/core/v1"
|
2017-01-11 14:09:48 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
2016-02-29 19:16:42 +00:00
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/mock"
|
2017-11-19 19:42:29 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2017-01-11 14:09:48 +00:00
|
|
|
"k8s.io/apimachinery/pkg/types"
|
2017-05-30 14:46:00 +00:00
|
|
|
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
|
2014-09-09 21:25:35 +00:00
|
|
|
)
|
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
const TestClusterId = "clusterid.test"
|
2016-05-29 03:54:26 +00:00
|
|
|
const TestClusterName = "testCluster"
|
2015-05-23 00:12:53 +00:00
|
|
|
|
2017-09-15 14:58:46 +00:00
|
|
|
type MockedFakeEC2 struct {
|
|
|
|
*FakeEC2Impl
|
|
|
|
mock.Mock
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockedFakeEC2) expectDescribeSecurityGroups(clusterId, groupName, clusterID string) {
|
|
|
|
tags := []*ec2.Tag{
|
|
|
|
{Key: aws.String(TagNameKubernetesClusterLegacy), Value: aws.String(clusterId)},
|
|
|
|
{Key: aws.String(fmt.Sprintf("%s%s", TagNameKubernetesClusterPrefix, clusterId)), Value: aws.String(ResourceLifecycleOwned)},
|
|
|
|
}
|
|
|
|
|
|
|
|
m.On("DescribeSecurityGroups", &ec2.DescribeSecurityGroupsInput{Filters: []*ec2.Filter{
|
|
|
|
newEc2Filter("group-name", groupName),
|
|
|
|
newEc2Filter("vpc-id", ""),
|
|
|
|
}}).Return([]*ec2.SecurityGroup{{Tags: tags}})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockedFakeEC2) DescribeVolumes(request *ec2.DescribeVolumesInput) ([]*ec2.Volume, error) {
|
|
|
|
args := m.Called(request)
|
|
|
|
return args.Get(0).([]*ec2.Volume), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockedFakeEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error) {
|
|
|
|
args := m.Called(request)
|
|
|
|
return args.Get(0).([]*ec2.SecurityGroup), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type MockedFakeELB struct {
|
|
|
|
*FakeELB
|
|
|
|
mock.Mock
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockedFakeELB) DescribeLoadBalancers(input *elb.DescribeLoadBalancersInput) (*elb.DescribeLoadBalancersOutput, error) {
|
|
|
|
args := m.Called(input)
|
|
|
|
return args.Get(0).(*elb.DescribeLoadBalancersOutput), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockedFakeELB) expectDescribeLoadBalancers(loadBalancerName string) {
|
|
|
|
m.On("DescribeLoadBalancers", &elb.DescribeLoadBalancersInput{LoadBalancerNames: []*string{aws.String(loadBalancerName)}}).Return(&elb.DescribeLoadBalancersOutput{
|
|
|
|
LoadBalancerDescriptions: []*elb.LoadBalancerDescription{{}},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-11-14 19:22:16 +00:00
|
|
|
func (m *MockedFakeELB) AddTags(input *elb.AddTagsInput) (*elb.AddTagsOutput, error) {
|
|
|
|
args := m.Called(input)
|
|
|
|
return args.Get(0).(*elb.AddTagsOutput), nil
|
|
|
|
}
|
|
|
|
|
2017-11-19 19:42:29 +00:00
|
|
|
func (m *MockedFakeELB) ConfigureHealthCheck(input *elb.ConfigureHealthCheckInput) (*elb.ConfigureHealthCheckOutput, error) {
|
|
|
|
args := m.Called(input)
|
|
|
|
if args.Get(0) == nil {
|
|
|
|
return nil, args.Error(1)
|
|
|
|
}
|
|
|
|
return args.Get(0).(*elb.ConfigureHealthCheckOutput), args.Error(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockedFakeELB) expectConfigureHealthCheck(loadBalancerName *string, expectedHC *elb.HealthCheck, returnErr error) {
|
|
|
|
expected := &elb.ConfigureHealthCheckInput{HealthCheck: expectedHC, LoadBalancerName: loadBalancerName}
|
|
|
|
call := m.On("ConfigureHealthCheck", expected)
|
|
|
|
if returnErr != nil {
|
|
|
|
call.Return(nil, returnErr)
|
|
|
|
} else {
|
|
|
|
call.Return(&elb.ConfigureHealthCheckOutput{}, nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-09 21:25:35 +00:00
|
|
|
func TestReadAWSCloudConfig(t *testing.T) {
|
2015-03-27 14:31:16 +00:00
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
reader io.Reader
|
2016-04-17 20:31:02 +00:00
|
|
|
aws Services
|
2015-03-27 14:31:16 +00:00
|
|
|
|
|
|
|
expectError bool
|
|
|
|
zone string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"No config reader",
|
|
|
|
nil, nil,
|
|
|
|
true, "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Empty config, no metadata",
|
|
|
|
strings.NewReader(""), nil,
|
|
|
|
true, "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"No zone in config, no metadata",
|
|
|
|
strings.NewReader("[global]\n"), nil,
|
|
|
|
true, "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Zone in config, no metadata",
|
|
|
|
strings.NewReader("[global]\nzone = eu-west-1a"), nil,
|
|
|
|
false, "eu-west-1a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"No zone in config, metadata does not have zone",
|
2017-09-15 14:58:46 +00:00
|
|
|
strings.NewReader("[global]\n"), newMockedFakeAWSServices(TestClusterId).WithAz(""),
|
2015-03-27 14:31:16 +00:00
|
|
|
true, "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"No zone in config, metadata has zone",
|
2017-09-15 14:58:46 +00:00
|
|
|
strings.NewReader("[global]\n"), newMockedFakeAWSServices(TestClusterId),
|
2015-05-23 00:12:53 +00:00
|
|
|
false, "us-east-1a",
|
2015-03-27 14:31:16 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"Zone in config should take precedence over metadata",
|
2017-09-15 14:58:46 +00:00
|
|
|
strings.NewReader("[global]\nzone = eu-west-1a"), newMockedFakeAWSServices(TestClusterId),
|
2015-05-23 00:12:53 +00:00
|
|
|
false, "eu-west-1a",
|
2015-03-27 14:31:16 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Logf("Running test case %s", test.name)
|
2015-09-20 20:09:08 +00:00
|
|
|
var metadata EC2Metadata
|
2015-05-23 00:12:53 +00:00
|
|
|
if test.aws != nil {
|
2015-09-20 20:09:08 +00:00
|
|
|
metadata, _ = test.aws.Metadata()
|
2015-05-23 00:12:53 +00:00
|
|
|
}
|
|
|
|
cfg, err := readAWSCloudConfig(test.reader, metadata)
|
2015-03-27 14:31:16 +00:00
|
|
|
if test.expectError {
|
|
|
|
if err == nil {
|
2015-05-23 00:12:53 +00:00
|
|
|
t.Errorf("Should error for case %s (cfg=%v)", test.name, cfg)
|
2015-03-27 14:31:16 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Should succeed for case: %s", test.name)
|
|
|
|
}
|
|
|
|
if cfg.Global.Zone != test.zone {
|
|
|
|
t.Errorf("Incorrect zone value (%s vs %s) for case: %s",
|
|
|
|
cfg.Global.Zone, test.zone, test.name)
|
|
|
|
}
|
|
|
|
}
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
func TestNewAWSCloud(t *testing.T) {
|
2015-03-27 14:31:16 +00:00
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
reader io.Reader
|
2016-04-17 20:31:02 +00:00
|
|
|
awsServices Services
|
2015-03-27 14:31:16 +00:00
|
|
|
|
|
|
|
expectError bool
|
2016-02-29 15:22:26 +00:00
|
|
|
region string
|
2015-03-27 14:31:16 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
"No config reader",
|
2017-09-15 14:58:46 +00:00
|
|
|
nil, newMockedFakeAWSServices(TestClusterId).WithAz(""),
|
2015-03-27 14:31:16 +00:00
|
|
|
true, "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Config specifies valid zone",
|
2017-09-15 14:58:46 +00:00
|
|
|
strings.NewReader("[global]\nzone = eu-west-1a"), newMockedFakeAWSServices(TestClusterId),
|
2016-02-29 15:22:26 +00:00
|
|
|
false, "eu-west-1",
|
2015-03-27 14:31:16 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"Gets zone from metadata when not in config",
|
|
|
|
strings.NewReader("[global]\n"),
|
2017-09-15 14:58:46 +00:00
|
|
|
newMockedFakeAWSServices(TestClusterId),
|
2016-02-29 15:22:26 +00:00
|
|
|
false, "us-east-1",
|
2015-03-27 14:31:16 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"No zone in config or metadata",
|
2015-05-23 00:12:53 +00:00
|
|
|
strings.NewReader("[global]\n"),
|
2017-09-15 14:58:46 +00:00
|
|
|
newMockedFakeAWSServices(TestClusterId).WithAz(""),
|
2015-03-27 14:31:16 +00:00
|
|
|
true, "",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Logf("Running test case %s", test.name)
|
2015-05-23 00:12:53 +00:00
|
|
|
c, err := newAWSCloud(test.reader, test.awsServices)
|
2015-03-27 14:31:16 +00:00
|
|
|
if test.expectError {
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("Should error for case %s", test.name)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err != nil {
|
2015-05-23 00:12:53 +00:00
|
|
|
t.Errorf("Should succeed for case: %s, got %v", test.name, err)
|
2016-02-29 15:22:26 +00:00
|
|
|
} else if c.region != test.region {
|
|
|
|
t.Errorf("Incorrect region value (%s vs %s) for case: %s",
|
|
|
|
c.region, test.region, test.name)
|
2015-03-27 14:31:16 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-17 20:31:02 +00:00
|
|
|
func mockInstancesResp(selfInstance *ec2.Instance, instances []*ec2.Instance) (*Cloud, *FakeAWSServices) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
2016-02-29 15:22:26 +00:00
|
|
|
awsServices.instances = instances
|
|
|
|
awsServices.selfInstance = selfInstance
|
|
|
|
awsCloud, err := newAWSCloud(nil, awsServices)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return awsCloud, awsServices
|
2015-03-10 04:15:53 +00:00
|
|
|
}
|
|
|
|
|
2016-04-17 20:31:02 +00:00
|
|
|
func mockAvailabilityZone(availabilityZone string) *Cloud {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId).WithAz(availabilityZone)
|
2016-02-29 15:22:26 +00:00
|
|
|
awsCloud, err := newAWSCloud(nil, awsServices)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
2015-03-10 04:15:53 +00:00
|
|
|
}
|
2016-02-29 15:22:26 +00:00
|
|
|
return awsCloud
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
|
|
|
|
2016-11-18 20:58:42 +00:00
|
|
|
func testHasNodeAddress(t *testing.T, addrs []v1.NodeAddress, addressType v1.NodeAddressType, address string) {
|
2015-04-20 04:00:18 +00:00
|
|
|
for _, addr := range addrs {
|
|
|
|
if addr.Type == addressType && addr.Address == address {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t.Errorf("Did not find expected address: %s:%s in %v", addressType, address, addrs)
|
|
|
|
}
|
|
|
|
|
2015-03-13 22:09:31 +00:00
|
|
|
func TestNodeAddresses(t *testing.T) {
|
2015-03-04 23:32:47 +00:00
|
|
|
// Note these instances have the same name
|
|
|
|
// (we test that this produces an error)
|
2015-05-14 23:53:47 +00:00
|
|
|
var instance0 ec2.Instance
|
|
|
|
var instance1 ec2.Instance
|
2016-02-03 21:43:47 +00:00
|
|
|
var instance2 ec2.Instance
|
2015-05-14 23:53:47 +00:00
|
|
|
|
|
|
|
//0
|
2016-02-29 15:22:26 +00:00
|
|
|
instance0.InstanceId = aws.String("i-0")
|
2015-09-20 21:10:57 +00:00
|
|
|
instance0.PrivateDnsName = aws.String("instance-same.ec2.internal")
|
|
|
|
instance0.PrivateIpAddress = aws.String("192.168.0.1")
|
2016-11-22 16:27:09 +00:00
|
|
|
instance0.PublicDnsName = aws.String("instance-same.ec2.external")
|
2015-09-20 21:10:57 +00:00
|
|
|
instance0.PublicIpAddress = aws.String("1.2.3.4")
|
2017-08-03 22:57:59 +00:00
|
|
|
instance0.NetworkInterfaces = []*ec2.InstanceNetworkInterface{
|
|
|
|
{
|
|
|
|
Status: aws.String(ec2.NetworkInterfaceStatusInUse),
|
|
|
|
PrivateIpAddresses: []*ec2.InstancePrivateIpAddress{
|
|
|
|
{
|
|
|
|
PrivateIpAddress: aws.String("192.168.0.1"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2015-05-14 23:53:47 +00:00
|
|
|
instance0.InstanceType = aws.String("c3.large")
|
2016-02-29 15:22:26 +00:00
|
|
|
instance0.Placement = &ec2.Placement{AvailabilityZone: aws.String("us-east-1a")}
|
2015-05-14 23:53:47 +00:00
|
|
|
state0 := ec2.InstanceState{
|
|
|
|
Name: aws.String("running"),
|
|
|
|
}
|
|
|
|
instance0.State = &state0
|
|
|
|
|
|
|
|
//1
|
2016-02-29 15:22:26 +00:00
|
|
|
instance1.InstanceId = aws.String("i-1")
|
2015-09-20 21:10:57 +00:00
|
|
|
instance1.PrivateDnsName = aws.String("instance-same.ec2.internal")
|
|
|
|
instance1.PrivateIpAddress = aws.String("192.168.0.2")
|
2015-05-14 23:53:47 +00:00
|
|
|
instance1.InstanceType = aws.String("c3.large")
|
2016-02-29 15:22:26 +00:00
|
|
|
instance1.Placement = &ec2.Placement{AvailabilityZone: aws.String("us-east-1a")}
|
2015-05-14 23:53:47 +00:00
|
|
|
state1 := ec2.InstanceState{
|
|
|
|
Name: aws.String("running"),
|
|
|
|
}
|
|
|
|
instance1.State = &state1
|
|
|
|
|
2016-02-03 21:43:47 +00:00
|
|
|
//2
|
2016-02-29 15:22:26 +00:00
|
|
|
instance2.InstanceId = aws.String("i-2")
|
2016-02-03 21:43:47 +00:00
|
|
|
instance2.PrivateDnsName = aws.String("instance-other.ec2.internal")
|
|
|
|
instance2.PrivateIpAddress = aws.String("192.168.0.1")
|
|
|
|
instance2.PublicIpAddress = aws.String("1.2.3.4")
|
|
|
|
instance2.InstanceType = aws.String("c3.large")
|
2016-02-29 15:22:26 +00:00
|
|
|
instance2.Placement = &ec2.Placement{AvailabilityZone: aws.String("us-east-1a")}
|
2016-02-03 21:43:47 +00:00
|
|
|
state2 := ec2.InstanceState{
|
|
|
|
Name: aws.String("running"),
|
|
|
|
}
|
|
|
|
instance2.State = &state2
|
|
|
|
|
|
|
|
instances := []*ec2.Instance{&instance0, &instance1, &instance2}
|
2015-05-14 19:18:25 +00:00
|
|
|
|
2016-02-29 15:22:26 +00:00
|
|
|
aws1, _ := mockInstancesResp(&instance0, []*ec2.Instance{&instance0})
|
2015-07-02 17:37:00 +00:00
|
|
|
_, err1 := aws1.NodeAddresses("instance-mismatch.ec2.internal")
|
2014-09-09 21:25:35 +00:00
|
|
|
if err1 == nil {
|
|
|
|
t.Errorf("Should error when no instance found")
|
|
|
|
}
|
|
|
|
|
2016-02-29 15:22:26 +00:00
|
|
|
aws2, _ := mockInstancesResp(&instance2, instances)
|
2015-07-02 17:37:00 +00:00
|
|
|
_, err2 := aws2.NodeAddresses("instance-same.ec2.internal")
|
2014-09-09 21:25:35 +00:00
|
|
|
if err2 == nil {
|
|
|
|
t.Errorf("Should error when multiple instances found")
|
|
|
|
}
|
|
|
|
|
2016-02-29 15:22:26 +00:00
|
|
|
aws3, _ := mockInstancesResp(&instance0, instances[0:1])
|
2017-08-03 22:57:59 +00:00
|
|
|
// change node name so it uses the instance instead of metadata
|
|
|
|
aws3.selfAWSInstance.nodeName = "foo"
|
2015-07-02 17:37:00 +00:00
|
|
|
addrs3, err3 := aws3.NodeAddresses("instance-same.ec2.internal")
|
2014-09-09 21:25:35 +00:00
|
|
|
if err3 != nil {
|
|
|
|
t.Errorf("Should not error when instance found")
|
|
|
|
}
|
2017-04-24 02:21:29 +00:00
|
|
|
if len(addrs3) != 4 {
|
|
|
|
t.Errorf("Should return exactly 4 NodeAddresses")
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
2016-11-18 20:58:42 +00:00
|
|
|
testHasNodeAddress(t, addrs3, v1.NodeInternalIP, "192.168.0.1")
|
|
|
|
testHasNodeAddress(t, addrs3, v1.NodeExternalIP, "1.2.3.4")
|
2016-11-22 16:27:09 +00:00
|
|
|
testHasNodeAddress(t, addrs3, v1.NodeExternalDNS, "instance-same.ec2.external")
|
|
|
|
testHasNodeAddress(t, addrs3, v1.NodeInternalDNS, "instance-same.ec2.internal")
|
2017-08-03 22:57:59 +00:00
|
|
|
}
|
2015-10-30 23:37:03 +00:00
|
|
|
|
2017-08-03 22:57:59 +00:00
|
|
|
func TestNodeAddressesWithMetadata(t *testing.T) {
|
|
|
|
var instance ec2.Instance
|
2015-10-30 23:37:03 +00:00
|
|
|
|
2017-08-03 22:57:59 +00:00
|
|
|
instanceName := "instance.ec2.internal"
|
|
|
|
instance.InstanceId = aws.String("i-0")
|
|
|
|
instance.PrivateDnsName = &instanceName
|
|
|
|
instance.PublicIpAddress = aws.String("2.3.4.5")
|
|
|
|
instance.InstanceType = aws.String("c3.large")
|
|
|
|
instance.Placement = &ec2.Placement{AvailabilityZone: aws.String("us-east-1a")}
|
|
|
|
state := ec2.InstanceState{
|
|
|
|
Name: aws.String("running"),
|
|
|
|
}
|
|
|
|
instance.State = &state
|
|
|
|
|
|
|
|
instances := []*ec2.Instance{&instance}
|
|
|
|
awsCloud, awsServices := mockInstancesResp(&instance, instances)
|
|
|
|
|
2017-08-21 22:24:44 +00:00
|
|
|
awsServices.networkInterfacesMacs = []string{"0a:26:89:f3:9c:f6", "0a:77:64:c4:6a:48"}
|
|
|
|
awsServices.networkInterfacesPrivateIPs = [][]string{{"192.168.0.1"}, {"192.168.0.2"}}
|
2017-08-03 22:57:59 +00:00
|
|
|
addrs, err := awsCloud.NodeAddresses("")
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("unexpected error: %v", err)
|
2015-10-30 23:37:03 +00:00
|
|
|
}
|
2017-08-03 22:57:59 +00:00
|
|
|
testHasNodeAddress(t, addrs, v1.NodeInternalIP, "192.168.0.1")
|
|
|
|
testHasNodeAddress(t, addrs, v1.NodeInternalIP, "192.168.0.2")
|
|
|
|
testHasNodeAddress(t, addrs, v1.NodeExternalIP, "2.3.4.5")
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
2015-03-10 04:15:53 +00:00
|
|
|
|
|
|
|
func TestGetRegion(t *testing.T) {
|
2016-02-29 15:22:26 +00:00
|
|
|
aws := mockAvailabilityZone("us-west-2e")
|
2015-03-10 04:15:53 +00:00
|
|
|
zones, ok := aws.Zones()
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("Unexpected missing zones impl")
|
|
|
|
}
|
|
|
|
zone, err := zones.GetZone()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unexpected error %v", err)
|
|
|
|
}
|
|
|
|
if zone.Region != "us-west-2" {
|
|
|
|
t.Errorf("Unexpected region: %s", zone.Region)
|
|
|
|
}
|
|
|
|
if zone.FailureDomain != "us-west-2e" {
|
|
|
|
t.Errorf("Unexpected FailureDomain: %s", zone.FailureDomain)
|
|
|
|
}
|
|
|
|
}
|
2015-09-02 15:28:55 +00:00
|
|
|
|
|
|
|
func TestFindVPCID(t *testing.T) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
2015-09-02 15:28:55 +00:00
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Error building aws cloud: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
vpcID, err := c.findVPCID()
|
|
|
|
if err != nil {
|
2016-03-23 00:26:50 +00:00
|
|
|
t.Errorf("Unexpected error: %v", err)
|
2015-09-02 15:28:55 +00:00
|
|
|
}
|
|
|
|
if vpcID != "vpc-mac0" {
|
|
|
|
t.Errorf("Unexpected vpcID: %s", vpcID)
|
|
|
|
}
|
|
|
|
}
|
2015-09-21 18:44:26 +00:00
|
|
|
|
2015-10-01 20:38:11 +00:00
|
|
|
func constructSubnets(subnetsIn map[int]map[string]string) (subnetsOut []*ec2.Subnet) {
|
|
|
|
for i := range subnetsIn {
|
|
|
|
subnetsOut = append(
|
|
|
|
subnetsOut,
|
|
|
|
constructSubnet(
|
|
|
|
subnetsIn[i]["id"],
|
|
|
|
subnetsIn[i]["az"],
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func constructSubnet(id string, az string) *ec2.Subnet {
|
|
|
|
return &ec2.Subnet{
|
|
|
|
SubnetId: &id,
|
|
|
|
AvailabilityZone: &az,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-29 11:14:19 +00:00
|
|
|
func constructRouteTables(routeTablesIn map[string]bool) (routeTablesOut []*ec2.RouteTable) {
|
2016-02-25 22:25:35 +00:00
|
|
|
routeTablesOut = append(routeTablesOut,
|
|
|
|
&ec2.RouteTable{
|
|
|
|
Associations: []*ec2.RouteTableAssociation{{Main: aws.Bool(true)}},
|
|
|
|
Routes: []*ec2.Route{{
|
|
|
|
DestinationCidrBlock: aws.String("0.0.0.0/0"),
|
|
|
|
GatewayId: aws.String("igw-main"),
|
|
|
|
}},
|
|
|
|
})
|
|
|
|
|
2015-11-29 11:14:19 +00:00
|
|
|
for subnetID := range routeTablesIn {
|
|
|
|
routeTablesOut = append(
|
|
|
|
routeTablesOut,
|
|
|
|
constructRouteTable(
|
|
|
|
subnetID,
|
|
|
|
routeTablesIn[subnetID],
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func constructRouteTable(subnetID string, public bool) *ec2.RouteTable {
|
|
|
|
var gatewayID string
|
|
|
|
if public {
|
|
|
|
gatewayID = "igw-" + subnetID[len(subnetID)-8:8]
|
|
|
|
} else {
|
|
|
|
gatewayID = "vgw-" + subnetID[len(subnetID)-8:8]
|
|
|
|
}
|
|
|
|
return &ec2.RouteTable{
|
|
|
|
Associations: []*ec2.RouteTableAssociation{{SubnetId: aws.String(subnetID)}},
|
|
|
|
Routes: []*ec2.Route{{
|
|
|
|
DestinationCidrBlock: aws.String("0.0.0.0/0"),
|
|
|
|
GatewayId: aws.String(gatewayID),
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-01 20:38:11 +00:00
|
|
|
func TestSubnetIDsinVPC(t *testing.T) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
2015-10-01 20:38:11 +00:00
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Error building aws cloud: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// test with 3 subnets from 3 different AZs
|
|
|
|
subnets := make(map[int]map[string]string)
|
|
|
|
subnets[0] = make(map[string]string)
|
|
|
|
subnets[0]["id"] = "subnet-a0000001"
|
|
|
|
subnets[0]["az"] = "af-south-1a"
|
|
|
|
subnets[1] = make(map[string]string)
|
|
|
|
subnets[1]["id"] = "subnet-b0000001"
|
|
|
|
subnets[1]["az"] = "af-south-1b"
|
|
|
|
subnets[2] = make(map[string]string)
|
|
|
|
subnets[2]["id"] = "subnet-c0000001"
|
|
|
|
subnets[2]["az"] = "af-south-1c"
|
2017-09-15 14:58:46 +00:00
|
|
|
constructedSubnets := constructSubnets(subnets)
|
|
|
|
awsServices.ec2.RemoveSubnets()
|
|
|
|
for _, subnet := range constructedSubnets {
|
|
|
|
awsServices.ec2.CreateSubnet(subnet)
|
|
|
|
}
|
2015-10-01 20:38:11 +00:00
|
|
|
|
2015-11-29 11:14:19 +00:00
|
|
|
routeTables := map[string]bool{
|
|
|
|
"subnet-a0000001": true,
|
|
|
|
"subnet-b0000001": true,
|
|
|
|
"subnet-c0000001": true,
|
|
|
|
}
|
2017-09-15 14:58:46 +00:00
|
|
|
constructedRouteTables := constructRouteTables(routeTables)
|
|
|
|
awsServices.ec2.RemoveRouteTables()
|
|
|
|
for _, rt := range constructedRouteTables {
|
|
|
|
awsServices.ec2.CreateRouteTable(rt)
|
|
|
|
}
|
2015-11-29 11:14:19 +00:00
|
|
|
|
2016-03-04 17:15:12 +00:00
|
|
|
result, err := c.findELBSubnets(false)
|
2015-10-01 20:38:11 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Error listing subnets: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result) != 3 {
|
|
|
|
t.Errorf("Expected 3 subnets but got %d", len(result))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
result_set := make(map[string]bool)
|
|
|
|
for _, v := range result {
|
|
|
|
result_set[v] = true
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range subnets {
|
|
|
|
if !result_set[subnets[i]["id"]] {
|
|
|
|
t.Errorf("Expected subnet%d '%s' in result: %v", i, subnets[i]["id"], result)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-25 22:25:35 +00:00
|
|
|
// test implicit routing table - when subnets are not explicitly linked to a table they should use main
|
2017-09-15 14:58:46 +00:00
|
|
|
constructedRouteTables = constructRouteTables(map[string]bool{})
|
|
|
|
awsServices.ec2.RemoveRouteTables()
|
|
|
|
for _, rt := range constructedRouteTables {
|
|
|
|
awsServices.ec2.CreateRouteTable(rt)
|
|
|
|
}
|
2016-02-25 22:25:35 +00:00
|
|
|
|
2016-03-04 17:15:12 +00:00
|
|
|
result, err = c.findELBSubnets(false)
|
2016-02-25 22:25:35 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Error listing subnets: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result) != 3 {
|
|
|
|
t.Errorf("Expected 3 subnets but got %d", len(result))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
result_set = make(map[string]bool)
|
|
|
|
for _, v := range result {
|
|
|
|
result_set[v] = true
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range subnets {
|
|
|
|
if !result_set[subnets[i]["id"]] {
|
|
|
|
t.Errorf("Expected subnet%d '%s' in result: %v", i, subnets[i]["id"], result)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-29 23:27:05 +00:00
|
|
|
// Test with 5 subnets from 3 different AZs.
|
|
|
|
// Add 2 duplicate AZ subnets lexicographically chosen one is the middle element in array to
|
|
|
|
// check that we both choose the correct entry when it comes after and before another element
|
|
|
|
// in the same AZ.
|
2015-10-01 20:38:11 +00:00
|
|
|
subnets[3] = make(map[string]string)
|
2017-06-29 23:27:05 +00:00
|
|
|
subnets[3]["id"] = "subnet-c0000000"
|
2015-10-01 20:38:11 +00:00
|
|
|
subnets[3]["az"] = "af-south-1c"
|
2017-06-29 23:27:05 +00:00
|
|
|
subnets[4] = make(map[string]string)
|
|
|
|
subnets[4]["id"] = "subnet-c0000002"
|
|
|
|
subnets[4]["az"] = "af-south-1c"
|
2017-09-15 14:58:46 +00:00
|
|
|
constructedSubnets = constructSubnets(subnets)
|
|
|
|
awsServices.ec2.RemoveSubnets()
|
|
|
|
for _, subnet := range constructedSubnets {
|
|
|
|
awsServices.ec2.CreateSubnet(subnet)
|
|
|
|
}
|
2017-06-29 23:27:05 +00:00
|
|
|
routeTables["subnet-c0000000"] = true
|
2015-11-29 11:14:19 +00:00
|
|
|
routeTables["subnet-c0000002"] = true
|
2017-09-15 14:58:46 +00:00
|
|
|
constructedRouteTables = constructRouteTables(routeTables)
|
|
|
|
awsServices.ec2.RemoveRouteTables()
|
|
|
|
for _, rt := range constructedRouteTables {
|
|
|
|
awsServices.ec2.CreateRouteTable(rt)
|
|
|
|
}
|
2015-10-01 20:38:11 +00:00
|
|
|
|
2016-03-04 17:15:12 +00:00
|
|
|
result, err = c.findELBSubnets(false)
|
2015-10-01 20:38:11 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Error listing subnets: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result) != 3 {
|
|
|
|
t.Errorf("Expected 3 subnets but got %d", len(result))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-29 23:27:05 +00:00
|
|
|
expected := []*string{aws.String("subnet-a0000001"), aws.String("subnet-b0000001"), aws.String("subnet-c0000000")}
|
|
|
|
for _, s := range result {
|
|
|
|
if !contains(expected, s) {
|
|
|
|
t.Errorf("Unexpected subnet '%s' found", s)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(routeTables, "subnet-c0000002")
|
|
|
|
|
2015-11-29 11:14:19 +00:00
|
|
|
// test with 6 subnets from 3 different AZs
|
|
|
|
// with 3 private subnets
|
|
|
|
subnets[4] = make(map[string]string)
|
|
|
|
subnets[4]["id"] = "subnet-d0000001"
|
|
|
|
subnets[4]["az"] = "af-south-1a"
|
|
|
|
subnets[5] = make(map[string]string)
|
|
|
|
subnets[5]["id"] = "subnet-d0000002"
|
|
|
|
subnets[5]["az"] = "af-south-1b"
|
|
|
|
|
2017-09-15 14:58:46 +00:00
|
|
|
constructedSubnets = constructSubnets(subnets)
|
|
|
|
awsServices.ec2.RemoveSubnets()
|
|
|
|
for _, subnet := range constructedSubnets {
|
|
|
|
awsServices.ec2.CreateSubnet(subnet)
|
|
|
|
}
|
|
|
|
|
2015-11-29 11:14:19 +00:00
|
|
|
routeTables["subnet-a0000001"] = false
|
|
|
|
routeTables["subnet-b0000001"] = false
|
|
|
|
routeTables["subnet-c0000001"] = false
|
2017-06-29 23:27:05 +00:00
|
|
|
routeTables["subnet-c0000000"] = true
|
2015-11-29 11:14:19 +00:00
|
|
|
routeTables["subnet-d0000001"] = true
|
|
|
|
routeTables["subnet-d0000002"] = true
|
2017-09-15 14:58:46 +00:00
|
|
|
constructedRouteTables = constructRouteTables(routeTables)
|
|
|
|
awsServices.ec2.RemoveRouteTables()
|
|
|
|
for _, rt := range constructedRouteTables {
|
|
|
|
awsServices.ec2.CreateRouteTable(rt)
|
|
|
|
}
|
2016-03-04 17:15:12 +00:00
|
|
|
result, err = c.findELBSubnets(false)
|
2015-11-29 11:14:19 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Error listing subnets: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result) != 3 {
|
|
|
|
t.Errorf("Expected 3 subnets but got %d", len(result))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-29 23:27:05 +00:00
|
|
|
expected = []*string{aws.String("subnet-c0000000"), aws.String("subnet-d0000001"), aws.String("subnet-d0000002")}
|
2015-11-29 11:14:19 +00:00
|
|
|
for _, s := range result {
|
|
|
|
if !contains(expected, s) {
|
|
|
|
t.Errorf("Unexpected subnet '%s' found", s)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2015-10-01 20:38:11 +00:00
|
|
|
}
|
2016-01-26 11:01:15 +00:00
|
|
|
|
2016-01-26 13:57:52 +00:00
|
|
|
func TestIpPermissionExistsHandlesMultipleGroupIds(t *testing.T) {
|
2016-01-26 15:13:25 +00:00
|
|
|
oldIpPermission := ec2.IpPermission{
|
|
|
|
UserIdGroupPairs: []*ec2.UserIdGroupPair{
|
|
|
|
{GroupId: aws.String("firstGroupId")},
|
|
|
|
{GroupId: aws.String("secondGroupId")},
|
|
|
|
{GroupId: aws.String("thirdGroupId")},
|
2016-01-26 11:01:15 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-01-26 15:13:25 +00:00
|
|
|
existingIpPermission := ec2.IpPermission{
|
|
|
|
UserIdGroupPairs: []*ec2.UserIdGroupPair{
|
|
|
|
{GroupId: aws.String("secondGroupId")},
|
2016-01-26 11:01:15 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-01-26 15:13:25 +00:00
|
|
|
newIpPermission := ec2.IpPermission{
|
|
|
|
UserIdGroupPairs: []*ec2.UserIdGroupPair{
|
|
|
|
{GroupId: aws.String("fourthGroupId")},
|
2016-01-26 11:01:15 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-01-26 13:57:52 +00:00
|
|
|
equals := ipPermissionExists(&existingIpPermission, &oldIpPermission, false)
|
2016-01-26 11:01:15 +00:00
|
|
|
if !equals {
|
|
|
|
t.Errorf("Should have been considered equal since first is in the second array of groups")
|
|
|
|
}
|
|
|
|
|
2016-01-26 13:57:52 +00:00
|
|
|
equals = ipPermissionExists(&newIpPermission, &oldIpPermission, false)
|
2016-01-26 11:01:15 +00:00
|
|
|
if equals {
|
|
|
|
t.Errorf("Should have not been considered equal since first is not in the second array of groups")
|
|
|
|
}
|
2017-06-15 02:54:58 +00:00
|
|
|
|
|
|
|
// The first pair matches, but the second does not
|
|
|
|
newIpPermission2 := ec2.IpPermission{
|
|
|
|
UserIdGroupPairs: []*ec2.UserIdGroupPair{
|
|
|
|
{GroupId: aws.String("firstGroupId")},
|
|
|
|
{GroupId: aws.String("fourthGroupId")},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
equals = ipPermissionExists(&newIpPermission2, &oldIpPermission, false)
|
|
|
|
if equals {
|
|
|
|
t.Errorf("Should have not been considered equal since first is not in the second array of groups")
|
2016-01-26 11:01:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-05 14:18:46 +00:00
|
|
|
func TestIpPermissionExistsHandlesRangeSubsets(t *testing.T) {
|
|
|
|
// Two existing scenarios we'll test against
|
|
|
|
emptyIpPermission := ec2.IpPermission{}
|
|
|
|
|
|
|
|
oldIpPermission := ec2.IpPermission{
|
|
|
|
IpRanges: []*ec2.IpRange{
|
|
|
|
{CidrIp: aws.String("10.0.0.0/8")},
|
|
|
|
{CidrIp: aws.String("192.168.1.0/24")},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Two already existing ranges and a new one
|
|
|
|
existingIpPermission := ec2.IpPermission{
|
|
|
|
IpRanges: []*ec2.IpRange{
|
|
|
|
{CidrIp: aws.String("10.0.0.0/8")},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
existingIpPermission2 := ec2.IpPermission{
|
|
|
|
IpRanges: []*ec2.IpRange{
|
|
|
|
{CidrIp: aws.String("192.168.1.0/24")},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
newIpPermission := ec2.IpPermission{
|
|
|
|
IpRanges: []*ec2.IpRange{
|
|
|
|
{CidrIp: aws.String("172.16.0.0/16")},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
exists := ipPermissionExists(&emptyIpPermission, &emptyIpPermission, false)
|
|
|
|
if !exists {
|
|
|
|
t.Errorf("Should have been considered existing since we're comparing a range array against itself")
|
|
|
|
}
|
|
|
|
exists = ipPermissionExists(&oldIpPermission, &oldIpPermission, false)
|
|
|
|
if !exists {
|
|
|
|
t.Errorf("Should have been considered existing since we're comparing a range array against itself")
|
|
|
|
}
|
|
|
|
|
|
|
|
exists = ipPermissionExists(&existingIpPermission, &oldIpPermission, false)
|
|
|
|
if !exists {
|
|
|
|
t.Errorf("Should have been considered existing since 10.* is in oldIpPermission's array of ranges")
|
|
|
|
}
|
|
|
|
exists = ipPermissionExists(&existingIpPermission2, &oldIpPermission, false)
|
|
|
|
if !exists {
|
|
|
|
t.Errorf("Should have been considered existing since 192.* is in oldIpPermission2's array of ranges")
|
|
|
|
}
|
|
|
|
|
|
|
|
exists = ipPermissionExists(&newIpPermission, &emptyIpPermission, false)
|
|
|
|
if exists {
|
|
|
|
t.Errorf("Should have not been considered existing since we compared against a missing array of ranges")
|
|
|
|
}
|
|
|
|
exists = ipPermissionExists(&newIpPermission, &oldIpPermission, false)
|
|
|
|
if exists {
|
|
|
|
t.Errorf("Should have not been considered existing since 172.* is not in oldIpPermission's array of ranges")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-26 13:57:52 +00:00
|
|
|
func TestIpPermissionExistsHandlesMultipleGroupIdsWithUserIds(t *testing.T) {
|
2016-01-26 15:13:25 +00:00
|
|
|
oldIpPermission := ec2.IpPermission{
|
|
|
|
UserIdGroupPairs: []*ec2.UserIdGroupPair{
|
|
|
|
{GroupId: aws.String("firstGroupId"), UserId: aws.String("firstUserId")},
|
|
|
|
{GroupId: aws.String("secondGroupId"), UserId: aws.String("secondUserId")},
|
|
|
|
{GroupId: aws.String("thirdGroupId"), UserId: aws.String("thirdUserId")},
|
2016-01-26 11:01:15 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-01-26 15:13:25 +00:00
|
|
|
existingIpPermission := ec2.IpPermission{
|
|
|
|
UserIdGroupPairs: []*ec2.UserIdGroupPair{
|
|
|
|
{GroupId: aws.String("secondGroupId"), UserId: aws.String("secondUserId")},
|
2016-01-26 11:01:15 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-01-26 15:13:25 +00:00
|
|
|
newIpPermission := ec2.IpPermission{
|
|
|
|
UserIdGroupPairs: []*ec2.UserIdGroupPair{
|
|
|
|
{GroupId: aws.String("secondGroupId"), UserId: aws.String("anotherUserId")},
|
2016-01-26 11:01:15 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-01-26 13:57:52 +00:00
|
|
|
equals := ipPermissionExists(&existingIpPermission, &oldIpPermission, true)
|
2016-01-26 11:01:15 +00:00
|
|
|
if !equals {
|
|
|
|
t.Errorf("Should have been considered equal since first is in the second array of groups")
|
|
|
|
}
|
|
|
|
|
2016-01-26 13:57:52 +00:00
|
|
|
equals = ipPermissionExists(&newIpPermission, &oldIpPermission, true)
|
2016-01-26 11:01:15 +00:00
|
|
|
if equals {
|
|
|
|
t.Errorf("Should have not been considered equal since first is not in the second array of groups")
|
|
|
|
}
|
|
|
|
}
|
2017-02-18 18:11:08 +00:00
|
|
|
|
2016-02-23 14:44:03 +00:00
|
|
|
func TestFindInstanceByNodeNameExcludesTerminatedInstances(t *testing.T) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
2016-02-23 14:44:03 +00:00
|
|
|
|
2016-07-16 06:10:29 +00:00
|
|
|
nodeName := types.NodeName("my-dns.internal")
|
2016-02-23 14:44:03 +00:00
|
|
|
|
|
|
|
var tag ec2.Tag
|
2017-02-18 18:11:08 +00:00
|
|
|
tag.Key = aws.String(TagNameKubernetesClusterLegacy)
|
2016-02-23 14:44:03 +00:00
|
|
|
tag.Value = aws.String(TestClusterId)
|
|
|
|
tags := []*ec2.Tag{&tag}
|
|
|
|
|
|
|
|
var runningInstance ec2.Instance
|
|
|
|
runningInstance.InstanceId = aws.String("i-running")
|
2016-07-16 06:10:29 +00:00
|
|
|
runningInstance.PrivateDnsName = aws.String(string(nodeName))
|
2016-02-23 14:44:03 +00:00
|
|
|
runningInstance.State = &ec2.InstanceState{Code: aws.Int64(16), Name: aws.String("running")}
|
|
|
|
runningInstance.Tags = tags
|
|
|
|
|
|
|
|
var terminatedInstance ec2.Instance
|
|
|
|
terminatedInstance.InstanceId = aws.String("i-terminated")
|
2016-07-16 06:10:29 +00:00
|
|
|
terminatedInstance.PrivateDnsName = aws.String(string(nodeName))
|
2016-02-23 14:44:03 +00:00
|
|
|
terminatedInstance.State = &ec2.InstanceState{Code: aws.Int64(48), Name: aws.String("terminated")}
|
|
|
|
terminatedInstance.Tags = tags
|
|
|
|
|
|
|
|
instances := []*ec2.Instance{&terminatedInstance, &runningInstance}
|
|
|
|
awsServices.instances = append(awsServices.instances, instances...)
|
|
|
|
|
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Error building aws cloud: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
instance, err := c.findInstanceByNodeName(nodeName)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Failed to find instance: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if *instance.InstanceId != "i-running" {
|
|
|
|
t.Errorf("Expected running instance but got %v", *instance.InstanceId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-13 19:12:07 +00:00
|
|
|
func TestGetInstanceByNodeNameBatching(t *testing.T) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
2017-06-13 19:12:07 +00:00
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
assert.Nil(t, err, "Error building aws cloud: %v", err)
|
|
|
|
var tag ec2.Tag
|
|
|
|
tag.Key = aws.String(TagNameKubernetesClusterPrefix + TestClusterId)
|
|
|
|
tag.Value = aws.String("")
|
|
|
|
tags := []*ec2.Tag{&tag}
|
|
|
|
nodeNames := []string{}
|
|
|
|
for i := 0; i < 200; i++ {
|
|
|
|
nodeName := fmt.Sprintf("ip-171-20-42-%d.ec2.internal", i)
|
|
|
|
nodeNames = append(nodeNames, nodeName)
|
|
|
|
ec2Instance := &ec2.Instance{}
|
|
|
|
instanceId := fmt.Sprintf("i-abcedf%d", i)
|
|
|
|
ec2Instance.InstanceId = aws.String(instanceId)
|
|
|
|
ec2Instance.PrivateDnsName = aws.String(nodeName)
|
|
|
|
ec2Instance.State = &ec2.InstanceState{Code: aws.Int64(48), Name: aws.String("running")}
|
|
|
|
ec2Instance.Tags = tags
|
|
|
|
awsServices.instances = append(awsServices.instances, ec2Instance)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
instances, err := c.getInstancesByNodeNames(nodeNames)
|
|
|
|
assert.NotEmpty(t, instances)
|
|
|
|
assert.Equal(t, 200, len(instances), "Expected 200 but got less")
|
|
|
|
}
|
|
|
|
|
2016-02-29 19:16:42 +00:00
|
|
|
func TestGetVolumeLabels(t *testing.T) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
2016-02-29 19:16:42 +00:00
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
assert.Nil(t, err, "Error building aws cloud: %v", err)
|
2016-10-31 01:49:14 +00:00
|
|
|
volumeId := awsVolumeID("vol-VolumeId")
|
|
|
|
expectedVolumeRequest := &ec2.DescribeVolumesInput{VolumeIds: []*string{volumeId.awsString()}}
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices.ec2.(*MockedFakeEC2).On("DescribeVolumes", expectedVolumeRequest).Return([]*ec2.Volume{
|
2016-02-29 19:16:42 +00:00
|
|
|
{
|
2016-10-31 01:49:14 +00:00
|
|
|
VolumeId: volumeId.awsString(),
|
2016-03-05 13:11:30 +00:00
|
|
|
AvailabilityZone: aws.String("us-east-1a"),
|
2016-02-29 19:16:42 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2016-10-31 01:49:14 +00:00
|
|
|
labels, err := c.GetVolumeLabels(KubernetesVolumeID("aws:///" + string(volumeId)))
|
2016-02-29 19:16:42 +00:00
|
|
|
|
|
|
|
assert.Nil(t, err, "Error creating Volume %v", err)
|
|
|
|
assert.Equal(t, map[string]string{
|
2017-05-30 14:46:00 +00:00
|
|
|
kubeletapis.LabelZoneFailureDomain: "us-east-1a",
|
|
|
|
kubeletapis.LabelZoneRegion: "us-east-1"}, labels)
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices.ec2.(*MockedFakeEC2).AssertExpectations(t)
|
2017-04-30 09:30:48 +00:00
|
|
|
}
|
|
|
|
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
func TestDescribeLoadBalancerOnDelete(t *testing.T) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices.elb.(*MockedFakeELB).expectDescribeLoadBalancers("aid")
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
|
2017-01-17 03:38:19 +00:00
|
|
|
c.EnsureLoadBalancerDeleted(TestClusterName, &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "myservice", UID: "id"}})
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestDescribeLoadBalancerOnUpdate(t *testing.T) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices.elb.(*MockedFakeELB).expectDescribeLoadBalancers("aid")
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
|
2017-01-17 03:38:19 +00:00
|
|
|
c.UpdateLoadBalancer(TestClusterName, &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "myservice", UID: "id"}}, []*v1.Node{})
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestDescribeLoadBalancerOnGet(t *testing.T) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices.elb.(*MockedFakeELB).expectDescribeLoadBalancers("aid")
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
|
2017-01-17 03:38:19 +00:00
|
|
|
c.GetLoadBalancer(TestClusterName, &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "myservice", UID: "id"}})
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestDescribeLoadBalancerOnEnsure(t *testing.T) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices.elb.(*MockedFakeELB).expectDescribeLoadBalancers("aid")
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
|
2017-01-17 03:38:19 +00:00
|
|
|
c.EnsureLoadBalancer(TestClusterName, &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "myservice", UID: "id"}}, []*v1.Node{})
|
Change LoadBalancer methods to take api.Service
This is a better abstraction than passing in specific pieces of the
Service that each of the cloudproviders may or may not need. For
instance, many of the providers don't need a region, yet this is passed
in. Similarly many of the providers want a string IP for the load
balancer, but it passes in a converted net ip. Affinity is unused by
AWS. A provider change may also require adding a new parameter which has
an effect on all other cloud provider implementations.
Further, this will simplify adding provider specific load balancer
options, such as with labels or some other metadata. For example, we
could add labels for configuring the details of an AWS elastic load
balancer, such as idle timeout on connections, whether it is
internal or external, cross-zone load balancing, and so on.
Authors: @chbatey, @jsravn
2016-02-17 11:36:50 +00:00
|
|
|
}
|
2016-03-25 17:36:06 +00:00
|
|
|
|
2016-05-10 15:40:34 +00:00
|
|
|
func TestBuildListener(t *testing.T) {
|
2016-03-25 17:36:06 +00:00
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
|
2016-05-02 21:43:26 +00:00
|
|
|
lbPort int64
|
2016-06-07 17:36:53 +00:00
|
|
|
portName string
|
2016-05-02 21:43:26 +00:00
|
|
|
instancePort int64
|
|
|
|
backendProtocolAnnotation string
|
|
|
|
certAnnotation string
|
2016-06-07 17:36:53 +00:00
|
|
|
sslPortAnnotation string
|
2016-03-25 17:36:06 +00:00
|
|
|
|
|
|
|
expectError bool
|
|
|
|
lbProtocol string
|
|
|
|
instanceProtocol string
|
|
|
|
certID string
|
|
|
|
}{
|
|
|
|
{
|
2016-05-02 21:43:26 +00:00
|
|
|
"No cert or BE protocol annotation, passthrough",
|
2016-06-07 17:36:53 +00:00
|
|
|
80, "", 7999, "", "", "",
|
2016-03-25 17:36:06 +00:00
|
|
|
false, "tcp", "tcp", "",
|
|
|
|
},
|
2016-05-10 15:53:44 +00:00
|
|
|
{
|
|
|
|
"Cert annotation without BE protocol specified, SSL->TCP",
|
2016-06-07 17:36:53 +00:00
|
|
|
80, "", 8000, "", "cert", "",
|
2016-05-10 15:53:44 +00:00
|
|
|
false, "ssl", "tcp", "cert",
|
|
|
|
},
|
2016-03-25 17:36:06 +00:00
|
|
|
{
|
2016-05-02 21:43:26 +00:00
|
|
|
"BE protocol without cert annotation, passthrough",
|
2016-06-07 17:36:53 +00:00
|
|
|
443, "", 8001, "https", "", "",
|
2016-05-02 21:43:26 +00:00
|
|
|
false, "tcp", "tcp", "",
|
2016-03-25 17:36:06 +00:00
|
|
|
},
|
|
|
|
{
|
2016-05-02 21:43:26 +00:00
|
|
|
"Invalid cert annotation, bogus backend protocol",
|
2016-06-07 17:36:53 +00:00
|
|
|
443, "", 8002, "bacon", "foo", "",
|
|
|
|
true, "tcp", "tcp", "",
|
2016-03-25 17:36:06 +00:00
|
|
|
},
|
|
|
|
{
|
2016-05-02 21:43:26 +00:00
|
|
|
"Invalid cert annotation, protocol followed by equal sign",
|
2016-06-07 17:36:53 +00:00
|
|
|
443, "", 8003, "http=", "=", "",
|
|
|
|
true, "tcp", "tcp", "",
|
2016-03-25 17:36:06 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"HTTPS->HTTPS",
|
2016-06-07 17:36:53 +00:00
|
|
|
443, "", 8004, "https", "cert", "",
|
2016-03-25 17:36:06 +00:00
|
|
|
false, "https", "https", "cert",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"HTTPS->HTTP",
|
2016-06-07 17:36:53 +00:00
|
|
|
443, "", 8005, "http", "cert", "",
|
2016-03-25 17:36:06 +00:00
|
|
|
false, "https", "http", "cert",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"SSL->SSL",
|
2016-06-07 17:36:53 +00:00
|
|
|
443, "", 8006, "ssl", "cert", "",
|
2016-03-25 17:36:06 +00:00
|
|
|
false, "ssl", "ssl", "cert",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"SSL->TCP",
|
2016-06-07 17:36:53 +00:00
|
|
|
443, "", 8007, "tcp", "cert", "",
|
2016-03-25 17:36:06 +00:00
|
|
|
false, "ssl", "tcp", "cert",
|
|
|
|
},
|
2016-06-07 17:36:53 +00:00
|
|
|
{
|
|
|
|
"Port in whitelist",
|
|
|
|
1234, "", 8008, "tcp", "cert", "1234,5678",
|
|
|
|
false, "ssl", "tcp", "cert",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Port not in whitelist, passthrough",
|
|
|
|
443, "", 8009, "tcp", "cert", "1234,5678",
|
|
|
|
false, "tcp", "tcp", "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Named port in whitelist",
|
|
|
|
1234, "bar", 8010, "tcp", "cert", "foo,bar",
|
|
|
|
false, "ssl", "tcp", "cert",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Named port not in whitelist, passthrough",
|
|
|
|
443, "", 8011, "tcp", "cert", "foo,bar",
|
|
|
|
false, "tcp", "tcp", "",
|
|
|
|
},
|
2016-08-13 00:30:35 +00:00
|
|
|
{
|
|
|
|
"HTTP->HTTP",
|
|
|
|
80, "", 8012, "http", "", "",
|
|
|
|
false, "http", "http", "",
|
|
|
|
},
|
2016-03-25 17:36:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Logf("Running test case %s", test.name)
|
|
|
|
annotations := make(map[string]string)
|
2016-05-02 21:43:26 +00:00
|
|
|
if test.backendProtocolAnnotation != "" {
|
|
|
|
annotations[ServiceAnnotationLoadBalancerBEProtocol] = test.backendProtocolAnnotation
|
|
|
|
}
|
|
|
|
if test.certAnnotation != "" {
|
|
|
|
annotations[ServiceAnnotationLoadBalancerCertificate] = test.certAnnotation
|
2016-03-25 17:36:06 +00:00
|
|
|
}
|
2016-06-07 17:36:53 +00:00
|
|
|
ports := getPortSets(test.sslPortAnnotation)
|
2016-11-18 20:58:42 +00:00
|
|
|
l, err := buildListener(v1.ServicePort{
|
2016-05-02 23:20:50 +00:00
|
|
|
NodePort: int32(test.instancePort),
|
|
|
|
Port: int32(test.lbPort),
|
2016-06-07 17:36:53 +00:00
|
|
|
Name: test.portName,
|
2016-11-18 20:58:42 +00:00
|
|
|
Protocol: v1.Protocol("tcp"),
|
2016-06-07 17:36:53 +00:00
|
|
|
}, annotations, ports)
|
2016-03-25 17:36:06 +00:00
|
|
|
if test.expectError {
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("Should error for case %s", test.name)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Should succeed for case: %s, got %v", test.name, err)
|
|
|
|
} else {
|
|
|
|
var cert *string
|
|
|
|
if test.certID != "" {
|
|
|
|
cert = &test.certID
|
|
|
|
}
|
|
|
|
expected := &elb.Listener{
|
|
|
|
InstancePort: &test.instancePort,
|
|
|
|
InstanceProtocol: &test.instanceProtocol,
|
|
|
|
LoadBalancerPort: &test.lbPort,
|
|
|
|
Protocol: &test.lbProtocol,
|
|
|
|
SSLCertificateId: cert,
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(l, expected) {
|
2016-05-02 21:43:26 +00:00
|
|
|
t.Errorf("Incorrect listener (%v vs expected %v) for case: %s",
|
2016-03-25 17:36:06 +00:00
|
|
|
l, expected, test.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-20 21:41:18 +00:00
|
|
|
|
|
|
|
func TestProxyProtocolEnabled(t *testing.T) {
|
|
|
|
policies := sets.NewString(ProxyProtocolPolicyName, "FooBarFoo")
|
|
|
|
fakeBackend := &elb.BackendServerDescription{
|
|
|
|
InstancePort: aws.Int64(80),
|
|
|
|
PolicyNames: stringSetToPointers(policies),
|
|
|
|
}
|
|
|
|
result := proxyProtocolEnabled(fakeBackend)
|
|
|
|
assert.True(t, result, "expected to find %s in %s", ProxyProtocolPolicyName, policies)
|
|
|
|
|
|
|
|
policies = sets.NewString("FooBarFoo")
|
|
|
|
fakeBackend = &elb.BackendServerDescription{
|
|
|
|
InstancePort: aws.Int64(80),
|
|
|
|
PolicyNames: []*string{
|
|
|
|
aws.String("FooBarFoo"),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
result = proxyProtocolEnabled(fakeBackend)
|
|
|
|
assert.False(t, result, "did not expect to find %s in %s", ProxyProtocolPolicyName, policies)
|
|
|
|
|
|
|
|
policies = sets.NewString()
|
|
|
|
fakeBackend = &elb.BackendServerDescription{
|
|
|
|
InstancePort: aws.Int64(80),
|
|
|
|
}
|
|
|
|
result = proxyProtocolEnabled(fakeBackend)
|
|
|
|
assert.False(t, result, "did not expect to find %s in %s", ProxyProtocolPolicyName, policies)
|
|
|
|
}
|
2017-05-16 02:56:39 +00:00
|
|
|
|
|
|
|
func TestGetLoadBalancerAdditionalTags(t *testing.T) {
|
|
|
|
tagTests := []struct {
|
|
|
|
Annotations map[string]string
|
|
|
|
Tags map[string]string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
Annotations: map[string]string{
|
|
|
|
ServiceAnnotationLoadBalancerAdditionalTags: "Key=Val",
|
|
|
|
},
|
|
|
|
Tags: map[string]string{
|
|
|
|
"Key": "Val",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Annotations: map[string]string{
|
|
|
|
ServiceAnnotationLoadBalancerAdditionalTags: "Key1=Val1, Key2=Val2",
|
|
|
|
},
|
|
|
|
Tags: map[string]string{
|
|
|
|
"Key1": "Val1",
|
|
|
|
"Key2": "Val2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Annotations: map[string]string{
|
|
|
|
ServiceAnnotationLoadBalancerAdditionalTags: "Key1=, Key2=Val2",
|
|
|
|
"anotherKey": "anotherValue",
|
|
|
|
},
|
|
|
|
Tags: map[string]string{
|
|
|
|
"Key1": "",
|
|
|
|
"Key2": "Val2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Annotations: map[string]string{
|
|
|
|
"Nothing": "Key1=, Key2=Val2, Key3",
|
|
|
|
},
|
|
|
|
Tags: map[string]string{},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Annotations: map[string]string{
|
|
|
|
ServiceAnnotationLoadBalancerAdditionalTags: "K=V K1=V2,Key1========, =====, ======Val, =Val, , 234,",
|
|
|
|
},
|
|
|
|
Tags: map[string]string{
|
|
|
|
"K": "V K1",
|
|
|
|
"Key1": "",
|
|
|
|
"234": "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tagTest := range tagTests {
|
|
|
|
result := getLoadBalancerAdditionalTags(tagTest.Annotations)
|
|
|
|
for k, v := range result {
|
|
|
|
if len(result) != len(tagTest.Tags) {
|
|
|
|
t.Errorf("incorrect expected length: %v != %v", result, tagTest.Tags)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if tagTest.Tags[k] != v {
|
|
|
|
t.Errorf("%s != %s", tagTest.Tags[k], v)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-30 09:30:48 +00:00
|
|
|
|
|
|
|
func TestLBExtraSecurityGroupsAnnotation(t *testing.T) {
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
2017-04-30 09:30:48 +00:00
|
|
|
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
|
|
|
|
sg1 := "sg-000001"
|
|
|
|
sg2 := "sg-000002"
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
|
|
|
|
extraSGsAnnotation string
|
|
|
|
expectedSGs []string
|
|
|
|
}{
|
|
|
|
{"No extra SG annotation", "", []string{}},
|
|
|
|
{"Empty extra SGs specified", ", ,,", []string{}},
|
|
|
|
{"SG specified", sg1, []string{sg1}},
|
|
|
|
{"Multiple SGs specified", fmt.Sprintf("%s, %s", sg1, sg2), []string{sg1, sg2}},
|
|
|
|
}
|
|
|
|
|
2017-09-15 14:58:46 +00:00
|
|
|
awsServices.ec2.(*MockedFakeEC2).expectDescribeSecurityGroups(TestClusterId, "k8s-elb-aid", "cluster.test")
|
2017-04-30 09:30:48 +00:00
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
serviceName := types.NamespacedName{Namespace: "default", Name: "myservice"}
|
|
|
|
|
|
|
|
sgList, err := c.buildELBSecurityGroupList(serviceName, "aid", test.extraSGsAnnotation)
|
|
|
|
assert.NoError(t, err, "buildELBSecurityGroupList failed")
|
|
|
|
extraSGs := sgList[1:]
|
|
|
|
assert.True(t, sets.NewString(test.expectedSGs...).Equal(sets.NewString(extraSGs...)),
|
|
|
|
"Security Groups expected=%q , returned=%q", test.expectedSGs, extraSGs)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2017-09-15 14:58:46 +00:00
|
|
|
|
2017-11-14 19:22:16 +00:00
|
|
|
// Test that we can add a load balancer tag
|
|
|
|
func TestAddLoadBalancerTags(t *testing.T) {
|
|
|
|
loadBalancerName := "test-elb"
|
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
|
|
|
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
|
|
|
|
want := make(map[string]string)
|
|
|
|
want["tag1"] = "val1"
|
|
|
|
|
|
|
|
expectedAddTagsRequest := &elb.AddTagsInput{
|
|
|
|
LoadBalancerNames: []*string{&loadBalancerName},
|
|
|
|
Tags: []*elb.Tag{
|
|
|
|
{
|
|
|
|
Key: aws.String("tag1"),
|
|
|
|
Value: aws.String("val1"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
awsServices.elb.(*MockedFakeELB).On("AddTags", expectedAddTagsRequest).Return(&elb.AddTagsOutput{})
|
|
|
|
|
|
|
|
err := c.addLoadBalancerTags(loadBalancerName, want)
|
|
|
|
assert.Nil(t, err, "Error adding load balancer tags: %v", err)
|
|
|
|
awsServices.elb.(*MockedFakeELB).AssertExpectations(t)
|
|
|
|
}
|
|
|
|
|
2017-11-19 19:42:29 +00:00
|
|
|
func TestEnsureLoadBalancerHealthCheck(t *testing.T) {
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
annotations map[string]string
|
|
|
|
overriddenFieldName string
|
|
|
|
overriddenValue int64
|
|
|
|
}{
|
|
|
|
{"falls back to HC defaults", map[string]string{}, "", int64(0)},
|
|
|
|
{"healthy threshold override", map[string]string{ServiceAnnotationLoadBalancerHCHealthyThreshold: "7"}, "HealthyThreshold", int64(7)},
|
|
|
|
{"unhealthy threshold override", map[string]string{ServiceAnnotationLoadBalancerHCUnhealthyThreshold: "7"}, "UnhealthyThreshold", int64(7)},
|
|
|
|
{"timeout override", map[string]string{ServiceAnnotationLoadBalancerHCTimeout: "7"}, "Timeout", int64(7)},
|
|
|
|
{"interval override", map[string]string{ServiceAnnotationLoadBalancerHCInterval: "7"}, "Interval", int64(7)},
|
|
|
|
}
|
|
|
|
lbName := "myLB"
|
|
|
|
// this HC will always differ from the expected HC and thus it is expected an
|
|
|
|
// API call will be made to update it
|
|
|
|
currentHC := &elb.HealthCheck{}
|
|
|
|
elbDesc := &elb.LoadBalancerDescription{LoadBalancerName: &lbName, HealthCheck: currentHC}
|
|
|
|
defaultHealthyThreshold := int64(2)
|
|
|
|
defaultUnhealthyThreshold := int64(6)
|
|
|
|
defaultTimeout := int64(5)
|
|
|
|
defaultInterval := int64(10)
|
|
|
|
protocol, path, port := "tcp", "", int32(8080)
|
|
|
|
target := "tcp:8080"
|
|
|
|
defaultHC := &elb.HealthCheck{
|
|
|
|
HealthyThreshold: &defaultHealthyThreshold,
|
|
|
|
UnhealthyThreshold: &defaultUnhealthyThreshold,
|
|
|
|
Timeout: &defaultTimeout,
|
|
|
|
Interval: &defaultInterval,
|
|
|
|
Target: &target,
|
|
|
|
}
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
assert.Nil(t, err, "Error building aws cloud: %v", err)
|
|
|
|
expectedHC := *defaultHC
|
|
|
|
if test.overriddenFieldName != "" { // cater for test case with no overrides
|
|
|
|
value := reflect.ValueOf(&test.overriddenValue)
|
|
|
|
reflect.ValueOf(&expectedHC).Elem().FieldByName(test.overriddenFieldName).Set(value)
|
|
|
|
}
|
|
|
|
awsServices.elb.(*MockedFakeELB).expectConfigureHealthCheck(&lbName, &expectedHC, nil)
|
|
|
|
|
|
|
|
err = c.ensureLoadBalancerHealthCheck(elbDesc, protocol, port, path, test.annotations)
|
|
|
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
awsServices.elb.(*MockedFakeELB).AssertExpectations(t)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("does not make an API call if the current health check is the same", func(t *testing.T) {
|
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
assert.Nil(t, err, "Error building aws cloud: %v", err)
|
|
|
|
expectedHC := *defaultHC
|
|
|
|
timeout := int64(3)
|
|
|
|
expectedHC.Timeout = &timeout
|
|
|
|
annotations := map[string]string{ServiceAnnotationLoadBalancerHCTimeout: "3"}
|
|
|
|
var currentHC elb.HealthCheck
|
|
|
|
currentHC = expectedHC
|
|
|
|
|
|
|
|
// NOTE no call expectations are set on the ELB mock
|
|
|
|
// test default HC
|
|
|
|
elbDesc := &elb.LoadBalancerDescription{LoadBalancerName: &lbName, HealthCheck: defaultHC}
|
|
|
|
err = c.ensureLoadBalancerHealthCheck(elbDesc, protocol, port, path, map[string]string{})
|
|
|
|
assert.Nil(t, err)
|
|
|
|
// test HC with override
|
|
|
|
elbDesc = &elb.LoadBalancerDescription{LoadBalancerName: &lbName, HealthCheck: ¤tHC}
|
|
|
|
err = c.ensureLoadBalancerHealthCheck(elbDesc, protocol, port, path, annotations)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("validates resulting expected health check before making an API call", func(t *testing.T) {
|
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
assert.Nil(t, err, "Error building aws cloud: %v", err)
|
|
|
|
expectedHC := *defaultHC
|
|
|
|
invalidThreshold := int64(1)
|
|
|
|
expectedHC.HealthyThreshold = &invalidThreshold
|
|
|
|
require.Error(t, expectedHC.Validate()) // confirm test precondition
|
|
|
|
annotations := map[string]string{ServiceAnnotationLoadBalancerHCTimeout: "1"}
|
|
|
|
|
|
|
|
// NOTE no call expectations are set on the ELB mock
|
|
|
|
err = c.ensureLoadBalancerHealthCheck(elbDesc, protocol, port, path, annotations)
|
|
|
|
|
|
|
|
require.Error(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("handles invalid override values", func(t *testing.T) {
|
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
assert.Nil(t, err, "Error building aws cloud: %v", err)
|
|
|
|
annotations := map[string]string{ServiceAnnotationLoadBalancerHCTimeout: "3.3"}
|
|
|
|
|
|
|
|
// NOTE no call expectations are set on the ELB mock
|
|
|
|
err = c.ensureLoadBalancerHealthCheck(elbDesc, protocol, port, path, annotations)
|
|
|
|
|
|
|
|
require.Error(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("returns error when updating the health check fails", func(t *testing.T) {
|
|
|
|
awsServices := newMockedFakeAWSServices(TestClusterId)
|
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
assert.Nil(t, err, "Error building aws cloud: %v", err)
|
|
|
|
returnErr := fmt.Errorf("throttling error")
|
|
|
|
awsServices.elb.(*MockedFakeELB).expectConfigureHealthCheck(&lbName, defaultHC, returnErr)
|
|
|
|
|
|
|
|
err = c.ensureLoadBalancerHealthCheck(elbDesc, protocol, port, path, map[string]string{})
|
|
|
|
|
|
|
|
require.Error(t, err)
|
|
|
|
awsServices.elb.(*MockedFakeELB).AssertExpectations(t)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-09-15 14:58:46 +00:00
|
|
|
func newMockedFakeAWSServices(id string) *FakeAWSServices {
|
|
|
|
s := NewFakeAWSServices(id)
|
|
|
|
s.ec2 = &MockedFakeEC2{FakeEC2Impl: s.ec2.(*FakeEC2Impl)}
|
|
|
|
s.elb = &MockedFakeELB{FakeELB: s.elb.(*FakeELB)}
|
|
|
|
return s
|
|
|
|
}
|