github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/orchestrator/jobs/orchestrator_restart_test.go (about) 1 package jobs 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 . "github.com/onsi/ginkgo" 9 . "github.com/onsi/gomega" 10 11 "github.com/docker/go-events" 12 gogotypes "github.com/gogo/protobuf/types" 13 14 "github.com/docker/swarmkit/api" 15 "github.com/docker/swarmkit/manager/state/store" 16 ) 17 18 // passEventsUntil is a helper method that calls handleEvent on all events from 19 // the orchestrator's watchChan until the provided closure returns True. 20 func passEventsUntil(o *Orchestrator, check func(event events.Event) bool) { 21 deadline := time.NewTimer(10 * time.Second) 22 defer deadline.Stop() 23 for { 24 select { 25 case event := <-o.watchChan: 26 // regardless of whether this is the desired event, 27 // pass it down to the orchestrator to handle. 28 o.handleEvent(context.Background(), event) 29 if check(event) { 30 return 31 } 32 // on the off chance that we never get an event, bail out of the 33 // loop after 10 seconds. 34 case <-deadline.C: 35 return 36 } 37 } 38 } 39 40 var _ = Describe("Jobs RestartSupervisor Integration", func() { 41 var ( 42 s *store.MemoryStore 43 o *Orchestrator 44 45 service *api.Service 46 ) 47 48 BeforeEach(func() { 49 s = store.NewMemoryStore(nil) 50 Expect(s).ToNot(BeNil()) 51 52 service = &api.Service{ 53 ID: "norestartservice", 54 Spec: api.ServiceSpec{ 55 Annotations: api.Annotations{ 56 Name: "norestartservice", 57 }, 58 Mode: &api.ServiceSpec_ReplicatedJob{ 59 ReplicatedJob: &api.ReplicatedJob{ 60 MaxConcurrent: uint64(1), 61 TotalCompletions: uint64(1), 62 }, 63 }, 64 Task: api.TaskSpec{}, 65 }, 66 JobStatus: &api.JobStatus{ 67 JobIteration: api.Version{ 68 Index: 0, 69 }, 70 }, 71 } 72 73 o = NewOrchestrator(s) 74 }) 75 76 JustBeforeEach(func() { 77 o.init(context.Background()) 78 }) 79 80 AfterEach(func() { 81 o.watchCancel() 82 }) 83 84 It("should not restart tasks if the Condition is RestartOnNone", func() { 85 service.Spec.Task.Restart = &api.RestartPolicy{ 86 Condition: api.RestartOnNone, 87 } 88 err := s.Update(func(tx store.Tx) error { 89 return store.CreateService(tx, service) 90 }) 91 Expect(err).ToNot(HaveOccurred()) 92 93 // read out from the events channel until we send down the 94 // EventCreateService for the service we just created. By doing this 95 // manually and in a blocking fashion like this, we avoid relying on 96 // the go scheduler to pick up this routine. 97 passEventsUntil(o, serviceCreated(service)) 98 99 // now, after having handled the creation event, we should have created 100 // a Task 101 found := false 102 s.View(func(tx store.ReadTx) { 103 var tasks []*api.Task 104 tasks, err = store.FindTasks(tx, store.ByServiceID(service.ID)) 105 if err != nil { 106 return 107 } 108 109 // the task is found if it exists, its job iteration is 0, its 110 // state is New, and it's DesiredState is Completed. 111 found = len(tasks) == 1 && 112 tasks[0].JobIteration != nil && 113 tasks[0].JobIteration.Index == 0 && 114 tasks[0].Status.State == api.TaskStateNew && 115 tasks[0].DesiredState == api.TaskStateCompleted 116 }) 117 Expect(err).ToNot(HaveOccurred()) 118 Expect(found).To(BeTrue()) 119 120 // now, fail the task 121 err = s.Update(func(tx store.Tx) error { 122 tasks, err := store.FindTasks(tx, store.ByServiceID(service.ID)) 123 if err != nil { 124 return err 125 } 126 if len(tasks) != 1 { 127 return fmt.Errorf("there should only be one task at this stage, but there are %v", len(tasks)) 128 } 129 tasks[0].Status.State = api.TaskStateFailed 130 return store.UpdateTask(tx, tasks[0]) 131 }) 132 Expect(err).ToNot(HaveOccurred()) 133 134 // pass events again, this time until the TaskUpdate event with the 135 // failed state goes through 136 passEventsUntil(o, func(event events.Event) bool { 137 updateEv, ok := event.(api.EventUpdateTask) 138 return ok && updateEv.Task.Status.State == api.TaskStateFailed 139 }) 140 141 // now, having processed that event, we should verify that no new Task 142 // has been created. 143 count := 0 144 s.View(func(tx store.ReadTx) { 145 var tasks []*api.Task 146 tasks, err = store.FindTasks(tx, store.ByServiceID(service.ID)) 147 count = len(tasks) 148 }) 149 Expect(count).To(Equal(1)) 150 }) 151 152 It("should only restart tasks MaxAttempt times", func() { 153 service.Spec.Task.Restart = &api.RestartPolicy{ 154 Condition: api.RestartOnFailure, 155 MaxAttempts: 3, 156 // set a low but non-zero delay duration, so we avoid default 157 // duration, which may be long. 158 Delay: gogotypes.DurationProto(100 * time.Millisecond), 159 } 160 err := s.Update(func(tx store.Tx) error { 161 return store.CreateService(tx, service) 162 }) 163 Expect(err).ToNot(HaveOccurred()) 164 165 passEventsUntil(o, serviceCreated(service)) 166 167 // first, we will check that the previous iteration successfully 168 // created the task. if so, we fail it. then, we wait until the task 169 // has successfully fully started. this means we fail 3 tasks in total. 170 // failing the fourth task happens after this loop, and is the 171 // iteration that should not create a new task. 172 for i := 0; i < 3; i++ { 173 err = s.Update(func(tx store.Tx) error { 174 tasks, err := store.FindTasks(tx, store.ByTaskState(api.TaskStateNew)) 175 if err != nil { 176 return err 177 } 178 if len(tasks) < 1 { 179 return fmt.Errorf("could not find new task") 180 } 181 if len(tasks) > 1 { 182 return fmt.Errorf("too many new tasks, there are %v", len(tasks)) 183 } 184 tasks[0].Status.State = api.TaskStateFailed 185 return store.UpdateTask(tx, tasks[0]) 186 }) 187 Expect(err).ToNot(HaveOccurred()) 188 189 // first, make sure the task fail event is handled 190 passEventsUntil(o, taskFailed) 191 192 // now wait for the new task to be created, and for it to move past 193 // desired state READY. the task delay means this part is async, 194 // but we have a long timeout and a short delay, so the scheduler 195 // shouldn't mess this up. 196 passEventsUntil(o, func(event events.Event) bool { 197 updated, ok := event.(api.EventUpdateTask) 198 return ok && 199 updated.Task.DesiredState == api.TaskStateCompleted && 200 updated.OldTask.DesiredState == api.TaskStateReady 201 }) 202 } 203 204 err = s.Update(func(tx store.Tx) error { 205 tasks, err := store.FindTasks(tx, store.ByTaskState(api.TaskStateNew)) 206 if err != nil { 207 return err 208 } 209 if len(tasks) < 1 { 210 return fmt.Errorf("could not find new task") 211 } 212 if len(tasks) > 1 { 213 return fmt.Errorf("too many new tasks, there are %v", len(tasks)) 214 } 215 tasks[0].Status.State = api.TaskStateFailed 216 return store.UpdateTask(tx, tasks[0]) 217 }) 218 219 passEventsUntil(o, taskFailed) 220 221 // now check that no new task has been created 222 var tasks []*api.Task 223 s.View(func(tx store.ReadTx) { 224 tasks, err = store.FindTasks(tx, store.All) 225 }) 226 Expect(err).ToNot(HaveOccurred()) 227 Expect(tasks).To(HaveLen(4)) 228 229 for _, task := range tasks { 230 Expect(task.Status.State).To(Equal(api.TaskStateFailed)) 231 } 232 }) 233 }) 234 235 func serviceCreated(service *api.Service) func(events.Event) bool { 236 return func(event events.Event) bool { 237 create, ok := event.(api.EventCreateService) 238 return ok && create.Service.ID == service.ID 239 } 240 } 241 242 func taskFailed(event events.Event) bool { 243 updated, ok := event.(api.EventUpdateTask) 244 return ok && 245 updated.Task.Status.State == api.TaskStateFailed && 246 updated.OldTask.Status.State == api.TaskStateNew 247 }