@@ -53,8 +53,7 @@ var ValidationConditionMessages = map[string]string{
53
53
}
54
54
55
55
type Validation struct {
56
- ec2api sdk.EC2API
57
-
56
+ ec2api sdk.EC2API
58
57
amiProvider amifamily.Provider
59
58
cache * cache.Cache
60
59
}
@@ -69,22 +68,29 @@ func NewValidationReconciler(ec2api sdk.EC2API, amiProvider amifamily.Provider,
69
68
70
69
// nolint:gocyclo
71
70
func (v * Validation ) Reconcile (ctx context.Context , nodeClass * v1.EC2NodeClass ) (reconcile.Result , error ) {
72
- for _ , cond := range []string {
73
- v1 .ConditionTypeAMIsReady ,
74
- v1 .ConditionTypeInstanceProfileReady ,
75
- v1 .ConditionTypeSecurityGroupsReady ,
76
- v1 .ConditionTypeSubnetsReady ,
77
- } {
78
- if nodeClass .StatusConditions ().Get (cond ).IsTrue () {
79
- continue
80
- }
71
+ if _ , ok := lo .Find (v .requiredConditions (), func (cond string ) bool {
72
+ return nodeClass .StatusConditions ().Get (cond ).IsFalse ()
73
+ }); ok {
74
+ // If any of the required status conditions are false, we know validation will fail regardless of the other values.
81
75
nodeClass .StatusConditions ().SetFalse (
82
76
v1 .ConditionTypeValidationSucceeded ,
83
77
"DependenciesNotReady" ,
84
78
"Awaiting AMI, Instance Profile, Security Group, and Subnet resolution" ,
85
79
)
86
80
return reconcile.Result {}, nil
87
81
}
82
+ if _ , ok := lo .Find (v .requiredConditions (), func (cond string ) bool {
83
+ return nodeClass .StatusConditions ().Get (cond ).IsUnknown ()
84
+ }); ok {
85
+ // If none of the status conditions are false, but at least one is unknown, we should also consider the validation
86
+ // state to be unknown. Once all required conditions collapse to a true or false state, we can test validation.
87
+ nodeClass .StatusConditions ().SetUnknownWithReason (
88
+ v1 .ConditionTypeValidationSucceeded ,
89
+ "DependenciesNotReady" ,
90
+ "Awaiting AMI, Instance Profile, Security Group, and Subnet resolution" ,
91
+ )
92
+ return reconcile.Result {}, nil
93
+ }
88
94
89
95
if val , ok := v .cache .Get (v .cacheKey (nodeClass )); ok {
90
96
// We still update the status condition even if it's cached since we may have had a conflict error previously
@@ -113,32 +119,19 @@ func (v *Validation) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass)
113
119
return reconcile.Result {}, reconcile .TerminalError (fmt .Errorf ("validating tags, %w" , err ))
114
120
}
115
121
116
- type validator struct {
117
- isValid validatorFunc
118
- failureReason string
119
- }
120
- for _ , val := range []validator {
121
- {
122
- isValid : v .validateCreateFleetAuthorization ,
123
- failureReason : ConditionReasonCreateFleetAuthFailed ,
124
- },
125
- {
126
- isValid : v .validateCreateLaunchTemplateAuthorization ,
127
- failureReason : ConditionReasonCreateLaunchTemplateAuthFailed ,
128
- },
129
- {
130
- isValid : v .validateRunInstancesAuthorization ,
131
- failureReason : ConditionReasonRunInstancesAuthFailed ,
132
- },
122
+ for _ , isValid := range []validatorFunc {
123
+ v .validateCreateFleetAuthorization ,
124
+ v .validateCreateLaunchTemplateAuthorization ,
125
+ v .validateRunInstancesAuthorization ,
133
126
} {
134
- if ok , err := val . isValid (ctx , nodeClass , nodeClaim , tags ); err != nil {
127
+ if failureReason , err := isValid (ctx , nodeClass , nodeClaim , tags ); err != nil {
135
128
return reconcile.Result {}, err
136
- } else if ! ok {
137
- v .cache .SetDefault (v .cacheKey (nodeClass ), val . failureReason )
129
+ } else if failureReason != "" {
130
+ v .cache .SetDefault (v .cacheKey (nodeClass ), failureReason )
138
131
nodeClass .StatusConditions ().SetFalse (
139
132
v1 .ConditionTypeValidationSucceeded ,
140
- val . failureReason ,
141
- ValidationConditionMessages [val . failureReason ],
133
+ failureReason ,
134
+ ValidationConditionMessages [failureReason ],
142
135
)
143
136
return reconcile.Result {}, nil
144
137
}
@@ -149,55 +142,55 @@ func (v *Validation) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass)
149
142
return reconcile.Result {}, nil
150
143
}
151
144
152
- type validatorFunc func (context.Context , * v1.EC2NodeClass , * karpv1.NodeClaim , map [string ]string ) (bool , error )
145
+ type validatorFunc func (context.Context , * v1.EC2NodeClass , * karpv1.NodeClaim , map [string ]string ) (string , error )
153
146
154
147
func (v * Validation ) validateCreateFleetAuthorization (
155
148
ctx context.Context ,
156
149
nodeClass * v1.EC2NodeClass ,
157
150
_ * karpv1.NodeClaim ,
158
151
tags map [string ]string ,
159
- ) (bool , error ) {
152
+ ) (reason string , err error ) {
160
153
createFleetInput := instance .GetCreateFleetInput (nodeClass , string (karpv1 .CapacityTypeOnDemand ), tags , mockLaunchTemplateConfig ())
161
154
createFleetInput .DryRun = aws .Bool (true )
162
155
if _ , err := v .ec2api .CreateFleet (ctx , createFleetInput ); awserrors .IgnoreDryRunError (err ) != nil {
163
156
if awserrors .IgnoreUnauthorizedOperationError (err ) != nil {
164
157
// Dry run should only ever return UnauthorizedOperation or DryRunOperation so if we receive any other error
165
158
// it would be an unexpected state
166
- return false , err
159
+ return "" , err
167
160
}
168
- return false , nil
161
+ return ConditionReasonCreateFleetAuthFailed , nil
169
162
}
170
- return true , nil
163
+ return "" , nil
171
164
}
172
165
173
166
func (v * Validation ) validateCreateLaunchTemplateAuthorization (
174
167
ctx context.Context ,
175
168
nodeClass * v1.EC2NodeClass ,
176
169
nodeClaim * karpv1.NodeClaim ,
177
170
tags map [string ]string ,
178
- ) (bool , error ) {
171
+ ) (reason string , err error ) {
179
172
createLaunchTemplateInput := launchtemplate .GetCreateLaunchTemplateInput (ctx , mockOptions (* nodeClaim , nodeClass , tags ), corev1 .IPv4Protocol , "" )
180
173
createLaunchTemplateInput .DryRun = aws .Bool (true )
181
174
if _ , err := v .ec2api .CreateLaunchTemplate (ctx , createLaunchTemplateInput ); awserrors .IgnoreDryRunError (err ) != nil {
182
175
if awserrors .IgnoreUnauthorizedOperationError (err ) != nil {
183
176
// Dry run should only ever return UnauthorizedOperation or DryRunOperation so if we receive any other error
184
177
// it would be an unexpected state
185
- return false , fmt .Errorf ("validating ec2:CreateLaunchTemplates authorization, %w" , err )
178
+ return "" , fmt .Errorf ("validating ec2:CreateLaunchTemplates authorization, %w" , err )
186
179
}
187
- return false , nil
180
+ return ConditionReasonCreateLaunchTemplateAuthFailed , nil
188
181
}
189
- return true , nil
182
+ return "" , nil
190
183
}
191
184
192
185
func (v * Validation ) validateRunInstancesAuthorization (
193
186
ctx context.Context ,
194
187
nodeClass * v1.EC2NodeClass ,
195
188
nodeClaim * karpv1.NodeClaim ,
196
189
tags map [string ]string ,
197
- ) (bool , error ) {
190
+ ) (reason string , err error ) {
198
191
// NOTE: Since we've already validated the AMI status condition is true, this should never occur
199
192
if len (nodeClass .Status .AMIs ) == 0 {
200
- return false , fmt .Errorf ("no resolved amis in status" )
193
+ return "" , fmt .Errorf ("no resolved amis in status" )
201
194
}
202
195
203
196
var instanceType ec2types.InstanceType
@@ -243,11 +236,20 @@ func (v *Validation) validateRunInstancesAuthorization(
243
236
if awserrors .IgnoreUnauthorizedOperationError (err ) != nil {
244
237
// Dry run should only ever return UnauthorizedOperation or DryRunOperation so if we receive any other error
245
238
// it would be an unexpected state
246
- return false , fmt .Errorf ("validating ec2:RunInstances authorization, %w" , err )
239
+ return "" , fmt .Errorf ("validating ec2:RunInstances authorization, %w" , err )
247
240
}
248
- return false , nil
241
+ return ConditionReasonRunInstancesAuthFailed , nil
242
+ }
243
+ return "" , nil
244
+ }
245
+
246
+ func (* Validation ) requiredConditions () []string {
247
+ return []string {
248
+ v1 .ConditionTypeAMIsReady ,
249
+ v1 .ConditionTypeInstanceProfileReady ,
250
+ v1 .ConditionTypeSecurityGroupsReady ,
251
+ v1 .ConditionTypeSubnetsReady ,
249
252
}
250
- return true , nil
251
253
}
252
254
253
255
func (* Validation ) cacheKey (nodeClass * v1.EC2NodeClass ) string {
0 commit comments