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  }