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