...

Source file src/github.com/chaos-mesh/chaos-mesh/controllers/schedule/schedule_test.go

Documentation: github.com/chaos-mesh/chaos-mesh/controllers/schedule

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  
    16  package schedule
    17  
    18  import (
    19  	"context"
    20  	"time"
    21  
    22  	. "github.com/onsi/ginkgo"
    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  // These tests use Ginkgo (BDD-style Go testing framework). Refer to
    33  // http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
    34  
    35  var _ = Describe("Schedule", func() {
    36  
    37  	BeforeEach(func() {
    38  		// Add any setup steps that needs to be executed before each test
    39  	})
    40  
    41  	AfterEach(func() {
    42  		// Add any teardown steps that needs to be executed after each test
    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.Poll(time.Second*1, time.Minute*1, func() (ok bool, err error) {
   136  					err = k8sClient.Get(context.TODO(), key, schedule)
   137  					if err != nil {
   138  						return false, err
   139  					}
   140  					return len(schedule.Status.Active) > 0, nil
   141  				})
   142  				Expect(err).ToNot(HaveOccurred())
   143  			}
   144  
   145  			By("Disallow concurrency")
   146  			{
   147  				time.Sleep(5 * time.Second)
   148  				err := k8sClient.Get(context.TODO(), key, schedule)
   149  				Expect(err).ToNot(HaveOccurred())
   150  				Expect(len(schedule.Status.Active)).To(Equal(1))
   151  			}
   152  
   153  			By("deleting the created object")
   154  			{
   155  				Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
   156  				Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
   157  			}
   158  		})
   159  		It("should create concurrent chaos", func() {
   160  			key := types.NamespacedName{
   161  				Name:      "foo2",
   162  				Namespace: "default",
   163  			}
   164  			duration := "100s"
   165  			schedule := &v1alpha1.Schedule{
   166  				ObjectMeta: metav1.ObjectMeta{
   167  					Name:      "foo2",
   168  					Namespace: "default",
   169  				},
   170  				Spec: v1alpha1.ScheduleSpec{
   171  					Schedule: "@every 2s",
   172  					ScheduleItem: v1alpha1.ScheduleItem{
   173  						EmbedChaos: v1alpha1.EmbedChaos{TimeChaos: &v1alpha1.TimeChaosSpec{
   174  							TimeOffset: "100ms",
   175  							ClockIds:   []string{"CLOCK_REALTIME"},
   176  							Duration:   &duration,
   177  							ContainerSelector: v1alpha1.ContainerSelector{
   178  								PodSelector: v1alpha1.PodSelector{
   179  									Mode: v1alpha1.OneMode,
   180  								},
   181  							},
   182  						}},
   183  					},
   184  					ConcurrencyPolicy: v1alpha1.AllowConcurrent,
   185  					HistoryLimit:      2,
   186  					Type:              v1alpha1.ScheduleTypeTimeChaos,
   187  				},
   188  				Status: v1alpha1.ScheduleStatus{
   189  					LastScheduleTime: metav1.NewTime(time.Now()),
   190  				},
   191  			}
   192  
   193  			By("creating a schedule obj")
   194  			{
   195  				Expect(k8sClient.Create(context.TODO(), schedule)).To(Succeed())
   196  			}
   197  
   198  			By("Allowing concurrency and skip deleting running chaos")
   199  			{
   200  				err := wait.Poll(5*time.Second, 1*time.Minute, func() (done bool, err error) {
   201  					err = k8sClient.Get(context.TODO(), key, schedule)
   202  					if err != nil {
   203  						return false, err
   204  					}
   205  					ctrl.Log.Info("active chaos", "size", len(schedule.Status.Active))
   206  					return len(schedule.Status.Active) >= 4, nil
   207  				})
   208  				Expect(err).ToNot(HaveOccurred())
   209  			}
   210  
   211  			By("deleting the created object")
   212  			{
   213  				Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
   214  				Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
   215  			}
   216  		})
   217  		It("should collect garbage", func() {
   218  			key := types.NamespacedName{
   219  				Name:      "foo3",
   220  				Namespace: "default",
   221  			}
   222  			duration := "1s"
   223  			schedule := &v1alpha1.Schedule{
   224  				ObjectMeta: metav1.ObjectMeta{
   225  					Name:      "foo3",
   226  					Namespace: "default",
   227  				},
   228  				Spec: v1alpha1.ScheduleSpec{
   229  					Schedule: "@every 3s",
   230  					ScheduleItem: v1alpha1.ScheduleItem{
   231  						EmbedChaos: v1alpha1.EmbedChaos{TimeChaos: &v1alpha1.TimeChaosSpec{
   232  							TimeOffset: "100ms",
   233  							ClockIds:   []string{"CLOCK_REALTIME"},
   234  							Duration:   &duration,
   235  							ContainerSelector: v1alpha1.ContainerSelector{
   236  								PodSelector: v1alpha1.PodSelector{
   237  									Mode: v1alpha1.OneMode,
   238  								},
   239  							},
   240  						}},
   241  					},
   242  					ConcurrencyPolicy: v1alpha1.AllowConcurrent,
   243  					HistoryLimit:      2,
   244  					Type:              v1alpha1.ScheduleTypeTimeChaos,
   245  				},
   246  				Status: v1alpha1.ScheduleStatus{
   247  					LastScheduleTime: metav1.NewTime(time.Now()),
   248  				},
   249  			}
   250  
   251  			By("creating a schedule obj")
   252  			{
   253  				Expect(k8sClient.Create(context.TODO(), schedule)).To(Succeed())
   254  			}
   255  
   256  			By("deleting outdated chaos")
   257  			{
   258  				time.Sleep(time.Second * 10)
   259  				err := wait.Poll(5*time.Second, 1*time.Minute, func() (done bool, err error) {
   260  					err = k8sClient.Get(context.TODO(), key, schedule)
   261  					if err != nil {
   262  						return false, err
   263  					}
   264  					ctrl.Log.Info("active chaos", "size", len(schedule.Status.Active))
   265  					return len(schedule.Status.Active) == 2, nil
   266  				})
   267  				Expect(err).ToNot(HaveOccurred())
   268  			}
   269  
   270  			By("deleting the created object")
   271  			{
   272  				Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
   273  				Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
   274  			}
   275  		})
   276  	})
   277  
   278  	Context(("Schedule workflow"), func() {
   279  		It(("Should forbid concurrent"), func() {
   280  			key := types.NamespacedName{
   281  				Name:      "foo10",
   282  				Namespace: "default",
   283  			}
   284  			duration := "10000s"
   285  			schedule := &v1alpha1.Schedule{
   286  				ObjectMeta: metav1.ObjectMeta{
   287  					Name:      "foo10",
   288  					Namespace: "default",
   289  				},
   290  				Spec: v1alpha1.ScheduleSpec{
   291  					Schedule: "@every 3s",
   292  					ScheduleItem: v1alpha1.ScheduleItem{
   293  						Workflow: &v1alpha1.WorkflowSpec{
   294  							Entry: "the-entry",
   295  							Templates: []v1alpha1.Template{
   296  								{
   297  									Name:     "the-entry",
   298  									Type:     v1alpha1.TypeSerial,
   299  									Deadline: &duration,
   300  									Children: []string{"hardwork"},
   301  								},
   302  								{
   303  									Name:     "hardwork",
   304  									Type:     v1alpha1.TypeSuspend,
   305  									Deadline: &duration,
   306  									Children: nil,
   307  								},
   308  							},
   309  						},
   310  					},
   311  					ConcurrencyPolicy: v1alpha1.ForbidConcurrent,
   312  					HistoryLimit:      2,
   313  					Type:              v1alpha1.ScheduleTypeWorkflow,
   314  				},
   315  				Status: v1alpha1.ScheduleStatus{
   316  					LastScheduleTime: metav1.NewTime(time.Time{}),
   317  				},
   318  			}
   319  
   320  			By("creating a schedule obj")
   321  			{
   322  				Expect(k8sClient.Create(context.TODO(), schedule)).To(Succeed())
   323  			}
   324  
   325  			By("disallowing concurrent")
   326  			{
   327  				time.Sleep(time.Second * 10)
   328  				err := wait.Poll(5*time.Second, 1*time.Minute, func() (done bool, err error) {
   329  					err = k8sClient.Get(context.TODO(), key, schedule)
   330  					if err != nil {
   331  						return false, err
   332  					}
   333  					ctrl.Log.Info("active chaos", "size", len(schedule.Status.Active))
   334  					return len(schedule.Status.Active) == 1, nil
   335  				})
   336  				Expect(err).ToNot(HaveOccurred())
   337  			}
   338  
   339  			By("deleting the created object")
   340  			{
   341  				Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
   342  				Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
   343  			}
   344  		})
   345  
   346  		It(("Should be garbage collected successfully"), func() {
   347  			key := types.NamespacedName{
   348  				Name:      "foo11",
   349  				Namespace: "default",
   350  			}
   351  			duration := "1s"
   352  			schedule := &v1alpha1.Schedule{
   353  				ObjectMeta: metav1.ObjectMeta{
   354  					Name:      "foo11",
   355  					Namespace: "default",
   356  				},
   357  				Spec: v1alpha1.ScheduleSpec{
   358  					Schedule: "@every 3s",
   359  					ScheduleItem: v1alpha1.ScheduleItem{
   360  						Workflow: &v1alpha1.WorkflowSpec{
   361  							Entry: "the-entry",
   362  							Templates: []v1alpha1.Template{
   363  								{
   364  									Name:     "the-entry",
   365  									Type:     v1alpha1.TypeSerial,
   366  									Deadline: &duration,
   367  									Children: nil,
   368  								},
   369  							},
   370  						},
   371  					},
   372  					ConcurrencyPolicy: v1alpha1.AllowConcurrent,
   373  					HistoryLimit:      2,
   374  					Type:              v1alpha1.ScheduleTypeWorkflow,
   375  				},
   376  				Status: v1alpha1.ScheduleStatus{
   377  					LastScheduleTime: metav1.NewTime(time.Time{}),
   378  				},
   379  			}
   380  
   381  			By("creating a schedule obj")
   382  			{
   383  				Expect(k8sClient.Create(context.TODO(), schedule)).To(Succeed())
   384  			}
   385  
   386  			By("deleting outdated workflow")
   387  			{
   388  				time.Sleep(time.Second * 10)
   389  				err := wait.Poll(5*time.Second, 1*time.Minute, func() (done bool, err error) {
   390  					err = k8sClient.Get(context.TODO(), key, schedule)
   391  					if err != nil {
   392  						return false, err
   393  					}
   394  					ctrl.Log.Info("active chaos", "size", len(schedule.Status.Active))
   395  					return len(schedule.Status.Active) == 2, nil
   396  				})
   397  				Expect(err).ToNot(HaveOccurred())
   398  			}
   399  
   400  			By("deleting the created object")
   401  			{
   402  				Expect(k8sClient.Delete(context.TODO(), schedule)).To(Succeed())
   403  				Expect(k8sClient.Get(context.TODO(), key, schedule)).ToNot(Succeed())
   404  			}
   405  		})
   406  	})
   407  })
   408