1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package schedule
17
18 import (
19 "context"
20 "time"
21
22 . "github.com/onsi/ginkgo/v2"
23 . "github.com/onsi/gomega"
24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25 "k8s.io/apimachinery/pkg/types"
26 "k8s.io/apimachinery/pkg/util/wait"
27 ctrl "sigs.k8s.io/controller-runtime"
28
29 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
30 )
31
32
33
34
35 var _ = Describe("Schedule", func() {
36
37 BeforeEach(func() {
38
39 })
40
41 AfterEach(func() {
42
43 })
44
45 Context(("Schedule basic"), func() {
46 It(("Should be created and deleted successfully"), func() {
47 key := types.NamespacedName{
48 Name: "foo0",
49 Namespace: "default",
50 }
51 duration := "100m"
52 schedule := &v1alpha1.Schedule{
53 ObjectMeta: metav1.ObjectMeta{
54 Name: "foo0",
55 Namespace: "default",
56 },
57 Spec: v1alpha1.ScheduleSpec{
58 Schedule: "@every 10s",
59 ScheduleItem: v1alpha1.ScheduleItem{
60 EmbedChaos: v1alpha1.EmbedChaos{TimeChaos: &v1alpha1.TimeChaosSpec{
61 TimeOffset: "100ms",
62 ClockIds: []string{"CLOCK_REALTIME"},
63 Duration: &duration,
64 ContainerSelector: v1alpha1.ContainerSelector{
65 PodSelector: v1alpha1.PodSelector{
66 Mode: v1alpha1.OneMode,
67 },
68 },
69 }},
70 },
71 ConcurrencyPolicy: v1alpha1.ForbidConcurrent,
72 HistoryLimit: 5,
73 Type: v1alpha1.ScheduleTypeTimeChaos,
74 },
75 Status: v1alpha1.ScheduleStatus{
76 LastScheduleTime: metav1.NewTime(time.Time{}),
77 },
78 }
79
80 By("creating an API obj")
81 Expect(k8sClient.Create(context.TODO(), schedule)).To(Succeed())
82
83 fetched := &v1alpha1.Schedule{}
84 Expect(k8sClient.Get(context.TODO(), key, fetched)).To(Succeed())
85 Expect(fetched).To(Equal(schedule))
86
87 By("deleting the created object")
88 Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
89 Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
90 })
91 })
92
93 Context("Schedule cron", func() {
94 It("should create non-concurrent chaos", func() {
95 key := types.NamespacedName{
96 Name: "foo1",
97 Namespace: "default",
98 }
99 duration := "100s"
100 schedule := &v1alpha1.Schedule{
101 ObjectMeta: metav1.ObjectMeta{
102 Name: "foo1",
103 Namespace: "default",
104 },
105 Spec: v1alpha1.ScheduleSpec{
106 Schedule: "@every 1s",
107 ScheduleItem: v1alpha1.ScheduleItem{
108 EmbedChaos: v1alpha1.EmbedChaos{TimeChaos: &v1alpha1.TimeChaosSpec{
109 TimeOffset: "100ms",
110 ClockIds: []string{"CLOCK_REALTIME"},
111 Duration: &duration,
112 ContainerSelector: v1alpha1.ContainerSelector{
113 PodSelector: v1alpha1.PodSelector{
114 Mode: v1alpha1.OneMode,
115 },
116 },
117 }},
118 },
119 ConcurrencyPolicy: v1alpha1.ForbidConcurrent,
120 HistoryLimit: 2,
121 Type: v1alpha1.ScheduleTypeTimeChaos,
122 },
123 Status: v1alpha1.ScheduleStatus{
124 LastScheduleTime: metav1.NewTime(time.Now()),
125 },
126 }
127
128 By("creating a schedule obj")
129 {
130 Expect(k8sClient.Create(context.TODO(), schedule)).To(Succeed())
131 }
132
133 By("Reconciling the created schedule obj")
134 {
135 err := wait.PollUntilContextTimeout(context.TODO(), time.Second, time.Minute, true,
136 func(ctx context.Context) (ok bool, err error) {
137 err = k8sClient.Get(ctx, key, schedule)
138 if err != nil {
139 return false, err
140 }
141 return len(schedule.Status.Active) > 0, nil
142 })
143 Expect(err).ToNot(HaveOccurred())
144 }
145
146 By("Disallow concurrency")
147 {
148 time.Sleep(5 * time.Second)
149 err := k8sClient.Get(context.TODO(), key, schedule)
150 Expect(err).ToNot(HaveOccurred())
151 Expect(len(schedule.Status.Active)).To(Equal(1))
152 }
153
154 By("deleting the created object")
155 {
156 Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
157 Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
158 }
159 })
160 It("should create concurrent chaos", func() {
161 key := types.NamespacedName{
162 Name: "foo2",
163 Namespace: "default",
164 }
165 duration := "100s"
166 schedule := &v1alpha1.Schedule{
167 ObjectMeta: metav1.ObjectMeta{
168 Name: "foo2",
169 Namespace: "default",
170 },
171 Spec: v1alpha1.ScheduleSpec{
172 Schedule: "@every 2s",
173 ScheduleItem: v1alpha1.ScheduleItem{
174 EmbedChaos: v1alpha1.EmbedChaos{TimeChaos: &v1alpha1.TimeChaosSpec{
175 TimeOffset: "100ms",
176 ClockIds: []string{"CLOCK_REALTIME"},
177 Duration: &duration,
178 ContainerSelector: v1alpha1.ContainerSelector{
179 PodSelector: v1alpha1.PodSelector{
180 Mode: v1alpha1.OneMode,
181 },
182 },
183 }},
184 },
185 ConcurrencyPolicy: v1alpha1.AllowConcurrent,
186 HistoryLimit: 2,
187 Type: v1alpha1.ScheduleTypeTimeChaos,
188 },
189 Status: v1alpha1.ScheduleStatus{
190 LastScheduleTime: metav1.NewTime(time.Now()),
191 },
192 }
193
194 By("creating a schedule obj")
195 {
196 Expect(k8sClient.Create(context.TODO(), schedule)).To(Succeed())
197 }
198
199 By("Allowing concurrency and skip deleting running chaos")
200 {
201 err := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 1*time.Minute, true,
202 func(ctx context.Context) (done bool, err error) {
203 err = k8sClient.Get(ctx, key, schedule)
204 if err != nil {
205 return false, err
206 }
207 ctrl.Log.Info("active chaos", "size", len(schedule.Status.Active))
208 return len(schedule.Status.Active) >= 4, nil
209 })
210 Expect(err).ToNot(HaveOccurred())
211 }
212
213 By("deleting the created object")
214 {
215 Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
216 Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
217 }
218 })
219 It("should collect garbage", func() {
220 key := types.NamespacedName{
221 Name: "foo3",
222 Namespace: "default",
223 }
224 duration := "1s"
225 schedule := &v1alpha1.Schedule{
226 ObjectMeta: metav1.ObjectMeta{
227 Name: "foo3",
228 Namespace: "default",
229 },
230 Spec: v1alpha1.ScheduleSpec{
231 Schedule: "@every 3s",
232 ScheduleItem: v1alpha1.ScheduleItem{
233 EmbedChaos: v1alpha1.EmbedChaos{TimeChaos: &v1alpha1.TimeChaosSpec{
234 TimeOffset: "100ms",
235 ClockIds: []string{"CLOCK_REALTIME"},
236 Duration: &duration,
237 ContainerSelector: v1alpha1.ContainerSelector{
238 PodSelector: v1alpha1.PodSelector{
239 Mode: v1alpha1.OneMode,
240 },
241 },
242 }},
243 },
244 ConcurrencyPolicy: v1alpha1.AllowConcurrent,
245 HistoryLimit: 2,
246 Type: v1alpha1.ScheduleTypeTimeChaos,
247 },
248 Status: v1alpha1.ScheduleStatus{
249 LastScheduleTime: metav1.NewTime(time.Now()),
250 },
251 }
252
253 By("creating a schedule obj")
254 {
255 Expect(k8sClient.Create(context.TODO(), schedule)).To(Succeed())
256 }
257
258 By("deleting outdated chaos")
259 {
260 time.Sleep(time.Second * 10)
261 err := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 1*time.Minute, true,
262 func(ctx context.Context) (done bool, err error) {
263 err = k8sClient.Get(ctx, key, schedule)
264 if err != nil {
265 return false, err
266 }
267 ctrl.Log.Info("active chaos", "size", len(schedule.Status.Active))
268 return len(schedule.Status.Active) == 2, nil
269 })
270 Expect(err).ToNot(HaveOccurred())
271 }
272
273 By("deleting the created object")
274 {
275 Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
276 Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
277 }
278 })
279 })
280
281 Context(("Schedule workflow"), func() {
282 It(("Should forbid concurrent"), func() {
283 key := types.NamespacedName{
284 Name: "foo10",
285 Namespace: "default",
286 }
287 duration := "10000s"
288 schedule := &v1alpha1.Schedule{
289 ObjectMeta: metav1.ObjectMeta{
290 Name: "foo10",
291 Namespace: "default",
292 },
293 Spec: v1alpha1.ScheduleSpec{
294 Schedule: "@every 3s",
295 ScheduleItem: v1alpha1.ScheduleItem{
296 Workflow: &v1alpha1.WorkflowSpec{
297 Entry: "the-entry",
298 Templates: []v1alpha1.Template{
299 {
300 Name: "the-entry",
301 Type: v1alpha1.TypeSerial,
302 Deadline: &duration,
303 Children: []string{"hardwork"},
304 },
305 {
306 Name: "hardwork",
307 Type: v1alpha1.TypeSuspend,
308 Deadline: &duration,
309 Children: nil,
310 },
311 },
312 },
313 },
314 ConcurrencyPolicy: v1alpha1.ForbidConcurrent,
315 HistoryLimit: 2,
316 Type: v1alpha1.ScheduleTypeWorkflow,
317 },
318 Status: v1alpha1.ScheduleStatus{
319 LastScheduleTime: metav1.NewTime(time.Time{}),
320 },
321 }
322
323 By("creating a schedule obj")
324 {
325 Expect(k8sClient.Create(context.TODO(), schedule)).To(Succeed())
326 }
327
328 By("disallowing concurrent")
329 {
330 time.Sleep(time.Second * 10)
331 err := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 1*time.Minute, true,
332 func(ctx context.Context) (done bool, err error) {
333 err = k8sClient.Get(ctx, key, schedule)
334 if err != nil {
335 return false, err
336 }
337 ctrl.Log.Info("active chaos", "size", len(schedule.Status.Active))
338 return len(schedule.Status.Active) == 1, nil
339 })
340 Expect(err).ToNot(HaveOccurred())
341 }
342
343 By("deleting the created object")
344 {
345 Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
346 Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
347 }
348 })
349
350 It(("Should be garbage collected successfully"), func() {
351 key := types.NamespacedName{
352 Name: "foo11",
353 Namespace: "default",
354 }
355 duration := "1s"
356 schedule := &v1alpha1.Schedule{
357 ObjectMeta: metav1.ObjectMeta{
358 Name: "foo11",
359 Namespace: "default",
360 },
361 Spec: v1alpha1.ScheduleSpec{
362 Schedule: "@every 3s",
363 ScheduleItem: v1alpha1.ScheduleItem{
364 Workflow: &v1alpha1.WorkflowSpec{
365 Entry: "the-entry",
366 Templates: []v1alpha1.Template{
367 {
368 Name: "the-entry",
369 Type: v1alpha1.TypeSerial,
370 Deadline: &duration,
371 Children: nil,
372 },
373 },
374 },
375 },
376 ConcurrencyPolicy: v1alpha1.AllowConcurrent,
377 HistoryLimit: 2,
378 Type: v1alpha1.ScheduleTypeWorkflow,
379 },
380 Status: v1alpha1.ScheduleStatus{
381 LastScheduleTime: metav1.NewTime(time.Time{}),
382 },
383 }
384
385 By("creating a schedule obj")
386 {
387 Expect(k8sClient.Create(context.TODO(), schedule)).To(Succeed())
388 }
389
390 By("deleting outdated workflow")
391 {
392 time.Sleep(time.Second * 10)
393 err := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 1*time.Minute, true,
394 func(ctx context.Context) (done bool, err error) {
395 err = k8sClient.Get(ctx, key, schedule)
396 if err != nil {
397 return false, err
398 }
399 ctrl.Log.Info("active chaos", "size", len(schedule.Status.Active))
400 return len(schedule.Status.Active) == 2, nil
401 })
402 Expect(err).ToNot(HaveOccurred())
403 }
404
405 By("deleting the created object")
406 {
407 Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
408 Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
409 }
410 })
411 })
412 })
413