Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug 1772163: Stop retieving instances by state tag filter on API call #269

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions pkg/actuators/machine/actuator.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,14 +483,14 @@ func (a *Actuator) Update(context context.Context, cluster *clusterv1.Cluster, m
return a.updateStatus(machine, newestInstance)
}

// Exists determines if the given machine currently exists. For AWS we query for instances in
// running state, with a matching name tag, to determine a match.
// Exists determines if the given machine currently exists.
// A machine which is not terminated is considered as existing.
func (a *Actuator) Exists(context context.Context, cluster *clusterv1.Cluster, machine *machinev1.Machine) (bool, error) {
glog.Infof("%s: Checking if machine exists", machine.Name)

instances, err := a.getMachineInstances(cluster, machine)
if err != nil {
glog.Errorf("%s: Error getting running instances: %v", machine.Name, err)
glog.Errorf("%s: Error getting existing instances: %v", machine.Name, err)
return false, err
}
if len(instances) == 0 {
Expand Down Expand Up @@ -545,13 +545,11 @@ func (a *Actuator) getMachineInstances(cluster *clusterv1.Cluster, machine *mach
// ID, search using that, otherwise fallback to filtering based on tags.
if err == nil && status.InstanceID != nil && *status.InstanceID != "" {
i, err := getExistingInstanceByID(*status.InstanceID, client)

if err != nil {
glog.Warningf("%s: Failed to find running instance by id %s: %v",
glog.Warningf("%s: Failed to find existing instance by id %s: %v",
machine.Name, *status.InstanceID, err)
} else {
glog.Infof("%s: Found instance by id: %s", machine.Name, *status.InstanceID)

return []*ec2.Instance{i}, nil
}
}
Expand Down
57 changes: 43 additions & 14 deletions pkg/actuators/machine/actuator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,7 @@ func TestGetMachineInstances(t *testing.T) {
testcase string
providerStatus providerconfigv1.AWSMachineProviderStatus
awsClientFunc func(*gomock.Controller) awsclient.Client
exists bool
}{
{
testcase: "empty-status-search-by-tag",
Expand All @@ -1170,11 +1171,6 @@ func TestGetMachineInstances(t *testing.T) {
},

clusterFilter(clusterID),

{
Name: aws.String("instance-state-name"),
Values: existingInstanceStates(),
},
},
}

Expand All @@ -1185,6 +1181,7 @@ func TestGetMachineInstances(t *testing.T) {

return mockAWSClient
},
exists: true,
},
{
testcase: "has-status-search-by-id",
Expand All @@ -1195,25 +1192,54 @@ func TestGetMachineInstances(t *testing.T) {
mockAWSClient := mockaws.NewMockClient(ctrl)

request := &ec2.DescribeInstancesInput{
InstanceIds: aws.StringSlice([]string{instanceID}),
}

mockAWSClient.EXPECT().DescribeInstances(request).Return(
stubDescribeInstancesOutput(imageID, instanceID),
nil,
).Times(1)

return mockAWSClient
},
exists: true,
},
{
testcase: "has-status-search-by-id and machine is terminated",
providerStatus: providerconfigv1.AWSMachineProviderStatus{
InstanceID: aws.String(instanceID),
},
awsClientFunc: func(ctrl *gomock.Controller) awsclient.Client {
mockAWSClient := mockaws.NewMockClient(ctrl)

request := &ec2.DescribeInstancesInput{
InstanceIds: aws.StringSlice([]string{instanceID}),
}

mockAWSClient.EXPECT().DescribeInstances(request).Return(
stubTerminatedInstanceDescribeInstancesOutput(imageID, instanceID),
nil,
).Times(1)

request2 := &ec2.DescribeInstancesInput{
Filters: []*ec2.Filter{
{
Name: aws.String("instance-id"),
Values: aws.StringSlice([]string{instanceID}),
},
{
Name: aws.String("instance-state-name"),
Values: existingInstanceStates(),
Name: awsTagFilter("Name"),
Values: aws.StringSlice([]string{machine.Name}),
},

clusterFilter(clusterID),
},
}

mockAWSClient.EXPECT().DescribeInstances(request).Return(
stubDescribeInstancesOutput(imageID, instanceID),
mockAWSClient.EXPECT().DescribeInstances(request2).Return(
stubTerminatedInstanceDescribeInstancesOutput(imageID, instanceID),
nil,
).Times(1)

return mockAWSClient
},
exists: false,
},
}

Expand Down Expand Up @@ -1242,10 +1268,13 @@ func TestGetMachineInstances(t *testing.T) {
t.Errorf("Error creating Actuator: %v", err)
}

_, err = actuator.getMachineInstances(nil, machineCopy)
instance, err := actuator.getMachineInstances(nil, machineCopy)
if err != nil {
t.Errorf("Unexpected error from getMachineInstances: %v", err)
}
if tc.exists != (instance != nil) {
t.Errorf("Expected instance exists: %t, got instance: %v", tc.exists, instance)
}
})
}
}
Expand Down
20 changes: 20 additions & 0 deletions pkg/actuators/machine/stubs.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,23 @@ func stubDescribeInstancesOutput(imageID, instanceID string) *ec2.DescribeInstan
},
}
}

