github.com/bigcommerce/nomad@v0.9.3-bc/command/agent/retry_join_test.go (about)

     1  package agent
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/hashicorp/nomad/helper/testlog"
    11  	"github.com/hashicorp/nomad/testutil"
    12  	"github.com/mitchellh/cli"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  type MockDiscover struct {
    17  	ReceivedAddrs string
    18  }
    19  
    20  const stubAddress = "127.0.0.1"
    21  
    22  func (m *MockDiscover) Addrs(s string, l *log.Logger) ([]string, error) {
    23  	m.ReceivedAddrs = s
    24  	return []string{stubAddress}, nil
    25  }
    26  func (m *MockDiscover) Help() string { return "" }
    27  func (m *MockDiscover) Names() []string {
    28  	return []string{""}
    29  }
    30  
    31  func TestRetryJoin_Integration(t *testing.T) {
    32  	t.Parallel()
    33  
    34  	// Create two agents and have one retry join the other
    35  	agent := NewTestAgent(t, t.Name(), nil)
    36  	defer agent.Shutdown()
    37  
    38  	agent2 := NewTestAgent(t, t.Name(), func(c *Config) {
    39  		c.NodeName = "foo"
    40  		if c.Server.ServerJoin == nil {
    41  			c.Server.ServerJoin = &ServerJoin{}
    42  		}
    43  		c.Server.ServerJoin.RetryJoin = []string{agent.Config.normalizedAddrs.Serf}
    44  		c.Server.ServerJoin.RetryInterval = 1 * time.Second
    45  	})
    46  	defer agent2.Shutdown()
    47  
    48  	// Create a fake command and have it wrap the second agent and run the retry
    49  	// join handler
    50  	cmd := &Command{
    51  		Ui: &cli.BasicUi{
    52  			Reader:      os.Stdin,
    53  			Writer:      os.Stdout,
    54  			ErrorWriter: os.Stderr,
    55  		},
    56  		agent: agent2.Agent,
    57  	}
    58  
    59  	if err := cmd.handleRetryJoin(agent2.Config); err != nil {
    60  		t.Fatalf("handleRetryJoin failed: %v", err)
    61  	}
    62  
    63  	// Ensure the retry join occurred.
    64  	testutil.WaitForResult(func() (bool, error) {
    65  		mem := agent.server.Members()
    66  		if len(mem) != 2 {
    67  			return false, fmt.Errorf("bad :%#v", mem)
    68  		}
    69  		return true, nil
    70  	}, func(err error) {
    71  		t.Fatalf(err.Error())
    72  	})
    73  }
    74  
    75  func TestRetryJoin_Server_NonCloud(t *testing.T) {
    76  	t.Parallel()
    77  	require := require.New(t)
    78  
    79  	serverJoin := &ServerJoin{
    80  		RetryMaxAttempts: 1,
    81  		RetryJoin:        []string{"127.0.0.1"},
    82  	}
    83  
    84  	var output []string
    85  
    86  	mockJoin := func(s []string) (int, error) {
    87  		output = s
    88  		return 0, nil
    89  	}
    90  
    91  	joiner := retryJoiner{
    92  		discover:      &MockDiscover{},
    93  		serverJoin:    mockJoin,
    94  		serverEnabled: true,
    95  		logger:        testlog.HCLogger(t),
    96  		errCh:         make(chan struct{}),
    97  	}
    98  
    99  	joiner.RetryJoin(serverJoin)
   100  
   101  	require.Equal(1, len(output))
   102  	require.Equal(stubAddress, output[0])
   103  }
   104  
   105  func TestRetryJoin_Server_Cloud(t *testing.T) {
   106  	t.Parallel()
   107  	require := require.New(t)
   108  
   109  	serverJoin := &ServerJoin{
   110  		RetryMaxAttempts: 1,
   111  		RetryJoin:        []string{"provider=aws, tag_value=foo"},
   112  	}
   113  
   114  	var output []string
   115  
   116  	mockJoin := func(s []string) (int, error) {
   117  		output = s
   118  		return 0, nil
   119  	}
   120  
   121  	mockDiscover := &MockDiscover{}
   122  	joiner := retryJoiner{
   123  		discover:      mockDiscover,
   124  		serverJoin:    mockJoin,
   125  		serverEnabled: true,
   126  		logger:        testlog.HCLogger(t),
   127  		errCh:         make(chan struct{}),
   128  	}
   129  
   130  	joiner.RetryJoin(serverJoin)
   131  
   132  	require.Equal(1, len(output))
   133  	require.Equal("provider=aws, tag_value=foo", mockDiscover.ReceivedAddrs)
   134  	require.Equal(stubAddress, output[0])
   135  }
   136  
   137  func TestRetryJoin_Server_MixedProvider(t *testing.T) {
   138  	t.Parallel()
   139  	require := require.New(t)
   140  
   141  	serverJoin := &ServerJoin{
   142  		RetryMaxAttempts: 1,
   143  		RetryJoin:        []string{"provider=aws, tag_value=foo", "127.0.0.1"},
   144  	}
   145  
   146  	var output []string
   147  
   148  	mockJoin := func(s []string) (int, error) {
   149  		output = s
   150  		return 0, nil
   151  	}
   152  
   153  	mockDiscover := &MockDiscover{}
   154  	joiner := retryJoiner{
   155  		discover:      mockDiscover,
   156  		serverJoin:    mockJoin,
   157  		serverEnabled: true,
   158  		logger:        testlog.HCLogger(t),
   159  		errCh:         make(chan struct{}),
   160  	}
   161  
   162  	joiner.RetryJoin(serverJoin)
   163  
   164  	require.Equal(2, len(output))
   165  	require.Equal("provider=aws, tag_value=foo", mockDiscover.ReceivedAddrs)
   166  	require.Equal(stubAddress, output[0])
   167  }
   168  
   169  func TestRetryJoin_Client(t *testing.T) {
   170  	t.Parallel()
   171  	require := require.New(t)
   172  
   173  	serverJoin := &ServerJoin{
   174  		RetryMaxAttempts: 1,
   175  		RetryJoin:        []string{"127.0.0.1"},
   176  	}
   177  
   178  	var output []string
   179  
   180  	mockJoin := func(s []string) (int, error) {
   181  		output = s
   182  		return 0, nil
   183  	}
   184  
   185  	joiner := retryJoiner{
   186  		discover:      &MockDiscover{},
   187  		clientJoin:    mockJoin,
   188  		clientEnabled: true,
   189  		logger:        testlog.HCLogger(t),
   190  		errCh:         make(chan struct{}),
   191  	}
   192  
   193  	joiner.RetryJoin(serverJoin)
   194  
   195  	require.Equal(1, len(output))
   196  	require.Equal(stubAddress, output[0])
   197  }
   198  
   199  func TestRetryJoin_Validate(t *testing.T) {
   200  	t.Parallel()
   201  	type validateExpect struct {
   202  		config  *Config
   203  		isValid bool
   204  		reason  string
   205  	}
   206  
   207  	scenarios := []*validateExpect{
   208  		{
   209  			config: &Config{
   210  				Server: &ServerConfig{
   211  					ServerJoin: &ServerJoin{
   212  						RetryJoin:        []string{"127.0.0.1"},
   213  						RetryMaxAttempts: 0,
   214  						RetryInterval:    0,
   215  						StartJoin:        []string{},
   216  					},
   217  					RetryJoin:        []string{"127.0.0.1"},
   218  					RetryMaxAttempts: 0,
   219  					RetryInterval:    0,
   220  					StartJoin:        []string{},
   221  				},
   222  			},
   223  			isValid: false,
   224  			reason:  "server_join cannot be defined if retry_join is defined on the server stanza",
   225  		},
   226  		{
   227  			config: &Config{
   228  				Server: &ServerConfig{
   229  					ServerJoin: &ServerJoin{
   230  						RetryJoin:        []string{"127.0.0.1"},
   231  						RetryMaxAttempts: 0,
   232  						RetryInterval:    0,
   233  						StartJoin:        []string{},
   234  					},
   235  					StartJoin:        []string{"127.0.0.1"},
   236  					RetryMaxAttempts: 0,
   237  					RetryInterval:    0,
   238  					RetryJoin:        []string{},
   239  				},
   240  			},
   241  			isValid: false,
   242  			reason:  "server_join cannot be defined if start_join is defined on the server stanza",
   243  		},
   244  		{
   245  			config: &Config{
   246  				Server: &ServerConfig{
   247  					ServerJoin: &ServerJoin{
   248  						RetryJoin:        []string{"127.0.0.1"},
   249  						RetryMaxAttempts: 0,
   250  						RetryInterval:    0,
   251  						StartJoin:        []string{},
   252  					},
   253  					StartJoin:        []string{},
   254  					RetryMaxAttempts: 1,
   255  					RetryInterval:    0,
   256  					RetryJoin:        []string{},
   257  				},
   258  			},
   259  			isValid: false,
   260  			reason:  "server_join cannot be defined if retry_max_attempts is defined on the server stanza",
   261  		},
   262  		{
   263  			config: &Config{
   264  				Server: &ServerConfig{
   265  					ServerJoin: &ServerJoin{
   266  						RetryJoin:        []string{"127.0.0.1"},
   267  						RetryMaxAttempts: 0,
   268  						RetryInterval:    time.Duration(1),
   269  						StartJoin:        []string{},
   270  					},
   271  					StartJoin:        []string{},
   272  					RetryMaxAttempts: 0,
   273  					RetryInterval:    3 * time.Second,
   274  					RetryJoin:        []string{},
   275  				},
   276  			},
   277  			isValid: false,
   278  			reason:  "server_join cannot be defined if retry_interval is defined on the server stanza",
   279  		},
   280  		{
   281  			config: &Config{
   282  				Server: &ServerConfig{
   283  					ServerJoin: &ServerJoin{
   284  						RetryJoin:        []string{"127.0.0.1"},
   285  						RetryMaxAttempts: 0,
   286  						RetryInterval:    0,
   287  						StartJoin:        []string{"127.0.0.1"},
   288  					},
   289  				},
   290  			},
   291  			isValid: false,
   292  			reason:  "start_join and retry_join should not both be defined",
   293  		},
   294  		{
   295  			config: &Config{
   296  				Client: &ClientConfig{
   297  					ServerJoin: &ServerJoin{
   298  						RetryJoin:        []string{},
   299  						RetryMaxAttempts: 0,
   300  						RetryInterval:    0,
   301  						StartJoin:        []string{"127.0.0.1"},
   302  					},
   303  				},
   304  			},
   305  			isValid: false,
   306  			reason:  "start_join should not be defined on the client",
   307  		},
   308  		{
   309  			config: &Config{
   310  				Client: &ClientConfig{
   311  					ServerJoin: &ServerJoin{
   312  						RetryJoin:        []string{"127.0.0.1"},
   313  						RetryMaxAttempts: 0,
   314  						RetryInterval:    0,
   315  					},
   316  				},
   317  			},
   318  			isValid: true,
   319  			reason:  "client server_join should be valid",
   320  		},
   321  		{
   322  			config: &Config{
   323  				Server: &ServerConfig{
   324  					ServerJoin: &ServerJoin{
   325  						RetryJoin:        []string{"127.0.0.1"},
   326  						RetryMaxAttempts: 1,
   327  						RetryInterval:    1,
   328  						StartJoin:        []string{},
   329  					},
   330  				},
   331  			},
   332  			isValid: true,
   333  			reason:  "server server_join should be valid",
   334  		},
   335  	}
   336  
   337  	joiner := retryJoiner{}
   338  	for _, scenario := range scenarios {
   339  		t.Run(scenario.reason, func(t *testing.T) {
   340  			err := joiner.Validate(scenario.config)
   341  			if scenario.isValid {
   342  				require.NoError(t, err)
   343  			} else {
   344  				require.Error(t, err)
   345  			}
   346  		})
   347  	}
   348  }