github.com/quite/nomad@v0.8.6/command/node_status_test.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 "time" 8 9 "github.com/hashicorp/nomad/api" 10 "github.com/hashicorp/nomad/command/agent" 11 "github.com/hashicorp/nomad/testutil" 12 "github.com/mitchellh/cli" 13 "github.com/posener/complete" 14 "github.com/stretchr/testify/assert" 15 ) 16 17 func TestNodeStatusCommand_Implements(t *testing.T) { 18 t.Parallel() 19 var _ cli.Command = &NodeStatusCommand{} 20 } 21 22 func TestNodeStatusCommand_Self(t *testing.T) { 23 t.Parallel() 24 // Start in dev mode so we get a node registration 25 srv, client, url := testServer(t, true, func(c *agent.Config) { 26 c.NodeName = "mynode" 27 }) 28 defer srv.Shutdown() 29 30 ui := new(cli.MockUi) 31 cmd := &NodeStatusCommand{Meta: Meta{Ui: ui}} 32 33 // Wait for a node to appear 34 var nodeID string 35 testutil.WaitForResult(func() (bool, error) { 36 nodes, _, err := client.Nodes().List(nil) 37 if err != nil { 38 return false, err 39 } 40 if len(nodes) == 0 { 41 return false, fmt.Errorf("missing node") 42 } 43 nodeID = nodes[0].ID 44 return true, nil 45 }, func(err error) { 46 t.Fatalf("err: %s", err) 47 }) 48 49 // Query self node 50 if code := cmd.Run([]string{"-address=" + url, "-self"}); code != 0 { 51 t.Fatalf("expected exit 0, got: %d", code) 52 } 53 out := ui.OutputWriter.String() 54 if !strings.Contains(out, "mynode") { 55 t.Fatalf("expect to find mynode, got: %s", out) 56 } 57 if !strings.Contains(out, "No allocations placed") { 58 t.Fatalf("should not dump allocations") 59 } 60 ui.OutputWriter.Reset() 61 62 // Request full id output 63 if code := cmd.Run([]string{"-address=" + url, "-self", "-verbose"}); code != 0 { 64 t.Fatalf("expected exit 0, got: %d", code) 65 } 66 out = ui.OutputWriter.String() 67 if !strings.Contains(out, nodeID) { 68 t.Fatalf("expected full node id %q, got: %s", nodeID, out) 69 } 70 ui.OutputWriter.Reset() 71 } 72 73 func TestNodeStatusCommand_Run(t *testing.T) { 74 t.Parallel() 75 // Start in dev mode so we get a node registration 76 srv, client, url := testServer(t, true, func(c *agent.Config) { 77 c.NodeName = "mynode" 78 }) 79 defer srv.Shutdown() 80 81 ui := new(cli.MockUi) 82 cmd := &NodeStatusCommand{Meta: Meta{Ui: ui}} 83 84 // Wait for a node to appear 85 var nodeID string 86 testutil.WaitForResult(func() (bool, error) { 87 nodes, _, err := client.Nodes().List(nil) 88 if err != nil { 89 return false, err 90 } 91 if len(nodes) == 0 { 92 return false, fmt.Errorf("missing node") 93 } 94 nodeID = nodes[0].ID 95 return true, nil 96 }, func(err error) { 97 t.Fatalf("err: %s", err) 98 }) 99 100 // Query all node statuses 101 if code := cmd.Run([]string{"-address=" + url}); code != 0 { 102 t.Fatalf("expected exit 0, got: %d", code) 103 } 104 out := ui.OutputWriter.String() 105 if !strings.Contains(out, "mynode") { 106 t.Fatalf("expect to find mynode, got: %s", out) 107 } 108 ui.OutputWriter.Reset() 109 110 // Query a single node 111 if code := cmd.Run([]string{"-address=" + url, nodeID}); code != 0 { 112 t.Fatalf("expected exit 0, got: %d", code) 113 } 114 out = ui.OutputWriter.String() 115 if !strings.Contains(out, "mynode") { 116 t.Fatalf("expect to find mynode, got: %s", out) 117 } 118 ui.OutputWriter.Reset() 119 120 // Query single node in short view 121 if code := cmd.Run([]string{"-address=" + url, "-short", nodeID}); code != 0 { 122 t.Fatalf("expected exit 0, got: %d", code) 123 } 124 out = ui.OutputWriter.String() 125 if !strings.Contains(out, "mynode") { 126 t.Fatalf("expect to find mynode, got: %s", out) 127 } 128 if !strings.Contains(out, "No allocations placed") { 129 t.Fatalf("should not dump allocations") 130 } 131 132 // Query a single node based on a prefix that is even without the hyphen 133 if code := cmd.Run([]string{"-address=" + url, nodeID[:13]}); code != 0 { 134 t.Fatalf("expected exit 0, got: %d", code) 135 } 136 out = ui.OutputWriter.String() 137 if !strings.Contains(out, "mynode") { 138 t.Fatalf("expect to find mynode, got: %s", out) 139 } 140 if strings.Contains(out, nodeID) { 141 t.Fatalf("expected truncated node id, got: %s", out) 142 } 143 if !strings.Contains(out, nodeID[:8]) { 144 t.Fatalf("expected node id %q, got: %s", nodeID[:8], out) 145 } 146 ui.OutputWriter.Reset() 147 148 // Request full id output 149 if code := cmd.Run([]string{"-address=" + url, "-verbose", nodeID[:4]}); code != 0 { 150 t.Fatalf("expected exit 0, got: %d", code) 151 } 152 out = ui.OutputWriter.String() 153 if !strings.Contains(out, nodeID) { 154 t.Fatalf("expected full node id %q, got: %s", nodeID, out) 155 } 156 ui.OutputWriter.Reset() 157 158 // Identifiers with uneven length should produce a query result 159 if code := cmd.Run([]string{"-address=" + url, nodeID[:3]}); code != 0 { 160 t.Fatalf("expected exit 0, got: %d", code) 161 } 162 out = ui.OutputWriter.String() 163 if !strings.Contains(out, "mynode") { 164 t.Fatalf("expect to find mynode, got: %s", out) 165 } 166 } 167 168 func TestNodeStatusCommand_Fails(t *testing.T) { 169 t.Parallel() 170 srv, _, url := testServer(t, false, nil) 171 defer srv.Shutdown() 172 173 ui := new(cli.MockUi) 174 cmd := &NodeStatusCommand{Meta: Meta{Ui: ui}} 175 176 // Fails on misuse 177 if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 { 178 t.Fatalf("expected exit code 1, got: %d", code) 179 } 180 if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) { 181 t.Fatalf("expected help output, got: %s", out) 182 } 183 ui.ErrorWriter.Reset() 184 185 // Fails on connection failure 186 if code := cmd.Run([]string{"-address=nope"}); code != 1 { 187 t.Fatalf("expected exit code 1, got: %d", code) 188 } 189 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying node status") { 190 t.Fatalf("expected failed query error, got: %s", out) 191 } 192 ui.ErrorWriter.Reset() 193 194 // Fails on nonexistent node 195 if code := cmd.Run([]string{"-address=" + url, "12345678-abcd-efab-cdef-123456789abc"}); code != 1 { 196 t.Fatalf("expected exit 1, got: %d", code) 197 } 198 if out := ui.ErrorWriter.String(); !strings.Contains(out, "No node(s) with prefix") { 199 t.Fatalf("expected not found error, got: %s", out) 200 } 201 ui.ErrorWriter.Reset() 202 203 // Fail on identifier with too few characters 204 if code := cmd.Run([]string{"-address=" + url, "1"}); code != 1 { 205 t.Fatalf("expected exit 1, got: %d", code) 206 } 207 if out := ui.ErrorWriter.String(); !strings.Contains(out, "must contain at least two characters.") { 208 t.Fatalf("expected too few characters error, got: %s", out) 209 } 210 ui.ErrorWriter.Reset() 211 212 // Failed on both -json and -t options are specified 213 if code := cmd.Run([]string{"-address=" + url, "-json", "-t", "{{.ID}}"}); code != 1 { 214 t.Fatalf("expected exit 1, got: %d", code) 215 } 216 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Both json and template formatting are not allowed") { 217 t.Fatalf("expected getting formatter error, got: %s", out) 218 } 219 } 220 221 func TestNodeStatusCommand_AutocompleteArgs(t *testing.T) { 222 assert := assert.New(t) 223 t.Parallel() 224 225 srv, client, url := testServer(t, true, nil) 226 defer srv.Shutdown() 227 228 // Wait for a node to appear 229 var nodeID string 230 testutil.WaitForResult(func() (bool, error) { 231 nodes, _, err := client.Nodes().List(nil) 232 if err != nil { 233 return false, err 234 } 235 if len(nodes) == 0 { 236 return false, fmt.Errorf("missing node") 237 } 238 nodeID = nodes[0].ID 239 return true, nil 240 }, func(err error) { 241 t.Fatalf("err: %s", err) 242 }) 243 244 ui := new(cli.MockUi) 245 cmd := &NodeStatusCommand{Meta: Meta{Ui: ui, flagAddress: url}} 246 247 prefix := nodeID[:len(nodeID)-5] 248 args := complete.Args{Last: prefix} 249 predictor := cmd.AutocompleteArgs() 250 251 res := predictor.Predict(args) 252 assert.Equal(1, len(res)) 253 assert.Equal(nodeID, res[0]) 254 } 255 256 func TestNodeStatusCommand_FormatDrain(t *testing.T) { 257 t.Parallel() 258 assert := assert.New(t) 259 260 node := &api.Node{} 261 262 assert.Equal("false", formatDrain(node)) 263 264 node.DrainStrategy = &api.DrainStrategy{} 265 assert.Equal("true; no deadline", formatDrain(node)) 266 267 node.DrainStrategy = &api.DrainStrategy{} 268 node.DrainStrategy.Deadline = -1 * time.Second 269 assert.Equal("true; force drain", formatDrain(node)) 270 271 // formatTime special cases Unix(0, 0), so increment by 1 272 node.DrainStrategy = &api.DrainStrategy{} 273 node.DrainStrategy.ForceDeadline = time.Unix(1, 0).UTC() 274 assert.Equal("true; 1970-01-01T00:00:01Z deadline", formatDrain(node)) 275 276 node.DrainStrategy.IgnoreSystemJobs = true 277 assert.Equal("true; 1970-01-01T00:00:01Z deadline; ignoring system jobs", formatDrain(node)) 278 }