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  }