Skip to content

Commit 419be6b

Browse files
authored
feat: add validation for timeoutInSeconds for MonitorConfig in TrafficManagerProfile (#292)
1 parent 0650598 commit 419be6b

File tree

4 files changed

+261
-6
lines changed

4 files changed

+261
-6
lines changed

api/v1beta1/trafficmanagerprofile_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ type TrafficManagerProfileSpec struct {
5757

5858
// MonitorConfig defines the endpoint monitoring settings of the Traffic Manager profile.
5959
// https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-monitoring
60+
// +kubebuilder:validation:XValidation:rule="has(self.intervalInSeconds) && self.intervalInSeconds == 30 ? (!has(self.timeoutInSeconds) || (self.timeoutInSeconds >= 5 && self.timeoutInSeconds <= 10)) : true",message="timeoutInSeconds must be between 5 and 10 when intervalInSeconds is 30"
61+
// +kubebuilder:validation:XValidation:rule="has(self.intervalInSeconds) && self.intervalInSeconds == 10 ? (!has(self.timeoutInSeconds) || (self.timeoutInSeconds >= 5 && self.timeoutInSeconds <= 9)) : true",message="timeoutInSeconds must be between 5 and 9 when intervalInSeconds is 10"
6062
type MonitorConfig struct {
6163
// The monitor interval for endpoints in this profile. This is the interval at which Traffic Manager will check the health
6264
// of each endpoint in this profile.

config/crd/bases/networking.fleet.azure.com_trafficmanagerprofiles.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,17 @@ spec:
305305
minimum: 0
306306
type: integer
307307
type: object
308+
x-kubernetes-validations:
309+
- message: timeoutInSeconds must be between 5 and 10 when intervalInSeconds
310+
is 30
311+
rule: 'has(self.intervalInSeconds) && self.intervalInSeconds ==
312+
30 ? (!has(self.timeoutInSeconds) || (self.timeoutInSeconds >=
313+
5 && self.timeoutInSeconds <= 10)) : true'
314+
- message: timeoutInSeconds must be between 5 and 9 when intervalInSeconds
315+
is 10
316+
rule: 'has(self.intervalInSeconds) && self.intervalInSeconds ==
317+
10 ? (!has(self.timeoutInSeconds) || (self.timeoutInSeconds >=
318+
5 && self.timeoutInSeconds <= 9)) : true'
308319
resourceGroup:
309320
description: |-
310321
The name of the resource group to contain the Azure Traffic Manager resource corresponding to this profile.

pkg/controllers/hub/trafficmanagerprofile/controller_integration_test.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,18 @@ var _ = Describe("Test TrafficManagerProfile Controller", func() {
155155
validateTrafficManagerProfileMetricsEmitted(customRegistry, wantMetrics...)
156156
})
157157

158-
It("Update the trafficManagerProfile spec", func() {
158+
It("Update the trafficManagerProfile spec and should fail", func() {
159159
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: testNamespace, Name: name}, profile)).Should(Succeed(), "failed to get the trafficManagerProfile")
160160
profile.Spec.MonitorConfig.IntervalInSeconds = ptr.To[int64](10)
161161
profile.Spec.MonitorConfig.TimeoutInSeconds = ptr.To[int64](10)
162-
Expect(k8sClient.Update(ctx, profile)).Should(Succeed(), "failed to update the trafficManagerProfile")
162+
Expect(k8sClient.Update(ctx, profile)).ShouldNot(Succeed(), "failed to update the trafficManagerProfile")
163163
})
164164

165-
It("Validating trafficManagerProfile status and update should fail", func() {
165+
It("Updating trafficManagerProfile spec to valid and validating trafficManagerProfile status", func() {
166+
profile.Spec.MonitorConfig.IntervalInSeconds = ptr.To[int64](30)
167+
profile.Spec.MonitorConfig.TimeoutInSeconds = ptr.To[int64](10)
168+
Expect(k8sClient.Update(ctx, profile)).Should(Succeed(), "failed to update the trafficManagerProfile")
169+
166170
want := fleetnetv1beta1.TrafficManagerProfile{
167171
ObjectMeta: metav1.ObjectMeta{
168172
Name: name,
@@ -171,11 +175,13 @@ var _ = Describe("Test TrafficManagerProfile Controller", func() {
171175
},
172176
Spec: profile.Spec,
173177
Status: fleetnetv1beta1.TrafficManagerProfileStatus{
178+
DNSName: ptr.To(fqdn),
179+
ResourceID: profileResourceID,
174180
Conditions: []metav1.Condition{
175181
{
176-
Status: metav1.ConditionFalse,
182+
Status: metav1.ConditionTrue,
177183
Type: string(fleetnetv1beta1.TrafficManagerProfileConditionProgrammed),
178-
Reason: string(fleetnetv1beta1.TrafficManagerProfileReasonInvalid),
184+
Reason: string(fleetnetv1beta1.TrafficManagerProfileReasonProgrammed),
179185
ObservedGeneration: profile.Generation,
180186
},
181187
},
@@ -184,7 +190,7 @@ var _ = Describe("Test TrafficManagerProfile Controller", func() {
184190
validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want, timeout)
185191

186192
By("By validating the status metrics")
187-
wantMetrics = append(wantMetrics, generateMetrics(profile, want.Status.Conditions[0]))
193+
// It overwrites the previous one as they have the same condition.
188194
validateTrafficManagerProfileMetricsEmitted(customRegistry, wantMetrics...)
189195
})
190196

test/apis/v1beta1/api_validation_integration_test.go

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,110 @@ var _ = Describe("Test networking v1alpha1 API validation", func() {
179179
Expect(hubClient.Delete(ctx, profile)).Should(Succeed(), "failed to delete trafficManagerProfile")
180180
})
181181

182+
It("should deny creating API with timeoutInSeconds < 5 when intervalInSeconds is 30", func() {
183+
profile := &fleetnetv1beta1.TrafficManagerProfile{
184+
ObjectMeta: objectMetaWithNameValid,
185+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
186+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
187+
IntervalInSeconds: ptr.To(int64(30)),
188+
TimeoutInSeconds: ptr.To(int64(4)),
189+
},
190+
ResourceGroup: "test-resource-group",
191+
},
192+
}
193+
By("expecting denial of CREATE API with invalid timeoutInSeconds")
194+
var err = hubClient.Create(ctx, profile)
195+
Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create API call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8serrors.StatusError{})))
196+
Expect(statusErr.Status().Message).Should(ContainSubstring("spec.monitorConfig.timeoutInSeconds in body should be greater than or equal to 5"))
197+
Expect(statusErr.Status().Message).Should(ContainSubstring("timeoutInSeconds must be between 5 and 10 when intervalInSeconds is 30"))
198+
})
199+
200+
It("should deny creating API with timeoutInSeconds > 10 when intervalInSeconds is 30", func() {
201+
profile := &fleetnetv1beta1.TrafficManagerProfile{
202+
ObjectMeta: objectMetaWithNameValid,
203+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
204+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
205+
IntervalInSeconds: ptr.To(int64(30)),
206+
TimeoutInSeconds: ptr.To(int64(15)),
207+
},
208+
ResourceGroup: "test-resource-group",
209+
},
210+
}
211+
By("expecting denial of CREATE API with invalid timeoutInSeconds")
212+
var err = hubClient.Create(ctx, profile)
213+
Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create API call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8serrors.StatusError{})))
214+
Expect(statusErr.Status().Message).Should(ContainSubstring("spec.monitorConfig.timeoutInSeconds in body should be less than or equal to 10"))
215+
Expect(statusErr.Status().Message).Should(ContainSubstring("timeoutInSeconds must be between 5 and 10 when intervalInSeconds is 30"))
216+
})
217+
218+
It("should deny creating API with timeoutInSeconds < 5 when intervalInSeconds is 10", func() {
219+
profile := &fleetnetv1beta1.TrafficManagerProfile{
220+
ObjectMeta: objectMetaWithNameValid,
221+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
222+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
223+
IntervalInSeconds: ptr.To(int64(10)),
224+
TimeoutInSeconds: ptr.To(int64(4)),
225+
},
226+
ResourceGroup: "test-resource-group",
227+
},
228+
}
229+
By("expecting denial of CREATE API with invalid timeoutInSeconds")
230+
var err = hubClient.Create(ctx, profile)
231+
Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create API call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8serrors.StatusError{})))
232+
Expect(statusErr.Status().Message).Should(ContainSubstring("spec.monitorConfig.timeoutInSeconds in body should be greater than or equal to 5"))
233+
Expect(statusErr.Status().Message).Should(ContainSubstring("timeoutInSeconds must be between 5 and 9 when intervalInSeconds is 10"))
234+
})
235+
236+
It("should deny creating API with timeoutInSeconds > 9 when intervalInSeconds is 10", func() {
237+
profile := &fleetnetv1beta1.TrafficManagerProfile{
238+
ObjectMeta: objectMetaWithNameValid,
239+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
240+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
241+
IntervalInSeconds: ptr.To(int64(10)),
242+
TimeoutInSeconds: ptr.To(int64(10)),
243+
},
244+
ResourceGroup: "test-resource-group",
245+
},
246+
}
247+
By("expecting denial of CREATE API with invalid timeoutInSeconds")
248+
var err = hubClient.Create(ctx, profile)
249+
Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create API call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8serrors.StatusError{})))
250+
Expect(statusErr.Status().Message).Should(ContainSubstring("spec.monitorConfig: Invalid value: \"object\": timeoutInSeconds must be between 5 and 9 when intervalInSeconds is 10"))
251+
})
252+
253+
It("should deny creating API with timeoutInSeconds < 5 when intervalInSeconds is not defined", func() {
254+
profile := &fleetnetv1beta1.TrafficManagerProfile{
255+
ObjectMeta: objectMetaWithNameValid,
256+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
257+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
258+
TimeoutInSeconds: ptr.To(int64(3)),
259+
},
260+
ResourceGroup: "test-resource-group",
261+
},
262+
}
263+
By("expecting denial of CREATE API with invalid timeoutInSeconds")
264+
var err = hubClient.Create(ctx, profile)
265+
Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create API call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8serrors.StatusError{})))
266+
Expect(statusErr.Status().Message).Should(ContainSubstring("spec.monitorConfig.timeoutInSeconds in body should be greater than or equal to 5"))
267+
Expect(statusErr.Status().Message).Should(ContainSubstring("timeoutInSeconds must be between 5 and 10 when intervalInSeconds is 30"))
268+
})
269+
270+
It("should deny creating API with timeoutInSeconds > 10 when intervalInSeconds is not defined", func() {
271+
profile := &fleetnetv1beta1.TrafficManagerProfile{
272+
ObjectMeta: objectMetaWithNameValid,
273+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
274+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
275+
TimeoutInSeconds: ptr.To(int64(11)),
276+
},
277+
ResourceGroup: "test-resource-group",
278+
},
279+
}
280+
By("expecting denial of CREATE API with invalid timeoutInSeconds")
281+
var err = hubClient.Create(ctx, profile)
282+
Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create API call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8serrors.StatusError{})))
283+
Expect(statusErr.Status().Message).Should(ContainSubstring("spec.monitorConfig.timeoutInSeconds in body should be less than or equal to 10"))
284+
Expect(statusErr.Status().Message).Should(ContainSubstring("timeoutInSeconds must be between 5 and 10 when intervalInSeconds is 30"))
285+
})
182286
})
183287

