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

     1  package scheduler
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/hashicorp/nomad/nomad/mock"
     7  	"github.com/hashicorp/nomad/nomad/structs"
     8  )
     9  
    10  func TestFeasibleRankIterator(t *testing.T) {
    11  	_, ctx := testContext(t)
    12  	var nodes []*structs.Node
    13  	for i := 0; i < 10; i++ {
    14  		nodes = append(nodes, mock.Node())
    15  	}
    16  	static := NewStaticIterator(ctx, nodes)
    17  
    18  	feasible := NewFeasibleRankIterator(ctx, static)
    19  
    20  	out := collectRanked(feasible)
    21  	if len(out) != len(nodes) {
    22  		t.Fatalf("bad: %v", out)
    23  	}
    24  }
    25  
    26  func TestBinPackIterator_NoExistingAlloc(t *testing.T) {
    27  	_, ctx := testContext(t)
    28  	nodes := []*RankedNode{
    29  		&RankedNode{
    30  			Node: &structs.Node{
    31  				// Perfect fit
    32  				Resources: &structs.Resources{
    33  					CPU:      2048,
    34  					MemoryMB: 2048,
    35  				},
    36  				Reserved: &structs.Resources{
    37  					CPU:      1024,
    38  					MemoryMB: 1024,
    39  				},
    40  			},
    41  		},
    42  		&RankedNode{
    43  			Node: &structs.Node{
    44  				// Overloaded
    45  				Resources: &structs.Resources{
    46  					CPU:      1024,
    47  					MemoryMB: 1024,
    48  				},
    49  				Reserved: &structs.Resources{
    50  					CPU:      512,
    51  					MemoryMB: 512,
    52  				},
    53  			},
    54  		},
    55  		&RankedNode{
    56  			Node: &structs.Node{
    57  				// 50% fit
    58  				Resources: &structs.Resources{
    59  					CPU:      4096,
    60  					MemoryMB: 4096,
    61  				},
    62  				Reserved: &structs.Resources{
    63  					CPU:      1024,
    64  					MemoryMB: 1024,
    65  				},
    66  			},
    67  		},
    68  	}
    69  	static := NewStaticRankIterator(ctx, nodes)
    70  
    71  	task := &structs.Task{
    72  		Name: "web",
    73  		Resources: &structs.Resources{
    74  			CPU:      1024,
    75  			MemoryMB: 1024,
    76  		},
    77  	}
    78  
    79  	binp := NewBinPackIterator(ctx, static, false, 0)
    80  	binp.SetTasks([]*structs.Task{task})
    81  
    82  	out := collectRanked(binp)
    83  	if len(out) != 2 {
    84  		t.Fatalf("Bad: %v", out)
    85  	}
    86  	if out[0] != nodes[0] || out[1] != nodes[2] {
    87  		t.Fatalf("Bad: %v", out)
    88  	}
    89  
    90  	if out[0].Score != 18 {
    91  		t.Fatalf("Bad: %v", out[0])
    92  	}
    93  	if out[1].Score < 10 || out[1].Score > 16 {
    94  		t.Fatalf("Bad: %v", out[1])
    95  	}
    96  }
    97  
    98  func TestBinPackIterator_PlannedAlloc(t *testing.T) {
    99  	_, ctx := testContext(t)
   100  	nodes := []*RankedNode{
   101  		&RankedNode{
   102  			Node: &structs.Node{
   103  				// Perfect fit
   104  				ID: structs.GenerateUUID(),
   105  				Resources: &structs.Resources{
   106  					CPU:      2048,
   107  					MemoryMB: 2048,
   108  				},
   109  			},
   110  		},
   111  		&RankedNode{
   112  			Node: &structs.Node{
   113  				// Perfect fit
   114  				ID: structs.GenerateUUID(),
   115  				Resources: &structs.Resources{
   116  					CPU:      2048,
   117  					MemoryMB: 2048,
   118  				},
   119  			},
   120  		},
   121  	}
   122  	static := NewStaticRankIterator(ctx, nodes)
   123  
   124  	// Add a planned alloc to node1 that fills it
   125  	plan := ctx.Plan()
   126  	plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{
   127  		&structs.Allocation{
   128  			Resources: &structs.Resources{
   129  				CPU:      2048,
   130  				MemoryMB: 2048,
   131  			},
   132  		},
   133  	}
   134  
   135  	// Add a planned alloc to node2 that half fills it
   136  	plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{
   137  		&structs.Allocation{
   138  			Resources: &structs.Resources{
   139  				CPU:      1024,
   140  				MemoryMB: 1024,
   141  			},
   142  		},
   143  	}
   144  
   145  	task := &structs.Task{
   146  		Name: "web",
   147  		Resources: &structs.Resources{
   148  			CPU:      1024,
   149  			MemoryMB: 1024,
   150  		},
   151  	}
   152  
   153  	binp := NewBinPackIterator(ctx, static, false, 0)
   154  	binp.SetTasks([]*structs.Task{task})
   155  
   156  	out := collectRanked(binp)
   157  	if len(out) != 1 {
   158  		t.Fatalf("Bad: %#v", out)
   159  	}
   160  	if out[0] != nodes[1] {
   161  		t.Fatalf("Bad: %v", out)
   162  	}
   163  
   164  	if out[0].Score != 18 {
   165  		t.Fatalf("Bad: %v", out[0])
   166  	}
   167  }
   168  
   169  func TestBinPackIterator_ExistingAlloc(t *testing.T) {
   170  	state, ctx := testContext(t)
   171  	nodes := []*RankedNode{
   172  		&RankedNode{
   173  			Node: &structs.Node{
   174  				// Perfect fit
   175  				ID: structs.GenerateUUID(),
   176  				Resources: &structs.Resources{
   177  					CPU:      2048,
   178  					MemoryMB: 2048,
   179  				},
   180  			},
   181  		},
   182  		&RankedNode{
   183  			Node: &structs.Node{
   184  				// Perfect fit
   185  				ID: structs.GenerateUUID(),
   186  				Resources: &structs.Resources{
   187  					CPU:      2048,
   188  					MemoryMB: 2048,
   189  				},
   190  			},
   191  		},
   192  	}
   193  	static := NewStaticRankIterator(ctx, nodes)
   194  
   195  	// Add existing allocations
   196  	alloc1 := &structs.Allocation{
   197  		ID:     structs.GenerateUUID(),
   198  		EvalID: structs.GenerateUUID(),
   199  		NodeID: nodes[0].Node.ID,
   200  		JobID:  structs.GenerateUUID(),
   201  		Resources: &structs.Resources{
   202  			CPU:      2048,
   203  			MemoryMB: 2048,
   204  		},
   205  		DesiredStatus: structs.AllocDesiredStatusRun,
   206  	}
   207  	alloc2 := &structs.Allocation{
   208  		ID:     structs.GenerateUUID(),
   209  		EvalID: structs.GenerateUUID(),
   210  		NodeID: nodes[1].Node.ID,
   211  		JobID:  structs.GenerateUUID(),
   212  		Resources: &structs.Resources{
   213  			CPU:      1024,
   214  			MemoryMB: 1024,
   215  		},
   216  		DesiredStatus: structs.AllocDesiredStatusRun,
   217  	}
   218  	noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2}))
   219  
   220  	task := &structs.Task{
   221  		Name: "web",
   222  		Resources: &structs.Resources{
   223  			CPU:      1024,
   224  			MemoryMB: 1024,
   225  		},
   226  	}
   227  
   228  	binp := NewBinPackIterator(ctx, static, false, 0)
   229  	binp.SetTasks([]*structs.Task{task})
   230  
   231  	out := collectRanked(binp)
   232  	if len(out) != 1 {
   233  		t.Fatalf("Bad: %#v", out)
   234  	}
   235  	if out[0] != nodes[1] {
   236  		t.Fatalf("Bad: %v", out)
   237  	}
   238  	if out[0].Score != 18 {
   239  		t.Fatalf("Bad: %v", out[0])
   240  	}
   241  }
   242  
   243  func TestBinPackIterator_ExistingAlloc_PlannedEvict(t *testing.T) {
   244  	state, ctx := testContext(t)
   245  	nodes := []*RankedNode{
   246  		&RankedNode{
   247  			Node: &structs.Node{
   248  				// Perfect fit
   249  				ID: structs.GenerateUUID(),
   250  				Resources: &structs.Resources{
   251  					CPU:      2048,
   252  					MemoryMB: 2048,
   253  				},
   254  			},
   255  		},
   256  		&RankedNode{
   257  			Node: &structs.Node{
   258  				// Perfect fit
   259  				ID: structs.GenerateUUID(),
   260  				Resources: &structs.Resources{
   261  					CPU:      2048,
   262  					MemoryMB: 2048,
   263  				},
   264  			},
   265  		},
   266  	}
   267  	static := NewStaticRankIterator(ctx, nodes)
   268  
   269  	// Add existing allocations
   270  	alloc1 := &structs.Allocation{
   271  		ID:     structs.GenerateUUID(),
   272  		EvalID: structs.GenerateUUID(),
   273  		NodeID: nodes[0].Node.ID,
   274  		JobID:  structs.GenerateUUID(),
   275  		Resources: &structs.Resources{
   276  			CPU:      2048,
   277  			MemoryMB: 2048,
   278  		},
   279  		DesiredStatus: structs.AllocDesiredStatusRun,
   280  	}
   281  	alloc2 := &structs.Allocation{
   282  		ID:     structs.GenerateUUID(),
   283  		EvalID: structs.GenerateUUID(),
   284  		NodeID: nodes[1].Node.ID,
   285  		JobID:  structs.GenerateUUID(),
   286  		Resources: &structs.Resources{
   287  			CPU:      1024,
   288  			MemoryMB: 1024,
   289  		},
   290  		DesiredStatus: structs.AllocDesiredStatusRun,
   291  	}
   292  	noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2}))
   293  
   294  	// Add a planned eviction to alloc1
   295  	plan := ctx.Plan()
   296  	plan.NodeUpdate[nodes[0].Node.ID] = []*structs.Allocation{alloc1}
   297  
   298  	task := &structs.Task{
   299  		Name: "web",
   300  		Resources: &structs.Resources{
   301  			CPU:      1024,
   302  			MemoryMB: 1024,
   303  		},
   304  	}
   305  
   306  	binp := NewBinPackIterator(ctx, static, false, 0)
   307  	binp.SetTasks([]*structs.Task{task})
   308  
   309  	out := collectRanked(binp)
   310  	if len(out) != 2 {
   311  		t.Fatalf("Bad: %#v", out)
   312  	}
   313  	if out[0] != nodes[0] || out[1] != nodes[1] {
   314  		t.Fatalf("Bad: %v", out)
   315  	}
   316  	if out[0].Score < 10 || out[0].Score > 16 {
   317  		t.Fatalf("Bad: %v", out[0])
   318  	}
   319  	if out[1].Score != 18 {
   320  		t.Fatalf("Bad: %v", out[0])
   321  	}
   322  }
   323  
   324  func TestJobAntiAffinity_PlannedAlloc(t *testing.T) {
   325  	_, ctx := testContext(t)
   326  	nodes := []*RankedNode{
   327  		&RankedNode{
   328  			Node: &structs.Node{
   329  				ID: structs.GenerateUUID(),
   330  			},
   331  		},
   332  		&RankedNode{
   333  			Node: &structs.Node{
   334  				ID: structs.GenerateUUID(),
   335  			},
   336  		},
   337  	}
   338  	static := NewStaticRankIterator(ctx, nodes)
   339  
   340  	// Add a planned alloc to node1 that fills it
   341  	plan := ctx.Plan()
   342  	plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{
   343  		&structs.Allocation{
   344  			JobID: "foo",
   345  		},
   346  		&structs.Allocation{
   347  			JobID: "foo",
   348  		},
   349  	}
   350  
   351  	// Add a planned alloc to node2 that half fills it
   352  	plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{
   353  		&structs.Allocation{
   354  			JobID: "bar",
   355  		},
   356  	}
   357  
   358  	binp := NewJobAntiAffinityIterator(ctx, static, 5.0, "foo")
   359  
   360  	out := collectRanked(binp)
   361  	if len(out) != 2 {
   362  		t.Fatalf("Bad: %#v", out)
   363  	}
   364  	if out[0] != nodes[0] {
   365  		t.Fatalf("Bad: %v", out)
   366  	}
   367  	if out[0].Score != -10.0 {
   368  		t.Fatalf("Bad: %v", out[0])
   369  	}
   370  
   371  	if out[1] != nodes[1] {
   372  		t.Fatalf("Bad: %v", out)
   373  	}
   374  	if out[1].Score != 0.0 {
   375  		t.Fatalf("Bad: %v", out[1])
   376  	}
   377  }
   378  
   379  func collectRanked(iter RankIterator) (out []*RankedNode) {
   380  	for {
   381  		next := iter.Next()
   382  		if next == nil {
   383  			break
   384  		}
   385  		out = append(out, next)
   386  	}
   387  	return
   388  }