github.com/hernad/nomad@v1.6.112/command/job_inspect_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package command
     5  
     6  import (
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/hernad/nomad/api"
    11  	"github.com/hernad/nomad/ci"
    12  	"github.com/hernad/nomad/command/agent"
    13  	"github.com/hernad/nomad/nomad/mock"
    14  	"github.com/hernad/nomad/nomad/structs"
    15  	"github.com/mitchellh/cli"
    16  	"github.com/posener/complete"
    17  	"github.com/shoenig/test/must"
    18  	"github.com/stretchr/testify/assert"
    19  )
    20  
    21  func TestInspectCommand_Implements(t *testing.T) {
    22  	ci.Parallel(t)
    23  	var _ cli.Command = &JobInspectCommand{}
    24  }
    25  
    26  func TestInspectCommand_Fails(t *testing.T) {
    27  	ci.Parallel(t)
    28  	srv, _, url := testServer(t, false, nil)
    29  	defer srv.Shutdown()
    30  
    31  	ui := cli.NewMockUi()
    32  	cmd := &JobInspectCommand{Meta: Meta{Ui: ui}}
    33  
    34  	// Fails on misuse
    35  	if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
    36  		t.Fatalf("expected exit code 1, got: %d", code)
    37  	}
    38  	if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
    39  		t.Fatalf("expected help output, got: %s", out)
    40  	}
    41  	ui.ErrorWriter.Reset()
    42  
    43  	// Fails on nonexistent job ID
    44  	if code := cmd.Run([]string{"-address=" + url, "nope"}); code != 1 {
    45  		t.Fatalf("expect exit 1, got: %d", code)
    46  	}
    47  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "No job(s) with prefix or ID") {
    48  		t.Fatalf("expect not found error, got: %s", out)
    49  	}
    50  	ui.ErrorWriter.Reset()
    51  
    52  	// Fails on connection failure
    53  	if code := cmd.Run([]string{"-address=nope", "nope"}); code != 1 {
    54  		t.Fatalf("expected exit code 1, got: %d", code)
    55  	}
    56  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying job prefix") {
    57  		t.Fatalf("expected failed query error, got: %s", out)
    58  	}
    59  	ui.ErrorWriter.Reset()
    60  
    61  	// Failed on both -json and -t options are specified
    62  	if code := cmd.Run([]string{"-address=" + url, "-json", "-t", "{{.ID}}"}); code != 1 {
    63  		t.Fatalf("expected exit 1, got: %d", code)
    64  	}
    65  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "Both json and template formatting are not allowed") {
    66  		t.Fatalf("expected getting formatter error, got: %s", out)
    67  	}
    68  }
    69  
    70  func TestInspectCommand_AutocompleteArgs(t *testing.T) {
    71  	ci.Parallel(t)
    72  	assert := assert.New(t)
    73  
    74  	srv, _, url := testServer(t, true, nil)
    75  	defer srv.Shutdown()
    76  
    77  	ui := cli.NewMockUi()
    78  	cmd := &JobInspectCommand{Meta: Meta{Ui: ui, flagAddress: url}}
    79  
    80  	state := srv.Agent.Server().State()
    81  	j := mock.Job()
    82  	assert.Nil(state.UpsertJob(structs.MsgTypeTestSetup, 1000, nil, j))
    83  
    84  	prefix := j.ID[:len(j.ID)-5]
    85  	args := complete.Args{Last: prefix}
    86  	predictor := cmd.AutocompleteArgs()
    87  
    88  	res := predictor.Predict(args)
    89  	assert.Equal(1, len(res))
    90  	assert.Equal(j.ID, res[0])
    91  }
    92  
    93  func TestJobInspectCommand_ACL(t *testing.T) {
    94  	ci.Parallel(t)
    95  
    96  	// Start server with ACL enabled.
    97  	srv, _, url := testServer(t, true, func(c *agent.Config) {
    98  		c.ACL.Enabled = true
    99  	})
   100  	defer srv.Shutdown()
   101  
   102  	// Create a job
   103  	job := mock.MinJob()
   104  	state := srv.Agent.Server().State()
   105  	err := state.UpsertJob(structs.MsgTypeTestSetup, 100, nil, job)
   106  	must.NoError(t, err)
   107  
   108  	testCases := []struct {
   109  		name        string
   110  		jobPrefix   bool
   111  		aclPolicy   string
   112  		expectedErr string
   113  	}{
   114  		{
   115  			name:        "no token",
   116  			aclPolicy:   "",
   117  			expectedErr: api.PermissionDeniedErrorContent,
   118  		},
   119  		{
   120  			name: "missing read-job",
   121  			aclPolicy: `
   122  namespace "default" {
   123  	capabilities = ["list-jobs"]
   124  }
   125  `,
   126  			expectedErr: api.PermissionDeniedErrorContent,
   127  		},
   128  		{
   129  			name: "read-job allowed",
   130  			aclPolicy: `
   131  namespace "default" {
   132  	capabilities = ["read-job"]
   133  }
   134  `,
   135  		},
   136  		{
   137  			name:      "job prefix requires list-job",
   138  			jobPrefix: true,
   139  			aclPolicy: `
   140  namespace "default" {
   141  	capabilities = ["read-job"]
   142  }
   143  `,
   144  			expectedErr: "job not found",
   145  		},
   146  		{
   147  			name:      "job prefix works with list-job",
   148  			jobPrefix: true,
   149  			aclPolicy: `
   150  namespace "default" {
   151  	capabilities = ["read-job", "list-jobs"]
   152  }
   153  `,
   154  		},
   155  	}
   156  
   157  	for i, tc := range testCases {
   158  		t.Run(tc.name, func(t *testing.T) {
   159  			ui := cli.NewMockUi()
   160  			cmd := &JobInspectCommand{Meta: Meta{Ui: ui}}
   161  			args := []string{
   162  				"-address", url,
   163  			}
   164  
   165  			if tc.aclPolicy != "" {
   166  				// Create ACL token with test case policy and add it to the
   167  				// command.
   168  				policyName := nonAlphaNum.ReplaceAllString(tc.name, "-")
   169  				token := mock.CreatePolicyAndToken(t, state, uint64(302+i), policyName, tc.aclPolicy)
   170  				args = append(args, "-token", token.SecretID)
   171  			}
   172  
   173  			// Add job ID or job ID prefix to the command.
   174  			if tc.jobPrefix {
   175  				args = append(args, job.ID[:3])
   176  			} else {
   177  				args = append(args, job.ID)
   178  			}
   179  
   180  			// Run command.
   181  			code := cmd.Run(args)
   182  			if tc.expectedErr == "" {
   183  				must.Zero(t, code)
   184  			} else {
   185  				must.One(t, code)
   186  				must.StrContains(t, ui.ErrorWriter.String(), tc.expectedErr)
   187  			}
   188  		})
   189  	}
   190  }