github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/nomad/util_test.go (about)

     1  package nomad
     2  
     3  import (
     4  	"net"
     5  	"reflect"
     6  	"testing"
     7  
     8  	version "github.com/hashicorp/go-version"
     9  	"github.com/hashicorp/nomad/ci"
    10  	"github.com/hashicorp/nomad/helper/uuid"
    11  	"github.com/hashicorp/serf/serf"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func TestIsNomadServer(t *testing.T) {
    16  	ci.Parallel(t)
    17  	m := serf.Member{
    18  		Name:   "foo",
    19  		Addr:   net.IP([]byte{127, 0, 0, 1}),
    20  		Status: serf.StatusAlive,
    21  		Tags: map[string]string{
    22  			"role":     "nomad",
    23  			"region":   "aws",
    24  			"dc":       "east-aws",
    25  			"rpc_addr": "1.1.1.1",
    26  			"port":     "10000",
    27  			"vsn":      "1",
    28  			"raft_vsn": "2",
    29  			"build":    "0.7.0+ent",
    30  			"nonvoter": "1",
    31  		},
    32  	}
    33  	valid, parts := isNomadServer(m)
    34  	if !valid || parts.Region != "aws" ||
    35  		parts.Datacenter != "east-aws" || parts.Port != 10000 {
    36  		t.Fatalf("bad: %v %v", valid, parts)
    37  	}
    38  	if parts.Name != "foo" {
    39  		t.Fatalf("bad: %v", parts)
    40  	}
    41  	if parts.Bootstrap {
    42  		t.Fatalf("unexpected bootstrap")
    43  	}
    44  	if parts.Expect != 0 {
    45  		t.Fatalf("bad: %v", parts.Expect)
    46  	}
    47  	if parts.Status != serf.StatusAlive {
    48  		t.Fatalf("bad: %v", parts.Status)
    49  	}
    50  	if parts.RaftVersion != 2 {
    51  		t.Fatalf("bad: %v", parts.RaftVersion)
    52  	}
    53  	if parts.RPCAddr.String() != "1.1.1.1:10000" {
    54  		t.Fatalf("bad: %v", parts.RPCAddr.String())
    55  	}
    56  	require.Equal(t, 1, parts.MajorVersion)
    57  	if seg := parts.Build.Segments(); len(seg) != 3 {
    58  		t.Fatalf("bad: %v", parts.Build)
    59  	} else if seg[0] != 0 && seg[1] != 7 && seg[2] != 0 {
    60  		t.Fatalf("bad: %v", parts.Build)
    61  	}
    62  	if !parts.NonVoter {
    63  		t.Fatalf("should be nonvoter")
    64  	}
    65  
    66  	m.Tags["bootstrap"] = "1"
    67  	valid, parts = isNomadServer(m)
    68  	if !valid || !parts.Bootstrap {
    69  		t.Fatalf("expected bootstrap")
    70  	}
    71  	if parts.Addr.String() != "127.0.0.1:10000" {
    72  		t.Fatalf("bad addr: %v", parts.Addr)
    73  	}
    74  
    75  	m.Tags["expect"] = "3"
    76  	delete(m.Tags, "bootstrap")
    77  	valid, parts = isNomadServer(m)
    78  	if !valid || parts.Expect != 3 {
    79  		t.Fatalf("bad: %v", parts.Expect)
    80  	}
    81  
    82  	delete(m.Tags, "nonvoter")
    83  	valid, parts = isNomadServer(m)
    84  	if !valid || parts.NonVoter {
    85  		t.Fatalf("should be a voter")
    86  	}
    87  }
    88  
    89  func TestServersMeetMinimumVersionExcludingFailed(t *testing.T) {
    90  	ci.Parallel(t)
    91  
    92  	cases := []struct {
    93  		members  []serf.Member
    94  		ver      *version.Version
    95  		expected bool
    96  	}{
    97  		// One server, meets reqs
    98  		{
    99  			members: []serf.Member{
   100  				makeMember("0.7.5", serf.StatusAlive),
   101  			},
   102  			ver:      version.Must(version.NewVersion("0.7.5")),
   103  			expected: true,
   104  		},
   105  		// One server in dev, meets reqs
   106  		{
   107  			members: []serf.Member{
   108  				makeMember("0.8.5-dev", serf.StatusAlive),
   109  			},
   110  			ver:      version.Must(version.NewVersion("0.7.5")),
   111  			expected: true,
   112  		},
   113  		// One server with meta, meets reqs
   114  		{
   115  			members: []serf.Member{
   116  				makeMember("0.7.5+ent", serf.StatusAlive),
   117  			},
   118  			ver:      version.Must(version.NewVersion("0.7.5")),
   119  			expected: true,
   120  		},
   121  		// One server, doesn't meet reqs
   122  		{
   123  			members: []serf.Member{
   124  				makeMember("0.7.5", serf.StatusAlive),
   125  			},
   126  			ver:      version.Must(version.NewVersion("0.8.0")),
   127  			expected: false,
   128  		},
   129  		// Multiple servers, meets req version, includes failed that doesn't meet req
   130  		{
   131  			members: []serf.Member{
   132  				makeMember("0.7.5", serf.StatusAlive),
   133  				makeMember("0.8.0", serf.StatusAlive),
   134  				makeMember("0.7.0", serf.StatusFailed),
   135  			},
   136  			ver:      version.Must(version.NewVersion("0.7.5")),
   137  			expected: true,
   138  		},
   139  		// Multiple servers, doesn't meet req version
   140  		{
   141  			members: []serf.Member{
   142  				makeMember("0.7.5", serf.StatusAlive),
   143  				makeMember("0.8.0", serf.StatusAlive),
   144  			},
   145  			ver:      version.Must(version.NewVersion("0.8.0")),
   146  			expected: false,
   147  		},
   148  	}
   149  
   150  	for _, tc := range cases {
   151  		result := ServersMeetMinimumVersion(tc.members, AllRegions, tc.ver, false)
   152  		if result != tc.expected {
   153  			t.Fatalf("bad: %v, %v, %v", result, tc.ver.String(), tc)
   154  		}
   155  	}
   156  }
   157  
   158  func TestServersMeetMinimumVersionIncludingFailed(t *testing.T) {
   159  	ci.Parallel(t)
   160  
   161  	cases := []struct {
   162  		members  []serf.Member
   163  		ver      *version.Version
   164  		expected bool
   165  	}{
   166  		// Multiple servers, meets req version
   167  		{
   168  			members: []serf.Member{
   169  				makeMember("0.7.5", serf.StatusAlive),
   170  				makeMember("0.8.0", serf.StatusAlive),
   171  				makeMember("0.7.5", serf.StatusFailed),
   172  			},
   173  			ver:      version.Must(version.NewVersion("0.7.5")),
   174  			expected: true,
   175  		},
   176  		// Multiple servers, doesn't meet req version
   177  		{
   178  			members: []serf.Member{
   179  				makeMember("0.7.5", serf.StatusAlive),
   180  				makeMember("0.8.0", serf.StatusAlive),
   181  				makeMember("0.7.0", serf.StatusFailed),
   182  			},
   183  			ver:      version.Must(version.NewVersion("0.7.5")),
   184  			expected: false,
   185  		},
   186  	}
   187  
   188  	for _, tc := range cases {
   189  		result := ServersMeetMinimumVersion(tc.members, AllRegions, tc.ver, true)
   190  		if result != tc.expected {
   191  			t.Fatalf("bad: %v, %v, %v", result, tc.ver.String(), tc)
   192  		}
   193  	}
   194  }
   195  
   196  func TestServersMeetMinimumVersionSuffix(t *testing.T) {
   197  	t.Parallel()
   198  
   199  	cases := []struct {
   200  		members  []serf.Member
   201  		ver      *version.Version
   202  		expected bool
   203  	}{
   204  		// Multiple servers, meets req version
   205  		{
   206  			members: []serf.Member{
   207  				makeMember("1.3.0", serf.StatusAlive),
   208  				makeMember("1.2.6", serf.StatusAlive),
   209  				makeMember("1.2.6-dev", serf.StatusFailed),
   210  			},
   211  			ver:      version.Must(version.NewVersion("1.2.6-dev")),
   212  			expected: true,
   213  		},
   214  		// Multiple servers, doesn't meet req version
   215  		{
   216  			members: []serf.Member{
   217  				makeMember("1.1.18", serf.StatusAlive),
   218  				makeMember("1.2.6-dev", serf.StatusAlive),
   219  				makeMember("1.0.11", serf.StatusFailed),
   220  			},
   221  			ver:      version.Must(version.NewVersion("1.2.6-dev")),
   222  			expected: false,
   223  		},
   224  	}
   225  
   226  	for _, tc := range cases {
   227  		result := ServersMeetMinimumVersion(tc.members, AllRegions, tc.ver, true)
   228  		if result != tc.expected {
   229  			t.Fatalf("bad: %v, %v, %v", result, tc.ver.String(), tc)
   230  		}
   231  	}
   232  }
   233  
   234  func makeMember(version string, status serf.MemberStatus) serf.Member {
   235  	return serf.Member{
   236  		Name: "foo",
   237  		Addr: net.IP([]byte{127, 0, 0, 1}),
   238  		Tags: map[string]string{
   239  			"role":   "nomad",
   240  			"region": "aws",
   241  			"dc":     "east-aws",
   242  			"port":   "10000",
   243  			"build":  version,
   244  			"vsn":    "1",
   245  		},
   246  		Status: status,
   247  	}
   248  }
   249  
   250  func TestShuffleStrings(t *testing.T) {
   251  	ci.Parallel(t)
   252  	// Generate input
   253  	inp := make([]string, 10)
   254  	for idx := range inp {
   255  		inp[idx] = uuid.Generate()
   256  	}
   257  
   258  	// Copy the input
   259  	orig := make([]string, len(inp))
   260  	copy(orig, inp)
   261  
   262  	// Shuffle
   263  	shuffleStrings(inp)
   264  
   265  	// Ensure order is not the same
   266  	if reflect.DeepEqual(inp, orig) {
   267  		t.Fatalf("shuffle failed")
   268  	}
   269  }
   270  
   271  func Test_partitionAll(t *testing.T) {
   272  	xs := []string{"a", "b", "c", "d", "e", "f"}
   273  	// evenly divisible
   274  	require.Equal(t, [][]string{{"a", "b"}, {"c", "d"}, {"e", "f"}}, partitionAll(2, xs))
   275  	require.Equal(t, [][]string{{"a", "b", "c"}, {"d", "e", "f"}}, partitionAll(3, xs))
   276  	// whole thing fits int the last part
   277  	require.Equal(t, [][]string{{"a", "b", "c", "d", "e", "f"}}, partitionAll(7, xs))
   278  	// odd remainder
   279  	require.Equal(t, [][]string{{"a", "b", "c", "d"}, {"e", "f"}}, partitionAll(4, xs))
   280  	// zero size
   281  	require.Equal(t, [][]string{{"a", "b", "c", "d", "e", "f"}}, partitionAll(0, xs))
   282  	// one size
   283  	require.Equal(t, [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}, {"f"}}, partitionAll(1, xs))
   284  }
   285  
   286  func TestMaxUint64(t *testing.T) {
   287  	ci.Parallel(t)
   288  	if maxUint64(1, 2) != 2 {
   289  		t.Fatalf("bad")
   290  	}
   291  	if maxUint64(2, 2) != 2 {
   292  		t.Fatalf("bad")
   293  	}
   294  	if maxUint64(2, 1) != 2 {
   295  		t.Fatalf("bad")
   296  	}
   297  }