Merge pull request #52656 from rrati/aws-fake

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Move AWS Fake implementations out of test 

The AWS fake implementations are in a test file and can't be imported into any other tests.  This makes integration testing difficult.  This PR moves the fake implementations such that they can be used by other entities.

@kubernetes/sig-aws-misc @justinsb
This commit is contained in:
Kubernetes Submit Queue 2017-10-02 22:35:33 -07:00 committed by GitHub
commit b4dd4edfae
5 changed files with 545 additions and 430 deletions

View File

@ -10,6 +10,7 @@ go_library(
name = "go_default_library",
srcs = [
"aws.go",
"aws_fakes.go",
"aws_instancegroups.go",
"aws_loadbalancer.go",
"aws_metrics.go",
@ -68,11 +69,8 @@ go_test(
deps = [
"//pkg/kubelet/apis:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/autoscaling:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/ec2:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/elb:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/kms:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/mock:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",

View File

@ -0,0 +1,430 @@
/*
Copyright 2017 The Kubernetes Authors.
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
import (
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/golang/glog"
)
type FakeAWSServices struct {
region string
instances []*ec2.Instance
selfInstance *ec2.Instance
networkInterfacesMacs []string
networkInterfacesPrivateIPs [][]string
networkInterfacesVpcIDs []string
ec2 FakeEC2
elb ELB
asg *FakeASG
metadata *FakeMetadata
kms *FakeKMS
}
func NewFakeAWSServices(clusterId string) *FakeAWSServices {
s := &FakeAWSServices{}
s.region = "us-east-1"
s.ec2 = &FakeEC2Impl{aws: s}
s.elb = &FakeELB{aws: s}
s.asg = &FakeASG{aws: s}
s.metadata = &FakeMetadata{aws: s}
s.kms = &FakeKMS{aws: s}
s.networkInterfacesMacs = []string{"aa:bb:cc:dd:ee:00", "aa:bb:cc:dd:ee:01"}
s.networkInterfacesVpcIDs = []string{"vpc-mac0", "vpc-mac1"}
selfInstance := &ec2.Instance{}
selfInstance.InstanceId = aws.String("i-self")
selfInstance.Placement = &ec2.Placement{
AvailabilityZone: aws.String("us-east-1a"),
}
selfInstance.PrivateDnsName = aws.String("ip-172-20-0-100.ec2.internal")
selfInstance.PrivateIpAddress = aws.String("192.168.0.1")
selfInstance.PublicIpAddress = aws.String("1.2.3.4")
s.selfInstance = selfInstance
s.instances = []*ec2.Instance{selfInstance}
var tag ec2.Tag
tag.Key = aws.String(TagNameKubernetesClusterLegacy)
tag.Value = aws.String(clusterId)
selfInstance.Tags = []*ec2.Tag{&tag}
return s
}
func (s *FakeAWSServices) WithAz(az string) *FakeAWSServices {
if s.selfInstance.Placement == nil {
s.selfInstance.Placement = &ec2.Placement{}
}
s.selfInstance.Placement.AvailabilityZone = aws.String(az)
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
}
func (s *FakeAWSServices) Autoscaling(region string) (ASG, error) {
return s.asg, nil
}
func (s *FakeAWSServices) Metadata() (EC2Metadata, error) {
return s.metadata, nil
}
func (s *FakeAWSServices) KeyManagement(region string) (KMS, error) {
return s.kms, nil
}
type FakeEC2 interface {
EC2
CreateSubnet(*ec2.Subnet) (*ec2.CreateSubnetOutput, error)
RemoveSubnets()
CreateRouteTable(*ec2.RouteTable) (*ec2.CreateRouteTableOutput, error)
RemoveRouteTables()
}
type FakeEC2Impl struct {
aws *FakeAWSServices
Subnets []*ec2.Subnet
DescribeSubnetsInput *ec2.DescribeSubnetsInput
RouteTables []*ec2.RouteTable
DescribeRouteTablesInput *ec2.DescribeRouteTablesInput
}
func (ec2i *FakeEC2Impl) DescribeInstances(request *ec2.DescribeInstancesInput) ([]*ec2.Instance, error) {
matches := []*ec2.Instance{}
for _, instance := range ec2i.aws.instances {
if request.InstanceIds != nil {
if instance.InstanceId == nil {
glog.Warning("Instance with no instance id: ", instance)
continue
}
found := false
for _, instanceID := range request.InstanceIds {
if *instanceID == *instance.InstanceId {
found = true
break
}
}
if !found {
continue
}
}
if request.Filters != nil {
allMatch := true
for _, filter := range request.Filters {
if !instanceMatchesFilter(instance, filter) {
allMatch = false
break
}
}
if !allMatch {
continue
}
}
matches = append(matches, instance)
}
return matches, nil
}
func (ec2i *FakeEC2Impl) AttachVolume(request *ec2.AttachVolumeInput) (resp *ec2.VolumeAttachment, err error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) DetachVolume(request *ec2.DetachVolumeInput) (resp *ec2.VolumeAttachment, err error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) DescribeVolumes(request *ec2.DescribeVolumesInput) ([]*ec2.Volume, error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) CreateVolume(request *ec2.CreateVolumeInput) (resp *ec2.Volume, err error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) DeleteVolume(request *ec2.DeleteVolumeInput) (resp *ec2.DeleteVolumeOutput, err error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) CreateSecurityGroup(*ec2.CreateSecurityGroupInput) (*ec2.CreateSecurityGroupOutput, error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) DeleteSecurityGroup(*ec2.DeleteSecurityGroupInput) (*ec2.DeleteSecurityGroupOutput, error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) AuthorizeSecurityGroupIngress(*ec2.AuthorizeSecurityGroupIngressInput) (*ec2.AuthorizeSecurityGroupIngressOutput, error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) RevokeSecurityGroupIngress(*ec2.RevokeSecurityGroupIngressInput) (*ec2.RevokeSecurityGroupIngressOutput, error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) CreateSubnet(request *ec2.Subnet) (*ec2.CreateSubnetOutput, error) {
ec2i.Subnets = append(ec2i.Subnets, request)
response := &ec2.CreateSubnetOutput{
Subnet: request,
}
return response, nil
}
func (ec2i *FakeEC2Impl) DescribeSubnets(request *ec2.DescribeSubnetsInput) ([]*ec2.Subnet, error) {
ec2i.DescribeSubnetsInput = request
return ec2i.Subnets, nil
}
func (ec2i *FakeEC2Impl) RemoveSubnets() {
ec2i.Subnets = ec2i.Subnets[:0]
}
func (ec2i *FakeEC2Impl) CreateTags(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) DescribeRouteTables(request *ec2.DescribeRouteTablesInput) ([]*ec2.RouteTable, error) {
ec2i.DescribeRouteTablesInput = request
return ec2i.RouteTables, nil
}
func (ec2i *FakeEC2Impl) CreateRouteTable(request *ec2.RouteTable) (*ec2.CreateRouteTableOutput, error) {
ec2i.RouteTables = append(ec2i.RouteTables, request)
response := &ec2.CreateRouteTableOutput{
RouteTable: request,
}
return response, nil
}
func (ec2i *FakeEC2Impl) RemoveRouteTables() {
ec2i.RouteTables = ec2i.RouteTables[:0]
}
func (ec2i *FakeEC2Impl) CreateRoute(request *ec2.CreateRouteInput) (*ec2.CreateRouteOutput, error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) DeleteRoute(request *ec2.DeleteRouteInput) (*ec2.DeleteRouteOutput, error) {
panic("Not implemented")
}
func (ec2i *FakeEC2Impl) ModifyInstanceAttribute(request *ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) {
panic("Not implemented")
}
type FakeMetadata struct {
aws *FakeAWSServices
}
func (m *FakeMetadata) GetMetadata(key string) (string, error) {
networkInterfacesPrefix := "network/interfaces/macs/"
i := m.aws.selfInstance
if key == "placement/availability-zone" {
az := ""
if i.Placement != nil {
az = aws.StringValue(i.Placement.AvailabilityZone)
}
return az, nil
} else if key == "instance-id" {
return aws.StringValue(i.InstanceId), nil
} else if key == "local-hostname" {
return aws.StringValue(i.PrivateDnsName), nil
} else if key == "public-hostname" {
return aws.StringValue(i.PublicDnsName), nil
} else if key == "local-ipv4" {
return aws.StringValue(i.PrivateIpAddress), nil
} else if key == "public-ipv4" {
return aws.StringValue(i.PublicIpAddress), nil
} else if strings.HasPrefix(key, networkInterfacesPrefix) {
if key == networkInterfacesPrefix {
return strings.Join(m.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 m.aws.networkInterfacesMacs {
if macParam == macElem {
return m.aws.networkInterfacesVpcIDs[i], nil
}
}
}
if len(keySplit) == 5 && keySplit[4] == "local-ipv4s" {
for i, macElem := range m.aws.networkInterfacesMacs {
if macParam == macElem {
return strings.Join(m.aws.networkInterfacesPrivateIPs[i], "/\n"), nil
}
}
}
return "", nil
}
} else {
return "", nil
}
}
type FakeELB struct {
aws *FakeAWSServices
}
func (elb *FakeELB) CreateLoadBalancer(*elb.CreateLoadBalancerInput) (*elb.CreateLoadBalancerOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) DeleteLoadBalancer(input *elb.DeleteLoadBalancerInput) (*elb.DeleteLoadBalancerOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) DescribeLoadBalancers(input *elb.DescribeLoadBalancersInput) (*elb.DescribeLoadBalancersOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) RegisterInstancesWithLoadBalancer(*elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) DeregisterInstancesFromLoadBalancer(*elb.DeregisterInstancesFromLoadBalancerInput) (*elb.DeregisterInstancesFromLoadBalancerOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) DetachLoadBalancerFromSubnets(*elb.DetachLoadBalancerFromSubnetsInput) (*elb.DetachLoadBalancerFromSubnetsOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) AttachLoadBalancerToSubnets(*elb.AttachLoadBalancerToSubnetsInput) (*elb.AttachLoadBalancerToSubnetsOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) CreateLoadBalancerListeners(*elb.CreateLoadBalancerListenersInput) (*elb.CreateLoadBalancerListenersOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) DeleteLoadBalancerListeners(*elb.DeleteLoadBalancerListenersInput) (*elb.DeleteLoadBalancerListenersOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) ApplySecurityGroupsToLoadBalancer(*elb.ApplySecurityGroupsToLoadBalancerInput) (*elb.ApplySecurityGroupsToLoadBalancerOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) ConfigureHealthCheck(*elb.ConfigureHealthCheckInput) (*elb.ConfigureHealthCheckOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) CreateLoadBalancerPolicy(*elb.CreateLoadBalancerPolicyInput) (*elb.CreateLoadBalancerPolicyOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) SetLoadBalancerPoliciesForBackendServer(*elb.SetLoadBalancerPoliciesForBackendServerInput) (*elb.SetLoadBalancerPoliciesForBackendServerOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) DescribeLoadBalancerAttributes(*elb.DescribeLoadBalancerAttributesInput) (*elb.DescribeLoadBalancerAttributesOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) ModifyLoadBalancerAttributes(*elb.ModifyLoadBalancerAttributesInput) (*elb.ModifyLoadBalancerAttributesOutput, error) {
panic("Not implemented")
}
func (self *FakeELB) expectDescribeLoadBalancers(loadBalancerName string) {
panic("Not implemented")
}
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")
}
type FakeKMS struct {
aws *FakeAWSServices
}
func (kms *FakeKMS) DescribeKey(*kms.DescribeKeyInput) (*kms.DescribeKeyOutput, error) {
panic("Not implemented")
}
func instanceMatchesFilter(instance *ec2.Instance, filter *ec2.Filter) bool {
name := *filter.Name
if name == "private-dns-name" {
if instance.PrivateDnsName == nil {
return false
}
return contains(filter.Values, *instance.PrivateDnsName)
}
if name == "instance-state-name" {
return contains(filter.Values, *instance.State.Name)
}
if name == "tag-key" {
for _, instanceTag := range instance.Tags {
if contains(filter.Values, aws.StringValue(instanceTag.Key)) {
return true
}
}
return false
}
if strings.HasPrefix(name, "tag:") {
tagName := name[4:]
for _, instanceTag := range instance.Tags {
if aws.StringValue(instanceTag.Key) == tagName && contains(filter.Values, aws.StringValue(instanceTag.Value)) {
return true
}
}
return false
}
panic("Unknown filter name: " + name)
}
func contains(haystack []*string, needle string) bool {
for _, s := range haystack {
// (deliberately panic if s == nil)
if needle == *s {
return true
}
}
return false
}

View File

@ -26,10 +26,6 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/golang/glog"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
@ -43,6 +39,49 @@ import (
const TestClusterId = "clusterid.test"
const TestClusterName = "testCluster"
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{{}},
})
}
func TestReadAWSCloudConfig(t *testing.T) {
tests := []struct {
name string
@ -75,17 +114,17 @@ func TestReadAWSCloudConfig(t *testing.T) {
},
{
"No zone in config, metadata does not have zone",
strings.NewReader("[global]\n"), NewFakeAWSServices().withAz(""),
strings.NewReader("[global]\n"), newMockedFakeAWSServices(TestClusterId).WithAz(""),
true, "",
},
{
"No zone in config, metadata has zone",
strings.NewReader("[global]\n"), NewFakeAWSServices(),
strings.NewReader("[global]\n"), newMockedFakeAWSServices(TestClusterId),
false, "us-east-1a",
},
{
"Zone in config should take precedence over metadata",
strings.NewReader("[global]\nzone = eu-west-1a"), NewFakeAWSServices(),
strings.NewReader("[global]\nzone = eu-west-1a"), newMockedFakeAWSServices(TestClusterId),
false, "eu-west-1a",
},
}
@ -113,80 +152,6 @@ func TestReadAWSCloudConfig(t *testing.T) {
}
}
type FakeAWSServices struct {
region string
instances []*ec2.Instance
selfInstance *ec2.Instance
networkInterfacesMacs []string
networkInterfacesPrivateIPs [][]string
networkInterfacesVpcIDs []string
ec2 *FakeEC2
elb *FakeELB
asg *FakeASG
metadata *FakeMetadata
kms *FakeKMS
}
func NewFakeAWSServices() *FakeAWSServices {
s := &FakeAWSServices{}
s.region = "us-east-1"
s.ec2 = &FakeEC2{aws: s}
s.elb = &FakeELB{aws: s}
s.asg = &FakeASG{aws: s}
s.metadata = &FakeMetadata{aws: s}
s.kms = &FakeKMS{aws: s}
s.networkInterfacesMacs = []string{"aa:bb:cc:dd:ee:00", "aa:bb:cc:dd:ee:01"}
s.networkInterfacesVpcIDs = []string{"vpc-mac0", "vpc-mac1"}
selfInstance := &ec2.Instance{}
selfInstance.InstanceId = aws.String("i-self")
selfInstance.Placement = &ec2.Placement{
AvailabilityZone: aws.String("us-east-1a"),
}
selfInstance.PrivateDnsName = aws.String("ip-172-20-0-100.ec2.internal")
selfInstance.PrivateIpAddress = aws.String("192.168.0.1")
selfInstance.PublicIpAddress = aws.String("1.2.3.4")
s.selfInstance = selfInstance
s.instances = []*ec2.Instance{selfInstance}
var tag ec2.Tag
tag.Key = aws.String(TagNameKubernetesClusterLegacy)
tag.Value = aws.String(TestClusterId)
selfInstance.Tags = []*ec2.Tag{&tag}
return s
}
func (s *FakeAWSServices) withAz(az string) *FakeAWSServices {
if s.selfInstance.Placement == nil {
s.selfInstance.Placement = &ec2.Placement{}
}
s.selfInstance.Placement.AvailabilityZone = aws.String(az)
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
}
func (s *FakeAWSServices) Autoscaling(region string) (ASG, error) {
return s.asg, nil
}
func (s *FakeAWSServices) Metadata() (EC2Metadata, error) {
return s.metadata, nil
}
func (s *FakeAWSServices) KeyManagement(region string) (KMS, error) {
return s.kms, nil
}
func TestNewAWSCloud(t *testing.T) {
tests := []struct {
name string
@ -199,24 +164,24 @@ func TestNewAWSCloud(t *testing.T) {
}{
{
"No config reader",
nil, NewFakeAWSServices().withAz(""),
nil, newMockedFakeAWSServices(TestClusterId).WithAz(""),
true, "",
},
{
"Config specifies valid zone",
strings.NewReader("[global]\nzone = eu-west-1a"), NewFakeAWSServices(),
strings.NewReader("[global]\nzone = eu-west-1a"), newMockedFakeAWSServices(TestClusterId),
false, "eu-west-1",
},
{
"Gets zone from metadata when not in config",
strings.NewReader("[global]\n"),
NewFakeAWSServices(),
newMockedFakeAWSServices(TestClusterId),
false, "us-east-1",
},
{
"No zone in config or metadata",
strings.NewReader("[global]\n"),
NewFakeAWSServices().withAz(""),
newMockedFakeAWSServices(TestClusterId).WithAz(""),
true, "",
},
}
@ -239,304 +204,8 @@ func TestNewAWSCloud(t *testing.T) {
}
}
type FakeEC2 struct {
aws *FakeAWSServices
Subnets []*ec2.Subnet
DescribeSubnetsInput *ec2.DescribeSubnetsInput
RouteTables []*ec2.RouteTable
DescribeRouteTablesInput *ec2.DescribeRouteTablesInput
mock.Mock
}
func contains(haystack []*string, needle string) bool {
for _, s := range haystack {
// (deliberately panic if s == nil)
if needle == *s {
return true
}
}
return false
}
func instanceMatchesFilter(instance *ec2.Instance, filter *ec2.Filter) bool {
name := *filter.Name
if name == "private-dns-name" {
if instance.PrivateDnsName == nil {
return false
}
return contains(filter.Values, *instance.PrivateDnsName)
}
if name == "instance-state-name" {
return contains(filter.Values, *instance.State.Name)
}
if name == "tag-key" {
for _, instanceTag := range instance.Tags {
if contains(filter.Values, aws.StringValue(instanceTag.Key)) {
return true
}
}
return false
}
if strings.HasPrefix(name, "tag:") {
tagName := name[4:]
for _, instanceTag := range instance.Tags {
if aws.StringValue(instanceTag.Key) == tagName && contains(filter.Values, aws.StringValue(instanceTag.Value)) {
return true
}
}
return false
}
panic("Unknown filter name: " + name)
}
func (self *FakeEC2) DescribeInstances(request *ec2.DescribeInstancesInput) ([]*ec2.Instance, error) {
matches := []*ec2.Instance{}
for _, instance := range self.aws.instances {
if request.InstanceIds != nil {
if instance.InstanceId == nil {
glog.Warning("Instance with no instance id: ", instance)
continue
}
found := false
for _, instanceID := range request.InstanceIds {
if *instanceID == *instance.InstanceId {
found = true
break
}
}
if !found {
continue
}
}
if request.Filters != nil {
allMatch := true
for _, filter := range request.Filters {
if !instanceMatchesFilter(instance, filter) {
allMatch = false
break
}
}
if !allMatch {
continue
}
}
matches = append(matches, instance)
}
return matches, nil
}
type FakeMetadata struct {
aws *FakeAWSServices
}
func (self *FakeMetadata) GetMetadata(key string) (string, error) {
networkInterfacesPrefix := "network/interfaces/macs/"
i := self.aws.selfInstance
if key == "placement/availability-zone" {
az := ""
if i.Placement != nil {
az = aws.StringValue(i.Placement.AvailabilityZone)
}
return az, nil
} else if key == "instance-id" {
return aws.StringValue(i.InstanceId), nil
} else if key == "local-hostname" {
return aws.StringValue(i.PrivateDnsName), nil
} else if key == "public-hostname" {
return aws.StringValue(i.PublicDnsName), nil
} else if key == "local-ipv4" {
return aws.StringValue(i.PrivateIpAddress), nil
} else if key == "public-ipv4" {
return aws.StringValue(i.PublicIpAddress), nil
} else if strings.HasPrefix(key, networkInterfacesPrefix) {
if key == networkInterfacesPrefix {
return 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 self.aws.networkInterfacesVpcIDs[i], nil
}
}
}
if len(keySplit) == 5 && keySplit[4] == "local-ipv4s" {
for i, macElem := range self.aws.networkInterfacesMacs {
if macParam == macElem {
return strings.Join(self.aws.networkInterfacesPrivateIPs[i], "/\n"), nil
}
}
}
return "", nil
}
} else {
return "", nil
}
}
func (ec2 *FakeEC2) AttachVolume(request *ec2.AttachVolumeInput) (resp *ec2.VolumeAttachment, err error) {
panic("Not implemented")
}
func (ec2 *FakeEC2) DetachVolume(request *ec2.DetachVolumeInput) (resp *ec2.VolumeAttachment, err error) {
panic("Not implemented")
}
func (e *FakeEC2) DescribeVolumes(request *ec2.DescribeVolumesInput) ([]*ec2.Volume, error) {
args := e.Called(request)
return args.Get(0).([]*ec2.Volume), nil
}
func (ec2 *FakeEC2) CreateVolume(request *ec2.CreateVolumeInput) (resp *ec2.Volume, err error) {
panic("Not implemented")
}
func (ec2 *FakeEC2) DeleteVolume(request *ec2.DeleteVolumeInput) (resp *ec2.DeleteVolumeOutput, err error) {
panic("Not implemented")
}
func (e *FakeEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error) {
args := e.Called(request)
return args.Get(0).([]*ec2.SecurityGroup), nil
}
func (ec2 *FakeEC2) CreateSecurityGroup(*ec2.CreateSecurityGroupInput) (*ec2.CreateSecurityGroupOutput, error) {
panic("Not implemented")
}
func (ec2 *FakeEC2) DeleteSecurityGroup(*ec2.DeleteSecurityGroupInput) (*ec2.DeleteSecurityGroupOutput, error) {
panic("Not implemented")
}
func (ec2 *FakeEC2) AuthorizeSecurityGroupIngress(*ec2.AuthorizeSecurityGroupIngressInput) (*ec2.AuthorizeSecurityGroupIngressOutput, error) {
panic("Not implemented")
}
func (ec2 *FakeEC2) RevokeSecurityGroupIngress(*ec2.RevokeSecurityGroupIngressInput) (*ec2.RevokeSecurityGroupIngressOutput, error) {
panic("Not implemented")
}
func (ec2 *FakeEC2) DescribeSubnets(request *ec2.DescribeSubnetsInput) ([]*ec2.Subnet, error) {
ec2.DescribeSubnetsInput = request
return ec2.Subnets, nil
}
func (ec2 *FakeEC2) CreateTags(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) {
panic("Not implemented")
}
func (ec2 *FakeEC2) DescribeRouteTables(request *ec2.DescribeRouteTablesInput) ([]*ec2.RouteTable, error) {
ec2.DescribeRouteTablesInput = request
return ec2.RouteTables, nil
}
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")
}
func (s *FakeEC2) ModifyInstanceAttribute(request *ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) {
panic("Not implemented")
}
type FakeELB struct {
aws *FakeAWSServices
mock.Mock
}
func (ec2 *FakeELB) CreateLoadBalancer(*elb.CreateLoadBalancerInput) (*elb.CreateLoadBalancerOutput, error) {
panic("Not implemented")
}
func (ec2 *FakeELB) DeleteLoadBalancer(input *elb.DeleteLoadBalancerInput) (*elb.DeleteLoadBalancerOutput, error) {
panic("Not implemented")
}
func (ec2 *FakeELB) DescribeLoadBalancers(input *elb.DescribeLoadBalancersInput) (*elb.DescribeLoadBalancersOutput, error) {
args := ec2.Called(input)
return args.Get(0).(*elb.DescribeLoadBalancersOutput), nil
}
func (ec2 *FakeELB) RegisterInstancesWithLoadBalancer(*elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) {
panic("Not implemented")
}
func (ec2 *FakeELB) DeregisterInstancesFromLoadBalancer(*elb.DeregisterInstancesFromLoadBalancerInput) (*elb.DeregisterInstancesFromLoadBalancerOutput, error) {
panic("Not implemented")
}
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")
}
func (elb *FakeELB) ConfigureHealthCheck(*elb.ConfigureHealthCheckInput) (*elb.ConfigureHealthCheckOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) CreateLoadBalancerPolicy(*elb.CreateLoadBalancerPolicyInput) (*elb.CreateLoadBalancerPolicyOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) SetLoadBalancerPoliciesForBackendServer(*elb.SetLoadBalancerPoliciesForBackendServerInput) (*elb.SetLoadBalancerPoliciesForBackendServerOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) DescribeLoadBalancerAttributes(*elb.DescribeLoadBalancerAttributesInput) (*elb.DescribeLoadBalancerAttributesOutput, error) {
panic("Not implemented")
}
func (elb *FakeELB) ModifyLoadBalancerAttributes(*elb.ModifyLoadBalancerAttributesInput) (*elb.ModifyLoadBalancerAttributesOutput, error) {
panic("Not implemented")
}
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")
}
type FakeKMS struct {
aws *FakeAWSServices
mock.Mock
}
func (kms *FakeKMS) DescribeKey(*kms.DescribeKeyInput) (*kms.DescribeKeyOutput, error) {
panic("Not implemented")
}
func mockInstancesResp(selfInstance *ec2.Instance, instances []*ec2.Instance) (*Cloud, *FakeAWSServices) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
awsServices.instances = instances
awsServices.selfInstance = selfInstance
awsCloud, err := newAWSCloud(nil, awsServices)
@ -547,7 +216,7 @@ func mockInstancesResp(selfInstance *ec2.Instance, instances []*ec2.Instance) (*
}
func mockAvailabilityZone(availabilityZone string) *Cloud {
awsServices := NewFakeAWSServices().withAz(availabilityZone)
awsServices := newMockedFakeAWSServices(TestClusterId).WithAz(availabilityZone)
awsCloud, err := newAWSCloud(nil, awsServices)
if err != nil {
panic(err)
@ -694,7 +363,7 @@ func TestGetRegion(t *testing.T) {
}
func TestFindVPCID(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
if err != nil {
t.Errorf("Error building aws cloud: %v", err)
@ -768,7 +437,7 @@ func constructRouteTable(subnetID string, public bool) *ec2.RouteTable {
}
func TestSubnetIDsinVPC(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
if err != nil {
t.Errorf("Error building aws cloud: %v", err)
@ -786,14 +455,22 @@ func TestSubnetIDsinVPC(t *testing.T) {
subnets[2] = make(map[string]string)
subnets[2]["id"] = "subnet-c0000001"
subnets[2]["az"] = "af-south-1c"
awsServices.ec2.Subnets = constructSubnets(subnets)
constructedSubnets := constructSubnets(subnets)
awsServices.ec2.RemoveSubnets()
for _, subnet := range constructedSubnets {
awsServices.ec2.CreateSubnet(subnet)
}
routeTables := map[string]bool{
"subnet-a0000001": true,
"subnet-b0000001": true,
"subnet-c0000001": true,
}
awsServices.ec2.RouteTables = constructRouteTables(routeTables)
constructedRouteTables := constructRouteTables(routeTables)
awsServices.ec2.RemoveRouteTables()
for _, rt := range constructedRouteTables {
awsServices.ec2.CreateRouteTable(rt)
}
result, err := c.findELBSubnets(false)
if err != nil {
@ -819,7 +496,11 @@ func TestSubnetIDsinVPC(t *testing.T) {
}
// test implicit routing table - when subnets are not explicitly linked to a table they should use main
awsServices.ec2.RouteTables = constructRouteTables(map[string]bool{})
constructedRouteTables = constructRouteTables(map[string]bool{})
awsServices.ec2.RemoveRouteTables()
for _, rt := range constructedRouteTables {
awsServices.ec2.CreateRouteTable(rt)
}
result, err = c.findELBSubnets(false)
if err != nil {
@ -854,10 +535,18 @@ func TestSubnetIDsinVPC(t *testing.T) {
subnets[4] = make(map[string]string)
subnets[4]["id"] = "subnet-c0000002"
subnets[4]["az"] = "af-south-1c"
awsServices.ec2.Subnets = constructSubnets(subnets)
constructedSubnets = constructSubnets(subnets)
awsServices.ec2.RemoveSubnets()
for _, subnet := range constructedSubnets {
awsServices.ec2.CreateSubnet(subnet)
}
routeTables["subnet-c0000000"] = true
routeTables["subnet-c0000002"] = true
awsServices.ec2.RouteTables = constructRouteTables(routeTables)
constructedRouteTables = constructRouteTables(routeTables)
awsServices.ec2.RemoveRouteTables()
for _, rt := range constructedRouteTables {
awsServices.ec2.CreateRouteTable(rt)
}
result, err = c.findELBSubnets(false)
if err != nil {
@ -889,14 +578,23 @@ func TestSubnetIDsinVPC(t *testing.T) {
subnets[5]["id"] = "subnet-d0000002"
subnets[5]["az"] = "af-south-1b"
awsServices.ec2.Subnets = constructSubnets(subnets)
constructedSubnets = constructSubnets(subnets)
awsServices.ec2.RemoveSubnets()
for _, subnet := range constructedSubnets {
awsServices.ec2.CreateSubnet(subnet)
}
routeTables["subnet-a0000001"] = false
routeTables["subnet-b0000001"] = false
routeTables["subnet-c0000001"] = false
routeTables["subnet-c0000000"] = true
routeTables["subnet-d0000001"] = true
routeTables["subnet-d0000002"] = true
awsServices.ec2.RouteTables = constructRouteTables(routeTables)
constructedRouteTables = constructRouteTables(routeTables)
awsServices.ec2.RemoveRouteTables()
for _, rt := range constructedRouteTables {
awsServices.ec2.CreateRouteTable(rt)
}
result, err = c.findELBSubnets(false)
if err != nil {
t.Errorf("Error listing subnets: %v", err)
@ -1051,7 +749,7 @@ func TestIpPermissionExistsHandlesMultipleGroupIdsWithUserIds(t *testing.T) {
}
func TestFindInstanceByNodeNameExcludesTerminatedInstances(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
nodeName := types.NodeName("my-dns.internal")
@ -1094,7 +792,7 @@ func TestFindInstanceByNodeNameExcludesTerminatedInstances(t *testing.T) {
}
func TestGetInstanceByNodeNameBatching(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
assert.Nil(t, err, "Error building aws cloud: %v", err)
var tag ec2.Tag
@ -1121,12 +819,12 @@ func TestGetInstanceByNodeNameBatching(t *testing.T) {
}
func TestGetVolumeLabels(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
assert.Nil(t, err, "Error building aws cloud: %v", err)
volumeId := awsVolumeID("vol-VolumeId")
expectedVolumeRequest := &ec2.DescribeVolumesInput{VolumeIds: []*string{volumeId.awsString()}}
awsServices.ec2.On("DescribeVolumes", expectedVolumeRequest).Return([]*ec2.Volume{
awsServices.ec2.(*MockedFakeEC2).On("DescribeVolumes", expectedVolumeRequest).Return([]*ec2.Volume{
{
VolumeId: volumeId.awsString(),
AvailabilityZone: aws.String("us-east-1a"),
@ -1139,55 +837,37 @@ func TestGetVolumeLabels(t *testing.T) {
assert.Equal(t, map[string]string{
kubeletapis.LabelZoneFailureDomain: "us-east-1a",
kubeletapis.LabelZoneRegion: "us-east-1"}, labels)
awsServices.ec2.AssertExpectations(t)
}
func (self *FakeELB) expectDescribeLoadBalancers(loadBalancerName string) {
self.On("DescribeLoadBalancers", &elb.DescribeLoadBalancersInput{LoadBalancerNames: []*string{aws.String(loadBalancerName)}}).Return(&elb.DescribeLoadBalancersOutput{
LoadBalancerDescriptions: []*elb.LoadBalancerDescription{{}},
})
}
func (self *FakeEC2) expectDescribeSecurityGroups(groupName, clusterID string) {
tags := []*ec2.Tag{
{Key: aws.String(TagNameKubernetesClusterLegacy), Value: aws.String(TestClusterId)},
{Key: aws.String(fmt.Sprintf("%s%s", TagNameKubernetesClusterPrefix, TestClusterId)), Value: aws.String(ResourceLifecycleOwned)},
}
self.On("DescribeSecurityGroups", &ec2.DescribeSecurityGroupsInput{Filters: []*ec2.Filter{
newEc2Filter("group-name", groupName),
newEc2Filter("vpc-id", ""),
}}).Return([]*ec2.SecurityGroup{{Tags: tags}})
awsServices.ec2.(*MockedFakeEC2).AssertExpectations(t)
}
func TestDescribeLoadBalancerOnDelete(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
awsServices.elb.expectDescribeLoadBalancers("aid")
awsServices.elb.(*MockedFakeELB).expectDescribeLoadBalancers("aid")
c.EnsureLoadBalancerDeleted(TestClusterName, &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "myservice", UID: "id"}})
}
func TestDescribeLoadBalancerOnUpdate(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
awsServices.elb.expectDescribeLoadBalancers("aid")
awsServices.elb.(*MockedFakeELB).expectDescribeLoadBalancers("aid")
c.UpdateLoadBalancer(TestClusterName, &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "myservice", UID: "id"}}, []*v1.Node{})
}
func TestDescribeLoadBalancerOnGet(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
awsServices.elb.expectDescribeLoadBalancers("aid")
awsServices.elb.(*MockedFakeELB).expectDescribeLoadBalancers("aid")
c.GetLoadBalancer(TestClusterName, &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "myservice", UID: "id"}})
}
func TestDescribeLoadBalancerOnEnsure(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
awsServices.elb.expectDescribeLoadBalancers("aid")
awsServices.elb.(*MockedFakeELB).expectDescribeLoadBalancers("aid")
c.EnsureLoadBalancer(TestClusterName, &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "myservice", UID: "id"}}, []*v1.Node{})
}
@ -1417,7 +1097,7 @@ func TestGetLoadBalancerAdditionalTags(t *testing.T) {
}
func TestLBExtraSecurityGroupsAnnotation(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := newMockedFakeAWSServices(TestClusterId)
c, _ := newAWSCloud(strings.NewReader("[global]"), awsServices)
sg1 := "sg-000001"
@ -1435,7 +1115,7 @@ func TestLBExtraSecurityGroupsAnnotation(t *testing.T) {
{"Multiple SGs specified", fmt.Sprintf("%s, %s", sg1, sg2), []string{sg1, sg2}},
}
awsServices.ec2.expectDescribeSecurityGroups("k8s-elb-aid", "cluster.test")
awsServices.ec2.(*MockedFakeEC2).expectDescribeSecurityGroups(TestClusterId, "k8s-elb-aid", "cluster.test")
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
@ -1449,3 +1129,10 @@ func TestLBExtraSecurityGroupsAnnotation(t *testing.T) {
})
}
}
func newMockedFakeAWSServices(id string) *FakeAWSServices {
s := NewFakeAWSServices(id)
s.ec2 = &MockedFakeEC2{FakeEC2Impl: s.ec2.(*FakeEC2Impl)}
s.elb = &MockedFakeELB{FakeELB: s.elb.(*FakeELB)}
return s
}

View File

@ -73,7 +73,7 @@ func TestRecognizesNewRegion(t *testing.T) {
t.Fatalf("region already valid: %q", region)
}
awsServices := NewFakeAWSServices().withAz(region + "a")
awsServices := NewFakeAWSServices(TestClusterId).WithAz(region + "a")
_, err := newAWSCloud(nil, awsServices)
if err != nil {
t.Errorf("error building AWS cloud: %v", err)

View File

@ -24,7 +24,7 @@ import (
)
func TestFilterTags(t *testing.T) {
awsServices := NewFakeAWSServices()
awsServices := NewFakeAWSServices(TestClusterId)
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
if err != nil {
t.Errorf("Error building aws cloud: %v", err)