github.com/ThomasObenaus/nomad@v0.11.1/command/monitor_test.go (about) 1 package command 2 3 import ( 4 "strings" 5 "testing" 6 "time" 7 8 "github.com/hashicorp/nomad/nomad/structs" 9 "github.com/mitchellh/cli" 10 ) 11 12 func TestMonitor_Update_Eval(t *testing.T) { 13 t.Parallel() 14 ui := new(cli.MockUi) 15 mon := newMonitor(ui, nil, fullId) 16 17 // Evals triggered by jobs log 18 state := &evalState{ 19 status: structs.EvalStatusPending, 20 job: "job1", 21 } 22 mon.update(state) 23 24 out := ui.OutputWriter.String() 25 if !strings.Contains(out, "job1") { 26 t.Fatalf("missing job\n\n%s", out) 27 } 28 ui.OutputWriter.Reset() 29 30 // Evals triggered by nodes log 31 state = &evalState{ 32 status: structs.EvalStatusPending, 33 node: "12345678-abcd-efab-cdef-123456789abc", 34 } 35 mon.update(state) 36 37 out = ui.OutputWriter.String() 38 if !strings.Contains(out, "12345678-abcd-efab-cdef-123456789abc") { 39 t.Fatalf("missing node\n\n%s", out) 40 } 41 42 // Transition to pending should not be logged 43 if strings.Contains(out, structs.EvalStatusPending) { 44 t.Fatalf("should skip status\n\n%s", out) 45 } 46 ui.OutputWriter.Reset() 47 48 // No logs sent if no update 49 mon.update(state) 50 if out := ui.OutputWriter.String(); out != "" { 51 t.Fatalf("expected no output\n\n%s", out) 52 } 53 54 // Status change sends more logs 55 state = &evalState{ 56 status: structs.EvalStatusComplete, 57 node: "12345678-abcd-efab-cdef-123456789abc", 58 } 59 mon.update(state) 60 out = ui.OutputWriter.String() 61 if !strings.Contains(out, structs.EvalStatusComplete) { 62 t.Fatalf("missing status\n\n%s", out) 63 } 64 } 65 66 func TestMonitor_Update_Allocs(t *testing.T) { 67 t.Parallel() 68 ui := new(cli.MockUi) 69 mon := newMonitor(ui, nil, fullId) 70 71 // New allocations write new logs 72 state := &evalState{ 73 allocs: map[string]*allocState{ 74 "alloc1": { 75 id: "87654321-abcd-efab-cdef-123456789abc", 76 group: "group1", 77 node: "12345678-abcd-efab-cdef-123456789abc", 78 desired: structs.AllocDesiredStatusRun, 79 client: structs.AllocClientStatusPending, 80 index: 1, 81 }, 82 }, 83 } 84 mon.update(state) 85 86 // Logs were output 87 out := ui.OutputWriter.String() 88 if !strings.Contains(out, "87654321-abcd-efab-cdef-123456789abc") { 89 t.Fatalf("missing alloc\n\n%s", out) 90 } 91 if !strings.Contains(out, "group1") { 92 t.Fatalf("missing group\n\n%s", out) 93 } 94 if !strings.Contains(out, "12345678-abcd-efab-cdef-123456789abc") { 95 t.Fatalf("missing node\n\n%s", out) 96 } 97 if !strings.Contains(out, "created") { 98 t.Fatalf("missing created\n\n%s", out) 99 } 100 ui.OutputWriter.Reset() 101 102 // No change yields no logs 103 mon.update(state) 104 if out := ui.OutputWriter.String(); out != "" { 105 t.Fatalf("expected no output\n\n%s", out) 106 } 107 ui.OutputWriter.Reset() 108 109 // Alloc updates cause more log lines 110 state = &evalState{ 111 allocs: map[string]*allocState{ 112 "alloc1": { 113 id: "87654321-abcd-efab-cdef-123456789abc", 114 group: "group1", 115 node: "12345678-abcd-efab-cdef-123456789abc", 116 desired: structs.AllocDesiredStatusRun, 117 client: structs.AllocClientStatusRunning, 118 index: 2, 119 }, 120 }, 121 } 122 mon.update(state) 123 124 // Updates were logged 125 out = ui.OutputWriter.String() 126 if !strings.Contains(out, "87654321-abcd-efab-cdef-123456789abc") { 127 t.Fatalf("missing alloc\n\n%s", out) 128 } 129 if !strings.Contains(out, "pending") { 130 t.Fatalf("missing old status\n\n%s", out) 131 } 132 if !strings.Contains(out, "running") { 133 t.Fatalf("missing new status\n\n%s", out) 134 } 135 } 136 137 func TestMonitor_Update_AllocModification(t *testing.T) { 138 t.Parallel() 139 ui := new(cli.MockUi) 140 mon := newMonitor(ui, nil, fullId) 141 142 // New allocs with a create index lower than the 143 // eval create index are logged as modifications 144 state := &evalState{ 145 index: 2, 146 allocs: map[string]*allocState{ 147 "alloc3": { 148 id: "87654321-abcd-bafe-cdef-123456789abc", 149 node: "12345678-abcd-efab-cdef-123456789abc", 150 group: "group2", 151 index: 1, 152 }, 153 }, 154 } 155 mon.update(state) 156 157 // Modification was logged 158 out := ui.OutputWriter.String() 159 if !strings.Contains(out, "87654321-abcd-bafe-cdef-123456789abc") { 160 t.Fatalf("missing alloc\n\n%s", out) 161 } 162 if !strings.Contains(out, "group2") { 163 t.Fatalf("missing group\n\n%s", out) 164 } 165 if !strings.Contains(out, "12345678-abcd-efab-cdef-123456789abc") { 166 t.Fatalf("missing node\n\n%s", out) 167 } 168 if !strings.Contains(out, "modified") { 169 t.Fatalf("missing modification\n\n%s", out) 170 } 171 } 172 173 func TestMonitor_Monitor(t *testing.T) { 174 t.Parallel() 175 srv, client, _ := testServer(t, false, nil) 176 defer srv.Shutdown() 177 178 // Create the monitor 179 ui := new(cli.MockUi) 180 mon := newMonitor(ui, client, fullId) 181 182 // Submit a job - this creates a new evaluation we can monitor 183 job := testJob("job1") 184 resp, _, err := client.Jobs().Register(job, nil) 185 if err != nil { 186 t.Fatalf("err: %s", err) 187 } 188 189 // Start monitoring the eval 190 var code int 191 doneCh := make(chan struct{}) 192 go func() { 193 defer close(doneCh) 194 code = mon.monitor(resp.EvalID, false) 195 }() 196 197 // Wait for completion 198 select { 199 case <-doneCh: 200 case <-time.After(5 * time.Second): 201 t.Fatalf("eval monitor took too long") 202 } 203 204 // Check the return code. We should get exit code 2 as there 205 // would be a scheduling problem on the test server (no clients). 206 if code != 2 { 207 t.Fatalf("expect exit 2, got: %d", code) 208 } 209 210 // Check the output 211 out := ui.OutputWriter.String() 212 if !strings.Contains(out, resp.EvalID) { 213 t.Fatalf("missing eval\n\n%s", out) 214 } 215 if !strings.Contains(out, "finished with status") { 216 t.Fatalf("missing final status\n\n%s", out) 217 } 218 } 219 220 func TestMonitor_MonitorWithPrefix(t *testing.T) { 221 t.Parallel() 222 srv, client, _ := testServer(t, false, nil) 223 defer srv.Shutdown() 224 225 // Create the monitor 226 ui := new(cli.MockUi) 227 mon := newMonitor(ui, client, shortId) 228 229 // Submit a job - this creates a new evaluation we can monitor 230 job := testJob("job1") 231 resp, _, err := client.Jobs().Register(job, nil) 232 if err != nil { 233 t.Fatalf("err: %s", err) 234 } 235 236 // Start monitoring the eval 237 var code int 238 doneCh := make(chan struct{}) 239 go func() { 240 defer close(doneCh) 241 code = mon.monitor(resp.EvalID[:13], true) 242 }() 243 244 // Wait for completion 245 select { 246 case <-doneCh: 247 case <-time.After(5 * time.Second): 248 t.Fatalf("eval monitor took too long") 249 } 250 251 // Check the return code. We should get exit code 2 as there 252 // would be a scheduling problem on the test server (no clients). 253 if code != 2 { 254 t.Fatalf("expect exit 2, got: %d", code) 255 } 256 257 // Check the output 258 out := ui.OutputWriter.String() 259 if !strings.Contains(out, resp.EvalID[:8]) { 260 t.Fatalf("missing eval\n\n%s", out) 261 } 262 if strings.Contains(out, resp.EvalID) { 263 t.Fatalf("expected truncated eval id, got: %s", out) 264 } 265 if !strings.Contains(out, "finished with status") { 266 t.Fatalf("missing final status\n\n%s", out) 267 } 268 269 // Fail on identifier with too few characters 270 code = mon.monitor(resp.EvalID[:1], true) 271 if code != 1 { 272 t.Fatalf("expect exit 1, got: %d", code) 273 } 274 if out := ui.ErrorWriter.String(); !strings.Contains(out, "must contain at least two characters.") { 275 t.Fatalf("expected too few characters error, got: %s", out) 276 } 277 ui.ErrorWriter.Reset() 278 279 code = mon.monitor(resp.EvalID[:3], true) 280 if code != 2 { 281 t.Fatalf("expect exit 2, got: %d", code) 282 } 283 if out := ui.OutputWriter.String(); !strings.Contains(out, "Monitoring evaluation") { 284 t.Fatalf("expected evaluation monitoring output, got: %s", out) 285 } 286 287 }