From ffa622f9c78ed2d9256fd9c8eed307e8c9e0687e Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Tue, 13 Jun 2017 15:12:07 -0400 Subject: [PATCH] Batch AWS getInstancesByNodeNames calls with FilterNodeLimit We are going to limit the getInstancesByNodeNames call with a batch size of 150 --- pkg/cloudprovider/providers/aws/aws.go | 47 ++++++++++++++------- pkg/cloudprovider/providers/aws/aws_test.go | 28 ++++++++++++ 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/pkg/cloudprovider/providers/aws/aws.go b/pkg/cloudprovider/providers/aws/aws.go index eec475e7f7a..c837c4c535c 100644 --- a/pkg/cloudprovider/providers/aws/aws.go +++ b/pkg/cloudprovider/providers/aws/aws.go @@ -157,6 +157,10 @@ const ( createTagInitialDelay = 1 * time.Second createTagFactor = 2.0 createTagSteps = 9 + + // Number of node names that can be added to a filter. The AWS limit is 200 + // but we are using a lower limit on purpose + filterNodeLimit = 150 ) // awsTagNameMasterRoles is a set of well-known AWS tag names that indicate the instance is a master @@ -3383,28 +3387,39 @@ func (c *Cloud) getInstancesByNodeNamesCached(nodeNames sets.String, states ...s func (c *Cloud) getInstancesByNodeNames(nodeNames []string, states ...string) ([]*ec2.Instance, error) { names := aws.StringSlice(nodeNames) + ec2Instances := []*ec2.Instance{} - nodeNameFilter := &ec2.Filter{ - Name: aws.String("private-dns-name"), - Values: names, + for i := 0; i < len(names); i += filterNodeLimit { + end := i + filterNodeLimit + if end > len(names) { + end = len(names) + } + + nameSlice := names[i:end] + + nodeNameFilter := &ec2.Filter{ + Name: aws.String("private-dns-name"), + Values: nameSlice, + } + + filters := []*ec2.Filter{nodeNameFilter} + if len(states) > 0 { + filters = append(filters, newEc2Filter("instance-state-name", states...)) + } + + instances, err := c.describeInstances(filters) + if err != nil { + glog.V(2).Infof("Failed to describe instances %v", nodeNames) + return nil, err + } + ec2Instances = append(ec2Instances, instances...) } - filters := []*ec2.Filter{nodeNameFilter} - if len(states) > 0 { - filters = append(filters, newEc2Filter("instance-state-name", states...)) - } - - instances, err := c.describeInstances(filters) - if err != nil { - glog.V(2).Infof("Failed to describe instances %v", nodeNames) - return nil, err - } - - if len(instances) == 0 { + if len(ec2Instances) == 0 { glog.V(3).Infof("Failed to find any instances %v", nodeNames) return nil, nil } - return instances, nil + return ec2Instances, nil } func (c *Cloud) describeInstancesByInstanceID(instanceID string) ([]*ec2.Instance, error) { diff --git a/pkg/cloudprovider/providers/aws/aws_test.go b/pkg/cloudprovider/providers/aws/aws_test.go index f75c1a571c3..6552cbbaec8 100644 --- a/pkg/cloudprovider/providers/aws/aws_test.go +++ b/pkg/cloudprovider/providers/aws/aws_test.go @@ -17,6 +17,7 @@ limitations under the License. package aws import ( + "fmt" "io" "reflect" "strings" @@ -1073,6 +1074,33 @@ func TestFindInstancesByNodeNameCached(t *testing.T) { } } +func TestGetInstanceByNodeNameBatching(t *testing.T) { + awsServices := NewFakeAWSServices() + 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") +} + func TestGetVolumeLabels(t *testing.T) { awsServices := NewFakeAWSServices() c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)