184288
Context("Test TrafficManagerProfile API validation - valid cases", func() {
@@ -231,6 +335,138 @@ var _ = Describe("Test networking v1alpha1 API validation", func() {
231335
Expect(hubClient.Create(ctx, trafficManagerProfileName)).Should(Succeed(), "failed to create trafficManagerProfile")
232336
Expect(hubClient.Delete(ctx, trafficManagerProfileName)).Should(Succeed(), "failed to delete trafficManagerProfile")
233337
})
338+
339+
It("should allow creating API with valid timeoutInSeconds when intervalInSeconds is 30", func() {
340+
profile := &fleetnetv1beta1.TrafficManagerProfile{
341+
ObjectMeta: objectMetaWithNameValid,
342+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
343+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
344+
IntervalInSeconds: ptr.To(int64(30)),
345+
TimeoutInSeconds: ptr.To(int64(7)),
346+
},
347+
ResourceGroup: "test-resource-group",
348+
},
349+
}
350+
Expect(hubClient.Create(ctx, profile)).Should(Succeed(), "failed to create trafficManagerProfile")
351+
Expect(hubClient.Delete(ctx, profile)).Should(Succeed(), "failed to delete trafficManagerProfile")
352+
})
353+
354+
It("should allow creating API with timeoutInSeconds at lower bound (5) when intervalInSeconds is 30", func() {
355+
profile := &fleetnetv1beta1.TrafficManagerProfile{
356+
ObjectMeta: objectMetaWithNameValid,
357+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
358+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
359+
IntervalInSeconds: ptr.To(int64(30)),
360+
TimeoutInSeconds: ptr.To(int64(5)),
361+
},
362+
ResourceGroup: "test-resource-group",
363+
},
364+
}
365+
Expect(hubClient.Create(ctx, profile)).Should(Succeed(), "failed to create trafficManagerProfile at lower bound")
366+
Expect(hubClient.Delete(ctx, profile)).Should(Succeed(), "failed to delete trafficManagerProfile at lower bound")
367+
})
368+
369+
It("should allow creating API with timeoutInSeconds at upper bound (10) when intervalInSeconds is 30", func() {
370+
profile := &fleetnetv1beta1.TrafficManagerProfile{
371+
ObjectMeta: objectMetaWithNameValid,
372+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
373+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
374+
IntervalInSeconds: ptr.To(int64(30)),
375+
TimeoutInSeconds: ptr.To(int64(10)),
376+
},
377+
ResourceGroup: "test-resource-group",
378+
},
379+
}
380+
Expect(hubClient.Create(ctx, profile)).Should(Succeed(), "failed to create trafficManagerProfile at upper bound")
381+
Expect(hubClient.Delete(ctx, profile)).Should(Succeed(), "failed to delete trafficManagerProfile at upper bound")
382+
})
383+
384+
It("should allow creating API with valid timeoutInSeconds when intervalInSeconds is 10", func() {
385+
profile := &fleetnetv1beta1.TrafficManagerProfile{
386+
ObjectMeta: objectMetaWithNameValid,
387+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
388+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
389+
IntervalInSeconds: ptr.To(int64(10)),
390+
TimeoutInSeconds: ptr.To(int64(8)),
391+
},
392+
ResourceGroup: "test-resource-group",
393+
},
394+
}
395+
Expect(hubClient.Create(ctx, profile)).Should(Succeed(), "failed to create trafficManagerProfile")
396+
Expect(hubClient.Delete(ctx, profile)).Should(Succeed(), "failed to delete trafficManagerProfile")
397+
})
398+
399+
It("should allow creating API with timeoutInSeconds at lower bound (5) when intervalInSeconds is 10", func() {
400+
profile := &fleetnetv1beta1.TrafficManagerProfile{
401+
ObjectMeta: objectMetaWithNameValid,
402+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
403+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
404+
IntervalInSeconds: ptr.To(int64(10)),
405+
TimeoutInSeconds: ptr.To(int64(5)),
406+
},
407+
ResourceGroup: "test-resource-group",
408+
},
409+
}
410+
Expect(hubClient.Create(ctx, profile)).Should(Succeed(), "failed to create trafficManagerProfile at lower bound")
411+
Expect(hubClient.Delete(ctx, profile)).Should(Succeed(), "failed to delete trafficManagerProfile at lower bound")
412+
})
413+
414+
It("should allow creating API with timeoutInSeconds at upper bound (9) when intervalInSeconds is 10", func() {
415+
profile := &fleetnetv1beta1.TrafficManagerProfile{
416+
ObjectMeta: objectMetaWithNameValid,
417+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
418+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
419+
IntervalInSeconds: ptr.To(int64(30)),
420+
TimeoutInSeconds: ptr.To(int64(9)),
421+
},
422+
ResourceGroup: "test-resource-group",
423+
},
424+
}
425+
Expect(hubClient.Create(ctx, profile)).Should(Succeed(), "failed to create trafficManagerProfile at upper bound")
426+
Expect(hubClient.Delete(ctx, profile)).Should(Succeed(), "failed to delete trafficManagerProfile at upper bound")
427+
})
428+
429+
It("should allow creating API with valid timeoutInSeconds when intervalInSeconds is not defined", func() {
430+
profile := &fleetnetv1beta1.TrafficManagerProfile{
431+
ObjectMeta: objectMetaWithNameValid,
432+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
433+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
434+
TimeoutInSeconds: ptr.To(int64(10)),
435+
},
436+
ResourceGroup: "test-resource-group",
437+
},
438+
}
439+
Expect(hubClient.Create(ctx, profile)).Should(Succeed(), "failed to create trafficManagerProfile")
440+
Expect(hubClient.Delete(ctx, profile)).Should(Succeed(), "failed to delete trafficManagerProfile")
441+
})
442+
443+
It("should allow creating API with timeoutInSeconds at lower bound (5) when intervalInSeconds is not defined", func() {
444+
profile := &fleetnetv1beta1.TrafficManagerProfile{
445+
ObjectMeta: objectMetaWithNameValid,
446+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
447+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
448+
TimeoutInSeconds: ptr.To(int64(5)),
449+
},
450+
ResourceGroup: "test-resource-group",
451+
},
452+
}
453+
Expect(hubClient.Create(ctx, profile)).Should(Succeed(), "failed to create trafficManagerProfile at lower bound")
454+
Expect(hubClient.Delete(ctx, profile)).Should(Succeed(), "failed to delete trafficManagerProfile at lower bound")
455+
})
456+
457+
It("should allow creating API with timeoutInSeconds at upper bound (10) when intervalInSeconds is not defined", func() {
458+
profile := &fleetnetv1beta1.TrafficManagerProfile{
459+
ObjectMeta: objectMetaWithNameValid,
460+
Spec: fleetnetv1beta1.TrafficManagerProfileSpec{
461+
MonitorConfig: &fleetnetv1beta1.MonitorConfig{
462+
TimeoutInSeconds: ptr.To(int64(10)),
463+
},
464+
ResourceGroup: "test-resource-group",
465+
},
466+
}
467+
Expect(hubClient.Create(ctx, profile)).Should(Succeed(), "failed to create trafficManagerProfile at upper bound")
468+
Expect(hubClient.Delete(ctx, profile)).Should(Succeed(), "failed to delete trafficManagerProfile at upper bound")
469+
})
234470
})
235471

236472
Context("Test TrafficManagerBackend API validation - invalid cases", func() {

0 commit comments

Comments
 (0)