Skip to content

Commit 66ad8a8

Browse files
committed
Fix lifecycle hook needs-update check for nil fields which get set to defaults by AWS
1 parent c35bbc3 commit 66ad8a8

File tree

2 files changed

+133
-3
lines changed

2 files changed

+133
-3
lines changed

pkg/cloud/services/autoscaling/lifecyclehook.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2018 The Kubernetes Authors.
2+
Copyright 2024 The Kubernetes Authors.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import (
2424
"github.com/aws/aws-sdk-go/service/autoscaling"
2525
"github.com/pkg/errors"
2626
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/utils/ptr"
2728

2829
expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
2930
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services"
@@ -203,8 +204,8 @@ func ReconcileLifecycleHooks(ctx context.Context, asgService services.ASGInterfa
203204
}
204205

205206
func lifecycleHookNeedsUpdate(existing *expinfrav1.AWSLifecycleHook, expected *expinfrav1.AWSLifecycleHook) bool {
206-
return existing.DefaultResult != expected.DefaultResult ||
207-
existing.HeartbeatTimeout != expected.HeartbeatTimeout ||
207+
return ptr.Deref(existing.DefaultResult, expinfrav1.LifecycleHookDefaultResultAbandon) != ptr.Deref(expected.DefaultResult, expinfrav1.LifecycleHookDefaultResultAbandon) ||
208+
ptr.Deref(existing.HeartbeatTimeout, metav1.Duration{Duration: 3600 * time.Second}) != ptr.Deref(expected.HeartbeatTimeout, metav1.Duration{Duration: 3600 * time.Second}) ||
208209
existing.LifecycleTransition != expected.LifecycleTransition ||
209210
existing.NotificationTargetARN != expected.NotificationTargetARN ||
210211
existing.NotificationMetadata != expected.NotificationMetadata
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
Copyright 2024 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 asg
18+
19+
import (
20+
"testing"
21+
"time"
22+
23+
. "github.com/onsi/gomega"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
26+
expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
27+
)
28+
29+
func TestLifecycleHookNeedsUpdate(t *testing.T) {
30+
defaultResultAbandon := expinfrav1.LifecycleHookDefaultResultAbandon
31+
defaultResultContinue := expinfrav1.LifecycleHookDefaultResultContinue
32+
33+
tests := []struct {
34+
name string
35+
existing expinfrav1.AWSLifecycleHook
36+
expected expinfrav1.AWSLifecycleHook
37+
wantUpdate bool
38+
}{
39+
{
40+
name: "exactly equal",
41+
existing: expinfrav1.AWSLifecycleHook{
42+
Name: "andreas-test",
43+
LifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING",
44+
HeartbeatTimeout: &metav1.Duration{Duration: 3600 * time.Second},
45+
DefaultResult: &defaultResultAbandon,
46+
},
47+
expected: expinfrav1.AWSLifecycleHook{
48+
Name: "andreas-test",
49+
LifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING",
50+
HeartbeatTimeout: &metav1.Duration{Duration: 3600 * time.Second},
51+
DefaultResult: &defaultResultAbandon,
52+
},
53+
wantUpdate: false,
54+
},
55+
56+
{
57+
name: "heartbeatTimeout and defaultResult not set in manifest, but set to defaults by AWS",
58+
existing: expinfrav1.AWSLifecycleHook{
59+
Name: "andreas-test",
60+
LifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING",
61+
// Describing with AWS SDK always fills these fields with the defaults
62+
HeartbeatTimeout: &metav1.Duration{Duration: 3600 * time.Second},
63+
DefaultResult: &defaultResultAbandon,
64+
},
65+
expected: expinfrav1.AWSLifecycleHook{
66+
Name: "andreas-test",
67+
LifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING",
68+
},
69+
wantUpdate: false,
70+
},
71+
72+
{
73+
name: "transition differs",
74+
existing: expinfrav1.AWSLifecycleHook{
75+
Name: "andreas-test",
76+
LifecycleTransition: "autoscaling:EC2_INSTANCE_LAUNCHING",
77+
HeartbeatTimeout: &metav1.Duration{Duration: 3600 * time.Second},
78+
DefaultResult: &defaultResultAbandon,
79+
},
80+
expected: expinfrav1.AWSLifecycleHook{
81+
Name: "andreas-test",
82+
LifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING",
83+
HeartbeatTimeout: &metav1.Duration{Duration: 3600 * time.Second},
84+
DefaultResult: &defaultResultAbandon,
85+
},
86+
wantUpdate: true,
87+
},
88+
89+
{
90+
name: "heartbeat timeout differs",
91+
existing: expinfrav1.AWSLifecycleHook{
92+
Name: "andreas-test",
93+
LifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING",
94+
HeartbeatTimeout: &metav1.Duration{Duration: 3600 * time.Second},
95+
DefaultResult: &defaultResultAbandon,
96+
},
97+
expected: expinfrav1.AWSLifecycleHook{
98+
Name: "andreas-test",
99+
LifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING",
100+
HeartbeatTimeout: &metav1.Duration{Duration: 3601 * time.Second},
101+
DefaultResult: &defaultResultAbandon,
102+
},
103+
wantUpdate: true,
104+
},
105+
106+
{
107+
name: "default result differs",
108+
existing: expinfrav1.AWSLifecycleHook{
109+
Name: "andreas-test",
110+
LifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING",
111+
HeartbeatTimeout: &metav1.Duration{Duration: 3600 * time.Second},
112+
DefaultResult: &defaultResultAbandon,
113+
},
114+
expected: expinfrav1.AWSLifecycleHook{
115+
Name: "andreas-test",
116+
LifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING",
117+
HeartbeatTimeout: &metav1.Duration{Duration: 3600 * time.Second},
118+
DefaultResult: &defaultResultContinue,
119+
},
120+
wantUpdate: true,
121+
},
122+
}
123+
for _, tt := range tests {
124+
t.Run(tt.name, func(t *testing.T) {
125+
g := NewWithT(t)
126+
g.Expect(lifecycleHookNeedsUpdate(&tt.existing, &tt.expected)).To(Equal(tt.wantUpdate))
127+
})
128+
}
129+
}

0 commit comments

Comments
 (0)