github.com/hernad/nomad@v1.6.112/command/job_validate_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package command 5 6 import ( 7 "os" 8 "strings" 9 "testing" 10 11 "github.com/hernad/nomad/ci" 12 "github.com/hernad/nomad/testutil" 13 "github.com/mitchellh/cli" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestValidateCommand_Implements(t *testing.T) { 18 ci.Parallel(t) 19 var _ cli.Command = &JobValidateCommand{} 20 } 21 22 func TestValidateCommand_Files(t *testing.T) { 23 24 // Create a Vault server 25 v := testutil.NewTestVault(t) 26 defer v.Stop() 27 28 // Create a Nomad server 29 s := testutil.NewTestServer(t, func(c *testutil.TestServerConfig) { 30 c.Vault.Address = v.HTTPAddr 31 c.Vault.Enabled = true 32 c.Vault.AllowUnauthenticated = false 33 c.Vault.Token = v.RootToken 34 }) 35 defer s.Stop() 36 37 t.Run("basic", func(t *testing.T) { 38 ui := cli.NewMockUi() 39 cmd := &JobValidateCommand{Meta: Meta{Ui: ui, flagAddress: "http://" + s.HTTPAddr}} 40 args := []string{"testdata/example-basic.nomad"} 41 code := cmd.Run(args) 42 require.Equal(t, 0, code) 43 }) 44 45 t.Run("vault no token", func(t *testing.T) { 46 ui := cli.NewMockUi() 47 cmd := &JobValidateCommand{Meta: Meta{Ui: ui}} 48 args := []string{"-address", "http://" + s.HTTPAddr, "testdata/example-vault.nomad"} 49 code := cmd.Run(args) 50 require.Contains(t, ui.ErrorWriter.String(), "* Vault used in the job but missing Vault token") 51 require.Equal(t, 1, code) 52 }) 53 54 t.Run("vault bad token via flag", func(t *testing.T) { 55 ui := cli.NewMockUi() 56 cmd := &JobValidateCommand{Meta: Meta{Ui: ui}} 57 args := []string{"-address", "http://" + s.HTTPAddr, "-vault-token=abc123", "testdata/example-vault.nomad"} 58 code := cmd.Run(args) 59 require.Contains(t, ui.ErrorWriter.String(), "* bad token") 60 require.Equal(t, 1, code) 61 }) 62 63 t.Run("vault token bad via env", func(t *testing.T) { 64 t.Setenv("VAULT_TOKEN", "abc123") 65 ui := cli.NewMockUi() 66 cmd := &JobValidateCommand{Meta: Meta{Ui: ui}} 67 args := []string{"-address", "http://" + s.HTTPAddr, "testdata/example-vault.nomad"} 68 code := cmd.Run(args) 69 require.Contains(t, ui.ErrorWriter.String(), "* bad token") 70 require.Equal(t, 1, code) 71 }) 72 } 73 func TestValidateCommand_hcl1_hcl2_strict(t *testing.T) { 74 ci.Parallel(t) 75 76 _, _, addr := testServer(t, false, nil) 77 78 t.Run("-hcl1 implies -hcl2-strict is false", func(t *testing.T) { 79 ui := cli.NewMockUi() 80 cmd := &JobValidateCommand{Meta: Meta{Ui: ui}} 81 got := cmd.Run([]string{ 82 "-hcl1", "-hcl2-strict", 83 "-address", addr, 84 "asset/example-short.nomad.hcl", 85 }) 86 require.Equal(t, 0, got, ui.ErrorWriter.String()) 87 }) 88 } 89 90 func TestValidateCommand_Fails(t *testing.T) { 91 ci.Parallel(t) 92 ui := cli.NewMockUi() 93 cmd := &JobValidateCommand{Meta: Meta{Ui: ui}} 94 95 // Fails on misuse 96 if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 { 97 t.Fatalf("expected exit code 1, got: %d", code) 98 } 99 if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) { 100 t.Fatalf("expected help output, got: %s", out) 101 } 102 ui.ErrorWriter.Reset() 103 104 // Fails when specified file does not exist 105 if code := cmd.Run([]string{"/unicorns/leprechauns"}); code != 1 { 106 t.Fatalf("expect exit 1, got: %d", code) 107 } 108 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error getting job struct") { 109 t.Fatalf("expect getting job struct error, got: %s", out) 110 } 111 ui.ErrorWriter.Reset() 112 113 // Fails on invalid HCL 114 fh1, err := os.CreateTemp("", "nomad") 115 if err != nil { 116 t.Fatalf("err: %s", err) 117 } 118 defer os.Remove(fh1.Name()) 119 if _, err := fh1.WriteString("nope"); err != nil { 120 t.Fatalf("err: %s", err) 121 } 122 if code := cmd.Run([]string{fh1.Name()}); code != 1 { 123 t.Fatalf("expect exit 1, got: %d", code) 124 } 125 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error getting job struct") { 126 t.Fatalf("expect parsing error, got: %s", out) 127 } 128 ui.ErrorWriter.Reset() 129 130 // Fails on invalid job spec 131 fh2, err := os.CreateTemp("", "nomad") 132 if err != nil { 133 t.Fatalf("err: %s", err) 134 } 135 defer os.Remove(fh2.Name()) 136 if _, err := fh2.WriteString(`job "job1" {}`); err != nil { 137 t.Fatalf("err: %s", err) 138 } 139 if code := cmd.Run([]string{fh2.Name()}); code != 1 { 140 t.Fatalf("expect exit 1, got: %d", code) 141 } 142 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Job validation errors") { 143 t.Fatalf("expect validation error, got: %s", out) 144 } 145 ui.ErrorWriter.Reset() 146 } 147 148 func TestValidateCommand_From_STDIN(t *testing.T) { 149 ci.Parallel(t) 150 stdinR, stdinW, err := os.Pipe() 151 if err != nil { 152 t.Fatalf("err: %s", err) 153 } 154 155 // Create a server 156 s := testutil.NewTestServer(t, nil) 157 defer s.Stop() 158 159 ui := cli.NewMockUi() 160 cmd := &JobValidateCommand{ 161 Meta: Meta{Ui: ui, flagAddress: "http://" + s.HTTPAddr}, 162 JobGetter: JobGetter{testStdin: stdinR}, 163 } 164 165 go func() { 166 stdinW.WriteString(` 167 job "job1" { 168 type = "service" 169 datacenters = [ "dc1" ] 170 group "group1" { 171 count = 1 172 task "task1" { 173 driver = "exec" 174 config { 175 command = "/bin/echo" 176 } 177 resources { 178 cpu = 1000 179 memory = 512 180 } 181 } 182 } 183 }`) 184 stdinW.Close() 185 }() 186 187 args := []string{"-"} 188 if code := cmd.Run(args); code != 0 { 189 t.Fatalf("expected exit code 0, got %d: %q", code, ui.ErrorWriter.String()) 190 } 191 ui.ErrorWriter.Reset() 192 } 193 194 func TestValidateCommand_From_URL(t *testing.T) { 195 ci.Parallel(t) 196 ui := cli.NewMockUi() 197 cmd := &JobRunCommand{ 198 Meta: Meta{Ui: ui}, 199 } 200 201 args := []string{"https://example.com/foo/bar"} 202 if code := cmd.Run(args); code != 1 { 203 t.Fatalf("expected exit code 1, got %d: %q", code, ui.ErrorWriter.String()) 204 } 205 206 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error getting jobfile") { 207 t.Fatalf("expected error getting jobfile, got: %s", out) 208 } 209 } 210 211 func TestValidateCommand_JSON(t *testing.T) { 212 ci.Parallel(t) 213 214 _, _, addr := testServer(t, false, nil) 215 216 ui := cli.NewMockUi() 217 cmd := &JobValidateCommand{ 218 Meta: Meta{Ui: ui}, 219 } 220 221 code := cmd.Run([]string{"-address", addr, "-json", "testdata/example-short.json"}) 222 223 require.Zerof(t, code, "stdout: %s\nstdout: %s\n", 224 ui.OutputWriter.String(), ui.ErrorWriter.String()) 225 226 code = cmd.Run([]string{"-address", addr, "-json", "testdata/example-short-bad.json"}) 227 228 require.Equalf(t, 1, code, "stdout: %s\nstdout: %s\n", 229 ui.OutputWriter.String(), ui.ErrorWriter.String()) 230 }