github.imxd.top/hashicorp/consul@v1.4.5/agent/router/manager_internal_test.go (about)

     1  package router
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"math/rand"
     8  	"net"
     9  	"os"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/hashicorp/consul/agent/metadata"
    14  )
    15  
    16  var (
    17  	localLogger    *log.Logger
    18  	localLogBuffer *bytes.Buffer
    19  )
    20  
    21  func init() {
    22  	localLogBuffer = new(bytes.Buffer)
    23  	localLogger = log.New(localLogBuffer, "", 0)
    24  }
    25  
    26  func GetBufferedLogger() *log.Logger {
    27  	return localLogger
    28  }
    29  
    30  type fauxConnPool struct {
    31  	// failPct between 0.0 and 1.0 == pct of time a Ping should fail
    32  	failPct float64
    33  }
    34  
    35  func (cp *fauxConnPool) Ping(string, net.Addr, int, bool) (bool, error) {
    36  	var success bool
    37  	successProb := rand.Float64()
    38  	if successProb > cp.failPct {
    39  		success = true
    40  	}
    41  	return success, nil
    42  }
    43  
    44  type fauxSerf struct {
    45  	numNodes int
    46  }
    47  
    48  func (s *fauxSerf) NumNodes() int {
    49  	return s.numNodes
    50  }
    51  
    52  func testManager() (m *Manager) {
    53  	logger := GetBufferedLogger()
    54  	shutdownCh := make(chan struct{})
    55  	m = New(logger, shutdownCh, &fauxSerf{numNodes: 16384}, &fauxConnPool{})
    56  	return m
    57  }
    58  
    59  func testManagerFailProb(failPct float64) (m *Manager) {
    60  	logger := GetBufferedLogger()
    61  	logger = log.New(os.Stderr, "", log.LstdFlags)
    62  	shutdownCh := make(chan struct{})
    63  	m = New(logger, shutdownCh, &fauxSerf{}, &fauxConnPool{failPct: failPct})
    64  	return m
    65  }
    66  
    67  // func (l *serverList) cycleServer() (servers []*metadata.Server) {
    68  func TestManagerInternal_cycleServer(t *testing.T) {
    69  	m := testManager()
    70  	l := m.getServerList()
    71  
    72  	server0 := &metadata.Server{Name: "server1"}
    73  	server1 := &metadata.Server{Name: "server2"}
    74  	server2 := &metadata.Server{Name: "server3"}
    75  	l.servers = append(l.servers, server0, server1, server2)
    76  	m.saveServerList(l)
    77  
    78  	l = m.getServerList()
    79  	if len(l.servers) != 3 {
    80  		t.Fatalf("server length incorrect: %d/3", len(l.servers))
    81  	}
    82  	if l.servers[0] != server0 &&
    83  		l.servers[1] != server1 &&
    84  		l.servers[2] != server2 {
    85  		t.Fatalf("initial server ordering not correct")
    86  	}
    87  
    88  	l.servers = l.cycleServer()
    89  	if len(l.servers) != 3 {
    90  		t.Fatalf("server length incorrect: %d/3", len(l.servers))
    91  	}
    92  	if l.servers[0] != server1 &&
    93  		l.servers[1] != server2 &&
    94  		l.servers[2] != server0 {
    95  		t.Fatalf("server ordering after one cycle not correct")
    96  	}
    97  
    98  	l.servers = l.cycleServer()
    99  	if len(l.servers) != 3 {
   100  		t.Fatalf("server length incorrect: %d/3", len(l.servers))
   101  	}
   102  	if l.servers[0] != server2 &&
   103  		l.servers[1] != server0 &&
   104  		l.servers[2] != server1 {
   105  		t.Fatalf("server ordering after two cycles not correct")
   106  	}
   107  
   108  	l.servers = l.cycleServer()
   109  	if len(l.servers) != 3 {
   110  		t.Fatalf("server length incorrect: %d/3", len(l.servers))
   111  	}
   112  	if l.servers[0] != server0 &&
   113  		l.servers[1] != server1 &&
   114  		l.servers[2] != server2 {
   115  		t.Fatalf("server ordering after three cycles not correct")
   116  	}
   117  }
   118  
   119  // func (m *Manager) getServerList() serverList {
   120  func TestManagerInternal_getServerList(t *testing.T) {
   121  	m := testManager()
   122  	l := m.getServerList()
   123  	if l.servers == nil {
   124  		t.Fatalf("serverList.servers nil")
   125  	}
   126  
   127  	if len(l.servers) != 0 {
   128  		t.Fatalf("serverList.servers length not zero")
   129  	}
   130  }
   131  
   132  // func New(logger *log.Logger, shutdownCh chan struct{}, clusterInfo ConsulClusterInfo) (m *Manager) {
   133  func TestManagerInternal_New(t *testing.T) {
   134  	m := testManager()
   135  	if m == nil {
   136  		t.Fatalf("Manager nil")
   137  	}
   138  
   139  	if m.clusterInfo == nil {
   140  		t.Fatalf("Manager.clusterInfo nil")
   141  	}
   142  
   143  	if m.logger == nil {
   144  		t.Fatalf("Manager.logger nil")
   145  	}
   146  
   147  	if m.shutdownCh == nil {
   148  		t.Fatalf("Manager.shutdownCh nil")
   149  	}
   150  }
   151  
   152  // func (m *Manager) reconcileServerList(l *serverList) bool {
   153  func TestManagerInternal_reconcileServerList(t *testing.T) {
   154  	tests := []int{0, 1, 2, 3, 4, 5, 10, 100}
   155  	for _, n := range tests {
   156  		ok, err := test_reconcileServerList(n)
   157  		if !ok {
   158  			t.Errorf("Expected %d to pass: %v", n, err)
   159  		}
   160  	}
   161  }
   162  
   163  func test_reconcileServerList(maxServers int) (bool, error) {
   164  	// Build a server list, reconcile, verify the missing servers are
   165  	// missing, the added have been added, and the original server is
   166  	// present.
   167  	const failPct = 0.5
   168  	m := testManagerFailProb(failPct)
   169  
   170  	var failedServers, healthyServers []*metadata.Server
   171  	for i := 0; i < maxServers; i++ {
   172  		nodeName := fmt.Sprintf("s%02d", i)
   173  
   174  		node := &metadata.Server{Name: nodeName}
   175  		// Add 66% of servers to Manager
   176  		if rand.Float64() > 0.33 {
   177  			m.AddServer(node)
   178  
   179  			// Of healthy servers, (ab)use connPoolPinger to
   180  			// failPct of the servers for the reconcile.  This
   181  			// allows for the selected server to no longer be
   182  			// healthy for the reconcile below.
   183  			if ok, _ := m.connPoolPinger.Ping(node.Datacenter, node.Addr, node.Version, node.UseTLS); ok {
   184  				// Will still be present
   185  				healthyServers = append(healthyServers, node)
   186  			} else {
   187  				// Will be missing
   188  				failedServers = append(failedServers, node)
   189  			}
   190  		} else {
   191  			// Will be added from the call to reconcile
   192  			healthyServers = append(healthyServers, node)
   193  		}
   194  	}
   195  
   196  	// Randomize Manager's server list
   197  	m.RebalanceServers()
   198  	selectedServer := m.FindServer()
   199  
   200  	var selectedServerFailed bool
   201  	for _, s := range failedServers {
   202  		if selectedServer.Key().Equal(s.Key()) {
   203  			selectedServerFailed = true
   204  			break
   205  		}
   206  	}
   207  
   208  	// Update Manager's server list to be "healthy" based on Serf.
   209  	// Reconcile this with origServers, which is shuffled and has a live
   210  	// connection, but possibly out of date.
   211  	origServers := m.getServerList()
   212  	m.saveServerList(serverList{servers: healthyServers})
   213  
   214  	// This should always succeed with non-zero server lists
   215  	if !selectedServerFailed && !m.reconcileServerList(&origServers) &&
   216  		len(m.getServerList().servers) != 0 &&
   217  		len(origServers.servers) != 0 {
   218  		// If the random gods are unfavorable and we end up with zero
   219  		// length lists, expect things to fail and retry the test.
   220  		return false, fmt.Errorf("Expected reconcile to succeed: %v %d %d",
   221  			selectedServerFailed,
   222  			len(m.getServerList().servers),
   223  			len(origServers.servers))
   224  	}
   225  
   226  	// If we have zero-length server lists, test succeeded in degenerate
   227  	// case.
   228  	if len(m.getServerList().servers) == 0 &&
   229  		len(origServers.servers) == 0 {
   230  		// Failed as expected w/ zero length list
   231  		return true, nil
   232  	}
   233  
   234  	resultingServerMap := make(map[metadata.Key]bool)
   235  	for _, s := range m.getServerList().servers {
   236  		resultingServerMap[*s.Key()] = true
   237  	}
   238  
   239  	// Test to make sure no failed servers are in the Manager's
   240  	// list.  Error if there are any failedServers in l.servers
   241  	for _, s := range failedServers {
   242  		_, ok := resultingServerMap[*s.Key()]
   243  		if ok {
   244  			return false, fmt.Errorf("Found failed server %v in merged list %v", s, resultingServerMap)
   245  		}
   246  	}
   247  
   248  	// Test to make sure all healthy servers are in the healthy list.
   249  	if len(healthyServers) != len(m.getServerList().servers) {
   250  		return false, fmt.Errorf("Expected healthy map and servers to match: %d/%d", len(healthyServers), len(healthyServers))
   251  	}
   252  
   253  	// Test to make sure all healthy servers are in the resultingServerMap list.
   254  	for _, s := range healthyServers {
   255  		_, ok := resultingServerMap[*s.Key()]
   256  		if !ok {
   257  			return false, fmt.Errorf("Server %v missing from healthy map after merged lists", s)
   258  		}
   259  	}
   260  	return true, nil
   261  }
   262  
   263  // func (l *serverList) refreshServerRebalanceTimer() {
   264  func TestManagerInternal_refreshServerRebalanceTimer(t *testing.T) {
   265  	type clusterSizes struct {
   266  		numNodes     int
   267  		numServers   int
   268  		minRebalance time.Duration
   269  	}
   270  	clusters := []clusterSizes{
   271  		{0, 3, 2 * time.Minute},
   272  		{1, 0, 2 * time.Minute}, // partitioned cluster
   273  		{1, 3, 2 * time.Minute},
   274  		{2, 3, 2 * time.Minute},
   275  		{100, 0, 2 * time.Minute}, // partitioned
   276  		{100, 1, 2 * time.Minute}, // partitioned
   277  		{100, 3, 2 * time.Minute},
   278  		{1024, 1, 2 * time.Minute}, // partitioned
   279  		{1024, 3, 2 * time.Minute}, // partitioned
   280  		{1024, 5, 2 * time.Minute},
   281  		{16384, 1, 4 * time.Minute}, // partitioned
   282  		{16384, 2, 2 * time.Minute}, // partitioned
   283  		{16384, 3, 2 * time.Minute}, // partitioned
   284  		{16384, 5, 2 * time.Minute},
   285  		{65535, 0, 2 * time.Minute}, // partitioned
   286  		{65535, 1, 8 * time.Minute}, // partitioned
   287  		{65535, 2, 3 * time.Minute}, // partitioned
   288  		{65535, 3, 5 * time.Minute}, // partitioned
   289  		{65535, 5, 3 * time.Minute}, // partitioned
   290  		{65535, 7, 2 * time.Minute},
   291  		{1000000, 1, 4 * time.Hour},     // partitioned
   292  		{1000000, 2, 2 * time.Hour},     // partitioned
   293  		{1000000, 3, 80 * time.Minute},  // partitioned
   294  		{1000000, 5, 50 * time.Minute},  // partitioned
   295  		{1000000, 11, 20 * time.Minute}, // partitioned
   296  		{1000000, 19, 10 * time.Minute},
   297  	}
   298  
   299  	logger := log.New(os.Stderr, "", log.LstdFlags)
   300  	shutdownCh := make(chan struct{})
   301  
   302  	for _, s := range clusters {
   303  		m := New(logger, shutdownCh, &fauxSerf{numNodes: s.numNodes}, &fauxConnPool{})
   304  		for i := 0; i < s.numServers; i++ {
   305  			nodeName := fmt.Sprintf("s%02d", i)
   306  			m.AddServer(&metadata.Server{Name: nodeName})
   307  		}
   308  
   309  		d := m.refreshServerRebalanceTimer()
   310  		if d < s.minRebalance {
   311  			t.Errorf("duration too short for cluster of size %d and %d servers (%s < %s)", s.numNodes, s.numServers, d, s.minRebalance)
   312  		}
   313  	}
   314  }
   315  
   316  // func (m *Manager) saveServerList(l serverList) {
   317  func TestManagerInternal_saveServerList(t *testing.T) {
   318  	m := testManager()
   319  
   320  	// Initial condition
   321  	func() {
   322  		l := m.getServerList()
   323  		if len(l.servers) != 0 {
   324  			t.Fatalf("Manager.saveServerList failed to load init config")
   325  		}
   326  
   327  		newServer := new(metadata.Server)
   328  		l.servers = append(l.servers, newServer)
   329  		m.saveServerList(l)
   330  	}()
   331  
   332  	// Test that save works
   333  	func() {
   334  		l1 := m.getServerList()
   335  		t1NumServers := len(l1.servers)
   336  		if t1NumServers != 1 {
   337  			t.Fatalf("Manager.saveServerList failed to save mutated config")
   338  		}
   339  	}()
   340  
   341  	// Verify mutation w/o a save doesn't alter the original
   342  	func() {
   343  		newServer := new(metadata.Server)
   344  		l := m.getServerList()
   345  		l.servers = append(l.servers, newServer)
   346  
   347  		l_orig := m.getServerList()
   348  		origNumServers := len(l_orig.servers)
   349  		if origNumServers >= len(l.servers) {
   350  			t.Fatalf("Manager.saveServerList unsaved config overwrote original")
   351  		}
   352  	}()
   353  }