github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/orchestrator/replicated/drain_test.go (about)

     1  package replicated
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/docker/swarmkit/api"
     8  	"github.com/docker/swarmkit/manager/orchestrator/testutils"
     9  	"github.com/docker/swarmkit/manager/state"
    10  	"github.com/docker/swarmkit/manager/state/store"
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  func TestDrain(t *testing.T) {
    15  	ctx := context.Background()
    16  	initialService := &api.Service{
    17  		ID: "id1",
    18  		Spec: api.ServiceSpec{
    19  			Annotations: api.Annotations{
    20  				Name: "name1",
    21  			},
    22  			Task: api.TaskSpec{
    23  				Runtime: &api.TaskSpec_Container{
    24  					Container: &api.ContainerSpec{},
    25  				},
    26  				Restart: &api.RestartPolicy{
    27  					Condition: api.RestartOnNone,
    28  				},
    29  			},
    30  			Mode: &api.ServiceSpec_Replicated{
    31  				Replicated: &api.ReplicatedService{
    32  					Replicas: 6,
    33  				},
    34  			},
    35  		},
    36  	}
    37  	initialNodeSet := []*api.Node{
    38  		{
    39  			ID: "id1",
    40  			Spec: api.NodeSpec{
    41  				Annotations: api.Annotations{
    42  					Name: "name1",
    43  				},
    44  				Availability: api.NodeAvailabilityActive,
    45  			},
    46  			Status: api.NodeStatus{
    47  				State: api.NodeStatus_READY,
    48  			},
    49  		},
    50  		{
    51  			ID: "id2",
    52  			Spec: api.NodeSpec{
    53  				Annotations: api.Annotations{
    54  					Name: "name2",
    55  				},
    56  				Availability: api.NodeAvailabilityActive,
    57  			},
    58  			Status: api.NodeStatus{
    59  				State: api.NodeStatus_DOWN,
    60  			},
    61  		},
    62  		// We should NOT kick out tasks on UNKNOWN nodes.
    63  		{
    64  			ID: "id3",
    65  			Spec: api.NodeSpec{
    66  				Annotations: api.Annotations{
    67  					Name: "name3",
    68  				},
    69  				Availability: api.NodeAvailabilityActive,
    70  			},
    71  			Status: api.NodeStatus{
    72  				State: api.NodeStatus_UNKNOWN,
    73  			},
    74  		},
    75  		{
    76  			ID: "id4",
    77  			Spec: api.NodeSpec{
    78  				Annotations: api.Annotations{
    79  					Name: "name4",
    80  				},
    81  				Availability: api.NodeAvailabilityPause,
    82  			},
    83  			Status: api.NodeStatus{
    84  				State: api.NodeStatus_READY,
    85  			},
    86  		},
    87  		{
    88  			ID: "id5",
    89  			Spec: api.NodeSpec{
    90  				Annotations: api.Annotations{
    91  					Name: "name5",
    92  				},
    93  				Availability: api.NodeAvailabilityDrain,
    94  			},
    95  			Status: api.NodeStatus{
    96  				State: api.NodeStatus_READY,
    97  			},
    98  		},
    99  	}
   100  
   101  	initialTaskSet := []*api.Task{
   102  		// Task not assigned to any node
   103  		{
   104  			ID:           "id0",
   105  			DesiredState: api.TaskStateRunning,
   106  			Spec:         initialService.Spec.Task,
   107  			Status: api.TaskStatus{
   108  				State: api.TaskStateNew,
   109  			},
   110  			Slot: 1,
   111  			ServiceAnnotations: api.Annotations{
   112  				Name: "name0",
   113  			},
   114  			ServiceID: "id1",
   115  		},
   116  		// Tasks assigned to the nodes defined above
   117  		{
   118  			ID:           "id1",
   119  			DesiredState: api.TaskStateRunning,
   120  			Spec:         initialService.Spec.Task,
   121  			Status: api.TaskStatus{
   122  				State: api.TaskStateNew,
   123  			},
   124  			Slot: 2,
   125  			ServiceAnnotations: api.Annotations{
   126  				Name: "name1",
   127  			},
   128  			ServiceID: "id1",
   129  			NodeID:    "id1",
   130  		},
   131  		{
   132  			ID:           "id2",
   133  			DesiredState: api.TaskStateRunning,
   134  			Spec:         initialService.Spec.Task,
   135  			Status: api.TaskStatus{
   136  				State: api.TaskStateNew,
   137  			},
   138  			Slot: 3,
   139  			ServiceAnnotations: api.Annotations{
   140  				Name: "name2",
   141  			},
   142  			ServiceID: "id1",
   143  			NodeID:    "id2",
   144  		},
   145  		{
   146  			ID:           "id3",
   147  			DesiredState: api.TaskStateRunning,
   148  			Spec:         initialService.Spec.Task,
   149  			Status: api.TaskStatus{
   150  				State: api.TaskStateNew,
   151  			},
   152  			Slot: 4,
   153  			ServiceAnnotations: api.Annotations{
   154  				Name: "name3",
   155  			},
   156  			ServiceID: "id1",
   157  			NodeID:    "id3",
   158  		},
   159  		{
   160  			ID:           "id4",
   161  			DesiredState: api.TaskStateRunning,
   162  			Spec:         initialService.Spec.Task,
   163  			Status: api.TaskStatus{
   164  				State: api.TaskStateNew,
   165  			},
   166  			Slot: 5,
   167  			ServiceAnnotations: api.Annotations{
   168  				Name: "name4",
   169  			},
   170  			ServiceID: "id1",
   171  			NodeID:    "id4",
   172  		},
   173  		{
   174  			ID:           "id5",
   175  			DesiredState: api.TaskStateRunning,
   176  			Spec:         initialService.Spec.Task,
   177  			Status: api.TaskStatus{
   178  				State: api.TaskStateNew,
   179  			},
   180  			Slot: 6,
   181  			ServiceAnnotations: api.Annotations{
   182  				Name: "name5",
   183  			},
   184  			ServiceID: "id1",
   185  			NodeID:    "id5",
   186  		},
   187  	}
   188  
   189  	s := store.NewMemoryStore(nil)
   190  	assert.NotNil(t, s)
   191  	defer s.Close()
   192  
   193  	err := s.Update(func(tx store.Tx) error {
   194  		// Prepopulate service
   195  		assert.NoError(t, store.CreateService(tx, initialService))
   196  		// Prepoulate nodes
   197  		for _, n := range initialNodeSet {
   198  			assert.NoError(t, store.CreateNode(tx, n))
   199  		}
   200  
   201  		// Prepopulate tasks
   202  		for _, task := range initialTaskSet {
   203  			assert.NoError(t, store.CreateTask(tx, task))
   204  		}
   205  		return nil
   206  	})
   207  	assert.NoError(t, err)
   208  
   209  	watch, cancel := state.Watch(s.WatchQueue(), api.EventUpdateTask{})
   210  	defer cancel()
   211  
   212  	orchestrator := NewReplicatedOrchestrator(s)
   213  	defer orchestrator.Stop()
   214  
   215  	go func() {
   216  		assert.NoError(t, orchestrator.Run(ctx))
   217  	}()
   218  
   219  	// id2 and id5 should be killed immediately
   220  	deletion1 := testutils.WatchShutdownTask(t, watch)
   221  	deletion2 := testutils.WatchShutdownTask(t, watch)
   222  
   223  	assert.Regexp(t, "id(2|5)", deletion1.ID)
   224  	assert.Regexp(t, "id(2|5)", deletion1.NodeID)
   225  	assert.Regexp(t, "id(2|5)", deletion2.ID)
   226  	assert.Regexp(t, "id(2|5)", deletion2.NodeID)
   227  
   228  	// Create a new task, assigned to node id2
   229  	err = s.Update(func(tx store.Tx) error {
   230  		task := initialTaskSet[2].Copy()
   231  		task.ID = "newtask"
   232  		task.NodeID = "id2"
   233  		assert.NoError(t, store.CreateTask(tx, task))
   234  		return nil
   235  	})
   236  	assert.NoError(t, err)
   237  
   238  	deletion3 := testutils.WatchShutdownTask(t, watch)
   239  	assert.Equal(t, "newtask", deletion3.ID)
   240  	assert.Equal(t, "id2", deletion3.NodeID)
   241  
   242  	// Set node id4 to the DRAINED state
   243  	err = s.Update(func(tx store.Tx) error {
   244  		n := initialNodeSet[3].Copy()
   245  		n.Spec.Availability = api.NodeAvailabilityDrain
   246  		assert.NoError(t, store.UpdateNode(tx, n))
   247  		return nil
   248  	})
   249  	assert.NoError(t, err)
   250  
   251  	deletion4 := testutils.WatchShutdownTask(t, watch)
   252  	assert.Equal(t, "id4", deletion4.ID)
   253  	assert.Equal(t, "id4", deletion4.NodeID)
   254  
   255  	// Delete node id1
   256  	err = s.Update(func(tx store.Tx) error {
   257  		assert.NoError(t, store.DeleteNode(tx, "id1"))
   258  		return nil
   259  	})
   260  	assert.NoError(t, err)
   261  
   262  	deletion5 := testutils.WatchShutdownTask(t, watch)
   263  	assert.Equal(t, "id1", deletion5.ID)
   264  	assert.Equal(t, "id1", deletion5.NodeID)
   265  }