github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/client/driver/raw_exec_test.go (about) 1 package driver 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "path/filepath" 7 "reflect" 8 "strings" 9 "syscall" 10 "testing" 11 "time" 12 13 "github.com/ncodes/nomad/client/config" 14 "github.com/ncodes/nomad/client/driver/env" 15 "github.com/ncodes/nomad/helper/testtask" 16 "github.com/ncodes/nomad/nomad/structs" 17 "github.com/ncodes/nomad/testutil" 18 ) 19 20 func TestRawExecDriver_Fingerprint(t *testing.T) { 21 task := &structs.Task{ 22 Name: "foo", 23 Driver: "raw_exec", 24 Resources: structs.DefaultResources(), 25 } 26 ctx := testDriverContexts(t, task) 27 defer ctx.AllocDir.Destroy() 28 d := NewRawExecDriver(ctx.DriverCtx) 29 node := &structs.Node{ 30 Attributes: make(map[string]string), 31 } 32 33 // Disable raw exec. 34 cfg := &config.Config{Options: map[string]string{rawExecConfigOption: "false"}} 35 36 apply, err := d.Fingerprint(cfg, node) 37 if err != nil { 38 t.Fatalf("err: %v", err) 39 } 40 if apply { 41 t.Fatalf("should not apply") 42 } 43 if node.Attributes["driver.raw_exec"] != "" { 44 t.Fatalf("driver incorrectly enabled") 45 } 46 47 // Enable raw exec. 48 cfg.Options[rawExecConfigOption] = "true" 49 apply, err = d.Fingerprint(cfg, node) 50 if err != nil { 51 t.Fatalf("err: %v", err) 52 } 53 if !apply { 54 t.Fatalf("should apply") 55 } 56 if node.Attributes["driver.raw_exec"] != "1" { 57 t.Fatalf("driver not enabled") 58 } 59 } 60 61 func TestRawExecDriver_StartOpen_Wait(t *testing.T) { 62 task := &structs.Task{ 63 Name: "sleep", 64 Driver: "raw_exec", 65 Config: map[string]interface{}{ 66 "command": testtask.Path(), 67 "args": []string{"sleep", "1s"}, 68 }, 69 LogConfig: &structs.LogConfig{ 70 MaxFiles: 10, 71 MaxFileSizeMB: 10, 72 }, 73 Resources: basicResources, 74 } 75 testtask.SetTaskEnv(task) 76 ctx := testDriverContexts(t, task) 77 defer ctx.AllocDir.Destroy() 78 d := NewRawExecDriver(ctx.DriverCtx) 79 80 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 81 t.Fatalf("prestart err: %v", err) 82 } 83 handle, err := d.Start(ctx.ExecCtx, task) 84 if err != nil { 85 t.Fatalf("err: %v", err) 86 } 87 if handle == nil { 88 t.Fatalf("missing handle") 89 } 90 91 // Attempt to open 92 handle2, err := d.Open(ctx.ExecCtx, handle.ID()) 93 if err != nil { 94 t.Fatalf("err: %v", err) 95 } 96 if handle2 == nil { 97 t.Fatalf("missing handle") 98 } 99 100 // Task should terminate quickly 101 select { 102 case <-handle2.WaitCh(): 103 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 104 t.Fatalf("timeout") 105 } 106 handle.Kill() 107 handle2.Kill() 108 } 109 110 func TestRawExecDriver_Start_Wait(t *testing.T) { 111 task := &structs.Task{ 112 Name: "sleep", 113 Driver: "raw_exec", 114 Config: map[string]interface{}{ 115 "command": testtask.Path(), 116 "args": []string{"sleep", "1s"}, 117 }, 118 LogConfig: &structs.LogConfig{ 119 MaxFiles: 10, 120 MaxFileSizeMB: 10, 121 }, 122 Resources: basicResources, 123 } 124 testtask.SetTaskEnv(task) 125 ctx := testDriverContexts(t, task) 126 defer ctx.AllocDir.Destroy() 127 d := NewRawExecDriver(ctx.DriverCtx) 128 129 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 130 t.Fatalf("prestart err: %v", err) 131 } 132 handle, err := d.Start(ctx.ExecCtx, task) 133 if err != nil { 134 t.Fatalf("err: %v", err) 135 } 136 if handle == nil { 137 t.Fatalf("missing handle") 138 } 139 140 // Update should be a no-op 141 err = handle.Update(task) 142 if err != nil { 143 t.Fatalf("err: %v", err) 144 } 145 146 // Task should terminate quickly 147 select { 148 case res := <-handle.WaitCh(): 149 if !res.Successful() { 150 t.Fatalf("err: %v", res) 151 } 152 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 153 t.Fatalf("timeout") 154 } 155 } 156 157 func TestRawExecDriver_Start_Wait_AllocDir(t *testing.T) { 158 exp := []byte{'w', 'i', 'n'} 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 handle, err := d.Start(ctx.ExecCtx, task) 187 if err != nil { 188 t.Fatalf("err: %v", err) 189 } 190 if handle == nil { 191 t.Fatalf("missing handle") 192 } 193 194 // Task should terminate quickly 195 select { 196 case res := <-handle.WaitCh(): 197 if !res.Successful() { 198 t.Fatalf("err: %v", res) 199 } 200 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 201 t.Fatalf("timeout") 202 } 203 204 // Check that data was written to the shared alloc directory. 205 outputFile := filepath.Join(ctx.AllocDir.SharedDir, file) 206 act, err := ioutil.ReadFile(outputFile) 207 if err != nil { 208 t.Fatalf("Couldn't read expected output: %v", err) 209 } 210 211 if !reflect.DeepEqual(act, exp) { 212 t.Fatalf("Command outputted %v; want %v", act, exp) 213 } 214 } 215 216 func TestRawExecDriver_Start_Kill_Wait(t *testing.T) { 217 task := &structs.Task{ 218 Name: "sleep", 219 Driver: "raw_exec", 220 Config: map[string]interface{}{ 221 "command": testtask.Path(), 222 "args": []string{"sleep", "45s"}, 223 }, 224 LogConfig: &structs.LogConfig{ 225 MaxFiles: 10, 226 MaxFileSizeMB: 10, 227 }, 228 Resources: basicResources, 229 } 230 testtask.SetTaskEnv(task) 231 232 ctx := testDriverContexts(t, task) 233 defer ctx.AllocDir.Destroy() 234 d := NewRawExecDriver(ctx.DriverCtx) 235 236 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 237 t.Fatalf("prestart err: %v", err) 238 } 239 handle, err := d.Start(ctx.ExecCtx, task) 240 if err != nil { 241 t.Fatalf("err: %v", err) 242 } 243 if handle == nil { 244 t.Fatalf("missing handle") 245 } 246 247 go func() { 248 time.Sleep(1 * time.Second) 249 err := handle.Kill() 250 251 // Can't rely on the ordering between wait and kill on travis... 252 if !testutil.IsTravis() && err != nil { 253 t.Fatalf("err: %v", err) 254 } 255 }() 256 257 // Task should terminate quickly 258 select { 259 case res := <-handle.WaitCh(): 260 if res.Successful() { 261 t.Fatal("should err") 262 } 263 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 264 t.Fatalf("timeout") 265 } 266 } 267 268 func TestRawExecDriverUser(t *testing.T) { 269 task := &structs.Task{ 270 Name: "sleep", 271 Driver: "raw_exec", 272 User: "alice", 273 Config: map[string]interface{}{ 274 "command": testtask.Path(), 275 "args": []string{"sleep", "45s"}, 276 }, 277 LogConfig: &structs.LogConfig{ 278 MaxFiles: 10, 279 MaxFileSizeMB: 10, 280 }, 281 Resources: basicResources, 282 } 283 testtask.SetTaskEnv(task) 284 285 ctx := testDriverContexts(t, task) 286 defer ctx.AllocDir.Destroy() 287 d := NewRawExecDriver(ctx.DriverCtx) 288 289 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 290 t.Fatalf("prestart err: %v", err) 291 } 292 handle, err := d.Start(ctx.ExecCtx, task) 293 if err == nil { 294 handle.Kill() 295 t.Fatalf("Should've failed") 296 } 297 msg := "unknown user alice" 298 if !strings.Contains(err.Error(), msg) { 299 t.Fatalf("Expecting '%v' in '%v'", msg, err) 300 } 301 } 302 303 func TestRawExecDriver_Signal(t *testing.T) { 304 task := &structs.Task{ 305 Name: "signal", 306 Driver: "raw_exec", 307 Config: map[string]interface{}{ 308 "command": "/bin/bash", 309 "args": []string{"test.sh"}, 310 }, 311 LogConfig: &structs.LogConfig{ 312 MaxFiles: 10, 313 MaxFileSizeMB: 10, 314 }, 315 Resources: basicResources, 316 KillTimeout: 10 * time.Second, 317 } 318 319 ctx := testDriverContexts(t, task) 320 defer ctx.AllocDir.Destroy() 321 d := NewRawExecDriver(ctx.DriverCtx) 322 323 testFile := filepath.Join(ctx.ExecCtx.TaskDir.Dir, "test.sh") 324 testData := []byte(` 325 at_term() { 326 echo 'Terminated.' 327 exit 3 328 } 329 trap at_term USR1 330 while true; do 331 sleep 1 332 done 333 `) 334 if err := ioutil.WriteFile(testFile, testData, 0777); err != nil { 335 t.Fatalf("Failed to write data: %v", err) 336 } 337 338 if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { 339 t.Fatalf("prestart err: %v", err) 340 } 341 handle, err := d.Start(ctx.ExecCtx, task) 342 if err != nil { 343 t.Fatalf("err: %v", err) 344 } 345 if handle == nil { 346 t.Fatalf("missing handle") 347 } 348 349 go func() { 350 time.Sleep(100 * time.Millisecond) 351 err := handle.Signal(syscall.SIGUSR1) 352 if err != nil { 353 t.Fatalf("err: %v", err) 354 } 355 }() 356 357 // Task should terminate quickly 358 select { 359 case res := <-handle.WaitCh(): 360 if res.Successful() { 361 t.Fatal("should err") 362 } 363 case <-time.After(time.Duration(testutil.TestMultiplier()*6) * time.Second): 364 t.Fatalf("timeout") 365 } 366 367 // Check the log file to see it exited because of the signal 368 outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "signal.stdout.0") 369 act, err := ioutil.ReadFile(outputFile) 370 if err != nil { 371 t.Fatalf("Couldn't read expected output: %v", err) 372 } 373 374 exp := "Terminated." 375 if strings.TrimSpace(string(act)) != exp { 376 t.Logf("Read from %v", outputFile) 377 t.Fatalf("Command outputted %v; want %v", act, exp) 378 } 379 }