github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/client/driver/exec_test.go (about) 1 package driver 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "reflect" 10 "strings" 11 "syscall" 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/nomad/structs" 18 "github.com/hashicorp/nomad/testutil" 19 20 ctestutils "github.com/hashicorp/nomad/client/testutil" 21 ) 22 23 func TestExecDriver_Fingerprint(t *testing.T) { 24 t.Parallel() 25 ctestutils.ExecCompatible(t) 26 driverCtx, _ := testDriverContexts(&structs.Task{Name: "foo"}) 27 d := NewExecDriver(driverCtx) 28 node := &structs.Node{ 29 Attributes: map[string]string{ 30 "unique.cgroup.mountpoint": "/sys/fs/cgroup", 31 }, 32 } 33 apply, err := d.Fingerprint(&config.Config{}, node) 34 if err != nil { 35 t.Fatalf("err: %v", err) 36 } 37 if !apply { 38 t.Fatalf("should apply") 39 } 40 if node.Attributes["driver.exec"] == "" { 41 t.Fatalf("missing driver") 42 } 43 } 44 45 func TestExecDriver_StartOpen_Wait(t *testing.T) { 46 t.Parallel() 47 ctestutils.ExecCompatible(t) 48 task := &structs.Task{ 49 Name: "sleep", 50 Config: map[string]interface{}{ 51 "command": "/bin/sleep", 52 "args": []string{"5"}, 53 }, 54 LogConfig: &structs.LogConfig{ 55 MaxFiles: 10, 56 MaxFileSizeMB: 10, 57 }, 58 Resources: basicResources, 59 } 60 61 driverCtx, execCtx := testDriverContexts(task) 62 defer execCtx.AllocDir.Destroy() 63 d := NewExecDriver(driverCtx) 64 65 handle, err := d.Start(execCtx, task) 66 if err != nil { 67 t.Fatalf("err: %v", err) 68 } 69 if handle == nil { 70 t.Fatalf("missing handle") 71 } 72 73 // Attempt to open 74 handle2, err := d.Open(execCtx, handle.ID()) 75 if err != nil { 76 t.Fatalf("err: %v", err) 77 } 78 if handle2 == nil { 79 t.Fatalf("missing handle") 80 } 81 82 handle.Kill() 83 handle2.Kill() 84 } 85 86 func TestExecDriver_KillUserPid_OnPluginReconnectFailure(t *testing.T) { 87 t.Parallel() 88 ctestutils.ExecCompatible(t) 89 task := &structs.Task{ 90 Name: "sleep", 91 Config: map[string]interface{}{ 92 "command": "/bin/sleep", 93 "args": []string{"1000000"}, 94 }, 95 LogConfig: &structs.LogConfig{ 96 MaxFiles: 10, 97 MaxFileSizeMB: 10, 98 }, 99 Resources: basicResources, 100 } 101 102 driverCtx, execCtx := testDriverContexts(task) 103 defer execCtx.AllocDir.Destroy() 104 d := NewExecDriver(driverCtx) 105 106 handle, err := d.Start(execCtx, task) 107 if err != nil { 108 t.Fatalf("err: %v", err) 109 } 110 if handle == nil { 111 t.Fatalf("missing handle") 112 } 113 defer handle.Kill() 114 115 id := &execId{} 116 if err := json.Unmarshal([]byte(handle.ID()), id); err != nil { 117 t.Fatalf("Failed to parse handle '%s': %v", handle.ID(), err) 118 } 119 pluginPid := id.PluginConfig.Pid 120 proc, err := os.FindProcess(pluginPid) 121 if err != nil { 122 t.Fatalf("can't find plugin pid: %v", pluginPid) 123 } 124 if err := proc.Kill(); err != nil { 125 t.Fatalf("can't kill plugin pid: %v", err) 126 } 127 128 // Attempt to open 129 handle2, err := d.Open(execCtx, handle.ID()) 130 if err == nil { 131 t.Fatalf("expected error") 132 } 133 if handle2 != nil { 134 handle2.Kill() 135 t.Fatalf("expected handle2 to be nil") 136 } 137 // Test if the userpid is still present 138 userProc, err := os.FindProcess(id.UserPid) 139 140 err = userProc.Signal(syscall.Signal(0)) 141 142 if err == nil { 143 t.Fatalf("expected user process to die") 144 } 145 } 146 147 func TestExecDriver_Start_Wait(t *testing.T) { 148 t.Parallel() 149 ctestutils.ExecCompatible(t) 150 task := &structs.Task{ 151 Name: "sleep", 152 Config: map[string]interface{}{ 153 "command": "/bin/sleep", 154 "args": []string{"2"}, 155 }, 156 LogConfig: &structs.LogConfig{ 157 MaxFiles: 10, 158 MaxFileSizeMB: 10, 159 }, 160 Resources: basicResources, 161 } 162 163 driverCtx, execCtx := testDriverContexts(task) 164 defer execCtx.AllocDir.Destroy() 165 d := NewExecDriver(driverCtx) 166 167 handle, err := d.Start(execCtx, task) 168 if err != nil { 169 t.Fatalf("err: %v", err) 170 } 171 if handle == nil { 172 t.Fatalf("missing handle") 173 } 174 175 // Update should be a no-op 176 err = handle.Update(task) 177 if err != nil { 178 t.Fatalf("err: %v", err) 179 } 180 181 // Task should terminate quickly 182 select { 183 case res := <-handle.WaitCh(): 184 if !res.Successful() { 185 t.Fatalf("err: %v", res) 186 } 187 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 188 t.Fatalf("timeout") 189 } 190 } 191 192 func TestExecDriver_Start_Wait_AllocDir(t *testing.T) { 193 t.Parallel() 194 ctestutils.ExecCompatible(t) 195 196 exp := []byte{'w', 'i', 'n'} 197 file := "output.txt" 198 task := &structs.Task{ 199 Name: "sleep", 200 Config: map[string]interface{}{ 201 "command": "/bin/bash", 202 "args": []string{ 203 "-c", 204 fmt.Sprintf(`sleep 1; echo -n %s > ${%s}/%s`, string(exp), env.AllocDir, file), 205 }, 206 }, 207 LogConfig: &structs.LogConfig{ 208 MaxFiles: 10, 209 MaxFileSizeMB: 10, 210 }, 211 Resources: basicResources, 212 } 213 214 driverCtx, execCtx := testDriverContexts(task) 215 defer execCtx.AllocDir.Destroy() 216 d := NewExecDriver(driverCtx) 217 218 handle, err := d.Start(execCtx, task) 219 if err != nil { 220 t.Fatalf("err: %v", err) 221 } 222 if handle == nil { 223 t.Fatalf("missing handle") 224 } 225 226 // Task should terminate quickly 227 select { 228 case res := <-handle.WaitCh(): 229 if !res.Successful() { 230 t.Fatalf("err: %v", res) 231 } 232 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 233 t.Fatalf("timeout") 234 } 235 236 // Check that data was written to the shared alloc directory. 237 outputFile := filepath.Join(execCtx.AllocDir.SharedDir, file) 238 act, err := ioutil.ReadFile(outputFile) 239 if err != nil { 240 t.Fatalf("Couldn't read expected output: %v", err) 241 } 242 243 if !reflect.DeepEqual(act, exp) { 244 t.Fatalf("Command outputted %v; want %v", act, exp) 245 } 246 } 247 248 func TestExecDriver_Start_Kill_Wait(t *testing.T) { 249 t.Parallel() 250 ctestutils.ExecCompatible(t) 251 task := &structs.Task{ 252 Name: "sleep", 253 Config: map[string]interface{}{ 254 "command": "/bin/sleep", 255 "args": []string{"100"}, 256 }, 257 LogConfig: &structs.LogConfig{ 258 MaxFiles: 10, 259 MaxFileSizeMB: 10, 260 }, 261 Resources: basicResources, 262 KillTimeout: 10 * time.Second, 263 } 264 265 driverCtx, execCtx := testDriverContexts(task) 266 defer execCtx.AllocDir.Destroy() 267 d := NewExecDriver(driverCtx) 268 269 handle, err := d.Start(execCtx, task) 270 if err != nil { 271 t.Fatalf("err: %v", err) 272 } 273 if handle == nil { 274 t.Fatalf("missing handle") 275 } 276 277 go func() { 278 time.Sleep(100 * time.Millisecond) 279 err := handle.Kill() 280 if err != nil { 281 t.Fatalf("err: %v", err) 282 } 283 }() 284 285 // Task should terminate quickly 286 select { 287 case res := <-handle.WaitCh(): 288 if res.Successful() { 289 t.Fatal("should err") 290 } 291 case <-time.After(time.Duration(testutil.TestMultiplier()*10) * time.Second): 292 t.Fatalf("timeout") 293 } 294 } 295 296 func TestExecDriverUser(t *testing.T) { 297 t.Parallel() 298 ctestutils.ExecCompatible(t) 299 task := &structs.Task{ 300 Name: "sleep", 301 User: "alice", 302 Config: map[string]interface{}{ 303 "command": "/bin/sleep", 304 "args": []string{"100"}, 305 }, 306 LogConfig: &structs.LogConfig{ 307 MaxFiles: 10, 308 MaxFileSizeMB: 10, 309 }, 310 Resources: basicResources, 311 KillTimeout: 10 * time.Second, 312 } 313 314 driverCtx, execCtx := testDriverContexts(task) 315 defer execCtx.AllocDir.Destroy() 316 d := NewExecDriver(driverCtx) 317 318 handle, err := d.Start(execCtx, task) 319 if err == nil { 320 handle.Kill() 321 t.Fatalf("Should've failed") 322 } 323 msg := "user alice" 324 if !strings.Contains(err.Error(), msg) { 325 t.Fatalf("Expecting '%v' in '%v'", msg, err) 326 } 327 }