2014-09-09 21:25:35 +00:00
|
|
|
/*
|
2015-05-01 16:19:44 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package aws_cloud
|
|
|
|
|
|
|
|
import (
|
2015-09-21 18:44:26 +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"
|
2015-03-05 18:10:56 +00:00
|
|
|
|
2015-06-13 14:24:35 +00:00
|
|
|
"github.com/aws/aws-sdk-go/service/autoscaling"
|
2015-06-05 02:03:50 +00:00
|
|
|
"github.com/golang/glog"
|
2015-08-05 22:05:17 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api"
|
2014-09-09 21:25:35 +00:00
|
|
|
)
|
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
const TestClusterId = "clusterid.test"
|
|
|
|
|
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
|
|
|
|
aws AWSServices
|
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",
|
2015-05-23 00:12:53 +00:00
|
|
|
strings.NewReader("[global]\n"), NewFakeAWSServices().withAz(""),
|
2015-03-27 14:31:16 +00:00
|
|
|
true, "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"No zone in config, metadata has zone",
|
2015-05-23 00:12:53 +00:00
|
|
|
strings.NewReader("[global]\n"), NewFakeAWSServices(),
|
|
|
|
false, "us-east-1a",
|
2015-03-27 14:31:16 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"Zone in config should take precedence over metadata",
|
2015-05-23 00:12:53 +00:00
|
|
|
strings.NewReader("[global]\nzone = eu-west-1a"), NewFakeAWSServices(),
|
|
|
|
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-05-23 00:12:53 +00:00
|
|
|
var metadata AWSMetadata
|
|
|
|
if test.aws != nil {
|
|
|
|
metadata = test.aws.Metadata()
|
|
|
|
}
|
|
|
|
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
|
|
|
type FakeAWSServices struct {
|
2015-09-02 15:28:55 +00:00
|
|
|
availabilityZone string
|
|
|
|
instances []*ec2.Instance
|
|
|
|
instanceId string
|
|
|
|
privateDnsName string
|
|
|
|
networkInterfacesMacs []string
|
|
|
|
networkInterfacesVpcIDs []string
|
2015-05-23 00:12:53 +00:00
|
|
|
|
|
|
|
ec2 *FakeEC2
|
|
|
|
elb *FakeELB
|
2015-06-13 14:24:35 +00:00
|
|
|
asg *FakeASG
|
2015-05-23 00:12:53 +00:00
|
|
|
metadata *FakeMetadata
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewFakeAWSServices() *FakeAWSServices {
|
|
|
|
s := &FakeAWSServices{}
|
|
|
|
s.availabilityZone = "us-east-1a"
|
|
|
|
s.ec2 = &FakeEC2{aws: s}
|
|
|
|
s.elb = &FakeELB{aws: s}
|
2015-06-13 14:24:35 +00:00
|
|
|
s.asg = &FakeASG{aws: s}
|
2015-05-23 00:12:53 +00:00
|
|
|
s.metadata = &FakeMetadata{aws: s}
|
|
|
|
|
2015-09-02 15:28:55 +00:00
|
|
|
s.networkInterfacesMacs = []string{"aa:bb:cc:dd:ee:00", "aa:bb:cc:dd:ee:01"}
|
|
|
|
s.networkInterfacesVpcIDs = []string{"vpc-mac0", "vpc-mac1"}
|
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
s.instanceId = "i-self"
|
2015-07-02 17:37:00 +00:00
|
|
|
s.privateDnsName = "ip-172-20-0-100.ec2.internal"
|
2015-05-23 00:12:53 +00:00
|
|
|
var selfInstance ec2.Instance
|
2015-09-20 21:10:57 +00:00
|
|
|
selfInstance.InstanceId = &s.instanceId
|
|
|
|
selfInstance.PrivateDnsName = &s.privateDnsName
|
2015-05-23 00:12:53 +00:00
|
|
|
s.instances = []*ec2.Instance{&selfInstance}
|
|
|
|
|
|
|
|
var tag ec2.Tag
|
|
|
|
tag.Key = aws.String(TagNameKubernetesCluster)
|
|
|
|
tag.Value = aws.String(TestClusterId)
|
|
|
|
selfInstance.Tags = []*ec2.Tag{&tag}
|
|
|
|
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FakeAWSServices) withAz(az string) *FakeAWSServices {
|
|
|
|
s.availabilityZone = az
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FakeAWSServices) withInstances(instances []*ec2.Instance) *FakeAWSServices {
|
|
|
|
s.instances = instances
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FakeAWSServices) Compute(region string) (EC2, error) {
|
|
|
|
return s.ec2, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FakeAWSServices) LoadBalancing(region string) (ELB, error) {
|
|
|
|
return s.elb, nil
|
|
|
|
}
|
|
|
|
|
2015-06-13 14:24:35 +00:00
|
|
|
func (s *FakeAWSServices) Autoscaling(region string) (ASG, error) {
|
|
|
|
return s.asg, nil
|
|
|
|
}
|
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
func (s *FakeAWSServices) Metadata() AWSMetadata {
|
|
|
|
return s.metadata
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFilterTags(t *testing.T) {
|
|
|
|
awsServices := NewFakeAWSServices()
|
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Error building aws cloud: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(c.filterTags) != 1 {
|
|
|
|
t.Errorf("unexpected filter tags: %v", c.filterTags)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.filterTags[TagNameKubernetesCluster] != TestClusterId {
|
|
|
|
t.Errorf("unexpected filter tags: %v", c.filterTags)
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
2015-05-23 00:12:53 +00:00
|
|
|
}
|
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
|
|
|
|
awsServices AWSServices
|
2015-03-27 14:31:16 +00:00
|
|
|
|
|
|
|
expectError bool
|
|
|
|
zone string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"No config reader",
|
2015-05-23 00:12:53 +00:00
|
|
|
nil, NewFakeAWSServices().withAz(""),
|
2015-03-27 14:31:16 +00:00
|
|
|
true, "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Config specified invalid zone",
|
2015-05-23 00:12:53 +00:00
|
|
|
strings.NewReader("[global]\nzone = blahonga"), NewFakeAWSServices(),
|
2015-03-27 14:31:16 +00:00
|
|
|
true, "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Config specifies valid zone",
|
2015-05-23 00:12:53 +00:00
|
|
|
strings.NewReader("[global]\nzone = eu-west-1a"), NewFakeAWSServices(),
|
2015-03-27 14:31:16 +00:00
|
|
|
false, "eu-west-1a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Gets zone from metadata when not in config",
|
|
|
|
|
|
|
|
strings.NewReader("[global]\n"),
|
2015-05-23 00:12:53 +00:00
|
|
|
NewFakeAWSServices(),
|
2015-03-27 14:31:16 +00:00
|
|
|
false, "us-east-1a",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"No zone in config or metadata",
|
2015-05-23 00:12:53 +00:00
|
|
|
strings.NewReader("[global]\n"),
|
|
|
|
NewFakeAWSServices().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)
|
|
|
|
} else if c.availabilityZone != test.zone {
|
2015-03-27 14:31:16 +00:00
|
|
|
t.Errorf("Incorrect zone value (%s vs %s) for case: %s",
|
|
|
|
c.availabilityZone, test.zone, test.name)
|
|
|
|
}
|
|
|
|
}
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type FakeEC2 struct {
|
2015-05-23 00:12:53 +00:00
|
|
|
aws *FakeAWSServices
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
|
|
|
|
2015-06-05 02:03:50 +00:00
|
|
|
func contains(haystack []*string, needle string) bool {
|
2015-04-21 13:50:36 +00:00
|
|
|
for _, s := range haystack {
|
2015-06-05 02:03:50 +00:00
|
|
|
// (deliberately panic if s == nil)
|
|
|
|
if needle == *s {
|
2015-04-21 13:50:36 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2015-06-05 02:03:50 +00:00
|
|
|
func instanceMatchesFilter(instance *ec2.Instance, filter *ec2.Filter) bool {
|
|
|
|
name := *filter.Name
|
|
|
|
if name == "private-dns-name" {
|
2015-09-20 21:10:57 +00:00
|
|
|
if instance.PrivateDnsName == nil {
|
2015-06-05 02:03:50 +00:00
|
|
|
return false
|
|
|
|
}
|
2015-09-20 21:10:57 +00:00
|
|
|
return contains(filter.Values, *instance.PrivateDnsName)
|
2015-06-05 02:03:50 +00:00
|
|
|
}
|
|
|
|
panic("Unknown filter name: " + name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *FakeEC2) DescribeInstances(request *ec2.DescribeInstancesInput) ([]*ec2.Instance, error) {
|
2015-05-14 19:18:25 +00:00
|
|
|
matches := []*ec2.Instance{}
|
2015-05-23 00:12:53 +00:00
|
|
|
for _, instance := range self.aws.instances {
|
2015-09-20 21:10:57 +00:00
|
|
|
if request.InstanceIds != nil {
|
|
|
|
if instance.InstanceId == nil {
|
2015-06-05 02:03:50 +00:00
|
|
|
glog.Warning("Instance with no instance id: ", instance)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
found := false
|
2015-09-20 21:10:57 +00:00
|
|
|
for _, instanceId := range request.InstanceIds {
|
|
|
|
if *instanceId == *instance.InstanceId {
|
2015-06-05 02:03:50 +00:00
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
continue
|
|
|
|
}
|
2015-04-21 13:50:36 +00:00
|
|
|
}
|
2015-06-05 02:03:50 +00:00
|
|
|
if request.Filters != nil {
|
|
|
|
allMatch := true
|
|
|
|
for _, filter := range request.Filters {
|
|
|
|
if !instanceMatchesFilter(instance, filter) {
|
|
|
|
allMatch = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !allMatch {
|
|
|
|
continue
|
|
|
|
}
|
2015-03-10 04:15:53 +00:00
|
|
|
}
|
2015-04-21 13:50:36 +00:00
|
|
|
matches = append(matches, instance)
|
2015-03-10 04:15:53 +00:00
|
|
|
}
|
2015-05-14 19:18:25 +00:00
|
|
|
|
|
|
|
return matches, nil
|
2015-03-10 04:15:53 +00:00
|
|
|
}
|
|
|
|
|
2015-03-26 19:47:49 +00:00
|
|
|
type FakeMetadata struct {
|
2015-05-23 00:12:53 +00:00
|
|
|
aws *FakeAWSServices
|
2015-03-26 19:47:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (self *FakeMetadata) GetMetaData(key string) ([]byte, error) {
|
2015-09-02 15:28:55 +00:00
|
|
|
networkInterfacesPrefix := "network/interfaces/macs/"
|
2015-03-10 04:15:53 +00:00
|
|
|
if key == "placement/availability-zone" {
|
2015-05-23 00:12:53 +00:00
|
|
|
return []byte(self.aws.availabilityZone), nil
|
2015-03-26 19:47:49 +00:00
|
|
|
} else if key == "instance-id" {
|
2015-05-23 00:12:53 +00:00
|
|
|
return []byte(self.aws.instanceId), nil
|
2015-07-02 17:37:00 +00:00
|
|
|
} else if key == "local-hostname" {
|
|
|
|
return []byte(self.aws.privateDnsName), nil
|
2015-09-02 15:28:55 +00:00
|
|
|
} else if strings.HasPrefix(key, networkInterfacesPrefix) {
|
|
|
|
if key == networkInterfacesPrefix {
|
|
|
|
return []byte(strings.Join(self.aws.networkInterfacesMacs, "/\n") + "/\n"), nil
|
|
|
|
} else {
|
|
|
|
keySplit := strings.Split(key, "/")
|
|
|
|
macParam := keySplit[3]
|
|
|
|
if len(keySplit) == 5 && keySplit[4] == "vpc-id" {
|
|
|
|
for i, macElem := range self.aws.networkInterfacesMacs {
|
|
|
|
if macParam == macElem {
|
|
|
|
return []byte(self.aws.networkInterfacesVpcIDs[i]), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
2015-03-10 04:15:53 +00:00
|
|
|
} else {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
|
|
|
|
2015-05-14 23:53:47 +00:00
|
|
|
func (ec2 *FakeEC2) AttachVolume(volumeID, instanceId, mountDevice string) (resp *ec2.VolumeAttachment, err error) {
|
2015-03-26 17:04:10 +00:00
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-05-25 19:05:56 +00:00
|
|
|
func (ec2 *FakeEC2) DetachVolume(request *ec2.DetachVolumeInput) (resp *ec2.VolumeAttachment, err error) {
|
2015-03-26 17:04:10 +00:00
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-05 02:03:50 +00:00
|
|
|
func (ec2 *FakeEC2) DescribeVolumes(request *ec2.DescribeVolumesInput) ([]*ec2.Volume, error) {
|
2015-03-26 17:04:10 +00:00
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-05-14 23:53:47 +00:00
|
|
|
func (ec2 *FakeEC2) CreateVolume(request *ec2.CreateVolumeInput) (resp *ec2.Volume, err error) {
|
2015-03-26 17:04:10 +00:00
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-05-14 23:53:47 +00:00
|
|
|
func (ec2 *FakeEC2) DeleteVolume(volumeID string) (resp *ec2.DeleteVolumeOutput, err error) {
|
2015-03-26 17:04:10 +00:00
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-05 02:03:50 +00:00
|
|
|
func (ec2 *FakeEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error) {
|
2015-05-23 00:12:53 +00:00
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ec2 *FakeEC2) CreateSecurityGroup(*ec2.CreateSecurityGroupInput) (*ec2.CreateSecurityGroupOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-05 02:03:50 +00:00
|
|
|
func (ec2 *FakeEC2) DeleteSecurityGroup(*ec2.DeleteSecurityGroupInput) (*ec2.DeleteSecurityGroupOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
func (ec2 *FakeEC2) AuthorizeSecurityGroupIngress(*ec2.AuthorizeSecurityGroupIngressInput) (*ec2.AuthorizeSecurityGroupIngressOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-05 02:03:50 +00:00
|
|
|
func (ec2 *FakeEC2) RevokeSecurityGroupIngress(*ec2.RevokeSecurityGroupIngressInput) (*ec2.RevokeSecurityGroupIngressOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-09-20 21:10:57 +00:00
|
|
|
func (ec2 *FakeEC2) DescribeVPCs(*ec2.DescribeVpcsInput) ([]*ec2.Vpc, error) {
|
2015-05-23 00:12:53 +00:00
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-05 02:03:50 +00:00
|
|
|
func (ec2 *FakeEC2) DescribeSubnets(*ec2.DescribeSubnetsInput) ([]*ec2.Subnet, error) {
|
2015-05-23 00:12:53 +00:00
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-05 02:03:50 +00:00
|
|
|
func (ec2 *FakeEC2) CreateTags(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:33:17 +00:00
|
|
|
func (s *FakeEC2) DescribeRouteTables(request *ec2.DescribeRouteTablesInput) ([]*ec2.RouteTable, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FakeEC2) CreateRoute(request *ec2.CreateRouteInput) (*ec2.CreateRouteOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FakeEC2) DeleteRoute(request *ec2.DeleteRouteInput) (*ec2.DeleteRouteOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-13 04:34:43 +00:00
|
|
|
func (s *FakeEC2) ModifyInstanceAttribute(request *ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
type FakeELB struct {
|
|
|
|
aws *FakeAWSServices
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ec2 *FakeELB) CreateLoadBalancer(*elb.CreateLoadBalancerInput) (*elb.CreateLoadBalancerOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
2015-06-13 18:45:38 +00:00
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
func (ec2 *FakeELB) DeleteLoadBalancer(*elb.DeleteLoadBalancerInput) (*elb.DeleteLoadBalancerOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
2015-06-13 18:45:38 +00:00
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
func (ec2 *FakeELB) DescribeLoadBalancers(*elb.DescribeLoadBalancersInput) (*elb.DescribeLoadBalancersOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
func (ec2 *FakeELB) RegisterInstancesWithLoadBalancer(*elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
2015-06-13 18:45:38 +00:00
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
func (ec2 *FakeELB) DeregisterInstancesFromLoadBalancer(*elb.DeregisterInstancesFromLoadBalancerInput) (*elb.DeregisterInstancesFromLoadBalancerOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-13 18:45:38 +00:00
|
|
|
func (ec2 *FakeELB) DetachLoadBalancerFromSubnets(*elb.DetachLoadBalancerFromSubnetsInput) (*elb.DetachLoadBalancerFromSubnetsOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ec2 *FakeELB) AttachLoadBalancerToSubnets(*elb.AttachLoadBalancerToSubnetsInput) (*elb.AttachLoadBalancerToSubnetsOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ec2 *FakeELB) CreateLoadBalancerListeners(*elb.CreateLoadBalancerListenersInput) (*elb.CreateLoadBalancerListenersOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ec2 *FakeELB) DeleteLoadBalancerListeners(*elb.DeleteLoadBalancerListenersInput) (*elb.DeleteLoadBalancerListenersOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ec2 *FakeELB) ApplySecurityGroupsToLoadBalancer(*elb.ApplySecurityGroupsToLoadBalancerInput) (*elb.ApplySecurityGroupsToLoadBalancerOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-07-31 04:24:46 +00:00
|
|
|
func (elb *FakeELB) ConfigureHealthCheck(*elb.ConfigureHealthCheckInput) (*elb.ConfigureHealthCheckOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-06-13 14:24:35 +00:00
|
|
|
type FakeASG struct {
|
|
|
|
aws *FakeAWSServices
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *FakeASG) UpdateAutoScalingGroup(*autoscaling.UpdateAutoScalingGroupInput) (*autoscaling.UpdateAutoScalingGroupOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *FakeASG) DescribeAutoScalingGroups(*autoscaling.DescribeAutoScalingGroupsInput) (*autoscaling.DescribeAutoScalingGroupsOutput, error) {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2015-05-23 00:12:53 +00:00
|
|
|
func mockInstancesResp(instances []*ec2.Instance) *AWSCloud {
|
|
|
|
awsServices := NewFakeAWSServices().withInstances(instances)
|
2015-03-10 04:15:53 +00:00
|
|
|
return &AWSCloud{
|
2015-05-23 00:12:53 +00:00
|
|
|
awsServices: awsServices,
|
|
|
|
ec2: awsServices.ec2,
|
|
|
|
availabilityZone: awsServices.availabilityZone,
|
2015-03-10 04:15:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func mockAvailabilityZone(region string, availabilityZone string) *AWSCloud {
|
2015-05-23 00:12:53 +00:00
|
|
|
awsServices := NewFakeAWSServices().withAz(availabilityZone)
|
2014-09-09 21:25:35 +00:00
|
|
|
return &AWSCloud{
|
2015-05-23 00:12:53 +00:00
|
|
|
awsServices: awsServices,
|
|
|
|
ec2: awsServices.ec2,
|
|
|
|
availabilityZone: awsServices.availabilityZone,
|
2015-05-14 19:18:25 +00:00
|
|
|
region: region,
|
2015-03-10 04:15:53 +00:00
|
|
|
}
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestList(t *testing.T) {
|
2015-05-14 23:53:47 +00:00
|
|
|
// TODO this setup is not very clean and could probably be improved
|
|
|
|
var instance0 ec2.Instance
|
|
|
|
var instance1 ec2.Instance
|
|
|
|
var instance2 ec2.Instance
|
|
|
|
var instance3 ec2.Instance
|
|
|
|
|
|
|
|
//0
|
|
|
|
tag0 := ec2.Tag{
|
2015-05-14 19:18:25 +00:00
|
|
|
Key: aws.String("Name"),
|
|
|
|
Value: aws.String("foo"),
|
2015-05-14 23:53:47 +00:00
|
|
|
}
|
|
|
|
instance0.Tags = []*ec2.Tag{&tag0}
|
2015-09-20 21:10:57 +00:00
|
|
|
instance0.InstanceId = aws.String("instance0")
|
|
|
|
instance0.PrivateDnsName = aws.String("instance0.ec2.internal")
|
2015-05-14 23:53:47 +00:00
|
|
|
state0 := ec2.InstanceState{
|
|
|
|
Name: aws.String("running"),
|
|
|
|
}
|
|
|
|
instance0.State = &state0
|
|
|
|
|
|
|
|
//1
|
|
|
|
tag1 := ec2.Tag{
|
2015-05-14 19:18:25 +00:00
|
|
|
Key: aws.String("Name"),
|
|
|
|
Value: aws.String("bar"),
|
2015-05-14 23:53:47 +00:00
|
|
|
}
|
|
|
|
instance1.Tags = []*ec2.Tag{&tag1}
|
2015-09-20 21:10:57 +00:00
|
|
|
instance1.InstanceId = aws.String("instance1")
|
|
|
|
instance1.PrivateDnsName = aws.String("instance1.ec2.internal")
|
2015-05-14 23:53:47 +00:00
|
|
|
state1 := ec2.InstanceState{
|
|
|
|
Name: aws.String("running"),
|
|
|
|
}
|
|
|
|
instance1.State = &state1
|
|
|
|
|
|
|
|
//2
|
|
|
|
tag2 := ec2.Tag{
|
2015-05-14 19:18:25 +00:00
|
|
|
Key: aws.String("Name"),
|
|
|
|
Value: aws.String("baz"),
|
2015-05-14 23:53:47 +00:00
|
|
|
}
|
|
|
|
instance2.Tags = []*ec2.Tag{&tag2}
|
2015-09-20 21:10:57 +00:00
|
|
|
instance2.InstanceId = aws.String("instance2")
|
|
|
|
instance2.PrivateDnsName = aws.String("instance2.ec2.internal")
|
2015-05-14 23:53:47 +00:00
|
|
|
state2 := ec2.InstanceState{
|
|
|
|
Name: aws.String("running"),
|
|
|
|
}
|
|
|
|
instance2.State = &state2
|
|
|
|
|
|
|
|
//3
|
|
|
|
tag3 := ec2.Tag{
|
2015-05-14 19:18:25 +00:00
|
|
|
Key: aws.String("Name"),
|
|
|
|
Value: aws.String("quux"),
|
2015-05-14 23:53:47 +00:00
|
|
|
}
|
|
|
|
instance3.Tags = []*ec2.Tag{&tag3}
|
2015-09-20 21:10:57 +00:00
|
|
|
instance3.InstanceId = aws.String("instance3")
|
|
|
|
instance3.PrivateDnsName = aws.String("instance3.ec2.internal")
|
2015-05-14 23:53:47 +00:00
|
|
|
state3 := ec2.InstanceState{
|
|
|
|
Name: aws.String("running"),
|
|
|
|
}
|
|
|
|
instance3.State = &state3
|
2014-09-09 21:25:35 +00:00
|
|
|
|
2015-05-14 23:53:47 +00:00
|
|
|
instances := []*ec2.Instance{&instance0, &instance1, &instance2, &instance3}
|
2014-09-09 21:25:35 +00:00
|
|
|
aws := mockInstancesResp(instances)
|
|
|
|
|
|
|
|
table := []struct {
|
|
|
|
input string
|
|
|
|
expect []string
|
|
|
|
}{
|
|
|
|
{"blahonga", []string{}},
|
2015-07-02 17:37:00 +00:00
|
|
|
{"quux", []string{"instance3.ec2.internal"}},
|
|
|
|
{"a", []string{"instance1.ec2.internal", "instance2.ec2.internal"}},
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range table {
|
|
|
|
result, err := aws.List(item.input)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Expected call with %v to succeed, failed with %s", item.input, err)
|
|
|
|
}
|
|
|
|
if e, a := item.expect, result; !reflect.DeepEqual(e, a) {
|
|
|
|
t.Errorf("Expected %v, got %v", e, a)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-20 04:00:18 +00:00
|
|
|
func testHasNodeAddress(t *testing.T, addrs []api.NodeAddress, addressType api.NodeAddressType, address string) {
|
|
|
|
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
|
|
|
|
|
|
|
|
//0
|
2015-09-20 21:10:57 +00:00
|
|
|
instance0.InstanceId = aws.String("instance-same")
|
|
|
|
instance0.PrivateDnsName = aws.String("instance-same.ec2.internal")
|
|
|
|
instance0.PrivateIpAddress = aws.String("192.168.0.1")
|
|
|
|
instance0.PublicIpAddress = aws.String("1.2.3.4")
|
2015-05-14 23:53:47 +00:00
|
|
|
instance0.InstanceType = aws.String("c3.large")
|
|
|
|
state0 := ec2.InstanceState{
|
|
|
|
Name: aws.String("running"),
|
|
|
|
}
|
|
|
|
instance0.State = &state0
|
|
|
|
|
|
|
|
//1
|
2015-09-20 21:10:57 +00:00
|
|
|
instance1.InstanceId = aws.String("instance-same")
|
|
|
|
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")
|
|
|
|
state1 := ec2.InstanceState{
|
|
|
|
Name: aws.String("running"),
|
|
|
|
}
|
|
|
|
instance1.State = &state1
|
|
|
|
|
|
|
|
instances := []*ec2.Instance{&instance0, &instance1}
|
2015-05-14 19:18:25 +00:00
|
|
|
|
|
|
|
aws1 := mockInstancesResp([]*ec2.Instance{})
|
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")
|
|
|
|
}
|
|
|
|
|
|
|
|
aws2 := mockInstancesResp(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")
|
|
|
|
}
|
|
|
|
|
|
|
|
aws3 := mockInstancesResp(instances[0:1])
|
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")
|
|
|
|
}
|
2015-04-20 04:00:18 +00:00
|
|
|
if len(addrs3) != 3 {
|
|
|
|
t.Errorf("Should return exactly 3 NodeAddresses")
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
2015-04-20 04:00:18 +00:00
|
|
|
testHasNodeAddress(t, addrs3, api.NodeInternalIP, "192.168.0.1")
|
|
|
|
testHasNodeAddress(t, addrs3, api.NodeLegacyHostIP, "192.168.0.1")
|
|
|
|
testHasNodeAddress(t, addrs3, api.NodeExternalIP, "1.2.3.4")
|
2014-09-09 21:25:35 +00:00
|
|
|
}
|
2015-03-10 04:15:53 +00:00
|
|
|
|
|
|
|
func TestGetRegion(t *testing.T) {
|
|
|
|
aws := mockAvailabilityZone("us-west-2", "us-west-2e")
|
|
|
|
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) {
|
|
|
|
awsServices := NewFakeAWSServices()
|
|
|
|
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 {
|
|
|
|
t.Errorf("Unexpected error:", err)
|
|
|
|
}
|
|
|
|
if vpcID != "vpc-mac0" {
|
|
|
|
t.Errorf("Unexpected vpcID: %s", vpcID)
|
|
|
|
}
|
|
|
|
}
|
2015-09-21 18:44:26 +00:00
|
|
|
|
|
|
|
func TestLoadBalancerMatchesClusterRegion(t *testing.T) {
|
|
|
|
awsServices := NewFakeAWSServices()
|
|
|
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Error building aws cloud: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
badELBRegion := "bad-elb-region"
|
|
|
|
errorMessage := fmt.Sprintf("requested load balancer region '%s' does not match cluster region '%s'", badELBRegion, c.region)
|
|
|
|
|
|
|
|
_, _, err = c.GetTCPLoadBalancer("elb-name", badELBRegion)
|
|
|
|
if err == nil || err.Error() != errorMessage {
|
|
|
|
t.Errorf("Expected GetTCPLoadBalancer region mismatch error.")
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = c.EnsureTCPLoadBalancer("elb-name", badELBRegion, nil, nil, nil, api.ServiceAffinityNone)
|
|
|
|
if err == nil || err.Error() != errorMessage {
|
|
|
|
t.Errorf("Expected EnsureTCPLoadBalancer region mismatch error.")
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.EnsureTCPLoadBalancerDeleted("elb-name", badELBRegion)
|
|
|
|
if err == nil || err.Error() != errorMessage {
|
|
|
|
t.Errorf("Expected EnsureTCPLoadBalancerDeleted region mismatch error.")
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.UpdateTCPLoadBalancer("elb-name", badELBRegion, nil)
|
|
|
|
if err == nil || err.Error() != errorMessage {
|
|
|
|
t.Errorf("Expected UpdateTCPLoadBalancer region mismatch error.")
|
|
|
|
}
|
|
|
|
}
|