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

pkg/actuators/machine/utils: Client-side state filtering in getInstanceByID and getInstances #268

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
43 changes: 19 additions & 24 deletions pkg/actuators/machine/actuator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func TestMachineEvents(t *testing.T) {

mockAWSClient.EXPECT().RunInstances(gomock.Any()).Return(stubReservation("ami-a9acbbd6", "i-02fcb933c5da7085c"), tc.runInstancesErr).AnyTimes()
if tc.describeInstancesOutput == nil {
mockAWSClient.EXPECT().DescribeInstances(gomock.Any()).Return(stubDescribeInstancesOutput("ami-a9acbbd6", "i-02fcb933c5da7085c"), tc.describeInstancesErr).AnyTimes()
mockAWSClient.EXPECT().DescribeInstances(gomock.Any()).Return(stubDescribeInstancesOutput("ami-a9acbbd6", "i-02fcb933c5da7085c", ec2.InstanceStateNameRunning), tc.describeInstancesErr).AnyTimes()
} else {
mockAWSClient.EXPECT().DescribeInstances(gomock.Any()).Return(tc.describeInstancesOutput, tc.describeInstancesErr).AnyTimes()
}
Expand Down Expand Up @@ -546,7 +546,7 @@ func TestActuator(t *testing.T) {
mockAWSClient.EXPECT().RunInstances(gomock.Any()).Return(stubReservation("ami-a9acbbd6", "i-02fcb933c5da7085c"), tc.runInstancesErr).AnyTimes()

if tc.describeInstancesOutput == nil {
mockAWSClient.EXPECT().DescribeInstances(gomock.Any()).Return(stubDescribeInstancesOutput("ami-a9acbbd6", "i-02fcb933c5da7085c"), tc.describeInstancesErr).AnyTimes()
mockAWSClient.EXPECT().DescribeInstances(gomock.Any()).Return(stubDescribeInstancesOutput("ami-a9acbbd6", "i-02fcb933c5da7085c", ec2.InstanceStateNameRunning), tc.describeInstancesErr).AnyTimes()
} else {
mockAWSClient.EXPECT().DescribeInstances(gomock.Any()).Return(tc.describeInstancesOutput, tc.describeInstancesErr).AnyTimes()
}
Expand Down Expand Up @@ -656,7 +656,7 @@ func TestAvailabilityZone(t *testing.T) {
ImageId: aws.String("ami-a9acbbd6"),
InstanceId: aws.String("i-02fcb933c5da7085c"),
State: &ec2.InstanceState{
Name: aws.String("Running"),
Name: aws.String(ec2.InstanceStateNameRunning),
},
LaunchTime: aws.Time(time.Now()),
Placement: &ec2.Placement{
Expand All @@ -675,7 +675,7 @@ func TestAvailabilityZone(t *testing.T) {
ImageId: aws.String("ami-a9acbbd6"),
InstanceId: aws.String("i-02fcb933c5da7085c"),
State: &ec2.InstanceState{
Name: aws.String("Running"),
Name: aws.String(ec2.InstanceStateNameRunning),
Code: aws.Int64(16),
},
LaunchTime: aws.Time(time.Now()),
Expand Down Expand Up @@ -804,7 +804,7 @@ func TestCreate(t *testing.T) {
mockAWSClient.EXPECT().DescribeSecurityGroups(gomock.Any()).Return(nil, fmt.Errorf("describeSecurityGroups error")).AnyTimes()
mockAWSClient.EXPECT().DescribeAvailabilityZones(gomock.Any()).Return(nil, fmt.Errorf("describeAvailabilityZones error")).AnyTimes()
mockAWSClient.EXPECT().DescribeImages(gomock.Any()).Return(nil, fmt.Errorf("describeImages error")).AnyTimes()
mockAWSClient.EXPECT().DescribeInstances(gomock.Any()).Return(stubDescribeInstancesOutput("ami-a9acbbd6", "i-02fcb933c5da7085c"), nil).AnyTimes()
mockAWSClient.EXPECT().DescribeInstances(gomock.Any()).Return(stubDescribeInstancesOutput("ami-a9acbbd6", "i-02fcb933c5da7085c", ec2.InstanceStateNameRunning), nil).AnyTimes()
mockAWSClient.EXPECT().TerminateInstances(gomock.Any()).Return(&ec2.TerminateInstancesOutput{}, nil).AnyTimes()
mockAWSClient.EXPECT().RunInstances(gomock.Any()).Return(stubReservation("ami-a9acbbd6", "i-02fcb933c5da7085c"), nil).AnyTimes()
mockAWSClient.EXPECT().RegisterInstancesWithLoadBalancer(gomock.Any()).Return(nil, nil).AnyTimes()
Expand Down Expand Up @@ -1175,7 +1175,7 @@ func TestGetMachineInstances(t *testing.T) {
}

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

Expand All @@ -1184,7 +1184,7 @@ func TestGetMachineInstances(t *testing.T) {
exists: true,
},
{
testcase: "has-status-search-by-id",
testcase: "has-status-search-by-id-running",
providerStatus: providerconfigv1.AWSMachineProviderStatus{
InstanceID: aws.String(instanceID),
},
Expand All @@ -1196,7 +1196,7 @@ func TestGetMachineInstances(t *testing.T) {
}

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

Expand All @@ -1205,23 +1205,21 @@ func TestGetMachineInstances(t *testing.T) {
exists: true,
},
{
testcase: "has-status-search-by-id and machine is terminated",
testcase: "has-status-search-by-id-terminated",
providerStatus: providerconfigv1.AWSMachineProviderStatus{
InstanceID: aws.String(instanceID),
},
awsClientFunc: func(ctrl *gomock.Controller) awsclient.Client {
mockAWSClient := mockaws.NewMockClient(ctrl)

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

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

request2 := &ec2.DescribeInstancesInput{
mockAWSClient.EXPECT().DescribeInstances(&ec2.DescribeInstancesInput{
Filters: []*ec2.Filter{
{
Name: awsTagFilter("Name"),
Expand All @@ -1230,16 +1228,13 @@ func TestGetMachineInstances(t *testing.T) {

clusterFilter(clusterID),
},
}

mockAWSClient.EXPECT().DescribeInstances(request2).Return(
stubTerminatedInstanceDescribeInstancesOutput(imageID, instanceID),
}).Return(
stubDescribeInstancesOutput(imageID, instanceID, ec2.InstanceStateNameTerminated),
nil,
).Times(1)
).Times(1).After(first)

return mockAWSClient
},
exists: false,
},
}

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

instance, err := actuator.getMachineInstances(nil, machineCopy)
instances, 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)
if tc.exists != (len(instances) > 0) {
t.Errorf("Expected instance exists: %t, got instances: %v", tc.exists, instances)
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/actuators/machine/awsclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestAwsClient(t *testing.T) {
ImageId: aws.String("ami-a9acbbd6"),
InstanceId: aws.String("i-02fcb933c5da7085c"),
State: &ec2.InstanceState{
Name: aws.String("Running"),
Name: aws.String(ec2.InstanceStateNameRunning),
},
LaunchTime: aws.Time(time.Now()),
PublicDnsName: aws.String(""),
Expand Down
2 changes: 1 addition & 1 deletion pkg/actuators/machine/instances_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func TestRunningInstance(t *testing.T) {
ImageId: aws.String("ami-a9acbbd6"),
InstanceId: aws.String("i-02fcb933c5da7085c"),
State: &ec2.InstanceState{
Name: aws.String("Running"),
Name: aws.String(ec2.InstanceStateNameRunning),
},
LaunchTime: aws.Time(time.Now()),
},
Expand Down
28 changes: 4 additions & 24 deletions pkg/actuators/machine/stubs.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func stubInstance(imageID, instanceID string) *ec2.Instance {
ImageId: aws.String(imageID),
InstanceId: aws.String(instanceID),
State: &ec2.InstanceState{
Name: aws.String("Running"),
Name: aws.String(ec2.InstanceStateNameRunning),
Code: aws.Int64(16),
},
LaunchTime: aws.Time(time.Now()),
Expand Down Expand Up @@ -261,7 +261,7 @@ func stubReservation(imageID, instanceID string) *ec2.Reservation {
ImageId: aws.String(imageID),
InstanceId: aws.String(instanceID),
State: &ec2.InstanceState{
Name: aws.String("Running"),
Name: aws.String(ec2.InstanceStateNameRunning),
Code: aws.Int64(16),
},
LaunchTime: aws.Time(time.Now()),
Expand All @@ -273,7 +273,7 @@ func stubReservation(imageID, instanceID string) *ec2.Reservation {
}
}

func stubDescribeInstancesOutput(imageID, instanceID string) *ec2.DescribeInstancesOutput {
func stubDescribeInstancesOutput(imageID, instanceID string, state string) *ec2.DescribeInstancesOutput {
return &ec2.DescribeInstancesOutput{
Reservations: []*ec2.Reservation{
{
Expand All @@ -282,27 +282,7 @@ func stubDescribeInstancesOutput(imageID, instanceID string) *ec2.DescribeInstan
ImageId: aws.String(imageID),
InstanceId: aws.String(instanceID),
State: &ec2.InstanceState{
Name: aws.String("Running"),
Code: aws.Int64(16),
},
LaunchTime: aws.Time(time.Now()),
},
},
},
},
}
}

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),
Name: aws.String(state),
Code: aws.Int64(16),
},
LaunchTime: aws.Time(time.Now()),
Expand Down
66 changes: 37 additions & 29 deletions pkg/actuators/machine/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package machine

import (
"fmt"
"strings"

"github.com/golang/glog"

Expand Down Expand Up @@ -87,37 +88,42 @@ func getStoppedInstances(machine *machinev1.Machine, client awsclient.Client) ([

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

func getExistingInstanceByID(id string, client awsclient.Client) (*ec2.Instance, error) {
return getInstanceByID(id, client, existingInstanceStates())
}

func instanceHasAllowedState(instance *ec2.Instance, instanceStateFilter []*string) error {
if instance.InstanceId == nil {
return fmt.Errorf("instance has nil ID")
}

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])
}
}
if instance.State == nil {
return fmt.Errorf("instance %s has nil state", *instance.InstanceId)
}
return existingInstances, nil
}

func getExistingInstanceByID(id string, client awsclient.Client) (*ec2.Instance, error) {
instance, err := getInstanceByID(id, client)
if err != nil {
return nil, err
if len(instanceStateFilter) == 0 {
return nil
}
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)

actualState := aws.StringValue(instance.State.Name)
for _, allowedState := range instanceStateFilter {
if aws.StringValue(allowedState) == actualState {
return nil
}
}
return instance, nil

allowedStates := make([]string, 0, len(instanceStateFilter))
for _, allowedState := range instanceStateFilter {
allowedStates = append(allowedStates, aws.StringValue(allowedState))
}
return fmt.Errorf("instance %s state %q is not in %s", *instance.InstanceId, actualState, strings.Join(allowedStates, ", "))
}

// getInstanceByID returns the instance with the given ID if it exists.
func getInstanceByID(id string, client awsclient.Client) (*ec2.Instance, error) {
func getInstanceByID(id string, client awsclient.Client, instanceStateFilter []*string) (*ec2.Instance, error) {
if id == "" {
return nil, fmt.Errorf("instance-id not specified")
}
Expand All @@ -141,7 +147,9 @@ func getInstanceByID(id string, client awsclient.Client) (*ec2.Instance, error)
return nil, fmt.Errorf("found %d instances for instance-id %s", len(reservation.Instances), id)
}

return reservation.Instances[0], nil
instance := reservation.Instances[0]

return instance, instanceHasAllowedState(instance, instanceStateFilter)
}

// getInstances returns all instances that have a tag matching our machine name,
Expand All @@ -159,12 +167,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"),
Values: instanceStateFilter,
})
}

// Query instances with our machine's name, and in running/pending state.
request := &ec2.DescribeInstancesInput{
Expand All @@ -177,9 +179,15 @@ func getInstances(machine *machinev1.Machine, client awsclient.Client, instanceS
}

instances := make([]*ec2.Instance, 0, len(result.Reservations))

for _, reservation := range result.Reservations {
for _, instance := range reservation.Instances {
instances = append(instances, instance)
err := instanceHasAllowedState(instance, instanceStateFilter)
if err != nil {
glog.Errorf("Excluding instance matching %s: %v", machine.Name, err)
} else {
instances = append(instances, instance)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/client/fake/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (c *awsClient) DescribeInstances(input *ec2.DescribeInstancesInput) (*ec2.D
ImageId: aws.String("ami-a9acbbd6"),
InstanceId: aws.String("i-02fcb933c5da7085c"),
State: &ec2.InstanceState{
Name: aws.String("Running"),
Name: aws.String(ec2.InstanceStateNameRunning),
Code: aws.Int64(16),
},
LaunchTime: aws.Time(time.Now()),
Expand Down