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