open-nomad/client/driver/lxc_test.go

325 lines
7.2 KiB
Go
Raw Normal View History

//+build linux,lxc
2016-09-12 02:33:09 +00:00
package driver
import (
"bytes"
2016-09-12 02:33:09 +00:00
"fmt"
"io/ioutil"
2016-10-13 19:45:33 +00:00
"os"
"os/exec"
2016-10-13 19:45:33 +00:00
"path/filepath"
2016-09-12 02:33:09 +00:00
"testing"
"time"
"github.com/hashicorp/nomad/client/config"
ctestutil "github.com/hashicorp/nomad/client/testutil"
2016-09-12 02:33:09 +00:00
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
lxc "gopkg.in/lxc/go-lxc.v2"
)
func TestLxcDriver_Fingerprint(t *testing.T) {
2017-07-21 19:06:39 +00:00
t.Parallel()
2016-09-12 02:33:09 +00:00
if !lxcPresent(t) {
t.Skip("lxc not present")
}
task := &structs.Task{
Name: "foo",
Driver: "lxc",
2016-09-12 02:33:09 +00:00
Resources: structs.DefaultResources(),
}
ctx := testDriverContexts(t, task)
defer ctx.AllocDir.Destroy()
d := NewLxcDriver(ctx.DriverCtx)
2016-09-12 02:33:09 +00:00
node := &structs.Node{
Attributes: map[string]string{},
}
apply, err := d.Fingerprint(&config.Config{}, node)
if err != nil {
t.Fatalf("err: %v", err)
}
if !apply {
t.Fatalf("should apply by default")
2016-10-13 20:22:12 +00:00
}
apply, err = d.Fingerprint(&config.Config{Options: map[string]string{lxcConfigOption: "0"}}, node)
2016-10-13 20:22:12 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
2016-11-01 21:35:09 +00:00
if apply {
t.Fatalf("should not apply with config")
2016-09-12 02:33:09 +00:00
}
if node.Attributes["driver.lxc"] == "" {
t.Fatalf("missing driver")
}
}
func TestLxcDriver_Start_Wait(t *testing.T) {
2017-07-23 04:01:22 +00:00
if !testutil.IsTravis() {
t.Parallel()
}
2016-09-12 02:33:09 +00:00
if !lxcPresent(t) {
t.Skip("lxc not present")
}
ctestutil.RequireRoot(t)
2016-09-12 02:33:09 +00:00
task := &structs.Task{
Name: "foo",
Driver: "lxc",
2016-09-12 02:33:09 +00:00
Config: map[string]interface{}{
"template": "/usr/share/lxc/templates/lxc-busybox",
"volumes": []string{"/tmp/:mnt/tmp"},
2016-09-12 02:33:09 +00:00
},
KillTimeout: 10 * time.Second,
Resources: structs.DefaultResources(),
}
testFileContents := []byte("this should be visible under /mnt/tmp")
tmpFile, err := ioutil.TempFile("/tmp", "testlxcdriver_start_wait")
if err != nil {
t.Fatalf("error writing temp file: %v", err)
}
defer os.Remove(tmpFile.Name())
if _, err := tmpFile.Write(testFileContents); err != nil {
t.Fatalf("error writing temp file: %v", err)
}
if err := tmpFile.Close(); err != nil {
t.Fatalf("error closing temp file: %v", err)
}
ctx := testDriverContexts(t, task)
defer ctx.AllocDir.Destroy()
d := NewLxcDriver(ctx.DriverCtx)
2016-09-12 02:33:09 +00:00
if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
t.Fatalf("prestart err: %v", err)
}
2017-06-20 20:21:55 +00:00
sresp, err := d.Start(ctx.ExecCtx, task)
2016-09-12 02:33:09 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
2017-06-20 20:21:55 +00:00
lxcHandle, _ := sresp.Handle.(*lxcDriverHandle)
2016-09-12 02:33:09 +00:00
// Destroy the container after the test
defer func() {
lxcHandle.container.Stop()
lxcHandle.container.Destroy()
}()
testutil.WaitForResult(func() (bool, error) {
state := lxcHandle.container.State()
if state == lxc.RUNNING {
return true, nil
}
return false, fmt.Errorf("container in state: %v", state)
}, func(err error) {
t.Fatalf("err: %v", err)
})
2016-10-13 19:45:33 +00:00
// Look for mounted directories in their proper location
containerName := fmt.Sprintf("%s-%s", task.Name, ctx.DriverCtx.allocID)
for _, mnt := range []string{"alloc", "local", "secrets", "mnt/tmp"} {
2016-10-13 19:45:33 +00:00
fullpath := filepath.Join(lxcHandle.lxcPath, containerName, "rootfs", mnt)
stat, err := os.Stat(fullpath)
if err != nil {
t.Fatalf("err %v", err)
}
if !stat.IsDir() {
t.Fatalf("expected %q to be a dir", fullpath)
}
}
// Test that /mnt/tmp/$tempFile exists in the container:
mountedContents, err := exec.Command("lxc-attach", "-n", containerName, "--", "cat", filepath.Join("/mnt/", tmpFile.Name())).Output()
if err != nil {
t.Fatalf("err reading temp file in bind mount: %v", err)
}
if !bytes.Equal(mountedContents, testFileContents) {
t.Fatalf("contents of temp bind mounted file did not match, was '%s'", mountedContents)
}
2016-09-12 02:33:09 +00:00
// Desroy the container
2017-06-20 20:21:55 +00:00
if err := sresp.Handle.Kill(); err != nil {
2016-09-12 02:33:09 +00:00
t.Fatalf("err: %v", err)
}
select {
2017-06-20 20:21:55 +00:00
case res := <-sresp.Handle.WaitCh():
2016-09-12 02:33:09 +00:00
if !res.Successful() {
t.Fatalf("err: %v", res)
}
case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
t.Fatalf("timeout")
}
}
func TestLxcDriver_Open_Wait(t *testing.T) {
2017-07-23 04:01:22 +00:00
if !testutil.IsTravis() {
t.Parallel()
}
2016-09-12 02:33:09 +00:00
if !lxcPresent(t) {
t.Skip("lxc not present")
}
ctestutil.RequireRoot(t)
2016-09-12 02:33:09 +00:00
task := &structs.Task{
Name: "foo",
Driver: "lxc",
2016-09-12 02:33:09 +00:00
Config: map[string]interface{}{
"template": "/usr/share/lxc/templates/lxc-busybox",
},
KillTimeout: 10 * time.Second,
Resources: structs.DefaultResources(),
}
ctx := testDriverContexts(t, task)
defer ctx.AllocDir.Destroy()
d := NewLxcDriver(ctx.DriverCtx)
2016-09-12 02:33:09 +00:00
if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
t.Fatalf("prestart err: %v", err)
}
2017-06-20 20:21:55 +00:00
sresp, err := d.Start(ctx.ExecCtx, task)
2016-09-12 02:33:09 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
// Destroy the container after the test
2017-06-20 20:21:55 +00:00
lh := sresp.Handle.(*lxcDriverHandle)
defer func() {
lh.container.Stop()
lh.container.Destroy()
}()
2016-09-12 02:33:09 +00:00
2017-06-20 20:21:55 +00:00
handle2, err := d.Open(ctx.ExecCtx, lh.ID())
2016-09-12 02:33:09 +00:00
if err != nil {
t.Fatalf("err: %v", err)
}
if handle2 == nil {
t.Fatalf("missing handle on open")
}
lxcHandle, _ := handle2.(*lxcDriverHandle)
testutil.WaitForResult(func() (bool, error) {
state := lxcHandle.container.State()
if state == lxc.RUNNING {
return true, nil
}
return false, fmt.Errorf("container in state: %v", state)
}, func(err error) {
t.Fatalf("err: %v", err)
})
// Desroy the container
if err := handle2.Kill(); err != nil {
t.Fatalf("err: %v", err)
}
}
func lxcPresent(t *testing.T) bool {
return lxc.Version() != ""
}
func TestLxcDriver_Volumes_ConfigValidation(t *testing.T) {
if !testutil.IsTravis() {
t.Parallel()
}
if !lxcPresent(t) {
t.Skip("lxc not present")
}
ctestutil.RequireRoot(t)
brokenVolumeConfigs := [][]string{
{
"foo:/var",
},
{
":",
},
{
"abc:",
},
{
":def",
},
{
"abc:def:ghi",
},
}
for _, bc := range brokenVolumeConfigs {
if err := testVolumeConfig(t, bc); err == nil {
t.Fatalf("error expected in validate for config %+v", bc)
}
}
if err := testVolumeConfig(t, []string{"abc:def"}); err != nil {
t.Fatalf("error in validate for syntactically valid config abc:def")
}
}
func testVolumeConfig(t *testing.T, volConfig []string) error {
task := &structs.Task{
Name: "voltest",
Driver: "lxc",
KillTimeout: 10 * time.Second,
Resources: structs.DefaultResources(),
Config: map[string]interface{}{
"template": "busybox",
},
}
task.Config["volumes"] = volConfig
ctx := testDriverContexts(t, task)
defer ctx.AllocDir.Destroy()
driver := NewLxcDriver(ctx.DriverCtx)
err := driver.Validate(task.Config)
return err
}
func TestLxcDriver_Start_NoVolumes(t *testing.T) {
if !testutil.IsTravis() {
t.Parallel()
}
if !lxcPresent(t) {
t.Skip("lxc not present")
}
ctestutil.RequireRoot(t)
task := &structs.Task{
Name: "foo",
Driver: "lxc",
Config: map[string]interface{}{
"template": "/usr/share/lxc/templates/lxc-busybox",
"volumes": []string{"/tmp/:mnt/tmp"},
},
KillTimeout: 10 * time.Second,
Resources: structs.DefaultResources(),
}
ctx := testDriverContexts(t, task)
defer ctx.AllocDir.Destroy()
ctx.DriverCtx.config.Options = map[string]string{lxcVolumesConfigOption: "false"}
d := NewLxcDriver(ctx.DriverCtx)
if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
t.Fatalf("prestart err: %v", err)
}
_, err := d.Start(ctx.ExecCtx, task)
if err == nil {
t.Fatalf("expected error in start, got nil.")
}
}