github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/scheduler/feasible_test.go (about)

     1  package scheduler
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/hashicorp/nomad/nomad/mock"
     8  	"github.com/hashicorp/nomad/nomad/structs"
     9  )
    10  
    11  func TestStaticIterator_Reset(t *testing.T) {
    12  	_, ctx := testContext(t)
    13  	var nodes []*structs.Node
    14  	for i := 0; i < 3; i++ {
    15  		nodes = append(nodes, mock.Node())
    16  	}
    17  	static := NewStaticIterator(ctx, nodes)
    18  
    19  	for i := 0; i < 6; i++ {
    20  		static.Reset()
    21  		for j := 0; j < i; j++ {
    22  			static.Next()
    23  		}
    24  		static.Reset()
    25  
    26  		out := collectFeasible(static)
    27  		if len(out) != len(nodes) {
    28  			t.Fatalf("out: %#v", out)
    29  			t.Fatalf("missing nodes %d %#v", i, static)
    30  		}
    31  
    32  		ids := make(map[string]struct{})
    33  		for _, o := range out {
    34  			if _, ok := ids[o.ID]; ok {
    35  				t.Fatalf("duplicate")
    36  			}
    37  			ids[o.ID] = struct{}{}
    38  		}
    39  	}
    40  }
    41  
    42  func TestStaticIterator_SetNodes(t *testing.T) {
    43  	_, ctx := testContext(t)
    44  	var nodes []*structs.Node
    45  	for i := 0; i < 3; i++ {
    46  		nodes = append(nodes, mock.Node())
    47  	}
    48  	static := NewStaticIterator(ctx, nodes)
    49  
    50  	newNodes := []*structs.Node{mock.Node()}
    51  	static.SetNodes(newNodes)
    52  
    53  	out := collectFeasible(static)
    54  	if !reflect.DeepEqual(out, newNodes) {
    55  		t.Fatalf("bad: %#v", out)
    56  	}
    57  }
    58  
    59  func TestRandomIterator(t *testing.T) {
    60  	_, ctx := testContext(t)
    61  	var nodes []*structs.Node
    62  	for i := 0; i < 10; i++ {
    63  		nodes = append(nodes, mock.Node())
    64  	}
    65  
    66  	nc := make([]*structs.Node, len(nodes))
    67  	copy(nc, nodes)
    68  	rand := NewRandomIterator(ctx, nc)
    69  
    70  	out := collectFeasible(rand)
    71  	if len(out) != len(nodes) {
    72  		t.Fatalf("missing nodes")
    73  	}
    74  	if reflect.DeepEqual(out, nodes) {
    75  		t.Fatalf("same order")
    76  	}
    77  }
    78  
    79  func TestDriverIterator(t *testing.T) {
    80  	_, ctx := testContext(t)
    81  	nodes := []*structs.Node{
    82  		mock.Node(),
    83  		mock.Node(),
    84  		mock.Node(),
    85  		mock.Node(),
    86  	}
    87  	static := NewStaticIterator(ctx, nodes)
    88  
    89  	nodes[0].Attributes["driver.foo"] = "1"
    90  	nodes[1].Attributes["driver.foo"] = "0"
    91  	nodes[2].Attributes["driver.foo"] = "true"
    92  	nodes[3].Attributes["driver.foo"] = "False"
    93  
    94  	drivers := map[string]struct{}{
    95  		"exec": struct{}{},
    96  		"foo":  struct{}{},
    97  	}
    98  	driver := NewDriverIterator(ctx, static, drivers)
    99  
   100  	out := collectFeasible(driver)
   101  	if len(out) != 2 {
   102  		t.Fatalf("missing nodes")
   103  	}
   104  	if out[0] != nodes[0] || out[1] != nodes[2] {
   105  		t.Fatalf("bad: %#v", out)
   106  	}
   107  }
   108  
   109  func TestConstraintIterator(t *testing.T) {
   110  	_, ctx := testContext(t)
   111  	nodes := []*structs.Node{
   112  		mock.Node(),
   113  		mock.Node(),
   114  		mock.Node(),
   115  	}
   116  	static := NewStaticIterator(ctx, nodes)
   117  
   118  	nodes[0].Attributes["kernel.name"] = "freebsd"
   119  	nodes[1].Datacenter = "dc2"
   120  
   121  	constraints := []*structs.Constraint{
   122  		&structs.Constraint{
   123  			Hard:    true,
   124  			Operand: "=",
   125  			LTarget: "$node.datacenter",
   126  			RTarget: "dc1",
   127  		},
   128  		&structs.Constraint{
   129  			Hard:    true,
   130  			Operand: "is",
   131  			LTarget: "$attr.kernel.name",
   132  			RTarget: "linux",
   133  		},
   134  	}
   135  	constr := NewConstraintIterator(ctx, static, constraints)
   136  
   137  	out := collectFeasible(constr)
   138  	if len(out) != 1 {
   139  		t.Fatalf("missing nodes")
   140  	}
   141  	if out[0] != nodes[2] {
   142  		t.Fatalf("bad: %#v", out)
   143  	}
   144  }
   145  
   146  func TestResolveConstraintTarget(t *testing.T) {
   147  	type tcase struct {
   148  		target string
   149  		node   *structs.Node
   150  		val    interface{}
   151  		result bool
   152  	}
   153  	node := mock.Node()
   154  	cases := []tcase{
   155  		{
   156  			target: "$node.id",
   157  			node:   node,
   158  			val:    node.ID,
   159  			result: true,
   160  		},
   161  		{
   162  			target: "$node.datacenter",
   163  			node:   node,
   164  			val:    node.Datacenter,
   165  			result: true,
   166  		},
   167  		{
   168  			target: "$node.name",
   169  			node:   node,
   170  			val:    node.Name,
   171  			result: true,
   172  		},
   173  		{
   174  			target: "$node.foo",
   175  			node:   node,
   176  			result: false,
   177  		},
   178  		{
   179  			target: "$attr.kernel.name",
   180  			node:   node,
   181  			val:    node.Attributes["kernel.name"],
   182  			result: true,
   183  		},
   184  		{
   185  			target: "$attr.rand",
   186  			node:   node,
   187  			result: false,
   188  		},
   189  		{
   190  			target: "$meta.pci-dss",
   191  			node:   node,
   192  			val:    node.Meta["pci-dss"],
   193  			result: true,
   194  		},
   195  		{
   196  			target: "$meta.rand",
   197  			node:   node,
   198  			result: false,
   199  		},
   200  	}
   201  
   202  	for _, tc := range cases {
   203  		res, ok := resolveConstraintTarget(tc.target, tc.node)
   204  		if ok != tc.result {
   205  			t.Fatalf("TC: %#v, Result: %v %v", tc, res, ok)
   206  		}
   207  		if ok && !reflect.DeepEqual(res, tc.val) {
   208  			t.Fatalf("TC: %#v, Result: %v %v", tc, res, ok)
   209  		}
   210  	}
   211  }
   212  
   213  func TestCheckConstraint(t *testing.T) {
   214  	type tcase struct {
   215  		op         string
   216  		lVal, rVal interface{}
   217  		result     bool
   218  	}
   219  	cases := []tcase{
   220  		{
   221  			op:   "=",
   222  			lVal: "foo", rVal: "foo",
   223  			result: true,
   224  		},
   225  		{
   226  			op:   "is",
   227  			lVal: "foo", rVal: "foo",
   228  			result: true,
   229  		},
   230  		{
   231  			op:   "==",
   232  			lVal: "foo", rVal: "foo",
   233  			result: true,
   234  		},
   235  		{
   236  			op:   "!=",
   237  			lVal: "foo", rVal: "foo",
   238  			result: false,
   239  		},
   240  		{
   241  			op:   "!=",
   242  			lVal: "foo", rVal: "bar",
   243  			result: true,
   244  		},
   245  		{
   246  			op:   "not",
   247  			lVal: "foo", rVal: "bar",
   248  			result: true,
   249  		},
   250  		{
   251  			op:   "version",
   252  			lVal: "1.2.3", rVal: "~> 1.0",
   253  			result: true,
   254  		},
   255  		{
   256  			op:   "regexp",
   257  			lVal: "foobarbaz", rVal: "[\\w]+",
   258  			result: true,
   259  		},
   260  		{
   261  			op:   "<",
   262  			lVal: "foo", rVal: "bar",
   263  			result: false,
   264  		},
   265  	}
   266  
   267  	for _, tc := range cases {
   268  		_, ctx := testContext(t)
   269  		if res := checkConstraint(ctx, tc.op, tc.lVal, tc.rVal); res != tc.result {
   270  			t.Fatalf("TC: %#v, Result: %v", tc, res)
   271  		}
   272  	}
   273  }
   274  
   275  func TestCheckLexicalOrder(t *testing.T) {
   276  	type tcase struct {
   277  		op         string
   278  		lVal, rVal interface{}
   279  		result     bool
   280  	}
   281  	cases := []tcase{
   282  		{
   283  			op:   "<",
   284  			lVal: "bar", rVal: "foo",
   285  			result: true,
   286  		},
   287  		{
   288  			op:   "<=",
   289  			lVal: "foo", rVal: "foo",
   290  			result: true,
   291  		},
   292  		{
   293  			op:   ">",
   294  			lVal: "bar", rVal: "foo",
   295  			result: false,
   296  		},
   297  		{
   298  			op:   ">=",
   299  			lVal: "bar", rVal: "bar",
   300  			result: true,
   301  		},
   302  		{
   303  			op:   ">",
   304  			lVal: 1, rVal: "foo",
   305  			result: false,
   306  		},
   307  	}
   308  	for _, tc := range cases {
   309  		if res := checkLexicalOrder(tc.op, tc.lVal, tc.rVal); res != tc.result {
   310  			t.Fatalf("TC: %#v, Result: %v", tc, res)
   311  		}
   312  	}
   313  }
   314  
   315  func TestCheckVersionConstraint(t *testing.T) {
   316  	type tcase struct {
   317  		lVal, rVal interface{}
   318  		result     bool
   319  	}
   320  	cases := []tcase{
   321  		{
   322  			lVal: "1.2.3", rVal: "~> 1.0",
   323  			result: true,
   324  		},
   325  		{
   326  			lVal: "1.2.3", rVal: ">= 1.0, < 1.4",
   327  			result: true,
   328  		},
   329  		{
   330  			lVal: "2.0.1", rVal: "~> 1.0",
   331  			result: false,
   332  		},
   333  		{
   334  			lVal: "1.4", rVal: ">= 1.0, < 1.4",
   335  			result: false,
   336  		},
   337  		{
   338  			lVal: 1, rVal: "~> 1.0",
   339  			result: true,
   340  		},
   341  	}
   342  	for _, tc := range cases {
   343  		_, ctx := testContext(t)
   344  		if res := checkVersionConstraint(ctx, tc.lVal, tc.rVal); res != tc.result {
   345  			t.Fatalf("TC: %#v, Result: %v", tc, res)
   346  		}
   347  	}
   348  }
   349  
   350  func TestCheckRegexpConstraint(t *testing.T) {
   351  	type tcase struct {
   352  		lVal, rVal interface{}
   353  		result     bool
   354  	}
   355  	cases := []tcase{
   356  		{
   357  			lVal: "foobar", rVal: "bar",
   358  			result: true,
   359  		},
   360  		{
   361  			lVal: "foobar", rVal: "^foo",
   362  			result: true,
   363  		},
   364  		{
   365  			lVal: "foobar", rVal: "^bar",
   366  			result: false,
   367  		},
   368  		{
   369  			lVal: "zipzap", rVal: "foo",
   370  			result: false,
   371  		},
   372  		{
   373  			lVal: 1, rVal: "foo",
   374  			result: false,
   375  		},
   376  	}
   377  	for _, tc := range cases {
   378  		_, ctx := testContext(t)
   379  		if res := checkRegexpConstraint(ctx, tc.lVal, tc.rVal); res != tc.result {
   380  			t.Fatalf("TC: %#v, Result: %v", tc, res)
   381  		}
   382  	}
   383  }
   384  
   385  func collectFeasible(iter FeasibleIterator) (out []*structs.Node) {
   386  	for {
   387  		next := iter.Next()
   388  		if next == nil {
   389  			break
   390  		}
   391  		out = append(out, next)
   392  	}
   393  	return
   394  }