2015-09-01 21:56:42 +00:00
|
|
|
package driver
|
|
|
|
|
|
|
|
import (
|
2016-03-15 20:28:25 +00:00
|
|
|
"io"
|
2015-09-01 21:56:42 +00:00
|
|
|
"log"
|
2015-11-18 03:45:33 +00:00
|
|
|
"math/rand"
|
2015-09-01 21:56:42 +00:00
|
|
|
"os"
|
2015-09-25 23:49:14 +00:00
|
|
|
"path/filepath"
|
2015-09-26 22:37:48 +00:00
|
|
|
"reflect"
|
2015-09-24 07:17:33 +00:00
|
|
|
"testing"
|
2015-12-23 00:10:30 +00:00
|
|
|
"time"
|
2015-09-08 19:43:02 +00:00
|
|
|
|
2015-09-25 23:49:14 +00:00
|
|
|
"github.com/hashicorp/nomad/client/allocdir"
|
2015-09-08 19:43:02 +00:00
|
|
|
"github.com/hashicorp/nomad/client/config"
|
2015-11-24 20:11:26 +00:00
|
|
|
"github.com/hashicorp/nomad/helper/testtask"
|
2016-03-02 00:22:33 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/mock"
|
2015-09-24 07:17:33 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
2015-09-01 21:56:42 +00:00
|
|
|
)
|
|
|
|
|
2015-09-24 07:59:57 +00:00
|
|
|
var basicResources = &structs.Resources{
|
|
|
|
CPU: 250,
|
|
|
|
MemoryMB: 256,
|
2016-08-23 21:51:09 +00:00
|
|
|
DiskMB: 20,
|
2015-09-24 07:59:57 +00:00
|
|
|
Networks: []*structs.NetworkResource{
|
|
|
|
&structs.NetworkResource{
|
2015-11-13 05:46:59 +00:00
|
|
|
IP: "0.0.0.0",
|
2015-11-15 08:37:00 +00:00
|
|
|
ReservedPorts: []structs.Port{{"main", 12345}},
|
2015-11-18 03:21:36 +00:00
|
|
|
DynamicPorts: []structs.Port{{"HTTP", 43330}},
|
2015-09-24 07:59:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2015-11-18 03:45:33 +00:00
|
|
|
func init() {
|
|
|
|
rand.Seed(49875)
|
|
|
|
}
|
|
|
|
|
2015-11-24 20:11:26 +00:00
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
if !testtask.Run() {
|
|
|
|
os.Exit(m.Run())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-15 20:28:25 +00:00
|
|
|
// copyFile moves an existing file to the destination
|
|
|
|
func copyFile(src, dst string, t *testing.T) {
|
|
|
|
in, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
|
|
|
|
}
|
|
|
|
defer in.Close()
|
|
|
|
out, err := os.Create(dst)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if err := out.Close(); err != nil {
|
|
|
|
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
if _, err = io.Copy(out, in); err != nil {
|
|
|
|
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
|
|
|
|
}
|
|
|
|
if err := out.Sync(); err != nil {
|
|
|
|
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-01 21:56:42 +00:00
|
|
|
func testLogger() *log.Logger {
|
|
|
|
return log.New(os.Stderr, "", log.LstdFlags)
|
|
|
|
}
|
2015-09-08 19:43:02 +00:00
|
|
|
|
|
|
|
func testConfig() *config.Config {
|
2016-06-17 21:24:49 +00:00
|
|
|
conf := config.DefaultConfig()
|
2015-09-25 23:49:14 +00:00
|
|
|
conf.StateDir = os.TempDir()
|
|
|
|
conf.AllocDir = os.TempDir()
|
2016-02-09 03:31:57 +00:00
|
|
|
conf.MaxKillTimeout = 10 * time.Second
|
2015-09-25 23:49:14 +00:00
|
|
|
return conf
|
2015-09-08 19:43:02 +00:00
|
|
|
}
|
2015-09-10 01:38:52 +00:00
|
|
|
|
2016-12-03 01:04:07 +00:00
|
|
|
type testContext struct {
|
|
|
|
AllocDir *allocdir.AllocDir
|
|
|
|
DriverCtx *DriverContext
|
|
|
|
ExecCtx *ExecContext
|
|
|
|
}
|
|
|
|
|
|
|
|
// testDriverContext sets up an alloc dir, task dir, DriverContext, and ExecContext.
|
|
|
|
//
|
|
|
|
// It is up to the caller to call AllocDir.Destroy to cleanup.
|
|
|
|
func testDriverContexts(t *testing.T, task *structs.Task) *testContext {
|
2015-09-10 01:38:52 +00:00
|
|
|
cfg := testConfig()
|
2017-01-05 23:57:58 +00:00
|
|
|
allocDir := allocdir.NewAllocDir(testLogger(), filepath.Join(cfg.AllocDir, structs.GenerateUUID()))
|
2016-12-03 01:04:07 +00:00
|
|
|
if err := allocDir.Build(); err != nil {
|
|
|
|
t.Fatalf("AllocDir.Build() failed: %v", err)
|
|
|
|
}
|
2016-03-02 00:22:33 +00:00
|
|
|
alloc := mock.Alloc()
|
2016-01-06 02:02:11 +00:00
|
|
|
|
2016-12-03 01:04:07 +00:00
|
|
|
// Build a temp driver so we can call FSIsolation and build the task dir
|
|
|
|
tmpdrv, err := NewDriver(task.Driver, NewEmptyDriverContext())
|
|
|
|
if err != nil {
|
|
|
|
allocDir.Destroy()
|
|
|
|
t.Fatalf("NewDriver(%q, nil) failed: %v", task.Driver, err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the task dir
|
|
|
|
td := allocDir.NewTaskDir(task.Name)
|
|
|
|
if err := td.Build(config.DefaultChrootEnv, tmpdrv.FSIsolation()); err != nil {
|
|
|
|
allocDir.Destroy()
|
|
|
|
t.Fatalf("TaskDir.Build(%#v, %q) failed: %v", config.DefaultChrootEnv, tmpdrv.FSIsolation())
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
execCtx := NewExecContext(td, alloc.ID)
|
|
|
|
|
|
|
|
taskEnv, err := GetTaskEnv(td, cfg.Node, task, alloc, cfg, "")
|
2016-01-06 02:02:11 +00:00
|
|
|
if err != nil {
|
2016-12-03 01:04:07 +00:00
|
|
|
allocDir.Destroy()
|
|
|
|
t.Fatalf("GetTaskEnv() failed: %v", err)
|
|
|
|
return nil
|
2016-01-06 02:02:11 +00:00
|
|
|
}
|
|
|
|
|
2016-11-30 00:39:36 +00:00
|
|
|
logger := testLogger()
|
|
|
|
emitter := func(m string, args ...interface{}) {
|
|
|
|
logger.Printf("[EVENT] "+m, args...)
|
|
|
|
}
|
|
|
|
driverCtx := NewDriverContext(task.Name, cfg, cfg.Node, logger, taskEnv, emitter)
|
2016-12-03 01:04:07 +00:00
|
|
|
|
|
|
|
return &testContext{allocDir, driverCtx, execCtx}
|
2015-09-10 01:38:52 +00:00
|
|
|
}
|
2015-09-24 07:17:33 +00:00
|
|
|
|
2016-01-06 02:02:11 +00:00
|
|
|
func TestDriver_GetTaskEnv(t *testing.T) {
|
2015-09-24 07:17:33 +00:00
|
|
|
task := &structs.Task{
|
2016-12-03 01:04:07 +00:00
|
|
|
Name: "Foo",
|
2017-01-06 20:19:32 +00:00
|
|
|
Driver: "mock_driver",
|
2015-10-01 11:22:26 +00:00
|
|
|
Env: map[string]string{
|
|
|
|
"HELLO": "world",
|
|
|
|
"lorem": "ipsum",
|
|
|
|
},
|
2015-09-24 07:17:33 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 1000,
|
|
|
|
MemoryMB: 500,
|
|
|
|
Networks: []*structs.NetworkResource{
|
|
|
|
&structs.NetworkResource{
|
|
|
|
IP: "1.2.3.4",
|
2016-04-15 17:27:51 +00:00
|
|
|
ReservedPorts: []structs.Port{{"one", 80}, {"two", 443}},
|
2015-11-15 08:37:00 +00:00
|
|
|
DynamicPorts: []structs.Port{{"admin", 8081}, {"web", 8086}},
|
2015-09-24 07:17:33 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Meta: map[string]string{
|
|
|
|
"chocolate": "cake",
|
|
|
|
"strawberry": "icecream",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-03-02 00:22:33 +00:00
|
|
|
alloc := mock.Alloc()
|
2016-12-19 00:32:14 +00:00
|
|
|
alloc.Job.TaskGroups[0].Tasks[0] = task
|
2016-03-02 00:22:33 +00:00
|
|
|
alloc.Name = "Bar"
|
2017-01-06 20:19:32 +00:00
|
|
|
conf := testConfig()
|
|
|
|
allocDir := allocdir.NewAllocDir(testLogger(), filepath.Join(conf.AllocDir, alloc.ID))
|
|
|
|
env, err := GetTaskEnv(allocDir.NewTaskDir(task.Name), nil, task, alloc, conf, "")
|
2016-01-11 17:58:26 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("GetTaskEnv() failed: %v", err)
|
|
|
|
}
|
2015-09-26 22:37:48 +00:00
|
|
|
exp := map[string]string{
|
2016-03-25 17:26:32 +00:00
|
|
|
"NOMAD_CPU_LIMIT": "1000",
|
|
|
|
"NOMAD_MEMORY_LIMIT": "500",
|
|
|
|
"NOMAD_ADDR_one": "1.2.3.4:80",
|
2016-04-15 17:27:51 +00:00
|
|
|
"NOMAD_IP_one": "1.2.3.4",
|
|
|
|
"NOMAD_PORT_one": "80",
|
2016-07-09 01:27:51 +00:00
|
|
|
"NOMAD_HOST_PORT_one": "80",
|
2016-03-25 17:26:32 +00:00
|
|
|
"NOMAD_ADDR_two": "1.2.3.4:443",
|
2016-04-15 17:27:51 +00:00
|
|
|
"NOMAD_IP_two": "1.2.3.4",
|
|
|
|
"NOMAD_PORT_two": "443",
|
2016-07-09 01:27:51 +00:00
|
|
|
"NOMAD_HOST_PORT_two": "443",
|
2016-03-25 17:26:32 +00:00
|
|
|
"NOMAD_ADDR_admin": "1.2.3.4:8081",
|
2016-04-15 17:27:51 +00:00
|
|
|
"NOMAD_IP_admin": "1.2.3.4",
|
|
|
|
"NOMAD_PORT_admin": "8081",
|
2016-07-09 01:27:51 +00:00
|
|
|
"NOMAD_HOST_PORT_admin": "8081",
|
2016-03-25 17:26:32 +00:00
|
|
|
"NOMAD_ADDR_web": "1.2.3.4:8086",
|
2016-04-15 17:27:51 +00:00
|
|
|
"NOMAD_IP_web": "1.2.3.4",
|
|
|
|
"NOMAD_PORT_web": "8086",
|
2016-07-09 01:27:51 +00:00
|
|
|
"NOMAD_HOST_PORT_web": "8086",
|
2016-03-25 17:26:32 +00:00
|
|
|
"NOMAD_META_CHOCOLATE": "cake",
|
|
|
|
"NOMAD_META_STRAWBERRY": "icecream",
|
|
|
|
"NOMAD_META_ELB_CHECK_INTERVAL": "30s",
|
|
|
|
"NOMAD_META_ELB_CHECK_TYPE": "http",
|
|
|
|
"NOMAD_META_ELB_CHECK_MIN": "3",
|
|
|
|
"NOMAD_META_OWNER": "armon",
|
|
|
|
"HELLO": "world",
|
|
|
|
"lorem": "ipsum",
|
|
|
|
"NOMAD_ALLOC_ID": alloc.ID,
|
|
|
|
"NOMAD_ALLOC_NAME": alloc.Name,
|
|
|
|
"NOMAD_TASK_NAME": task.Name,
|
2016-10-11 17:57:12 +00:00
|
|
|
"NOMAD_JOB_NAME": alloc.Job.Name,
|
2015-09-24 07:17:33 +00:00
|
|
|
}
|
|
|
|
|
2016-01-06 02:02:11 +00:00
|
|
|
act := env.EnvMap()
|
2016-12-20 22:37:35 +00:00
|
|
|
|
|
|
|
// Since host env vars are included only ensure expected env vars are present
|
|
|
|
for expk, expv := range exp {
|
|
|
|
v, ok := act[expk]
|
|
|
|
if !ok {
|
|
|
|
t.Errorf("%q not found in task env", expk)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if v != expv {
|
|
|
|
t.Errorf("Expected %s=%q but found %q", expk, expv, v)
|
|
|
|
}
|
2015-09-25 23:49:26 +00:00
|
|
|
}
|
2015-09-24 07:17:33 +00:00
|
|
|
}
|
2015-11-20 05:29:37 +00:00
|
|
|
|
|
|
|
func TestMapMergeStrInt(t *testing.T) {
|
|
|
|
a := map[string]int{
|
|
|
|
"cakes": 5,
|
|
|
|
"cookies": 3,
|
|
|
|
}
|
|
|
|
|
|
|
|
b := map[string]int{
|
|
|
|
"cakes": 3,
|
|
|
|
"pies": 2,
|
|
|
|
}
|
|
|
|
|
|
|
|
c := mapMergeStrInt(a, b)
|
|
|
|
|
|
|
|
d := map[string]int{
|
|
|
|
"cakes": 3,
|
|
|
|
"cookies": 3,
|
|
|
|
"pies": 2,
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(c, d) {
|
|
|
|
t.Errorf("\nExpected\n%+v\nGot\n%+v\n", d, c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMapMergeStrStr(t *testing.T) {
|
|
|
|
a := map[string]string{
|
|
|
|
"cake": "chocolate",
|
|
|
|
"cookie": "caramel",
|
|
|
|
}
|
|
|
|
|
|
|
|
b := map[string]string{
|
|
|
|
"cake": "strawberry",
|
|
|
|
"pie": "apple",
|
|
|
|
}
|
|
|
|
|
|
|
|
c := mapMergeStrStr(a, b)
|
|
|
|
|
|
|
|
d := map[string]string{
|
|
|
|
"cake": "strawberry",
|
|
|
|
"cookie": "caramel",
|
|
|
|
"pie": "apple",
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(c, d) {
|
|
|
|
t.Errorf("\nExpected\n%+v\nGot\n%+v\n", d, c)
|
|
|
|
}
|
|
|
|
}
|