func stubTerminatedInstanceDescribeInstancesOutput(imageID, instanceID string) *ec2.DescribeInstancesOutput {
return &ec2.DescribeInstancesOutput{
Reservations: []*ec2.Reservation{
{
Instances: []*ec2.Instance{
{
ImageId: aws.String(imageID),
InstanceId: aws.String(instanceID),
State: &ec2.InstanceState{
Name: aws.String(ec2.InstanceStateNameTerminated),
Code: aws.Int64(16),
},
LaunchTime: aws.Time(time.Now()),
},
},
},
},
}
}
47 changes: 28 additions & 19 deletions pkg/actuators/machine/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,36 +85,47 @@ func getStoppedInstances(machine *machinev1.Machine, client awsclient.Client) ([
return getInstances(machine, client, stoppedInstanceStateFilter)
}

// getExistingInstances returns all instances not terminated
func getExistingInstances(machine *machinev1.Machine, client awsclient.Client) ([]*ec2.Instance, error) {
return getInstances(machine, client, existingInstanceStates())
instances, err := getInstances(machine, client, nil)
if err != nil {
return nil, err
}

var existingInstances []*ec2.Instance
for key := range instances {
if (instances[key].State) != nil {
if aws.StringValue(instances[key].State.Name) != ec2.InstanceStateNameTerminated {
existingInstances = append(existingInstances, instances[key])
}
}
}
return existingInstances, nil
}

func getExistingInstanceByID(id string, client awsclient.Client) (*ec2.Instance, error) {
return getInstanceByID(id, client, existingInstanceStates())
instance, err := getInstanceByID(id, client)
if err != nil {
return nil, err
}
if instance.State != nil {
if aws.StringValue(instance.State.Name) == ec2.InstanceStateNameTerminated {
return nil, fmt.Errorf("failed to getExistingInstanceByID for instance-id %s, instance is terminated", id)
}
}
return instance, nil
}

// getInstanceByID returns the instance with the given ID if it exists.
func getInstanceByID(id string, client awsclient.Client, instanceStateFilter []*string) (*ec2.Instance, error) {
func getInstanceByID(id string, client awsclient.Client) (*ec2.Instance, error) {
if id == "" {
return nil, fmt.Errorf("instance-id not specified")
}

requestFilters := []*ec2.Filter{
{
Name: aws.String("instance-id"),
Values: aws.StringSlice([]string{id}),
},
}

if instanceStateFilter != nil {
requestFilters = append(requestFilters, &ec2.Filter{
Name: aws.String("instance-state-name"),
Values: instanceStateFilter,
})
request := &ec2.DescribeInstancesInput{
InstanceIds: aws.StringSlice([]string{id}),
}

request := &ec2.DescribeInstancesInput{Filters: requestFilters}

result, err := client.DescribeInstances(request)
if err != nil {
return nil, err
Expand All @@ -136,7 +147,6 @@ func getInstanceByID(id string, client awsclient.Client, instanceStateFilter []*
// getInstances returns all instances that have a tag matching our machine name,
// and cluster ID.
func getInstances(machine *machinev1.Machine, client awsclient.Client, instanceStateFilter []*string) ([]*ec2.Instance, error) {

clusterID, ok := getClusterID(machine)
if !ok {
return []*ec2.Instance{}, fmt.Errorf("unable to get cluster ID for machine: %q", machine.Name)
Expand All @@ -149,7 +159,6 @@ func getInstances(machine *machinev1.Machine, client awsclient.Client, instanceS
},
clusterFilter(clusterID),
}

if instanceStateFilter != nil {
requestFilters = append(requestFilters, &ec2.Filter{
Name: aws.String("instance-state-name"),
Expand Down