github.com/bigcommerce/nomad@v0.9.3-bc/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  }