Skip to content

Commit 0ec9303

Browse files
committed
feat: delete asg hooks in the cloud if they're not defined in the crd
1 parent 03f3b50 commit 0ec9303

File tree

7 files changed

+154
-28
lines changed

7 files changed

+154
-28
lines changed

exp/api/v1beta2/conditions_consts.go

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ const (
6565
LifecycleHookCreationFailedReason = "LifecycleHookCreationFailed"
6666
// LifecycleHookUpdateFailedReason used for failures during lifecycle hook update.
6767
LifecycleHookUpdateFailedReason = "LifecycleHookUpdateFailed"
68+
// LifecycleHookDeletionFailedReason used for failures during lifecycle hook deletion.
69+
LifecycleHookDeletionFailedReason = "LifecycleHookDeletionFailed"
6870
)
6971

7072
const (

exp/controllers/awsmachinepool_controller.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,18 @@ func (r *AWSMachinePoolReconciler) reconcileNormal(ctx context.Context, machineP
299299
return nil
300300
}
301301

302-
if err := reconSvc.ReconcileLifecycleHooks(machinePoolScope); err != nil {
302+
lifecycleHookScope, err := scope.NewLifecycleHookScope(scope.LifecycleHookScopeParams{
303+
Client: r.Client,
304+
Logger: &machinePoolScope.Logger,
305+
MachinePool: machinePoolScope.MachinePool,
306+
LifecycleHooks: machinePoolScope.AWSMachinePool.Spec.AWSLifecycleHooks,
307+
AWSMachinePool: machinePoolScope.AWSMachinePool,
308+
})
309+
if err != nil {
310+
return errors.Wrap(err, "failed to create lifecycle hook scope")
311+
}
312+
313+
if err := reconSvc.ReconcileLifecycleHooks(*lifecycleHookScope); err != nil {
303314
r.Recorder.Eventf(machinePoolScope.AWSMachinePool, corev1.EventTypeWarning, "FaileLifecycleHooksReconcile", "Failed to reconcile lifecycle hooks: %v", err)
304315
return errors.Wrap(err, "failed to reconcile lifecycle hooks")
305316
}

pkg/cloud/scope/lifecyclehook.go

+56-14
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,71 @@ limitations under the License.
1717
package scope
1818

1919
import (
20-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20+
"github.com/pkg/errors"
21+
"k8s.io/klog/v2"
2122
"sigs.k8s.io/controller-runtime/pkg/client"
2223

23-
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
2424
expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
2525
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
2626
expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
27-
"sigs.k8s.io/cluster-api/util/conditions"
2827
)
2928

3029
// LaunchTemplateScope defines a scope defined around a launch template.
31-
type LifecycleHookScope interface {
32-
GetMachinePool() *expclusterv1.MachinePool
33-
GetLifecycleHooks() []expinfrav1.AWSLifecycleHook
30+
type LifecycleHookScope struct {
31+
client.Client
32+
logger.Logger
3433

35-
IsEKSManaged() bool
36-
AdditionalTags() infrav1.Tags
34+
MachinePool *expclusterv1.MachinePool
35+
LifecycleHooks []expinfrav1.AWSLifecycleHook
36+
AWSMachinePool *expinfrav1.AWSMachinePool
37+
}
3738

38-
GetObjectMeta() *metav1.ObjectMeta
39-
GetSetter() conditions.Setter
40-
PatchObject() error
41-
GetEC2Scope() EC2Scope
39+
type LifecycleHookScopeParams struct {
40+
Client client.Client
41+
Logger *logger.Logger
4242

43-
client.Client
44-
logger.Wrapper
43+
MachinePool *expclusterv1.MachinePool
44+
LifecycleHooks []expinfrav1.AWSLifecycleHook
45+
AWSMachinePool *expinfrav1.AWSMachinePool
46+
}
47+
48+
// NewLifecycleHookScope creates a new LifecycleHookScope.
49+
func NewLifecycleHookScope(params LifecycleHookScopeParams) (*LifecycleHookScope, error) {
50+
if params.Client == nil {
51+
return nil, errors.New("client is required when creating a LifecycleHookScope")
52+
}
53+
if params.MachinePool == nil {
54+
return nil, errors.New("machine pool is required when creating a LifecycleHookScope")
55+
}
56+
if params.LifecycleHooks == nil {
57+
params.LifecycleHooks = make([]expinfrav1.AWSLifecycleHook, 0)
58+
}
59+
if params.AWSMachinePool == nil {
60+
return nil, errors.New("aws machine pool is required when creating a LifecycleHookScope")
61+
}
62+
63+
if params.Logger == nil {
64+
log := klog.Background()
65+
params.Logger = logger.NewLogger(log)
66+
}
67+
68+
return &LifecycleHookScope{
69+
Client: params.Client,
70+
Logger: *params.Logger,
71+
MachinePool: params.MachinePool,
72+
LifecycleHooks: params.LifecycleHooks,
73+
AWSMachinePool: params.AWSMachinePool,
74+
}, nil
75+
}
76+
77+
func (s *LifecycleHookScope) GetASGName() string {
78+
return s.AWSMachinePool.Name
79+
}
80+
81+
func (s *LifecycleHookScope) GetLifecycleHooks() []expinfrav1.AWSLifecycleHook {
82+
return s.LifecycleHooks
83+
}
84+
85+
func (s *LifecycleHookScope) GetMachinePool() *expclusterv1.MachinePool {
86+
return s.MachinePool
4587
}

pkg/cloud/services/autoscaling/lifecyclehook.go

+30-13
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121

2222
"github.com/aws/aws-sdk-go/aws"
2323
"github.com/aws/aws-sdk-go/service/autoscaling"
24-
"github.com/aws/aws-sdk-go/service/autoscaling/autoscalingiface"
2524
"github.com/pkg/errors"
2625
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
2726

@@ -36,16 +35,35 @@ func (s *Service) LifecycleHookNeedsUpdate(scope scope.LifecycleHookScope, exist
3635
existing.NotificationMetadata != expected.NotificationMetadata
3736
}
3837

38+
func (s *Service) GetLifecycleHooks(scope scope.LifecycleHookScope) ([]*expinfrav1.AWSLifecycleHook, error) {
39+
asgName := scope.GetASGName()
40+
input := &autoscaling.DescribeLifecycleHooksInput{
41+
AutoScalingGroupName: aws.String(asgName),
42+
}
43+
44+
out, err := s.ASGClient.DescribeLifecycleHooksWithContext(context.TODO(), input)
45+
if err != nil {
46+
return nil, errors.Wrapf(err, "failed to describe lifecycle hooks for AutoScalingGroup: %q", scope.GetASGName())
47+
}
48+
49+
hooks := make([]*expinfrav1.AWSLifecycleHook, len(out.LifecycleHooks))
50+
for i, hook := range out.LifecycleHooks {
51+
hooks[i] = s.SDKToLifecycleHook(hook)
52+
}
53+
54+
return hooks, nil
55+
}
56+
3957
func (s *Service) GetLifecycleHook(scope scope.LifecycleHookScope, hook *expinfrav1.AWSLifecycleHook) (*expinfrav1.AWSLifecycleHook, error) {
40-
asgName := scope.GetMachinePool().Name
58+
asgName := scope.GetASGName()
4159
input := &autoscaling.DescribeLifecycleHooksInput{
4260
AutoScalingGroupName: aws.String(asgName),
4361
LifecycleHookNames: []*string{aws.String(hook.Name)},
4462
}
4563

4664
out, err := s.ASGClient.DescribeLifecycleHooksWithContext(context.TODO(), input)
4765
if err != nil {
48-
return nil, errors.Wrapf(err, "failed to describe lifecycle hook %q for AutoScalingGroup: %q", hook.Name, scope.GetMachinePool().Name)
66+
return nil, errors.Wrapf(err, "failed to describe lifecycle hook %q for AutoScalingGroup: %q", hook.Name, scope.GetASGName())
4967
}
5068

5169
if len(out.LifecycleHooks) == 0 {
@@ -56,7 +74,7 @@ func (s *Service) GetLifecycleHook(scope scope.LifecycleHookScope, hook *expinfr
5674
}
5775

5876
func (s *Service) CreateLifecycleHook(scope scope.LifecycleHookScope, hook *expinfrav1.AWSLifecycleHook) error {
59-
asgName := scope.GetMachinePool().Name
77+
asgName := scope.GetASGName()
6078

6179
lifecycleHookName := hook.Name
6280
if lifecycleHookName == "" {
@@ -96,14 +114,14 @@ func (s *Service) CreateLifecycleHook(scope scope.LifecycleHookScope, hook *expi
96114
}
97115

98116
if _, err := s.ASGClient.PutLifecycleHookWithContext(context.TODO(), input); err != nil {
99-
return errors.Wrapf(err, "failed to create lifecycle hook %q for AutoScalingGroup: %q", lifecycleHookName, scope.GetMachinePool().Name)
117+
return errors.Wrapf(err, "failed to create lifecycle hook %q for AutoScalingGroup: %q", lifecycleHookName, scope.GetASGName())
100118
}
101119

102120
return nil
103121
}
104122

105123
func (s *Service) UpdateLifecycleHook(scope scope.LifecycleHookScope, hook *expinfrav1.AWSLifecycleHook) error {
106-
asgName := scope.GetMachinePool().Name
124+
asgName := scope.GetASGName()
107125

108126
lifecycleHookName := hook.Name
109127
if lifecycleHookName == "" {
@@ -143,23 +161,22 @@ func (s *Service) UpdateLifecycleHook(scope scope.LifecycleHookScope, hook *expi
143161
}
144162

145163
if _, err := s.ASGClient.PutLifecycleHookWithContext(context.TODO(), input); err != nil {
146-
return errors.Wrapf(err, "failed to update lifecycle hook %q for AutoScalingGroup: %q", lifecycleHookName, scope.GetMachinePool().Name)
164+
return errors.Wrapf(err, "failed to update lifecycle hook %q for AutoScalingGroup: %q", lifecycleHookName, scope.GetASGName())
147165
}
148166

149167
return nil
150168
}
151169

152170
func (s *Service) DeleteLifecycleHook(
153-
scope *scope.MachinePoolScope,
154-
asgClient autoscalingiface.AutoScalingAPI,
155-
hook expinfrav1.AWSLifecycleHook) error {
171+
scope scope.LifecycleHookScope,
172+
hook *expinfrav1.AWSLifecycleHook) error {
156173
input := &autoscaling.DeleteLifecycleHookInput{
157-
AutoScalingGroupName: aws.String(scope.Name()),
174+
AutoScalingGroupName: aws.String(scope.GetASGName()),
158175
LifecycleHookName: aws.String(hook.Name),
159176
}
160177

161-
if _, err := asgClient.DeleteLifecycleHookWithContext(context.TODO(), input); err != nil {
162-
return errors.Wrapf(err, "failed to delete lifecycle hook %q for AutoScalingGroup: %q", hook.Name, scope.Name())
178+
if _, err := s.ASGClient.DeleteLifecycleHookWithContext(context.TODO(), input); err != nil {
179+
return errors.Wrapf(err, "failed to delete lifecycle hook %q for AutoScalingGroup: %q", hook.Name, scope.GetASGName())
163180
}
164181

165182
return nil

pkg/cloud/services/interfaces.go

+2
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@ type ASGInterface interface {
5050
SuspendProcesses(name string, processes []string) error
5151
ResumeProcesses(name string, processes []string) error
5252
SubnetIDs(scope *scope.MachinePoolScope) ([]string, error)
53+
GetLifecycleHooks(scope scope.LifecycleHookScope) ([]*expinfrav1.AWSLifecycleHook, error)
5354
GetLifecycleHook(scope scope.LifecycleHookScope, hook *expinfrav1.AWSLifecycleHook) (*expinfrav1.AWSLifecycleHook, error)
5455
CreateLifecycleHook(scope scope.LifecycleHookScope, hook *expinfrav1.AWSLifecycleHook) error
5556
UpdateLifecycleHook(scope scope.LifecycleHookScope, hook *expinfrav1.AWSLifecycleHook) error
57+
DeleteLifecycleHook(scope scope.LifecycleHookScope, hook *expinfrav1.AWSLifecycleHook) error
5658
LifecycleHookNeedsUpdate(scope scope.LifecycleHookScope, incoming *expinfrav1.AWSLifecycleHook, existing *expinfrav1.AWSLifecycleHook) bool
5759
}
5860

pkg/cloud/services/machinepool/machinepool.go

+23
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,29 @@ func (s *Service) ReconcileLifecycleHooks(scope scope.LifecycleHookScope) error
3535
return err
3636
}
3737
}
38+
39+
// Get a list of lifecycle hooks that are registered with the ASG but not defined in the MachinePool and delete them.
40+
hooks, err := s.ASGInterface.GetLifecycleHooks(scope)
41+
if err != nil {
42+
return err
43+
}
44+
for _, hook := range hooks {
45+
found := false
46+
for _, definedHook := range scope.GetLifecycleHooks() {
47+
if hook.Name == definedHook.Name {
48+
found = true
49+
break
50+
}
51+
}
52+
if !found {
53+
scope.Info("Deleting lifecycle hook", "hook", hook.Name)
54+
if err := s.ASGInterface.DeleteLifecycleHook(scope, hook); err != nil {
55+
conditions.MarkFalse(scope.GetMachinePool(), expinfrav1.LifecycleHookExistsCondition, expinfrav1.LifecycleHookDeletionFailedReason, clusterv1.ConditionSeverityError, err.Error())
56+
return err
57+
}
58+
}
59+
}
60+
3861
return nil
3962
}
4063

pkg/cloud/services/mock_services/autoscaling_interface_mock.go

+29
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)