github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/driver/driver_test.go (about)

     1  package driver
     2  
     3  import (
     4  	"io"
     5  	"log"
     6  	"math/rand"
     7  	"os"
     8  	"path/filepath"
     9  	"reflect"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/hashicorp/nomad/client/allocdir"
    14  	"github.com/hashicorp/nomad/client/config"
    15  	"github.com/hashicorp/nomad/client/driver/env"
    16  	"github.com/hashicorp/nomad/helper/testtask"
    17  	"github.com/hashicorp/nomad/nomad/mock"
    18  	"github.com/hashicorp/nomad/nomad/structs"
    19  )
    20  
    21  var basicResources = &structs.Resources{
    22  	CPU:      250,
    23  	MemoryMB: 256,
    24  	DiskMB:   20,
    25  	Networks: []*structs.NetworkResource{
    26  		&structs.NetworkResource{
    27  			IP:            "0.0.0.0",
    28  			ReservedPorts: []structs.Port{{Label: "main", Value: 12345}},
    29  			DynamicPorts:  []structs.Port{{Label: "HTTP", Value: 43330}},
    30  		},
    31  	},
    32  }
    33  
    34  func init() {
    35  	rand.Seed(49875)
    36  }
    37  
    38  func TestMain(m *testing.M) {
    39  	if !testtask.Run() {
    40  		os.Exit(m.Run())
    41  	}
    42  }
    43  
    44  // copyFile moves an existing file to the destination
    45  func copyFile(src, dst string, t *testing.T) {
    46  	in, err := os.Open(src)
    47  	if err != nil {
    48  		t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
    49  	}
    50  	defer in.Close()
    51  	out, err := os.Create(dst)
    52  	if err != nil {
    53  		t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
    54  	}
    55  	defer func() {
    56  		if err := out.Close(); err != nil {
    57  			t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
    58  		}
    59  	}()
    60  	if _, err = io.Copy(out, in); err != nil {
    61  		t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
    62  	}
    63  	if err := out.Sync(); err != nil {
    64  		t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
    65  	}
    66  }
    67  
    68  func testLogger() *log.Logger {
    69  	return log.New(os.Stderr, "", log.LstdFlags)
    70  }
    71  
    72  func testConfig() *config.Config {
    73  	conf := config.DefaultConfig()
    74  	conf.StateDir = os.TempDir()
    75  	conf.AllocDir = os.TempDir()
    76  	conf.MaxKillTimeout = 10 * time.Second
    77  	conf.Region = "global"
    78  	conf.Node = mock.Node()
    79  	return conf
    80  }
    81  
    82  type testContext struct {
    83  	AllocDir   *allocdir.AllocDir
    84  	DriverCtx  *DriverContext
    85  	ExecCtx    *ExecContext
    86  	EnvBuilder *env.Builder
    87  }
    88  
    89  // testDriverContext sets up an alloc dir, task dir, DriverContext, and ExecContext.
    90  //
    91  // It is up to the caller to call AllocDir.Destroy to cleanup.
    92  func testDriverContexts(t *testing.T, task *structs.Task) *testContext {
    93  	cfg := testConfig()
    94  	cfg.Node = mock.Node()
    95  	allocDir := allocdir.NewAllocDir(testLogger(), filepath.Join(cfg.AllocDir, structs.GenerateUUID()))
    96  	if err := allocDir.Build(); err != nil {
    97  		t.Fatalf("AllocDir.Build() failed: %v", err)
    98  	}
    99  	alloc := mock.Alloc()
   100  
   101  	// Build a temp driver so we can call FSIsolation and build the task dir
   102  	tmpdrv, err := NewDriver(task.Driver, NewEmptyDriverContext())
   103  	if err != nil {
   104  		allocDir.Destroy()
   105  		t.Fatalf("NewDriver(%q, nil) failed: %v", task.Driver, err)
   106  		return nil
   107  	}
   108  
   109  	// Build the task dir
   110  	td := allocDir.NewTaskDir(task.Name)
   111  	if err := td.Build(false, config.DefaultChrootEnv, tmpdrv.FSIsolation()); err != nil {
   112  		allocDir.Destroy()
   113  		t.Fatalf("TaskDir.Build(%#v, %q) failed: %v", config.DefaultChrootEnv, tmpdrv.FSIsolation(), err)
   114  		return nil
   115  	}
   116  	eb := env.NewBuilder(cfg.Node, alloc, task, cfg.Region)
   117  	SetEnvvars(eb, tmpdrv.FSIsolation(), td, cfg)
   118  	execCtx := NewExecContext(td, eb.Build())
   119  
   120  	logger := testLogger()
   121  	emitter := func(m string, args ...interface{}) {
   122  		logger.Printf("[EVENT] "+m, args...)
   123  	}
   124  	driverCtx := NewDriverContext(task.Name, alloc.ID, cfg, cfg.Node, logger, emitter)
   125  
   126  	return &testContext{allocDir, driverCtx, execCtx, eb}
   127  }
   128  
   129  // setupTaskEnv creates a test env for GetTaskEnv testing. Returns task dir,
   130  // expected env, and actual env.
   131  func setupTaskEnv(t *testing.T, driver string) (*allocdir.TaskDir, map[string]string, map[string]string) {
   132  	task := &structs.Task{
   133  		Name:   "Foo",
   134  		Driver: driver,
   135  		Env: map[string]string{
   136  			"HELLO": "world",
   137  			"lorem": "ipsum",
   138  		},
   139  		Resources: &structs.Resources{
   140  			CPU:      1000,
   141  			MemoryMB: 500,
   142  			Networks: []*structs.NetworkResource{
   143  				&structs.NetworkResource{
   144  					IP:            "1.2.3.4",
   145  					ReservedPorts: []structs.Port{{Label: "one", Value: 80}, {Label: "two", Value: 443}},
   146  					DynamicPorts:  []structs.Port{{Label: "admin", Value: 8081}, {Label: "web", Value: 8086}},
   147  				},
   148  			},
   149  		},
   150  		Meta: map[string]string{
   151  			"chocolate":  "cake",
   152  			"strawberry": "icecream",
   153  		},
   154  	}
   155  
   156  	alloc := mock.Alloc()
   157  	alloc.Job.TaskGroups[0].Tasks[0] = task
   158  	alloc.Name = "Bar"
   159  	alloc.TaskResources["web"].Networks[0].DynamicPorts[0].Value = 2000
   160  	conf := testConfig()
   161  	allocDir := allocdir.NewAllocDir(testLogger(), filepath.Join(conf.AllocDir, alloc.ID))
   162  	taskDir := allocDir.NewTaskDir(task.Name)
   163  	eb := env.NewBuilder(conf.Node, alloc, task, conf.Region)
   164  	tmpDriver, err := NewDriver(driver, NewEmptyDriverContext())
   165  	if err != nil {
   166  		t.Fatalf("unable to create driver %q: %v", driver, err)
   167  	}
   168  	SetEnvvars(eb, tmpDriver.FSIsolation(), taskDir, conf)
   169  	exp := map[string]string{
   170  		"NOMAD_CPU_LIMIT":               "1000",
   171  		"NOMAD_MEMORY_LIMIT":            "500",
   172  		"NOMAD_ADDR_one":                "1.2.3.4:80",
   173  		"NOMAD_IP_one":                  "1.2.3.4",
   174  		"NOMAD_PORT_one":                "80",
   175  		"NOMAD_HOST_PORT_one":           "80",
   176  		"NOMAD_ADDR_two":                "1.2.3.4:443",
   177  		"NOMAD_IP_two":                  "1.2.3.4",
   178  		"NOMAD_PORT_two":                "443",
   179  		"NOMAD_HOST_PORT_two":           "443",
   180  		"NOMAD_ADDR_admin":              "1.2.3.4:8081",
   181  		"NOMAD_ADDR_web_main":           "192.168.0.100:5000",
   182  		"NOMAD_ADDR_web_http":           "192.168.0.100:2000",
   183  		"NOMAD_IP_web_main":             "192.168.0.100",
   184  		"NOMAD_IP_web_http":             "192.168.0.100",
   185  		"NOMAD_PORT_web_http":           "2000",
   186  		"NOMAD_PORT_web_main":           "5000",
   187  		"NOMAD_IP_admin":                "1.2.3.4",
   188  		"NOMAD_PORT_admin":              "8081",
   189  		"NOMAD_HOST_PORT_admin":         "8081",
   190  		"NOMAD_ADDR_web":                "1.2.3.4:8086",
   191  		"NOMAD_IP_web":                  "1.2.3.4",
   192  		"NOMAD_PORT_web":                "8086",
   193  		"NOMAD_HOST_PORT_web":           "8086",
   194  		"NOMAD_META_CHOCOLATE":          "cake",
   195  		"NOMAD_META_STRAWBERRY":         "icecream",
   196  		"NOMAD_META_ELB_CHECK_INTERVAL": "30s",
   197  		"NOMAD_META_ELB_CHECK_TYPE":     "http",
   198  		"NOMAD_META_ELB_CHECK_MIN":      "3",
   199  		"NOMAD_META_OWNER":              "armon",
   200  		"NOMAD_META_chocolate":          "cake",
   201  		"NOMAD_META_strawberry":         "icecream",
   202  		"NOMAD_META_elb_check_interval": "30s",
   203  		"NOMAD_META_elb_check_type":     "http",
   204  		"NOMAD_META_elb_check_min":      "3",
   205  		"NOMAD_META_owner":              "armon",
   206  		"HELLO":                         "world",
   207  		"lorem":                         "ipsum",
   208  		"NOMAD_ALLOC_ID":                alloc.ID,
   209  		"NOMAD_ALLOC_INDEX":             "0",
   210  		"NOMAD_ALLOC_NAME":              alloc.Name,
   211  		"NOMAD_TASK_NAME":               task.Name,
   212  		"NOMAD_GROUP_NAME":              alloc.TaskGroup,
   213  		"NOMAD_JOB_NAME":                alloc.Job.Name,
   214  		"NOMAD_DC":                      "dc1",
   215  		"NOMAD_REGION":                  "global",
   216  	}
   217  
   218  	act := eb.Build().Map()
   219  	return taskDir, exp, act
   220  }
   221  
   222  func TestDriver_GetTaskEnv_None(t *testing.T) {
   223  	t.Parallel()
   224  	taskDir, exp, act := setupTaskEnv(t, "raw_exec")
   225  
   226  	// raw_exec should use host alloc dir path
   227  	exp[env.AllocDir] = taskDir.SharedAllocDir
   228  	exp[env.TaskLocalDir] = taskDir.LocalDir
   229  	exp[env.SecretsDir] = taskDir.SecretsDir
   230  
   231  	// Since host env vars are included only ensure expected env vars are present
   232  	for expk, expv := range exp {
   233  		v, ok := act[expk]
   234  		if !ok {
   235  			t.Errorf("%q not found in task env", expk)
   236  			continue
   237  		}
   238  		if v != expv {
   239  			t.Errorf("Expected %s=%q but found %q", expk, expv, v)
   240  		}
   241  	}
   242  
   243  	// Make sure common host env vars are included.
   244  	for _, envvar := range [...]string{"PATH", "HOME", "USER"} {
   245  		if exp := os.Getenv(envvar); act[envvar] != exp {
   246  			t.Errorf("Expected envvar %s=%q  !=  %q", envvar, exp, act[envvar])
   247  		}
   248  	}
   249  }
   250  
   251  func TestDriver_GetTaskEnv_Chroot(t *testing.T) {
   252  	t.Parallel()
   253  	_, exp, act := setupTaskEnv(t, "exec")
   254  
   255  	exp[env.AllocDir] = allocdir.SharedAllocContainerPath
   256  	exp[env.TaskLocalDir] = allocdir.TaskLocalContainerPath
   257  	exp[env.SecretsDir] = allocdir.TaskSecretsContainerPath
   258  
   259  	// Since host env vars are included only ensure expected env vars are present
   260  	for expk, expv := range exp {
   261  		v, ok := act[expk]
   262  		if !ok {
   263  			t.Errorf("%q not found in task env", expk)
   264  			continue
   265  		}
   266  		if v != expv {
   267  			t.Errorf("Expected %s=%q but found %q", expk, expv, v)
   268  		}
   269  	}
   270  
   271  	// Make sure common host env vars are included.
   272  	for _, envvar := range [...]string{"PATH", "HOME", "USER"} {
   273  		if exp := os.Getenv(envvar); act[envvar] != exp {
   274  			t.Errorf("Expected envvar %s=%q  !=  %q", envvar, exp, act[envvar])
   275  		}
   276  	}
   277  }
   278  
   279  // TestDriver_TaskEnv_Image ensures host environment variables are not set
   280  // for image based drivers. See #2211
   281  func TestDriver_TaskEnv_Image(t *testing.T) {
   282  	t.Parallel()
   283  	_, exp, act := setupTaskEnv(t, "docker")
   284  
   285  	exp[env.AllocDir] = allocdir.SharedAllocContainerPath
   286  	exp[env.TaskLocalDir] = allocdir.TaskLocalContainerPath
   287  	exp[env.SecretsDir] = allocdir.TaskSecretsContainerPath
   288  
   289  	// Since host env vars are excluded expected and actual maps should be equal
   290  	for expk, expv := range exp {
   291  		v, ok := act[expk]
   292  		delete(act, expk)
   293  		if !ok {
   294  			t.Errorf("Env var %s missing. Expected %s=%q", expk, expk, expv)
   295  			continue
   296  		}
   297  		if v != expv {
   298  			t.Errorf("Env var %s=%q -- Expected %q", expk, v, expk)
   299  		}
   300  	}
   301  	// Any remaining env vars are unexpected
   302  	for actk, actv := range act {
   303  		t.Errorf("Env var %s=%q is unexpected", actk, actv)
   304  	}
   305  }
   306  
   307  func TestMapMergeStrInt(t *testing.T) {
   308  	t.Parallel()
   309  	a := map[string]int{
   310  		"cakes":   5,
   311  		"cookies": 3,
   312  	}
   313  
   314  	b := map[string]int{
   315  		"cakes": 3,
   316  		"pies":  2,
   317  	}
   318  
   319  	c := mapMergeStrInt(a, b)
   320  
   321  	d := map[string]int{
   322  		"cakes":   3,
   323  		"cookies": 3,
   324  		"pies":    2,
   325  	}
   326  
   327  	if !reflect.DeepEqual(c, d) {
   328  		t.Errorf("\nExpected\n%+v\nGot\n%+v\n", d, c)
   329  	}
   330  }
   331  
   332  func TestMapMergeStrStr(t *testing.T) {
   333  	t.Parallel()
   334  	a := map[string]string{
   335  		"cake":   "chocolate",
   336  		"cookie": "caramel",
   337  	}
   338  
   339  	b := map[string]string{
   340  		"cake": "strawberry",
   341  		"pie":  "apple",
   342  	}
   343  
   344  	c := mapMergeStrStr(a, b)
   345  
   346  	d := map[string]string{
   347  		"cake":   "strawberry",
   348  		"cookie": "caramel",
   349  		"pie":    "apple",
   350  	}
   351  
   352  	if !reflect.DeepEqual(c, d) {
   353  		t.Errorf("\nExpected\n%+v\nGot\n%+v\n", d, c)
   354  	}
   355  }
   356  
   357  func TestCreatedResources_AddMerge(t *testing.T) {
   358  	t.Parallel()
   359  	res1 := NewCreatedResources()
   360  	res1.Add("k1", "v1")
   361  	res1.Add("k1", "v2")
   362  	res1.Add("k1", "v1")
   363  	res1.Add("k2", "v1")
   364  
   365  	expected := map[string][]string{
   366  		"k1": {"v1", "v2"},
   367  		"k2": {"v1"},
   368  	}
   369  	if !reflect.DeepEqual(expected, res1.Resources) {
   370  		t.Fatalf("1.  %#v != expected %#v", res1.Resources, expected)
   371  	}
   372  
   373  	// Make sure merging nil works
   374  	var res2 *CreatedResources
   375  	res1.Merge(res2)
   376  	if !reflect.DeepEqual(expected, res1.Resources) {
   377  		t.Fatalf("2.  %#v != expected %#v", res1.Resources, expected)
   378  	}
   379  
   380  	// Make sure a normal merge works
   381  	res2 = NewCreatedResources()
   382  	res2.Add("k1", "v3")
   383  	res2.Add("k2", "v1")
   384  	res2.Add("k3", "v3")
   385  	res1.Merge(res2)
   386  
   387  	expected = map[string][]string{
   388  		"k1": {"v1", "v2", "v3"},
   389  		"k2": {"v1"},
   390  		"k3": {"v3"},
   391  	}
   392  	if !reflect.DeepEqual(expected, res1.Resources) {
   393  		t.Fatalf("3.  %#v != expected %#v", res1.Resources, expected)
   394  	}
   395  }
   396  
   397  func TestCreatedResources_CopyRemove(t *testing.T) {
   398  	t.Parallel()
   399  	res1 := NewCreatedResources()
   400  	res1.Add("k1", "v1")
   401  	res1.Add("k1", "v2")
   402  	res1.Add("k1", "v3")
   403  	res1.Add("k2", "v1")
   404  
   405  	// Assert Copy creates a deep copy
   406  	res2 := res1.Copy()
   407  
   408  	if !reflect.DeepEqual(res1, res2) {
   409  		t.Fatalf("%#v != %#v", res1, res2)
   410  	}
   411  
   412  	// Assert removing v1 from k1 returns true and updates Resources slice
   413  	if removed := res2.Remove("k1", "v1"); !removed {
   414  		t.Fatalf("expected v1 to be removed: %#v", res2)
   415  	}
   416  
   417  	if expected := []string{"v2", "v3"}; !reflect.DeepEqual(expected, res2.Resources["k1"]) {
   418  		t.Fatalf("unpexpected list for k1: %#v", res2.Resources["k1"])
   419  	}
   420  
   421  	// Assert removing the only value from a key removes the key
   422  	if removed := res2.Remove("k2", "v1"); !removed {
   423  		t.Fatalf("expected v1 to be removed from k2: %#v", res2.Resources)
   424  	}
   425  
   426  	if _, found := res2.Resources["k2"]; found {
   427  		t.Fatalf("k2 should have been removed from Resources: %#v", res2.Resources)
   428  	}
   429  
   430  	// Make sure res1 wasn't updated
   431  	if reflect.DeepEqual(res1, res2) {
   432  		t.Fatalf("res1 should not equal res2: #%v", res1)
   433  	}
   434  }