github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/command/job_status_test.go (about) 1 package command 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/hashicorp/nomad/api" 8 "github.com/hashicorp/nomad/command/agent" 9 "github.com/hashicorp/nomad/nomad/mock" 10 "github.com/mitchellh/cli" 11 "github.com/posener/complete" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 func TestJobStatusCommand_Implements(t *testing.T) { 16 t.Parallel() 17 var _ cli.Command = &JobStatusCommand{} 18 } 19 20 func TestJobStatusCommand_Run(t *testing.T) { 21 t.Parallel() 22 srv, client, url := testServer(t, true, nil) 23 defer srv.Shutdown() 24 25 ui := new(cli.MockUi) 26 cmd := &JobStatusCommand{Meta: Meta{Ui: ui}} 27 28 // Should return blank for no jobs 29 if code := cmd.Run([]string{"-address=" + url}); code != 0 { 30 t.Fatalf("expected exit 0, got: %d", code) 31 } 32 33 // Check for this awkward nil string, since a nil bytes.Buffer 34 // returns this purposely, and mitchellh/cli has a nil pointer 35 // if nothing was ever output. 36 exp := "No running jobs" 37 if out := strings.TrimSpace(ui.OutputWriter.String()); out != exp { 38 t.Fatalf("expected %q; got: %q", exp, out) 39 } 40 41 // Register two jobs 42 job1 := testJob("job1_sfx") 43 resp, _, err := client.Jobs().Register(job1, nil) 44 if err != nil { 45 t.Fatalf("err: %s", err) 46 } 47 if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 { 48 t.Fatalf("status code non zero saw %d", code) 49 } 50 51 job2 := testJob("job2_sfx") 52 resp2, _, err := client.Jobs().Register(job2, nil) 53 if err != nil { 54 t.Fatalf("err: %s", err) 55 } 56 if code := waitForSuccess(ui, client, fullId, t, resp2.EvalID); code != 0 { 57 t.Fatalf("status code non zero saw %d", code) 58 } 59 60 // Query again and check the result 61 if code := cmd.Run([]string{"-address=" + url}); code != 0 { 62 t.Fatalf("expected exit 0, got: %d", code) 63 } 64 out := ui.OutputWriter.String() 65 if !strings.Contains(out, "job1_sfx") || !strings.Contains(out, "job2_sfx") { 66 t.Fatalf("expected job1_sfx and job2_sfx, got: %s", out) 67 } 68 ui.OutputWriter.Reset() 69 70 // Query a single job 71 if code := cmd.Run([]string{"-address=" + url, "job2_sfx"}); code != 0 { 72 t.Fatalf("expected exit 0, got: %d", code) 73 } 74 out = ui.OutputWriter.String() 75 if strings.Contains(out, "job1_sfx") || !strings.Contains(out, "job2_sfx") { 76 t.Fatalf("expected only job2_sfx, got: %s", out) 77 } 78 if !strings.Contains(out, "Allocations") { 79 t.Fatalf("should dump allocations") 80 } 81 if !strings.Contains(out, "Summary") { 82 t.Fatalf("should dump summary") 83 } 84 ui.OutputWriter.Reset() 85 86 // Query a single job showing evals 87 if code := cmd.Run([]string{"-address=" + url, "-evals", "job2_sfx"}); code != 0 { 88 t.Fatalf("expected exit 0, got: %d", code) 89 } 90 out = ui.OutputWriter.String() 91 if strings.Contains(out, "job1_sfx") || !strings.Contains(out, "job2_sfx") { 92 t.Fatalf("expected only job2_sfx, got: %s", out) 93 } 94 if !strings.Contains(out, "Evaluations") { 95 t.Fatalf("should dump evaluations") 96 } 97 if !strings.Contains(out, "Allocations") { 98 t.Fatalf("should dump allocations") 99 } 100 ui.OutputWriter.Reset() 101 102 // Query a single job in verbose mode 103 if code := cmd.Run([]string{"-address=" + url, "-verbose", "job2_sfx"}); code != 0 { 104 t.Fatalf("expected exit 0, got: %d", code) 105 } 106 out = ui.OutputWriter.String() 107 if strings.Contains(out, "job1_sfx") || !strings.Contains(out, "job2_sfx") { 108 t.Fatalf("expected only job2_sfx, got: %s", out) 109 } 110 if !strings.Contains(out, "Evaluations") { 111 t.Fatalf("should dump evaluations") 112 } 113 if !strings.Contains(out, "Allocations") { 114 t.Fatalf("should dump allocations") 115 } 116 if !strings.Contains(out, "Created") { 117 t.Fatal("should have created header") 118 } 119 if !strings.Contains(out, "Modified") { 120 t.Fatal("should have modified header") 121 } 122 ui.ErrorWriter.Reset() 123 ui.OutputWriter.Reset() 124 125 // Query jobs with prefix match 126 if code := cmd.Run([]string{"-address=" + url, "job"}); code != 1 { 127 t.Fatalf("expected exit 1, got: %d", code) 128 } 129 out = ui.ErrorWriter.String() 130 if !strings.Contains(out, "job1_sfx") || !strings.Contains(out, "job2_sfx") { 131 t.Fatalf("expected job1_sfx and job2_sfx, got: %s", out) 132 } 133 ui.ErrorWriter.Reset() 134 ui.OutputWriter.Reset() 135 136 // Query a single job with prefix match 137 if code := cmd.Run([]string{"-address=" + url, "job1"}); code != 0 { 138 t.Fatalf("expected exit 0, got: %d", code) 139 } 140 out = ui.OutputWriter.String() 141 if !strings.Contains(out, "job1_sfx") || strings.Contains(out, "job2_sfx") { 142 t.Fatalf("expected only job1_sfx, got: %s", out) 143 } 144 145 if !strings.Contains(out, "Created") { 146 t.Fatal("should have created header") 147 } 148 149 if !strings.Contains(out, "Modified") { 150 t.Fatal("should have modified header") 151 } 152 ui.OutputWriter.Reset() 153 154 // Query in short view mode 155 if code := cmd.Run([]string{"-address=" + url, "-short", "job2"}); code != 0 { 156 t.Fatalf("expected exit 0, got: %d", code) 157 } 158 out = ui.OutputWriter.String() 159 if !strings.Contains(out, "job2") { 160 t.Fatalf("expected job2, got: %s", out) 161 } 162 if strings.Contains(out, "Evaluations") { 163 t.Fatalf("should not dump evaluations") 164 } 165 if strings.Contains(out, "Allocations") { 166 t.Fatalf("should not dump allocations") 167 } 168 if strings.Contains(out, resp.EvalID) { 169 t.Fatalf("should not contain full identifiers, got %s", out) 170 } 171 ui.OutputWriter.Reset() 172 173 // Request full identifiers 174 if code := cmd.Run([]string{"-address=" + url, "-verbose", "job1"}); code != 0 { 175 t.Fatalf("expected exit 0, got: %d", code) 176 } 177 out = ui.OutputWriter.String() 178 if !strings.Contains(out, resp.EvalID) { 179 t.Fatalf("should contain full identifiers, got %s", out) 180 } 181 } 182 183 func TestJobStatusCommand_Fails(t *testing.T) { 184 t.Parallel() 185 ui := new(cli.MockUi) 186 cmd := &JobStatusCommand{Meta: Meta{Ui: ui}} 187 188 // Fails on misuse 189 if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 { 190 t.Fatalf("expected exit code 1, got: %d", code) 191 } 192 if out := ui.ErrorWriter.String(); !strings.Contains(out, cmd.Help()) { 193 t.Fatalf("expected help output, got: %s", out) 194 } 195 ui.ErrorWriter.Reset() 196 197 // Fails on connection failure 198 if code := cmd.Run([]string{"-address=nope"}); code != 1 { 199 t.Fatalf("expected exit code 1, got: %d", code) 200 } 201 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying jobs") { 202 t.Fatalf("expected failed query error, got: %s", out) 203 } 204 } 205 206 func TestJobStatusCommand_AutocompleteArgs(t *testing.T) { 207 assert := assert.New(t) 208 t.Parallel() 209 210 srv, _, url := testServer(t, true, nil) 211 defer srv.Shutdown() 212 213 ui := new(cli.MockUi) 214 cmd := &JobStatusCommand{Meta: Meta{Ui: ui, flagAddress: url}} 215 216 // Create a fake job 217 state := srv.Agent.Server().State() 218 j := mock.Job() 219 assert.Nil(state.UpsertJob(1000, j)) 220 221 prefix := j.ID[:len(j.ID)-5] 222 args := complete.Args{Last: prefix} 223 predictor := cmd.AutocompleteArgs() 224 225 res := predictor.Predict(args) 226 assert.Equal(1, len(res)) 227 assert.Equal(j.ID, res[0]) 228 } 229 230 func TestJobStatusCommand_WithAccessPolicy(t *testing.T) { 231 assert := assert.New(t) 232 t.Parallel() 233 234 config := func(c *agent.Config) { 235 c.ACL.Enabled = true 236 } 237 238 srv, client, url := testServer(t, true, config) 239 defer srv.Shutdown() 240 241 // Bootstrap an initial ACL token 242 token := srv.RootToken 243 assert.NotNil(token, "failed to bootstrap ACL token") 244 245 // Register a job 246 j := testJob("job1_sfx") 247 248 invalidToken := mock.ACLToken() 249 250 ui := new(cli.MockUi) 251 cmd := &JobStatusCommand{Meta: Meta{Ui: ui, flagAddress: url}} 252 253 // registering a job without a token fails 254 client.SetSecretID(invalidToken.SecretID) 255 resp, _, err := client.Jobs().Register(j, nil) 256 assert.NotNil(err) 257 258 // registering a job with a valid token succeeds 259 client.SetSecretID(token.SecretID) 260 resp, _, err = client.Jobs().Register(j, nil) 261 assert.Nil(err) 262 code := waitForSuccess(ui, client, fullId, t, resp.EvalID) 263 assert.Equal(0, code) 264 265 // Request Job List without providing a valid token 266 code = cmd.Run([]string{"-address=" + url, "-token=" + invalidToken.SecretID, "-short"}) 267 assert.Equal(1, code) 268 269 // Request Job List with a valid token 270 code = cmd.Run([]string{"-address=" + url, "-token=" + token.SecretID, "-short"}) 271 assert.Equal(0, code) 272 273 out := ui.OutputWriter.String() 274 if !strings.Contains(out, *j.ID) { 275 t.Fatalf("should contain full identifiers, got %s", out) 276 } 277 } 278 279 func waitForSuccess(ui cli.Ui, client *api.Client, length int, t *testing.T, evalId string) int { 280 mon := newMonitor(ui, client, length) 281 monErr := mon.monitor(evalId, false) 282 return monErr 283 }