Skip to content

Commit 0b2e346

Browse files
authored
Merge pull request #2957 from sedefsavas/ec2-double-creation
Return error if providerID is set and instance cannot be found
2 parents 07aa1f0 + 734419d commit 0b2e346

File tree

4 files changed

+64
-28
lines changed

4 files changed

+64
-28
lines changed

controllers/awsmachine_controller.go

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,8 @@ func (r *AWSMachineReconciler) reconcileDelete(machineScope *scope.MachineScope,
323323
}
324324

325325
instance, err := r.findInstance(machineScope, ec2Service)
326-
if err != nil {
327-
machineScope.Error(err, "unable to find instance")
326+
if err != nil && err != ec2.ErrInstanceNotFoundByID {
327+
machineScope.Error(err, "query to find instance failed")
328328
return ctrl.Result{}, err
329329
}
330330

@@ -426,29 +426,34 @@ func (r *AWSMachineReconciler) reconcileDelete(machineScope *scope.MachineScope,
426426
return ctrl.Result{}, nil
427427
}
428428

429-
// findInstance queries the EC2 apis and retrieves the instance if it exists, returns nil otherwise.
429+
// findInstance queries the EC2 apis and retrieves the instance if it exists.
430+
// If providerID is empty, finds instance by tags and if it cannot be found, returns empty instance with nil error.
431+
// If providerID is set, either finds the instance by ID or returns error.
430432
func (r *AWSMachineReconciler) findInstance(scope *scope.MachineScope, ec2svc services.EC2MachineInterface) (*infrav1.Instance, error) {
433+
var instance *infrav1.Instance
434+
431435
// Parse the ProviderID.
432436
pid, err := noderefutil.NewProviderID(scope.GetProviderID())
433-
if err != nil && !errors.Is(err, noderefutil.ErrEmptyProviderID) {
434-
return nil, errors.Wrapf(err, "failed to parse Spec.ProviderID")
435-
}
436-
437-
// If the ProviderID is populated, describe the instance using the ID.
438-
if err == nil {
439-
instance, err := ec2svc.InstanceIfExists(pointer.StringPtr(pid.ID()))
437+
if err != nil {
438+
if !errors.Is(err, noderefutil.ErrEmptyProviderID) {
439+
return nil, errors.Wrapf(err, "failed to parse Spec.ProviderID")
440+
}
441+
// If the ProviderID is empty, try to query the instance using tags.
442+
// If an instance cannot be found, GetRunningInstanceByTags returns empty instance with nil error.
443+
instance, err = ec2svc.GetRunningInstanceByTags(scope)
440444
if err != nil {
441-
return nil, errors.Wrapf(err, "failed to query AWSMachine instance")
445+
return nil, errors.Wrapf(err, "failed to query AWSMachine instance by tags")
446+
}
447+
} else {
448+
// If the ProviderID is populated, describe the instance using the ID.
449+
// InstanceIfExists() returns error (ErrInstanceNotFoundByID or ErrDescribeInstance) if the instance could not be found.
450+
instance, err = ec2svc.InstanceIfExists(pointer.StringPtr(pid.ID()))
451+
if err != nil {
452+
return nil, err
442453
}
443-
return instance, nil
444-
}
445-
446-
// If the ProviderID is empty, try to query the instance using tags.
447-
instance, err := ec2svc.GetRunningInstanceByTags(scope)
448-
if err != nil {
449-
return nil, errors.Wrapf(err, "failed to query AWSMachine instance by tags")
450454
}
451455

456+
// The only case where the instance is nil here is when the providerId is empty and instance could not be found by tags.
452457
return instance, nil
453458
}
454459

@@ -501,7 +506,7 @@ func (r *AWSMachineReconciler) reconcileNormal(_ context.Context, machineScope *
501506
return ctrl.Result{}, err
502507
}
503508

504-
// Create new instance
509+
// Create new instance since providerId is nil and instance could not be found by tags.
505510
if instance == nil {
506511
// Avoid a flickering condition between InstanceProvisionStarted and InstanceProvisionFailed if there's a persistent failure with createInstance
507512
if conditions.GetReason(machineScope.AWSMachine, infrav1.InstanceReadyCondition) != infrav1.InstanceProvisionFailedReason {

pkg/cloud/services/ec2/errors.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package ec2
18+
19+
import "errors"
20+
21+
var (
22+
// ErrInstanceNotFoundByID defines an error for when the instance with the provided provider ID is missing.
23+
ErrInstanceNotFoundByID = errors.New("failed to find instance by id")
24+
25+
// ErrDescribeInstance defines an error for when AWS SDK returns error when describing instances.
26+
ErrDescribeInstance = errors.New("failed to describe instance by id")
27+
)

pkg/cloud/services/ec2/instances.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ func (s *Service) GetRunningInstanceByTags(scope *scope.MachineScope) (*infrav1.
7575
return nil, nil
7676
}
7777

78-
// InstanceIfExists returns the existing instance or nothing if it doesn't exist.
78+
// InstanceIfExists returns the existing instance by id and errors if it cannot find the instance(ErrInstanceNotFoundByID) or API call fails (ErrDescribeInstance).
79+
// Returns empty instance with nil error, only when providerID is nil.
7980
func (s *Service) InstanceIfExists(id *string) (*infrav1.Instance, error) {
8081
if id == nil {
8182
s.scope.Info("Instance does not have an instance id")
@@ -91,17 +92,20 @@ func (s *Service) InstanceIfExists(id *string) (*infrav1.Instance, error) {
9192
out, err := s.EC2Client.DescribeInstances(input)
9293
switch {
9394
case awserrors.IsNotFound(err):
94-
return nil, nil
95+
record.Eventf(s.scope.InfraCluster(), "FailedFindInstances", "failed to find instance by providerId %q: %v", *id, err)
96+
return nil, ErrInstanceNotFoundByID
9597
case err != nil:
9698
record.Eventf(s.scope.InfraCluster(), "FailedDescribeInstances", "failed to describe instance %q: %v", *id, err)
97-
return nil, errors.Wrapf(err, "failed to describe instance: %q", *id)
99+
return nil, ErrDescribeInstance
98100
}
99101

100102
if len(out.Reservations) > 0 && len(out.Reservations[0].Instances) > 0 {
101103
return s.SDKToInstance(out.Reservations[0].Instances[0])
104+
} else {
105+
// Failed to find instance with provider id.
106+
record.Eventf(s.scope.InfraCluster(), "FailedFindInstances", "failed to find instance by providerId %q: %v", *id, err)
107+
return nil, ErrInstanceNotFoundByID
102108
}
103-
104-
return nil, nil
105109
}
106110

107111
// CreateInstance runs an ec2 instance.

pkg/cloud/services/ec2/instances_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ func TestInstanceIfExists(t *testing.T) {
6161
Return(nil, awserrors.NewNotFound("not found"))
6262
},
6363
check: func(instance *infrav1.Instance, err error) {
64-
if err != nil {
65-
t.Fatalf("did not expect error: %v", err)
64+
if err == nil {
65+
t.Fatalf("expects error when instance could not be found: %v", err)
6666
}
6767

6868
if instance != nil {
@@ -80,8 +80,8 @@ func TestInstanceIfExists(t *testing.T) {
8080
Return(nil, awserr.New(awserrors.InvalidInstanceID, "does not exist", nil))
8181
},
8282
check: func(instance *infrav1.Instance, err error) {
83-
if err != nil {
84-
t.Fatalf("did not expect error: %v", err)
83+
if err == nil {
84+
t.Fatalf("expects error when DescribeInstances returns error: %v", err)
8585
}
8686

8787
if instance != nil {

0 commit comments

Comments
 (0)