github.com/smithx10/nomad@v0.9.1-rc1/client/servers/manager_test.go (about)

     1  package servers_test
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"net"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/hashicorp/nomad/client/servers"
    11  	"github.com/hashicorp/nomad/helper/testlog"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  type fauxAddr struct {
    16  	Addr string
    17  }
    18  
    19  func (fa *fauxAddr) String() string  { return fa.Addr }
    20  func (fa *fauxAddr) Network() string { return fa.Addr }
    21  
    22  type fauxConnPool struct {
    23  	// failPct between 0.0 and 1.0 == pct of time a Ping should fail
    24  	failPct float64
    25  }
    26  
    27  func (cp *fauxConnPool) Ping(net.Addr) error {
    28  	successProb := rand.Float64()
    29  	if successProb > cp.failPct {
    30  		return nil
    31  	}
    32  	return fmt.Errorf("bad server")
    33  }
    34  
    35  func testManager(t *testing.T) (m *servers.Manager) {
    36  	logger := testlog.HCLogger(t)
    37  	shutdownCh := make(chan struct{})
    38  	m = servers.New(logger, shutdownCh, &fauxConnPool{})
    39  	return m
    40  }
    41  
    42  func testManagerFailProb(t *testing.T, failPct float64) (m *servers.Manager) {
    43  	logger := testlog.HCLogger(t)
    44  	shutdownCh := make(chan struct{})
    45  	m = servers.New(logger, shutdownCh, &fauxConnPool{failPct: failPct})
    46  	return m
    47  }
    48  
    49  func TestServers_SetServers(t *testing.T) {
    50  	require := require.New(t)
    51  	m := testManager(t)
    52  	var num int
    53  	num = m.NumServers()
    54  	if num != 0 {
    55  		t.Fatalf("Expected zero servers to start")
    56  	}
    57  
    58  	s1 := &servers.Server{Addr: &fauxAddr{"server1"}}
    59  	s2 := &servers.Server{Addr: &fauxAddr{"server2"}}
    60  	require.True(m.SetServers([]*servers.Server{s1, s2}))
    61  	require.False(m.SetServers([]*servers.Server{s1, s2}))
    62  	require.False(m.SetServers([]*servers.Server{s2, s1}))
    63  	require.Equal(2, m.NumServers())
    64  	require.Len(m.GetServers(), 2)
    65  
    66  	require.True(m.SetServers([]*servers.Server{s1}))
    67  	require.Equal(1, m.NumServers())
    68  	require.Len(m.GetServers(), 1)
    69  }
    70  
    71  func TestServers_FindServer(t *testing.T) {
    72  	m := testManager(t)
    73  
    74  	if m.FindServer() != nil {
    75  		t.Fatalf("Expected nil return")
    76  	}
    77  
    78  	var srvs []*servers.Server
    79  	srvs = append(srvs, &servers.Server{Addr: &fauxAddr{"s1"}})
    80  	m.SetServers(srvs)
    81  	if m.NumServers() != 1 {
    82  		t.Fatalf("Expected one server")
    83  	}
    84  
    85  	s1 := m.FindServer()
    86  	if s1 == nil {
    87  		t.Fatalf("Expected non-nil server")
    88  	}
    89  	if s1.String() != "s1" {
    90  		t.Fatalf("Expected s1 server")
    91  	}
    92  
    93  	s1 = m.FindServer()
    94  	if s1 == nil || s1.String() != "s1" {
    95  		t.Fatalf("Expected s1 server (still)")
    96  	}
    97  
    98  	srvs = append(srvs, &servers.Server{Addr: &fauxAddr{"s2"}})
    99  	m.SetServers(srvs)
   100  	if m.NumServers() != 2 {
   101  		t.Fatalf("Expected two servers")
   102  	}
   103  	s1 = m.FindServer()
   104  
   105  	for _, srv := range srvs {
   106  		m.NotifyFailedServer(srv)
   107  	}
   108  
   109  	s2 := m.FindServer()
   110  	if s1.Equal(s2) {
   111  		t.Fatalf("Expected different server")
   112  	}
   113  }
   114  
   115  func TestServers_New(t *testing.T) {
   116  	logger := testlog.HCLogger(t)
   117  	shutdownCh := make(chan struct{})
   118  	m := servers.New(logger, shutdownCh, &fauxConnPool{})
   119  	if m == nil {
   120  		t.Fatalf("Manager nil")
   121  	}
   122  }
   123  
   124  func TestServers_NotifyFailedServer(t *testing.T) {
   125  	m := testManager(t)
   126  
   127  	if m.NumServers() != 0 {
   128  		t.Fatalf("Expected zero servers to start")
   129  	}
   130  
   131  	s1 := &servers.Server{Addr: &fauxAddr{"s1"}}
   132  	s2 := &servers.Server{Addr: &fauxAddr{"s2"}}
   133  
   134  	// Try notifying for a server that is not managed by Manager
   135  	m.NotifyFailedServer(s1)
   136  	if m.NumServers() != 0 {
   137  		t.Fatalf("Expected zero servers to start")
   138  	}
   139  	m.SetServers([]*servers.Server{s1})
   140  
   141  	// Test again w/ a server not in the list
   142  	m.NotifyFailedServer(s2)
   143  	if m.NumServers() != 1 {
   144  		t.Fatalf("Expected one server")
   145  	}
   146  
   147  	m.SetServers([]*servers.Server{s1, s2})
   148  	if m.NumServers() != 2 {
   149  		t.Fatalf("Expected two servers")
   150  	}
   151  
   152  	// Grab a server
   153  	first := m.FindServer()
   154  
   155  	// Find the other server
   156  	second := s1
   157  	if first.Equal(s1) {
   158  		second = s2
   159  	}
   160  
   161  	// Fail the other server
   162  	m.NotifyFailedServer(second)
   163  	next := m.FindServer()
   164  	if !next.Equal(first) {
   165  		t.Fatalf("Expected first server (still)")
   166  	}
   167  
   168  	// Fail the first
   169  	m.NotifyFailedServer(first)
   170  	next = m.FindServer()
   171  	if !next.Equal(second) {
   172  		t.Fatalf("Expected second server")
   173  	}
   174  
   175  	// Fail the second
   176  	m.NotifyFailedServer(second)
   177  	next = m.FindServer()
   178  	if !next.Equal(first) {
   179  		t.Fatalf("Expected first server")
   180  	}
   181  }
   182  
   183  func TestServers_NumServers(t *testing.T) {
   184  	m := testManager(t)
   185  	var num int
   186  	num = m.NumServers()
   187  	if num != 0 {
   188  		t.Fatalf("Expected zero servers to start")
   189  	}
   190  
   191  	s := &servers.Server{Addr: &fauxAddr{"server1"}}
   192  	m.SetServers([]*servers.Server{s})
   193  	num = m.NumServers()
   194  	if num != 1 {
   195  		t.Fatalf("Expected one server after SetServers")
   196  	}
   197  }
   198  
   199  func TestServers_RebalanceServers(t *testing.T) {
   200  	const failPct = 0.5
   201  	m := testManagerFailProb(t, failPct)
   202  	const maxServers = 100
   203  	const numShuffleTests = 100
   204  	const uniquePassRate = 0.5
   205  
   206  	// Make a huge list of nodes.
   207  	var srvs []*servers.Server
   208  	for i := 0; i < maxServers; i++ {
   209  		nodeName := fmt.Sprintf("s%02d", i)
   210  		srvs = append(srvs, &servers.Server{Addr: &fauxAddr{nodeName}})
   211  	}
   212  	m.SetServers(srvs)
   213  
   214  	// Keep track of how many unique shuffles we get.
   215  	uniques := make(map[string]struct{}, maxServers)
   216  	for i := 0; i < numShuffleTests; i++ {
   217  		m.RebalanceServers()
   218  
   219  		var names []string
   220  		for j := 0; j < maxServers; j++ {
   221  			server := m.FindServer()
   222  			m.NotifyFailedServer(server)
   223  			names = append(names, server.String())
   224  		}
   225  		key := strings.Join(names, "|")
   226  		uniques[key] = struct{}{}
   227  	}
   228  
   229  	// We have to allow for the fact that there won't always be a unique
   230  	// shuffle each pass, so we just look for smell here without the test
   231  	// being flaky.
   232  	if len(uniques) < int(maxServers*uniquePassRate) {
   233  		t.Fatalf("unique shuffle ratio too low: %d/%d", len(uniques), maxServers)
   234  	}
   235  }