github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/cli/command/service/progress/progress_test.go (about) 1 package progress 2 3 import ( 4 "fmt" 5 "strconv" 6 "testing" 7 8 "github.com/docker/docker/api/types/swarm" 9 "github.com/docker/docker/pkg/progress" 10 "gotest.tools/assert" 11 is "gotest.tools/assert/cmp" 12 ) 13 14 type mockProgress struct { 15 p []progress.Progress 16 } 17 18 func (mp *mockProgress) WriteProgress(p progress.Progress) error { 19 mp.p = append(mp.p, p) 20 return nil 21 } 22 23 func (mp *mockProgress) clear() { 24 mp.p = nil 25 } 26 27 type updaterTester struct { 28 t *testing.T 29 updater progressUpdater 30 p *mockProgress 31 service swarm.Service 32 activeNodes map[string]struct{} 33 rollback bool 34 } 35 36 func (u updaterTester) testUpdater(tasks []swarm.Task, expectedConvergence bool, expectedProgress []progress.Progress) { 37 u.p.clear() 38 39 converged, err := u.updater.update(u.service, tasks, u.activeNodes, u.rollback) 40 assert.Check(u.t, err) 41 assert.Check(u.t, is.Equal(expectedConvergence, converged)) 42 assert.Check(u.t, is.DeepEqual(expectedProgress, u.p.p)) 43 } 44 45 func TestReplicatedProgressUpdaterOneReplica(t *testing.T) { 46 replicas := uint64(1) 47 48 service := swarm.Service{ 49 Spec: swarm.ServiceSpec{ 50 Mode: swarm.ServiceMode{ 51 Replicated: &swarm.ReplicatedService{ 52 Replicas: &replicas, 53 }, 54 }, 55 }, 56 } 57 58 p := &mockProgress{} 59 updaterTester := updaterTester{ 60 t: t, 61 updater: &replicatedProgressUpdater{ 62 progressOut: p, 63 }, 64 p: p, 65 activeNodes: map[string]struct{}{"a": {}, "b": {}}, 66 service: service, 67 } 68 69 tasks := []swarm.Task{} 70 71 updaterTester.testUpdater(tasks, false, 72 []progress.Progress{ 73 {ID: "overall progress", Action: "0 out of 1 tasks"}, 74 {ID: "1/1", Action: " "}, 75 {ID: "overall progress", Action: "0 out of 1 tasks"}, 76 }) 77 78 // Task with DesiredState beyond Running is ignored 79 tasks = append(tasks, 80 swarm.Task{ID: "1", 81 NodeID: "a", 82 DesiredState: swarm.TaskStateShutdown, 83 Status: swarm.TaskStatus{State: swarm.TaskStateNew}, 84 }) 85 updaterTester.testUpdater(tasks, false, 86 []progress.Progress{ 87 {ID: "overall progress", Action: "0 out of 1 tasks"}, 88 }) 89 90 // Task with valid DesiredState and State updates progress bar 91 tasks[0].DesiredState = swarm.TaskStateRunning 92 updaterTester.testUpdater(tasks, false, 93 []progress.Progress{ 94 {ID: "1/1", Action: "new ", Current: 1, Total: 9, HideCounts: true}, 95 {ID: "overall progress", Action: "0 out of 1 tasks"}, 96 }) 97 98 // If the task exposes an error, we should show that instead of the 99 // progress bar. 100 tasks[0].Status.Err = "something is wrong" 101 updaterTester.testUpdater(tasks, false, 102 []progress.Progress{ 103 {ID: "1/1", Action: "something is wrong"}, 104 {ID: "overall progress", Action: "0 out of 1 tasks"}, 105 }) 106 107 // When the task reaches running, update should return true 108 tasks[0].Status.Err = "" 109 tasks[0].Status.State = swarm.TaskStateRunning 110 updaterTester.testUpdater(tasks, true, 111 []progress.Progress{ 112 {ID: "1/1", Action: "running ", Current: 9, Total: 9, HideCounts: true}, 113 {ID: "overall progress", Action: "1 out of 1 tasks"}, 114 }) 115 116 // If the task fails, update should return false again 117 tasks[0].Status.Err = "task failed" 118 tasks[0].Status.State = swarm.TaskStateFailed 119 updaterTester.testUpdater(tasks, false, 120 []progress.Progress{ 121 {ID: "1/1", Action: "task failed"}, 122 {ID: "overall progress", Action: "0 out of 1 tasks"}, 123 }) 124 125 // If the task is restarted, progress output should be shown for the 126 // replacement task, not the old task. 127 tasks[0].DesiredState = swarm.TaskStateShutdown 128 tasks = append(tasks, 129 swarm.Task{ID: "2", 130 NodeID: "b", 131 DesiredState: swarm.TaskStateRunning, 132 Status: swarm.TaskStatus{State: swarm.TaskStateRunning}, 133 }) 134 updaterTester.testUpdater(tasks, true, 135 []progress.Progress{ 136 {ID: "1/1", Action: "running ", Current: 9, Total: 9, HideCounts: true}, 137 {ID: "overall progress", Action: "1 out of 1 tasks"}, 138 }) 139 140 // Add a new task while the current one is still running, to simulate 141 // "start-then-stop" updates. 142 tasks = append(tasks, 143 swarm.Task{ID: "3", 144 NodeID: "b", 145 DesiredState: swarm.TaskStateRunning, 146 Status: swarm.TaskStatus{State: swarm.TaskStatePreparing}, 147 }) 148 updaterTester.testUpdater(tasks, false, 149 []progress.Progress{ 150 {ID: "1/1", Action: "preparing", Current: 6, Total: 9, HideCounts: true}, 151 {ID: "overall progress", Action: "0 out of 1 tasks"}, 152 }) 153 } 154 155 func TestReplicatedProgressUpdaterManyReplicas(t *testing.T) { 156 replicas := uint64(50) 157 158 service := swarm.Service{ 159 Spec: swarm.ServiceSpec{ 160 Mode: swarm.ServiceMode{ 161 Replicated: &swarm.ReplicatedService{ 162 Replicas: &replicas, 163 }, 164 }, 165 }, 166 } 167 168 p := &mockProgress{} 169 updaterTester := updaterTester{ 170 t: t, 171 updater: &replicatedProgressUpdater{ 172 progressOut: p, 173 }, 174 p: p, 175 activeNodes: map[string]struct{}{"a": {}, "b": {}}, 176 service: service, 177 } 178 179 tasks := []swarm.Task{} 180 181 // No per-task progress bars because there are too many replicas 182 updaterTester.testUpdater(tasks, false, 183 []progress.Progress{ 184 {ID: "overall progress", Action: fmt.Sprintf("0 out of %d tasks", replicas)}, 185 {ID: "overall progress", Action: fmt.Sprintf("0 out of %d tasks", replicas)}, 186 }) 187 188 for i := 0; i != int(replicas); i++ { 189 tasks = append(tasks, 190 swarm.Task{ 191 ID: strconv.Itoa(i), 192 Slot: i + 1, 193 NodeID: "a", 194 DesiredState: swarm.TaskStateRunning, 195 Status: swarm.TaskStatus{State: swarm.TaskStateNew}, 196 }) 197 198 if i%2 == 1 { 199 tasks[i].NodeID = "b" 200 } 201 updaterTester.testUpdater(tasks, false, 202 []progress.Progress{ 203 {ID: "overall progress", Action: fmt.Sprintf("%d out of %d tasks", i, replicas)}, 204 }) 205 206 tasks[i].Status.State = swarm.TaskStateRunning 207 updaterTester.testUpdater(tasks, uint64(i) == replicas-1, 208 []progress.Progress{ 209 {ID: "overall progress", Action: fmt.Sprintf("%d out of %d tasks", i+1, replicas)}, 210 }) 211 } 212 } 213 214 func TestGlobalProgressUpdaterOneNode(t *testing.T) { 215 service := swarm.Service{ 216 Spec: swarm.ServiceSpec{ 217 Mode: swarm.ServiceMode{ 218 Global: &swarm.GlobalService{}, 219 }, 220 }, 221 } 222 223 p := &mockProgress{} 224 updaterTester := updaterTester{ 225 t: t, 226 updater: &globalProgressUpdater{ 227 progressOut: p, 228 }, 229 p: p, 230 activeNodes: map[string]struct{}{"a": {}, "b": {}}, 231 service: service, 232 } 233 234 tasks := []swarm.Task{} 235 236 updaterTester.testUpdater(tasks, false, 237 []progress.Progress{ 238 {ID: "overall progress", Action: "waiting for new tasks"}, 239 }) 240 241 // Task with DesiredState beyond Running is ignored 242 tasks = append(tasks, 243 swarm.Task{ID: "1", 244 NodeID: "a", 245 DesiredState: swarm.TaskStateShutdown, 246 Status: swarm.TaskStatus{State: swarm.TaskStateNew}, 247 }) 248 updaterTester.testUpdater(tasks, false, 249 []progress.Progress{ 250 {ID: "overall progress", Action: "0 out of 1 tasks"}, 251 {ID: "overall progress", Action: "0 out of 1 tasks"}, 252 }) 253 254 // Task with valid DesiredState and State updates progress bar 255 tasks[0].DesiredState = swarm.TaskStateRunning 256 updaterTester.testUpdater(tasks, false, 257 []progress.Progress{ 258 {ID: "a", Action: "new ", Current: 1, Total: 9, HideCounts: true}, 259 {ID: "overall progress", Action: "0 out of 1 tasks"}, 260 }) 261 262 // If the task exposes an error, we should show that instead of the 263 // progress bar. 264 tasks[0].Status.Err = "something is wrong" 265 updaterTester.testUpdater(tasks, false, 266 []progress.Progress{ 267 {ID: "a", Action: "something is wrong"}, 268 {ID: "overall progress", Action: "0 out of 1 tasks"}, 269 }) 270 271 // When the task reaches running, update should return true 272 tasks[0].Status.Err = "" 273 tasks[0].Status.State = swarm.TaskStateRunning 274 updaterTester.testUpdater(tasks, true, 275 []progress.Progress{ 276 {ID: "a", Action: "running ", Current: 9, Total: 9, HideCounts: true}, 277 {ID: "overall progress", Action: "1 out of 1 tasks"}, 278 }) 279 280 // If the task fails, update should return false again 281 tasks[0].Status.Err = "task failed" 282 tasks[0].Status.State = swarm.TaskStateFailed 283 updaterTester.testUpdater(tasks, false, 284 []progress.Progress{ 285 {ID: "a", Action: "task failed"}, 286 {ID: "overall progress", Action: "0 out of 1 tasks"}, 287 }) 288 289 // If the task is restarted, progress output should be shown for the 290 // replacement task, not the old task. 291 tasks[0].DesiredState = swarm.TaskStateShutdown 292 tasks = append(tasks, 293 swarm.Task{ID: "2", 294 NodeID: "a", 295 DesiredState: swarm.TaskStateRunning, 296 Status: swarm.TaskStatus{State: swarm.TaskStateRunning}, 297 }) 298 updaterTester.testUpdater(tasks, true, 299 []progress.Progress{ 300 {ID: "a", Action: "running ", Current: 9, Total: 9, HideCounts: true}, 301 {ID: "overall progress", Action: "1 out of 1 tasks"}, 302 }) 303 304 // Add a new task while the current one is still running, to simulate 305 // "start-then-stop" updates. 306 tasks = append(tasks, 307 swarm.Task{ID: "3", 308 NodeID: "a", 309 DesiredState: swarm.TaskStateRunning, 310 Status: swarm.TaskStatus{State: swarm.TaskStatePreparing}, 311 }) 312 updaterTester.testUpdater(tasks, false, 313 []progress.Progress{ 314 {ID: "a", Action: "preparing", Current: 6, Total: 9, HideCounts: true}, 315 {ID: "overall progress", Action: "0 out of 1 tasks"}, 316 }) 317 } 318 319 func TestGlobalProgressUpdaterManyNodes(t *testing.T) { 320 nodes := 50 321 322 service := swarm.Service{ 323 Spec: swarm.ServiceSpec{ 324 Mode: swarm.ServiceMode{ 325 Global: &swarm.GlobalService{}, 326 }, 327 }, 328 } 329 330 p := &mockProgress{} 331 updaterTester := updaterTester{ 332 t: t, 333 updater: &globalProgressUpdater{ 334 progressOut: p, 335 }, 336 p: p, 337 activeNodes: map[string]struct{}{}, 338 service: service, 339 } 340 341 for i := 0; i != nodes; i++ { 342 updaterTester.activeNodes[strconv.Itoa(i)] = struct{}{} 343 } 344 345 tasks := []swarm.Task{} 346 347 updaterTester.testUpdater(tasks, false, 348 []progress.Progress{ 349 {ID: "overall progress", Action: "waiting for new tasks"}, 350 }) 351 352 for i := 0; i != nodes; i++ { 353 tasks = append(tasks, 354 swarm.Task{ 355 ID: "task" + strconv.Itoa(i), 356 NodeID: strconv.Itoa(i), 357 DesiredState: swarm.TaskStateRunning, 358 Status: swarm.TaskStatus{State: swarm.TaskStateNew}, 359 }) 360 } 361 362 updaterTester.testUpdater(tasks, false, 363 []progress.Progress{ 364 {ID: "overall progress", Action: fmt.Sprintf("0 out of %d tasks", nodes)}, 365 {ID: "overall progress", Action: fmt.Sprintf("0 out of %d tasks", nodes)}, 366 }) 367 368 for i := 0; i != nodes; i++ { 369 tasks[i].Status.State = swarm.TaskStateRunning 370 updaterTester.testUpdater(tasks, i == nodes-1, 371 []progress.Progress{ 372 {ID: "overall progress", Action: fmt.Sprintf("%d out of %d tasks", i+1, nodes)}, 373 }) 374 } 375 }