github.com/aminovpavel/nomad@v0.11.8/nomad/drainer/draining_node_test.go (about)

     1  package drainer
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/hashicorp/nomad/nomad/mock"
     8  	"github.com/hashicorp/nomad/nomad/state"
     9  	"github.com/hashicorp/nomad/nomad/structs"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  // testDrainingNode creates a *drainingNode with a 1h deadline but no allocs
    15  func testDrainingNode(t *testing.T) *drainingNode {
    16  	t.Helper()
    17  	state := state.TestStateStore(t)
    18  	node := mock.Node()
    19  	node.DrainStrategy = &structs.DrainStrategy{
    20  		DrainSpec: structs.DrainSpec{
    21  			Deadline: time.Hour,
    22  		},
    23  		ForceDeadline: time.Now().Add(time.Hour),
    24  	}
    25  
    26  	require.Nil(t, state.UpsertNode(100, node))
    27  	return NewDrainingNode(node, state)
    28  }
    29  
    30  func assertDrainingNode(t *testing.T, dn *drainingNode, isDone bool, remaining, running int) {
    31  	t.Helper()
    32  
    33  	done, err := dn.IsDone()
    34  	require.Nil(t, err)
    35  	assert.Equal(t, isDone, done, "IsDone mismatch")
    36  
    37  	allocs, err := dn.RemainingAllocs()
    38  	require.Nil(t, err)
    39  	assert.Len(t, allocs, remaining, "RemainingAllocs mismatch")
    40  
    41  	jobs, err := dn.DrainingJobs()
    42  	require.Nil(t, err)
    43  	assert.Len(t, jobs, running, "DrainingJobs mismatch")
    44  }
    45  
    46  func TestDrainingNode_Table(t *testing.T) {
    47  	cases := []struct {
    48  		name      string
    49  		isDone    bool
    50  		remaining int
    51  		running   int
    52  		setup     func(*testing.T, *drainingNode)
    53  	}{
    54  		{
    55  			name:      "Empty",
    56  			isDone:    true,
    57  			remaining: 0,
    58  			running:   0,
    59  			setup:     func(*testing.T, *drainingNode) {},
    60  		},
    61  		{
    62  			name:      "Batch",
    63  			isDone:    false,
    64  			remaining: 1,
    65  			running:   1,
    66  			setup: func(t *testing.T, dn *drainingNode) {
    67  				alloc := mock.BatchAlloc()
    68  				alloc.NodeID = dn.node.ID
    69  				require.Nil(t, dn.state.UpsertJob(101, alloc.Job))
    70  				require.Nil(t, dn.state.UpsertAllocs(102, []*structs.Allocation{alloc}))
    71  			},
    72  		},
    73  		{
    74  			name:      "Service",
    75  			isDone:    false,
    76  			remaining: 1,
    77  			running:   1,
    78  			setup: func(t *testing.T, dn *drainingNode) {
    79  				alloc := mock.Alloc()
    80  				alloc.NodeID = dn.node.ID
    81  				require.Nil(t, dn.state.UpsertJob(101, alloc.Job))
    82  				require.Nil(t, dn.state.UpsertAllocs(102, []*structs.Allocation{alloc}))
    83  			},
    84  		},
    85  		{
    86  			name:      "System",
    87  			isDone:    true,
    88  			remaining: 1,
    89  			running:   0,
    90  			setup: func(t *testing.T, dn *drainingNode) {
    91  				alloc := mock.SystemAlloc()
    92  				alloc.NodeID = dn.node.ID
    93  				require.Nil(t, dn.state.UpsertJob(101, alloc.Job))
    94  				require.Nil(t, dn.state.UpsertAllocs(102, []*structs.Allocation{alloc}))
    95  			},
    96  		},
    97  		{
    98  			name:      "AllTerminal",
    99  			isDone:    true,
   100  			remaining: 0,
   101  			running:   0,
   102  			setup: func(t *testing.T, dn *drainingNode) {
   103  				allocs := []*structs.Allocation{mock.Alloc(), mock.BatchAlloc(), mock.SystemAlloc()}
   104  				for _, a := range allocs {
   105  					a.NodeID = dn.node.ID
   106  					require.Nil(t, dn.state.UpsertJob(101, a.Job))
   107  				}
   108  				require.Nil(t, dn.state.UpsertAllocs(102, allocs))
   109  
   110  				// StateStore doesn't like inserting new allocs
   111  				// with a terminal status, so set the status in
   112  				// a second pass
   113  				for _, a := range allocs {
   114  					a.ClientStatus = structs.AllocClientStatusComplete
   115  				}
   116  				require.Nil(t, dn.state.UpsertAllocs(103, allocs))
   117  			},
   118  		},
   119  		{
   120  			name:      "ServiceTerminal",
   121  			isDone:    false,
   122  			remaining: 2,
   123  			running:   1,
   124  			setup: func(t *testing.T, dn *drainingNode) {
   125  				allocs := []*structs.Allocation{mock.Alloc(), mock.BatchAlloc(), mock.SystemAlloc()}
   126  				for _, a := range allocs {
   127  					a.NodeID = dn.node.ID
   128  					require.Nil(t, dn.state.UpsertJob(101, a.Job))
   129  				}
   130  				require.Nil(t, dn.state.UpsertAllocs(102, allocs))
   131  
   132  				// Set only the service job as terminal
   133  				allocs[0].ClientStatus = structs.AllocClientStatusComplete
   134  				require.Nil(t, dn.state.UpsertAllocs(103, allocs))
   135  			},
   136  		},
   137  		{
   138  			name:      "AllTerminalButBatch",
   139  			isDone:    false,
   140  			remaining: 1,
   141  			running:   1,
   142  			setup: func(t *testing.T, dn *drainingNode) {
   143  				allocs := []*structs.Allocation{mock.Alloc(), mock.BatchAlloc(), mock.SystemAlloc()}
   144  				for _, a := range allocs {
   145  					a.NodeID = dn.node.ID
   146  					require.Nil(t, dn.state.UpsertJob(101, a.Job))
   147  				}
   148  				require.Nil(t, dn.state.UpsertAllocs(102, allocs))
   149  
   150  				// Set only the service and batch jobs as terminal
   151  				allocs[0].ClientStatus = structs.AllocClientStatusComplete
   152  				allocs[2].ClientStatus = structs.AllocClientStatusComplete
   153  				require.Nil(t, dn.state.UpsertAllocs(103, allocs))
   154  			},
   155  		},
   156  		{
   157  			name:      "AllTerminalButSystem",
   158  			isDone:    true,
   159  			remaining: 1,
   160  			running:   0,
   161  			setup: func(t *testing.T, dn *drainingNode) {
   162  				allocs := []*structs.Allocation{mock.Alloc(), mock.BatchAlloc(), mock.SystemAlloc()}
   163  				for _, a := range allocs {
   164  					a.NodeID = dn.node.ID
   165  					require.Nil(t, dn.state.UpsertJob(101, a.Job))
   166  				}
   167  				require.Nil(t, dn.state.UpsertAllocs(102, allocs))
   168  
   169  				// Set only the service and batch jobs as terminal
   170  				allocs[0].ClientStatus = structs.AllocClientStatusComplete
   171  				allocs[1].ClientStatus = structs.AllocClientStatusComplete
   172  				require.Nil(t, dn.state.UpsertAllocs(103, allocs))
   173  			},
   174  		},
   175  		{
   176  			name:      "HalfTerminal",
   177  			isDone:    false,
   178  			remaining: 3,
   179  			running:   2,
   180  			setup: func(t *testing.T, dn *drainingNode) {
   181  				allocs := []*structs.Allocation{
   182  					mock.Alloc(),
   183  					mock.BatchAlloc(),
   184  					mock.SystemAlloc(),
   185  					mock.Alloc(),
   186  					mock.BatchAlloc(),
   187  					mock.SystemAlloc(),
   188  				}
   189  				for _, a := range allocs {
   190  					a.NodeID = dn.node.ID
   191  					require.Nil(t, dn.state.UpsertJob(101, a.Job))
   192  				}
   193  				require.Nil(t, dn.state.UpsertAllocs(102, allocs))
   194  
   195  				// Set only the service and batch jobs as terminal
   196  				allocs[0].ClientStatus = structs.AllocClientStatusComplete
   197  				allocs[1].ClientStatus = structs.AllocClientStatusComplete
   198  				allocs[2].ClientStatus = structs.AllocClientStatusComplete
   199  				require.Nil(t, dn.state.UpsertAllocs(103, allocs))
   200  			},
   201  		},
   202  	}
   203  
   204  	// Default test drainingNode has no allocs, so it should be done and
   205  	// have no remaining allocs.
   206  	for _, tc := range cases {
   207  		tc := tc
   208  		t.Run(tc.name, func(t *testing.T) {
   209  			t.Parallel()
   210  			dn := testDrainingNode(t)
   211  			tc.setup(t, dn)
   212  			assertDrainingNode(t, dn, tc.isDone, tc.remaining, tc.running)
   213  		})
   214  	}
   215  }