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 }