github.com/rohankumardubey/nomad@v0.11.8/command/job_plan_test.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "strconv" 8 "strings" 9 "testing" 10 11 "github.com/hashicorp/nomad/api" 12 "github.com/hashicorp/nomad/testutil" 13 "github.com/mitchellh/cli" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestPlanCommand_Implements(t *testing.T) { 18 t.Parallel() 19 var _ cli.Command = &JobRunCommand{} 20 } 21 22 func TestPlanCommand_Fails(t *testing.T) { 23 t.Parallel() 24 ui := new(cli.MockUi) 25 cmd := &JobPlanCommand{Meta: Meta{Ui: ui}} 26 27 // Create a server 28 s := testutil.NewTestServer(t, nil) 29 defer s.Stop() 30 os.Setenv("NOMAD_ADDR", fmt.Sprintf("http://%s", s.HTTPAddr)) 31 32 // Fails on misuse 33 if code := cmd.Run([]string{"some", "bad", "args"}); code != 255 { 34 t.Fatalf("expected exit code 1, got: %d", code) 35 } 36 if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) { 37 t.Fatalf("expected help output, got: %s", out) 38 } 39 ui.ErrorWriter.Reset() 40 41 // Fails when specified file does not exist 42 if code := cmd.Run([]string{"/unicorns/leprechauns"}); code != 255 { 43 t.Fatalf("expect exit 255, got: %d", code) 44 } 45 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error getting job struct") { 46 t.Fatalf("expect getting job struct error, got: %s", out) 47 } 48 ui.ErrorWriter.Reset() 49 50 // Fails on invalid HCL 51 fh1, err := ioutil.TempFile("", "nomad") 52 if err != nil { 53 t.Fatalf("err: %s", err) 54 } 55 defer os.Remove(fh1.Name()) 56 if _, err := fh1.WriteString("nope"); err != nil { 57 t.Fatalf("err: %s", err) 58 } 59 if code := cmd.Run([]string{fh1.Name()}); code != 255 { 60 t.Fatalf("expect exit 255, got: %d", code) 61 } 62 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error getting job struct") { 63 t.Fatalf("expect parsing error, got: %s", out) 64 } 65 ui.ErrorWriter.Reset() 66 67 // Fails on invalid job spec 68 fh2, err := ioutil.TempFile("", "nomad") 69 if err != nil { 70 t.Fatalf("err: %s", err) 71 } 72 defer os.Remove(fh2.Name()) 73 if _, err := fh2.WriteString(`job "job1" {}`); err != nil { 74 t.Fatalf("err: %s", err) 75 } 76 if code := cmd.Run([]string{fh2.Name()}); code != 255 { 77 t.Fatalf("expect exit 255, got: %d", code) 78 } 79 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error during plan") { 80 t.Fatalf("expect validation error, got: %s", out) 81 } 82 ui.ErrorWriter.Reset() 83 84 // Fails on connection failure (requires a valid job) 85 fh3, err := ioutil.TempFile("", "nomad") 86 if err != nil { 87 t.Fatalf("err: %s", err) 88 } 89 defer os.Remove(fh3.Name()) 90 _, err = fh3.WriteString(` 91 job "job1" { 92 type = "service" 93 datacenters = [ "dc1" ] 94 group "group1" { 95 count = 1 96 task "task1" { 97 driver = "exec" 98 resources = { 99 cpu = 1000 100 memory = 512 101 } 102 } 103 } 104 }`) 105 if err != nil { 106 t.Fatalf("err: %s", err) 107 } 108 if code := cmd.Run([]string{"-address=nope", fh3.Name()}); code != 255 { 109 t.Fatalf("expected exit code 255, got: %d", code) 110 } 111 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error during plan") { 112 t.Fatalf("expected failed query error, got: %s", out) 113 } 114 } 115 116 func TestPlanCommand_From_STDIN(t *testing.T) { 117 t.Parallel() 118 stdinR, stdinW, err := os.Pipe() 119 if err != nil { 120 t.Fatalf("err: %s", err) 121 } 122 123 ui := new(cli.MockUi) 124 cmd := &JobPlanCommand{ 125 Meta: Meta{Ui: ui}, 126 JobGetter: JobGetter{testStdin: stdinR}, 127 } 128 129 go func() { 130 stdinW.WriteString(` 131 job "job1" { 132 type = "service" 133 datacenters = [ "dc1" ] 134 group "group1" { 135 count = 1 136 task "task1" { 137 driver = "exec" 138 resources = { 139 cpu = 1000 140 memory = 512 141 } 142 } 143 } 144 }`) 145 stdinW.Close() 146 }() 147 148 args := []string{"-"} 149 if code := cmd.Run(args); code != 255 { 150 t.Fatalf("expected exit code 255, got %d: %q", code, ui.ErrorWriter.String()) 151 } 152 153 if out := ui.ErrorWriter.String(); !strings.Contains(out, "connection refused") { 154 t.Fatalf("expected connection refused error, got: %s", out) 155 } 156 ui.ErrorWriter.Reset() 157 } 158 159 func TestPlanCommand_From_URL(t *testing.T) { 160 t.Parallel() 161 ui := new(cli.MockUi) 162 cmd := &JobPlanCommand{ 163 Meta: Meta{Ui: ui}, 164 } 165 166 args := []string{"https://example.com/foo/bar"} 167 if code := cmd.Run(args); code != 255 { 168 t.Fatalf("expected exit code 255, got %d: %q", code, ui.ErrorWriter.String()) 169 } 170 171 if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error getting jobfile") { 172 t.Fatalf("expected error getting jobfile, got: %s", out) 173 } 174 } 175 176 func TestPlanCommad_Preemptions(t *testing.T) { 177 t.Parallel() 178 ui := new(cli.MockUi) 179 cmd := &JobPlanCommand{Meta: Meta{Ui: ui}} 180 require := require.New(t) 181 182 // Only one preempted alloc 183 resp1 := &api.JobPlanResponse{ 184 Annotations: &api.PlanAnnotations{ 185 PreemptedAllocs: []*api.AllocationListStub{ 186 { 187 ID: "alloc1", 188 JobID: "jobID1", 189 TaskGroup: "meta", 190 JobType: "batch", 191 Namespace: "test", 192 }, 193 }, 194 }, 195 } 196 cmd.addPreemptions(resp1) 197 out := ui.OutputWriter.String() 198 require.Contains(out, "Alloc ID") 199 require.Contains(out, "alloc1") 200 201 // Less than 10 unique job ids 202 var preemptedAllocs []*api.AllocationListStub 203 for i := 0; i < 12; i++ { 204 job_id := "job" + strconv.Itoa(i%4) 205 alloc := &api.AllocationListStub{ 206 ID: "alloc", 207 JobID: job_id, 208 TaskGroup: "meta", 209 JobType: "batch", 210 Namespace: "test", 211 } 212 preemptedAllocs = append(preemptedAllocs, alloc) 213 } 214 215 resp2 := &api.JobPlanResponse{ 216 Annotations: &api.PlanAnnotations{ 217 PreemptedAllocs: preemptedAllocs, 218 }, 219 } 220 ui.OutputWriter.Reset() 221 cmd.addPreemptions(resp2) 222 out = ui.OutputWriter.String() 223 require.Contains(out, "Job ID") 224 require.Contains(out, "Namespace") 225 226 // More than 10 unique job IDs 227 preemptedAllocs = make([]*api.AllocationListStub, 0) 228 var job_type string 229 for i := 0; i < 20; i++ { 230 job_id := "job" + strconv.Itoa(i) 231 if i%2 == 0 { 232 job_type = "service" 233 } else { 234 job_type = "batch" 235 } 236 alloc := &api.AllocationListStub{ 237 ID: "alloc", 238 JobID: job_id, 239 TaskGroup: "meta", 240 JobType: job_type, 241 Namespace: "test", 242 } 243 preemptedAllocs = append(preemptedAllocs, alloc) 244 } 245 246 resp3 := &api.JobPlanResponse{ 247 Annotations: &api.PlanAnnotations{ 248 PreemptedAllocs: preemptedAllocs, 249 }, 250 } 251 ui.OutputWriter.Reset() 252 cmd.addPreemptions(resp3) 253 out = ui.OutputWriter.String() 254 require.Contains(out, "Job Type") 255 require.Contains(out, "batch") 256 require.Contains(out, "service") 257 }