github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/driver/raw_exec_test.go (about) 1 package driver 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io/ioutil" 8 "path/filepath" 9 "reflect" 10 "runtime" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/hashicorp/nomad/client/config" 16 "github.com/hashicorp/nomad/client/driver/env" 17 "github.com/hashicorp/nomad/helper/testtask" 18 "github.com/hashicorp/nomad/nomad/structs" 19 "github.com/hashicorp/nomad/testutil" 20 ) 21 22 func TestRawExecDriver_Fingerprint(t *testing.T) { 23 t.Parallel() 24 task := &structs.Task{ 25 Name: "foo", 26 Driver: "raw_exec", 27 Resources: structs.DefaultResources(), 28 } 29 ctx := testDriverContexts(t, task) 30 defer ctx.AllocDir.Destroy() 31 d := NewRawExecDriver(ctx.DriverCtx) 32 node := &structs.Node{ 33 Attributes: make(map[string]string), 34 } 35 36 // Disable raw exec. 37 cfg := &config.Config{Options: map[string]string{rawExecConfigOption: "false"}} 38 39 apply, err := d.Fingerprint(cfg, node) 40 if err != nil { 41 t.Fatalf("err: %v", err) 42 } 43 if apply { 44 t.Fatalf("should not apply") 45 } 46 if node.Attributes["driver.raw_exec"] != "" { 47 t.Fatalf("driver incorrectly enabled") 48 } 49 50 // Enable raw exec. 51 cfg.Options[rawExecConfigOption] = "true" 52 apply, err = d.Fingerprint(cfg, node) 53 if err != nil { 54 t.Fatalf("err: %v", err) 55 } 56 if !apply { 57 t.Fatalf("should apply") 58 } 59 if node.Attributes["driver.raw_exec"] != "1" { 60 t.Fatalf("driver not enabled") 61 } 62 } 63 64 func TestRawExecDriver_StartOpen_Wait(t *testing.T) { 65 t.Parallel() 66 task := &structs.Task{ 67 Name: "sleep", 68 Driver: "raw_exec", 69 Config: map[string]interface{}{ 70 "command": testtask.Path(), 71 "args": []string{"sleep", "1s"}, 72 }, 73 LogConfig: &structs.LogConfig{ 74 MaxFiles: 10, 75 MaxFileSizeMB: 10, 76 }, 77 Resources: basicResources, 78 } 79 testtask.SetTaskEnv(task) 80 ctx := testDriverContexts(t, task) 81 defer ctx.AllocDir.Destroy() 82 d := NewRawExecDriver(ctx.DriverCtx) 83 84 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 85 t.Fatalf("prestart err: %v", err) 86 } 87 resp, err := d.Start(ctx.ExecCtx, task) 88 if err != nil { 89 t.Fatalf("err: %v", err) 90 } 91 92 // Attempt to open 93 handle2, err := d.Open(ctx.ExecCtx, resp.Handle.ID()) 94 if err != nil { 95 t.Fatalf("err: %v", err) 96 } 97 if handle2 == nil { 98 t.Fatalf("missing handle") 99 } 100 101 // Task should terminate quickly 102 select { 103 case <-handle2.WaitCh(): 104 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 105 t.Fatalf("timeout") 106 } 107 resp.Handle.Kill() 108 handle2.Kill() 109 } 110 111 func TestRawExecDriver_Start_Wait(t *testing.T) { 112 t.Parallel() 113 task := &structs.Task{ 114 Name: "sleep", 115 Driver: "raw_exec", 116 Config: map[string]interface{}{ 117 "command": testtask.Path(), 118 "args": []string{"sleep", "1s"}, 119 }, 120 LogConfig: &structs.LogConfig{ 121 MaxFiles: 10, 122 MaxFileSizeMB: 10, 123 }, 124 Resources: basicResources, 125 } 126 testtask.SetTaskEnv(task) 127 ctx := testDriverContexts(t, task) 128 defer ctx.AllocDir.Destroy() 129 d := NewRawExecDriver(ctx.DriverCtx) 130 131 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 132 t.Fatalf("prestart err: %v", err) 133 } 134 resp, err := d.Start(ctx.ExecCtx, task) 135 if err != nil { 136 t.Fatalf("err: %v", err) 137 } 138 139 // Update should be a no-op 140 err = resp.Handle.Update(task) 141 if err != nil { 142 t.Fatalf("err: %v", err) 143 } 144 145 // Task should terminate quickly 146 select { 147 case res := <-resp.Handle.WaitCh(): 148 if !res.Successful() { 149 t.Fatalf("err: %v", res) 150 } 151 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 152 t.Fatalf("timeout") 153 } 154 } 155 156 func TestRawExecDriver_Start_Wait_AllocDir(t *testing.T) { 157 t.Parallel() 158 exp := []byte("win") 159 file := "output.txt" 160 outPath := fmt.Sprintf(`${%s}/%s`, env.AllocDir, file) 161 task := &structs.Task{ 162 Name: "sleep", 163 Driver: "raw_exec", 164 Config: map[string]interface{}{ 165 "command": testtask.Path(), 166 "args": []string{ 167 "sleep", "1s", 168 "write", string(exp), outPath, 169 }, 170 }, 171 LogConfig: &structs.LogConfig{ 172 MaxFiles: 10, 173 MaxFileSizeMB: 10, 174 }, 175 Resources: basicResources, 176 } 177 testtask.SetTaskEnv(task) 178 179 ctx := testDriverContexts(t, task) 180 defer ctx.AllocDir.Destroy() 181 d := NewRawExecDriver(ctx.DriverCtx) 182 183 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 184 t.Fatalf("prestart err: %v", err) 185 } 186 resp, err := d.Start(ctx.ExecCtx, task) 187 if err != nil { 188 t.Fatalf("err: %v", err) 189 } 190 191 // Task should terminate quickly 192 select { 193 case res := <-resp.Handle.WaitCh(): 194 if !res.Successful() { 195 t.Fatalf("err: %v", res) 196 } 197 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 198 t.Fatalf("timeout") 199 } 200 201 // Check that data was written to the shared alloc directory. 202 outputFile := filepath.Join(ctx.AllocDir.SharedDir, file) 203 act, err := ioutil.ReadFile(outputFile) 204 if err != nil { 205 t.Fatalf("Couldn't read expected output: %v", err) 206 } 207 208 if !reflect.DeepEqual(act, exp) { 209 t.Fatalf("Command outputted %v; want %v", act, exp) 210 } 211 } 212 213 func TestRawExecDriver_Start_Kill_Wait(t *testing.T) { 214 t.Parallel() 215 task := &structs.Task{ 216 Name: "sleep", 217 Driver: "raw_exec", 218 Config: map[string]interface{}{ 219 "command": testtask.Path(), 220 "args": []string{"sleep", "45s"}, 221 }, 222 LogConfig: &structs.LogConfig{ 223 MaxFiles: 10, 224 MaxFileSizeMB: 10, 225 }, 226 Resources: basicResources, 227 } 228 testtask.SetTaskEnv(task) 229 230 ctx := testDriverContexts(t, task) 231 defer ctx.AllocDir.Destroy() 232 d := NewRawExecDriver(ctx.DriverCtx) 233 234 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 235 t.Fatalf("prestart err: %v", err) 236 } 237 resp, err := d.Start(ctx.ExecCtx, task) 238 if err != nil { 239 t.Fatalf("err: %v", err) 240 } 241 242 go func() { 243 time.Sleep(1 * time.Second) 244 err := resp.Handle.Kill() 245 246 // Can't rely on the ordering between wait and kill on travis... 247 if !testutil.IsTravis() && err != nil { 248 t.Fatalf("err: %v", err) 249 } 250 }() 251 252 // Task should terminate quickly 253 select { 254 case res := <-resp.Handle.WaitCh(): 255 if res.Successful() { 256 t.Fatal("should err") 257 } 258 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 259 t.Fatalf("timeout") 260 } 261 } 262 263 func TestRawExecDriverUser(t *testing.T) { 264 t.Parallel() 265 if runtime.GOOS != "linux" { 266 t.Skip("Linux only test") 267 } 268 task := &structs.Task{ 269 Name: "sleep", 270 Driver: "raw_exec", 271 User: "alice", 272 Config: map[string]interface{}{ 273 "command": testtask.Path(), 274 "args": []string{"sleep", "45s"}, 275 }, 276 LogConfig: &structs.LogConfig{ 277 MaxFiles: 10, 278 MaxFileSizeMB: 10, 279 }, 280 Resources: basicResources, 281 } 282 testtask.SetTaskEnv(task) 283 284 ctx := testDriverContexts(t, task) 285 defer ctx.AllocDir.Destroy() 286 d := NewRawExecDriver(ctx.DriverCtx) 287 288 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 289 t.Fatalf("prestart err: %v", err) 290 } 291 resp, err := d.Start(ctx.ExecCtx, task) 292 if err == nil { 293 resp.Handle.Kill() 294 t.Fatalf("Should've failed") 295 } 296 msg := "unknown user alice" 297 if !strings.Contains(err.Error(), msg) { 298 t.Fatalf("Expecting '%v' in '%v'", msg, err) 299 } 300 } 301 302 func TestRawExecDriver_HandlerExec(t *testing.T) { 303 t.Parallel() 304 task := &structs.Task{ 305 Name: "sleep", 306 Driver: "raw_exec", 307 Config: map[string]interface{}{ 308 "command": testtask.Path(), 309 "args": []string{"sleep", "9000"}, 310 }, 311 LogConfig: &structs.LogConfig{ 312 MaxFiles: 10, 313 MaxFileSizeMB: 10, 314 }, 315 Resources: basicResources, 316 } 317 testtask.SetTaskEnv(task) 318 ctx := testDriverContexts(t, task) 319 defer ctx.AllocDir.Destroy() 320 d := NewRawExecDriver(ctx.DriverCtx) 321 322 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 323 t.Fatalf("prestart err: %v", err) 324 } 325 resp, err := d.Start(ctx.ExecCtx, task) 326 if err != nil { 327 t.Fatalf("err: %v", err) 328 } 329 330 // Exec a command that should work 331 out, code, err := resp.Handle.Exec(context.TODO(), "/usr/bin/stat", []string{"/tmp"}) 332 if err != nil { 333 t.Fatalf("error exec'ing stat: %v", err) 334 } 335 if code != 0 { 336 t.Fatalf("expected `stat /alloc` to succeed but exit code was: %d", code) 337 } 338 if expected := 100; len(out) < expected { 339 t.Fatalf("expected at least %d bytes of output but found %d:\n%s", expected, len(out), out) 340 } 341 342 // Exec a command that should fail 343 out, code, err = resp.Handle.Exec(context.TODO(), "/usr/bin/stat", []string{"lkjhdsaflkjshowaisxmcvnlia"}) 344 if err != nil { 345 t.Fatalf("error exec'ing stat: %v", err) 346 } 347 if code == 0 { 348 t.Fatalf("expected `stat` to fail but exit code was: %d", code) 349 } 350 if expected := "No such file or directory"; !bytes.Contains(out, []byte(expected)) { 351 t.Fatalf("expected output to contain %q but found: %q", expected, out) 352 } 353 354 if err := resp.Handle.Kill(); err != nil { 355 t.Fatalf("error killing exec handle: %v", err) 356 } 357 }