From 35c8e996c3d23f0ac991ffcef5fb941524f9e583 Mon Sep 17 00:00:00 2001 From: Matt Keeler Date: Fri, 27 Mar 2020 12:31:43 -0400 Subject: [PATCH] =?UTF-8?q?Ensure=20server=20requirements=20checks=20are?= =?UTF-8?q?=20done=20against=20ALL=20known=20se=E2=80=A6=20(#7491)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paul Banks --- agent/consul/acl_server.go | 82 +-------- agent/consul/leader.go | 6 +- agent/consul/leader_connect.go | 4 +- agent/consul/util.go | 210 ++++++++++++++-------- agent/consul/util_test.go | 307 ++++++++++++++------------------- 5 files changed, 280 insertions(+), 329 deletions(-) diff --git a/agent/consul/acl_server.go b/agent/consul/acl_server.go index 3ac8eee61..8f65b234d 100644 --- a/agent/consul/acl_server.go +++ b/agent/consul/acl_server.go @@ -1,15 +1,12 @@ package consul import ( - "fmt" "sync/atomic" "time" "github.com/hashicorp/consul/acl" - "github.com/hashicorp/consul/agent/metadata" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/lib" - "github.com/hashicorp/serf/serf" ) var serverACLCacheConfig *structs.ACLCachesConfig = &structs.ACLCachesConfig{ @@ -105,96 +102,33 @@ func (s *Server) updateACLAdvertisement() { s.updateSerfTags("acls", string(structs.ACLModeEnabled)) } -type serversACLMode struct { - // leader is the address of the leader - leader string - - // mode indicates the overall ACL mode of the servers - mode structs.ACLMode - - // leaderMode is the ACL mode of the leader server - leaderMode structs.ACLMode - - // indicates that at least one server was processed - found bool - - // - server *Server -} - -func (s *serversACLMode) init(leader string) { - s.leader = leader - s.mode = structs.ACLModeEnabled - s.leaderMode = structs.ACLModeUnknown - s.found = false -} - -func (s *serversACLMode) update(srv *metadata.Server) bool { - fmt.Printf("Processing server acl mode for: %s - %s\n", srv.Name, srv.ACLs) - if srv.Status != serf.StatusAlive && srv.Status != serf.StatusFailed { - // they are left or something so regardless we treat these servers as meeting - // the version requirement - return true - } - - // mark that we processed at least one server - s.found = true - - if srvAddr := srv.Addr.String(); srvAddr == s.leader { - s.leaderMode = srv.ACLs - } - - switch srv.ACLs { - case structs.ACLModeDisabled: - // anything disabled means we cant enable ACLs - s.mode = structs.ACLModeDisabled - case structs.ACLModeEnabled: - // do nothing - case structs.ACLModeLegacy: - // This covers legacy mode and older server versions that don't advertise ACL support - if s.mode != structs.ACLModeDisabled && s.mode != structs.ACLModeUnknown { - s.mode = structs.ACLModeLegacy - } - default: - if s.mode != structs.ACLModeDisabled { - s.mode = structs.ACLModeUnknown - } - } - - return true -} - func (s *Server) canUpgradeToNewACLs(isLeader bool) bool { if atomic.LoadInt32(&s.useNewACLs) != 0 { // can't upgrade because we are already upgraded return false } - var state serversACLMode if !s.InACLDatacenter() { - state.init("") - // use the router to check server information for non-local datacenters - s.router.CheckServers(s.config.ACLDatacenter, state.update) - if state.mode != structs.ACLModeEnabled || !state.found { - s.logger.Info("Cannot upgrade to new ACLs, servers in acl datacenter are not yet upgraded", "ACLDatacenter", s.config.ACLDatacenter, "mode", state.mode, "found", state.found) + foundServers, mode, _ := ServersGetACLMode(s, "", s.config.ACLDatacenter) + if mode != structs.ACLModeEnabled || !foundServers { + s.logger.Info("Cannot upgrade to new ACLs, servers in acl datacenter are not yet upgraded", "ACLDatacenter", s.config.ACLDatacenter, "mode", mode, "found", foundServers) return false } } - state.init(string(s.raft.Leader())) - // this uses the serverLookup instead of the router as its for the local datacenter - s.serverLookup.CheckServers(state.update) + leaderAddr := string(s.raft.Leader()) + foundServers, mode, leaderMode := ServersGetACLMode(s, leaderAddr, s.config.Datacenter) if isLeader { - if state.mode == structs.ACLModeLegacy { + if mode == structs.ACLModeLegacy { return true } } else { - if state.leaderMode == structs.ACLModeEnabled { + if leaderMode == structs.ACLModeEnabled { return true } } - s.logger.Info("Cannot upgrade to new ACLs", "leaderMode", state.leaderMode, "mode", state.mode, "found", state.found, "leader", state.leader) + s.logger.Info("Cannot upgrade to new ACLs", "leaderMode", leaderMode, "mode", mode, "found", foundServers, "leader", leaderAddr) return false } diff --git a/agent/consul/leader.go b/agent/consul/leader.go index 524bf6f66..63c8e936a 100644 --- a/agent/consul/leader.go +++ b/agent/consul/leader.go @@ -444,7 +444,7 @@ func (s *Server) initializeLegacyACL() error { // of state in the state store that will cause problems with older // servers consuming snapshots, so we have to wait to create it. var minVersion = version.Must(version.NewVersion("0.9.1")) - if ServersMeetMinimumVersion(s.LANMembers(), minVersion) { + if ok, _ := ServersInDCMeetMinimumVersion(s, s.config.Datacenter, minVersion); ok { canBootstrap, _, err := state.CanBootstrapACLToken() if err != nil { return fmt.Errorf("failed looking for ACL bootstrap info: %v", err) @@ -978,7 +978,7 @@ func (s *Server) getOrCreateAutopilotConfig() *autopilot.Config { return config } - if !ServersMeetMinimumVersion(s.LANMembers(), minAutopilotVersion) { + if ok, _ := ServersInDCMeetMinimumVersion(s, s.config.Datacenter, minAutopilotVersion); !ok { logger.Warn("can't initialize until all servers are >= " + minAutopilotVersion.String()) return nil } @@ -1004,7 +1004,7 @@ func (s *Server) bootstrapConfigEntries(entries []structs.ConfigEntry) error { return nil } - if !ServersMeetMinimumVersion(s.LANMembers(), minCentralizedConfigVersion) { + if ok, _ := ServersInDCMeetMinimumVersion(s, s.config.Datacenter, minCentralizedConfigVersion); !ok { s.loggers. Named(logging.CentralConfig). Warn("config: can't initialize until all servers >=" + minCentralizedConfigVersion.String()) diff --git a/agent/consul/leader_connect.go b/agent/consul/leader_connect.go index a430fe7aa..c62eb6489 100644 --- a/agent/consul/leader_connect.go +++ b/agent/consul/leader_connect.go @@ -193,7 +193,7 @@ func (s *Server) initializeCA() error { // If this isn't the primary DC, run the secondary DC routine if the primary has already been upgraded to at least 1.6.0 if s.config.PrimaryDatacenter != s.config.Datacenter { - versionOk, foundPrimary := ServersInDCMeetMinimumVersion(s.WANMembers(), s.config.PrimaryDatacenter, minMultiDCConnectVersion) + versionOk, foundPrimary := ServersInDCMeetMinimumVersion(s, s.config.PrimaryDatacenter, minMultiDCConnectVersion) if !foundPrimary { connectLogger.Warn("primary datacenter is configured but unreachable - deferring initialization of the secondary datacenter CA") // return nil because we will initialize the secondary CA later @@ -738,7 +738,7 @@ func (s *Server) secondaryCARootWatch(ctx context.Context) error { return nil } if !s.configuredSecondaryCA() { - versionOk, primaryFound := ServersInDCMeetMinimumVersion(s.WANMembers(), s.config.PrimaryDatacenter, minMultiDCConnectVersion) + versionOk, primaryFound := ServersInDCMeetMinimumVersion(s, s.config.PrimaryDatacenter, minMultiDCConnectVersion) if !primaryFound { return fmt.Errorf("Primary datacenter is unreachable - deferring secondary CA initialization") } diff --git a/agent/consul/util.go b/agent/consul/util.go index e3d8be1b3..e8e461b4b 100644 --- a/agent/consul/util.go +++ b/agent/consul/util.go @@ -273,89 +273,163 @@ func runtimeStats() map[string]string { } } -// ServersMeetMinimumVersion returns whether the given alive servers are at least on the -// given Consul version -func ServersMeetMinimumVersion(members []serf.Member, minVersion *version.Version) bool { - return ServersMeetRequirements(members, func(srv *metadata.Server) bool { - return srv.Status != serf.StatusAlive || !srv.Build.LessThan(minVersion) - }) +// checkServersProvider exists so that we can unit tests the requirements checking functions +// without having to spin up a whole agent/server. +type checkServersProvider interface { + CheckServers(datacenter string, fn func(*metadata.Server) bool) } -// ServersMeetMinimumVersion returns whether the given alive servers from a particular -// datacenter are at least on the given Consul version. This requires at least 1 alive server in the DC -func ServersInDCMeetMinimumVersion(members []serf.Member, datacenter string, minVersion *version.Version) (bool, bool) { - found := false - ok := ServersMeetRequirements(members, func(srv *metadata.Server) bool { - if srv.Status != serf.StatusAlive || srv.Datacenter != datacenter { - return true +// serverRequirementsFn should inspect the given metadata.Server struct +// and return two booleans. The first indicates whether the given requirements +// are met. The second indicates whether this server should be considered filtered. +// +// The reason for the two booleans is so that a requirement function could "filter" +// out the left server members if we only want to consider things which are still +// around or likely to come back (failed state). +type serverRequirementFn func(*metadata.Server) (ok bool, filtered bool) + +type serversMeetRequirementsState struct { + // meetsRequirements is the callback to actual check for some specific requirement + meetsRequirements serverRequirementFn + + // ok indicates whether all unfiltered servers meet the desired requirements + ok bool + + // found is a boolean indicating that the meetsRequirement function accepted at + // least one unfiltered server. + found bool +} + +func (s *serversMeetRequirementsState) update(srv *metadata.Server) bool { + ok, filtered := s.meetsRequirements(srv) + + if filtered { + // keep going but don't update any of the internal state as this server + // was filtered by the requirements function + return true + } + + // mark that at least one server processed was not filtered + s.found = true + + if !ok { + // mark that at least one server does not meet the requirements + s.ok = false + + // prevent continuing server evaluation + return false + } + + // this should already be set but this will prevent accidentally reusing + // the state object from causing false-negatives. + s.ok = true + + // continue evaluating servers + return true +} + +// ServersInDCMeetRequirements returns whether the given server members meet the requirements as defined by the +// callback function and whether at least one server remains unfiltered by the requirements function. +func ServersInDCMeetRequirements(provider checkServersProvider, datacenter string, meetsRequirements serverRequirementFn) (ok bool, found bool) { + state := serversMeetRequirementsState{meetsRequirements: meetsRequirements, found: false, ok: true} + + provider.CheckServers(datacenter, state.update) + + return state.ok, state.found +} + +// ServersInDCMeetMinimumVersion returns whether the given alive servers from a particular +// datacenter are at least on the given Consul version. This also returns whether any +// alive or failed servers are known in that datacenter (ignoring left and leaving ones) +func ServersInDCMeetMinimumVersion(provider checkServersProvider, datacenter string, minVersion *version.Version) (ok bool, found bool) { + return ServersInDCMeetRequirements(provider, datacenter, func(srv *metadata.Server) (bool, bool) { + if srv.Status != serf.StatusAlive && srv.Status != serf.StatusFailed { + // filter out the left servers as those should not be factored into our requirements + return true, true } - found = true - return !srv.Build.LessThan(minVersion) + return !srv.Build.LessThan(minVersion), false }) - - return ok, found } -// ServersMeetRequirements returns whether the given server members meet the requirements as defined by the -// callback function -func ServersMeetRequirements(members []serf.Member, meetsRequirements func(*metadata.Server) bool) bool { - for _, member := range members { - if valid, parts := metadata.IsConsulServer(member); valid { - if !meetsRequirements(parts) { - return false - } +// CheckServers implements the checkServersProvider interface for the Server +func (s *Server) CheckServers(datacenter string, fn func(*metadata.Server) bool) { + if datacenter == s.config.Datacenter { + // use the ServerLookup type for the local DC + s.serverLookup.CheckServers(fn) + } else { + // use the router for all non-local DCs + s.router.CheckServers(datacenter, fn) + } +} + +type serversACLMode struct { + // leader is the address of the leader + leader string + + // mode indicates the overall ACL mode of the servers + mode structs.ACLMode + + // leaderMode is the ACL mode of the leader server + leaderMode structs.ACLMode + + // indicates that at least one server was processed + found bool +} + +func (s *serversACLMode) init(leader string) { + s.leader = leader + s.mode = structs.ACLModeEnabled + s.leaderMode = structs.ACLModeUnknown + s.found = false +} + +func (s *serversACLMode) update(srv *metadata.Server) bool { + if srv.Status != serf.StatusAlive && srv.Status != serf.StatusFailed { + // they are left or something so regardless we treat these servers as meeting + // the version requirement + return true + } + + // mark that we processed at least one server + s.found = true + + if srvAddr := srv.Addr.String(); srvAddr == s.leader { + s.leaderMode = srv.ACLs + } + + switch srv.ACLs { + case structs.ACLModeDisabled: + // anything disabled means we cant enable ACLs + s.mode = structs.ACLModeDisabled + case structs.ACLModeEnabled: + // do nothing + case structs.ACLModeLegacy: + // This covers legacy mode and older server versions that don't advertise ACL support + if s.mode != structs.ACLModeDisabled && s.mode != structs.ACLModeUnknown { + s.mode = structs.ACLModeLegacy + } + default: + if s.mode != structs.ACLModeDisabled { + s.mode = structs.ACLModeUnknown } } return true } -func ServersGetACLMode(members []serf.Member, leader string, datacenter string) (numServers int, mode structs.ACLMode, leaderMode structs.ACLMode) { - numServers = 0 - mode = structs.ACLModeEnabled - leaderMode = structs.ACLModeUnknown - for _, member := range members { - if valid, parts := metadata.IsConsulServer(member); valid { +// ServersGetACLMode checks all the servers in a particular datacenter and determines +// what the minimum ACL mode amongst them is and what the leaders ACL mode is. +// The "found" return value indicates whether there were any servers considered in +// this datacenter. If that is false then the other mode return values are meaningless +// as they will be ACLModeEnabled and ACLModeUnkown respectively. +func ServersGetACLMode(provider checkServersProvider, leaderAddr string, datacenter string) (found bool, mode structs.ACLMode, leaderMode structs.ACLMode) { + var state serversACLMode + state.init(leaderAddr) - if datacenter != "" && parts.Datacenter != datacenter { - continue - } + provider.CheckServers(datacenter, state.update) - if parts.Status != serf.StatusAlive && parts.Status != serf.StatusFailed { - // ignore any server that isn't alive or failed. We are considering failed - // because in this state there is a reasonable expectation that they could - // become stable again. Also autopilot should remove dead servers if they - // are truly gone. - continue - } - - numServers += 1 - - if memberAddr := (&net.TCPAddr{IP: member.Addr, Port: parts.Port}).String(); memberAddr == leader { - leaderMode = parts.ACLs - } - - switch parts.ACLs { - case structs.ACLModeDisabled: - // anything disabled means we cant enable ACLs - mode = structs.ACLModeDisabled - case structs.ACLModeEnabled: - // do nothing - case structs.ACLModeLegacy: - // This covers legacy mode and older server versions that don't advertise ACL support - if mode != structs.ACLModeDisabled && mode != structs.ACLModeUnknown { - mode = structs.ACLModeLegacy - } - default: - if mode != structs.ACLModeDisabled { - mode = structs.ACLModeUnknown - } - } - } - } - - return + return state.found, state.mode, state.leaderMode } // InterpolateHIL processes the string as if it were HIL and interpolates only diff --git a/agent/consul/util_test.go b/agent/consul/util_test.go index a0002c162..dbd458ad1 100644 --- a/agent/consul/util_test.go +++ b/agent/consul/util_test.go @@ -7,6 +7,7 @@ import ( "regexp" "testing" + "github.com/hashicorp/consul/agent/metadata" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/go-version" "github.com/hashicorp/serf/serf" @@ -336,108 +337,53 @@ func TestGetPublicIPv6(t *testing.T) { } } -func TestServersMeetMinimumVersion(t *testing.T) { - t.Parallel() - makeMember := func(version string) serf.Member { - return serf.Member{ - Name: "foo", - Addr: net.IP([]byte{127, 0, 0, 1}), - Tags: map[string]string{ - "role": "consul", - "id": "asdf", - "dc": "east-aws", - "port": "10000", - "build": version, - "wan_join_port": "1234", - "vsn": "1", - "expect": "3", - "raft_vsn": "3", - }, - Status: serf.StatusAlive, +type testServersProvider []metadata.Server + +func (p testServersProvider) CheckServers(datacenter string, fn func(*metadata.Server) bool) { + for _, srv := range p { + // filter these out - now I don't have to modify the tests. Originally the dc filtering + // happened in the ServersInDCMeetMinimumVersion, now we get a list of servers to check + // through the routing infrastructure or server lookup which will map a datacenter to a + // list of metadata.Server structs that are all in that datacenter. + if srv.Datacenter != datacenter { + continue } - } - cases := []struct { - members []serf.Member - ver *version.Version - expected bool - }{ - // One server, meets reqs - { - members: []serf.Member{ - makeMember("0.7.5"), - }, - ver: version.Must(version.NewVersion("0.7.5")), - expected: true, - }, - // One server, doesn't meet reqs - { - members: []serf.Member{ - makeMember("0.7.5"), - }, - ver: version.Must(version.NewVersion("0.8.0")), - expected: false, - }, - // Multiple servers, meets req version - { - members: []serf.Member{ - makeMember("0.7.5"), - makeMember("0.8.0"), - }, - ver: version.Must(version.NewVersion("0.7.5")), - expected: true, - }, - // Multiple servers, doesn't meet req version - { - members: []serf.Member{ - makeMember("0.7.5"), - makeMember("0.8.0"), - }, - ver: version.Must(version.NewVersion("0.8.0")), - expected: false, - }, - } - - for _, tc := range cases { - result := ServersMeetMinimumVersion(tc.members, tc.ver) - if result != tc.expected { - t.Fatalf("bad: %v, %v, %v", result, tc.ver.String(), tc) + if !fn(&srv) { + return } } } func TestServersInDCMeetMinimumVersion(t *testing.T) { t.Parallel() - makeMember := func(version string, datacenter string) serf.Member { - return serf.Member{ - Name: "foo", - Addr: net.IP([]byte{127, 0, 0, 1}), - Tags: map[string]string{ - "role": "consul", - "id": "asdf", - "dc": datacenter, - "port": "10000", - "build": version, - "wan_join_port": "1234", - "vsn": "1", - "expect": "3", - "raft_vsn": "3", - }, - Status: serf.StatusAlive, + makeServer := func(versionStr string, datacenter string) metadata.Server { + return metadata.Server{ + Name: "foo", + ShortName: "foo", + ID: "asdf", + Port: 10000, + Expect: 3, + RaftVersion: 3, + Status: serf.StatusAlive, + WanJoinPort: 1234, + Version: 1, + Build: *version.Must(version.NewVersion(versionStr)), + Datacenter: datacenter, } } cases := []struct { - members []serf.Member + servers testServersProvider ver *version.Version expected bool expectedFound bool }{ // One server, meets reqs { - members: []serf.Member{ - makeMember("0.7.5", "primary"), - makeMember("0.7.3", "secondary"), + servers: testServersProvider{ + makeServer("0.7.5", "primary"), + makeServer("0.7.3", "secondary"), }, ver: version.Must(version.NewVersion("0.7.5")), expected: true, @@ -445,9 +391,9 @@ func TestServersInDCMeetMinimumVersion(t *testing.T) { }, // One server, doesn't meet reqs { - members: []serf.Member{ - makeMember("0.7.5", "primary"), - makeMember("0.8.1", "secondary"), + servers: testServersProvider{ + makeServer("0.7.5", "primary"), + makeServer("0.8.1", "secondary"), }, ver: version.Must(version.NewVersion("0.8.0")), expected: false, @@ -455,10 +401,10 @@ func TestServersInDCMeetMinimumVersion(t *testing.T) { }, // Multiple servers, meets req version { - members: []serf.Member{ - makeMember("0.7.5", "primary"), - makeMember("0.8.0", "primary"), - makeMember("0.7.0", "secondary"), + servers: testServersProvider{ + makeServer("0.7.5", "primary"), + makeServer("0.8.0", "primary"), + makeServer("0.7.0", "secondary"), }, ver: version.Must(version.NewVersion("0.7.5")), expected: true, @@ -466,20 +412,20 @@ func TestServersInDCMeetMinimumVersion(t *testing.T) { }, // Multiple servers, doesn't meet req version { - members: []serf.Member{ - makeMember("0.7.5", "primary"), - makeMember("0.8.0", "primary"), - makeMember("0.9.1", "secondary"), + servers: testServersProvider{ + makeServer("0.7.5", "primary"), + makeServer("0.8.0", "primary"), + makeServer("0.9.1", "secondary"), }, ver: version.Must(version.NewVersion("0.8.0")), expected: false, expectedFound: true, }, { - members: []serf.Member{ - makeMember("0.7.5", "secondary"), - makeMember("0.8.0", "secondary"), - makeMember("0.9.1", "secondary"), + servers: testServersProvider{ + makeServer("0.7.5", "secondary"), + makeServer("0.8.0", "secondary"), + makeServer("0.9.1", "secondary"), }, ver: version.Must(version.NewVersion("0.7.0")), expected: true, @@ -488,7 +434,7 @@ func TestServersInDCMeetMinimumVersion(t *testing.T) { } for _, tc := range cases { - result, found := ServersInDCMeetMinimumVersion(tc.members, "primary", tc.ver) + result, found := ServersInDCMeetMinimumVersion(tc.servers, "primary", tc.ver) require.Equal(t, tc.expected, result) require.Equal(t, tc.expectedFound, found) } @@ -626,114 +572,111 @@ func TestInterpolateHIL(t *testing.T) { func TestServersGetACLMode(t *testing.T) { t.Parallel() - makeMember := func(role string, datacenter string, acls structs.ACLMode, status serf.MemberStatus, addr net.IP) serf.Member { - return serf.Member{ - Name: "foo", - Addr: addr, - Tags: map[string]string{ - "role": role, - "id": "asdf", - "dc": datacenter, - "port": "10000", - "build": "1.6.0", - "wan_join_port": "1234", - "vsn": "1", - "expect": "3", - "raft_vsn": "3", - "acls": string(acls), - }, - Status: status, + makeServer := func(datacenter string, acls structs.ACLMode, status serf.MemberStatus, addr net.IP) metadata.Server { + return metadata.Server{ + Name: "foo", + ShortName: "foo", + ID: "asdf", + Port: 10000, + Expect: 3, + RaftVersion: 3, + Status: status, + WanJoinPort: 1234, + Version: 1, + Addr: &net.TCPAddr{IP: addr, Port: 10000}, + // shouldn't matter for these tests + Build: *version.Must(version.NewVersion("1.7.0")), + Datacenter: datacenter, + ACLs: acls, } } type tcase struct { - members []serf.Member - leaderAddr string - datacenter string - numServers int - minMode structs.ACLMode - leaderMode structs.ACLMode + servers testServersProvider + leaderAddr string + datacenter string + foundServers bool + minMode structs.ACLMode + leaderMode structs.ACLMode } cases := map[string]tcase{ "filter-members": tcase{ - members: []serf.Member{ - makeMember("consul", "primary", structs.ACLModeLegacy, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), - makeMember("consul", "primary", structs.ACLModeLegacy, serf.StatusFailed, net.IP([]byte{127, 0, 0, 2})), - // filter non-server - makeMember("client", "primary", structs.ACLModeUnknown, serf.StatusAlive, net.IP([]byte{127, 0, 0, 3})), + servers: testServersProvider{ + makeServer("primary", structs.ACLModeLegacy, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), + makeServer("primary", structs.ACLModeLegacy, serf.StatusFailed, net.IP([]byte{127, 0, 0, 2})), // filtered datacenter - makeMember("consul", "secondary", structs.ACLModeUnknown, serf.StatusAlive, net.IP([]byte{127, 0, 0, 4})), + makeServer("secondary", structs.ACLModeUnknown, serf.StatusAlive, net.IP([]byte{127, 0, 0, 4})), // filtered status - makeMember("consul", "primary", structs.ACLModeUnknown, serf.StatusLeaving, net.IP([]byte{127, 0, 0, 5})), + makeServer("primary", structs.ACLModeUnknown, serf.StatusLeaving, net.IP([]byte{127, 0, 0, 5})), // filtered status - makeMember("consul", "primary", structs.ACLModeUnknown, serf.StatusLeft, net.IP([]byte{127, 0, 0, 6})), + makeServer("primary", structs.ACLModeUnknown, serf.StatusLeft, net.IP([]byte{127, 0, 0, 6})), // filtered status - makeMember("consul", "primary", structs.ACLModeUnknown, serf.StatusNone, net.IP([]byte{127, 0, 0, 7})), + makeServer("primary", structs.ACLModeUnknown, serf.StatusNone, net.IP([]byte{127, 0, 0, 7})), }, - numServers: 2, - leaderAddr: "127.0.0.1:10000", - datacenter: "primary", - minMode: structs.ACLModeLegacy, - leaderMode: structs.ACLModeLegacy, + foundServers: true, + leaderAddr: "127.0.0.1:10000", + datacenter: "primary", + minMode: structs.ACLModeLegacy, + leaderMode: structs.ACLModeLegacy, }, "disabled": tcase{ - members: []serf.Member{ - makeMember("consul", "primary", structs.ACLModeLegacy, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), - makeMember("consul", "primary", structs.ACLModeUnknown, serf.StatusAlive, net.IP([]byte{127, 0, 0, 2})), - makeMember("consul", "primary", structs.ACLModeDisabled, serf.StatusAlive, net.IP([]byte{127, 0, 0, 3})), + servers: testServersProvider{ + makeServer("primary", structs.ACLModeLegacy, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), + makeServer("primary", structs.ACLModeUnknown, serf.StatusAlive, net.IP([]byte{127, 0, 0, 2})), + makeServer("primary", structs.ACLModeDisabled, serf.StatusAlive, net.IP([]byte{127, 0, 0, 3})), }, - numServers: 3, - leaderAddr: "127.0.0.1:10000", - datacenter: "", - minMode: structs.ACLModeDisabled, - leaderMode: structs.ACLModeLegacy, + foundServers: true, + leaderAddr: "127.0.0.1:10000", + datacenter: "primary", + minMode: structs.ACLModeDisabled, + leaderMode: structs.ACLModeLegacy, }, "unknown": tcase{ - members: []serf.Member{ - makeMember("consul", "primary", structs.ACLModeLegacy, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), - makeMember("consul", "primary", structs.ACLModeUnknown, serf.StatusAlive, net.IP([]byte{127, 0, 0, 2})), + servers: testServersProvider{ + makeServer("primary", structs.ACLModeLegacy, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), + makeServer("primary", structs.ACLModeUnknown, serf.StatusAlive, net.IP([]byte{127, 0, 0, 2})), }, - numServers: 2, - leaderAddr: "127.0.0.1:10000", - datacenter: "", - minMode: structs.ACLModeUnknown, - leaderMode: structs.ACLModeLegacy, + foundServers: true, + leaderAddr: "127.0.0.1:10000", + datacenter: "primary", + minMode: structs.ACLModeUnknown, + leaderMode: structs.ACLModeLegacy, }, "legacy": tcase{ - members: []serf.Member{ - makeMember("consul", "primary", structs.ACLModeEnabled, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), - makeMember("consul", "primary", structs.ACLModeLegacy, serf.StatusAlive, net.IP([]byte{127, 0, 0, 2})), + servers: testServersProvider{ + makeServer("primary", structs.ACLModeEnabled, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), + makeServer("primary", structs.ACLModeLegacy, serf.StatusAlive, net.IP([]byte{127, 0, 0, 2})), }, - numServers: 2, - leaderAddr: "127.0.0.1:10000", - datacenter: "", - minMode: structs.ACLModeLegacy, - leaderMode: structs.ACLModeEnabled, + foundServers: true, + leaderAddr: "127.0.0.1:10000", + datacenter: "primary", + minMode: structs.ACLModeLegacy, + leaderMode: structs.ACLModeEnabled, }, "enabled": tcase{ - members: []serf.Member{ - makeMember("consul", "primary", structs.ACLModeEnabled, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), - makeMember("consul", "primary", structs.ACLModeEnabled, serf.StatusAlive, net.IP([]byte{127, 0, 0, 2})), - makeMember("consul", "primary", structs.ACLModeEnabled, serf.StatusAlive, net.IP([]byte{127, 0, 0, 3})), + servers: testServersProvider{ + makeServer("primary", structs.ACLModeEnabled, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), + makeServer("primary", structs.ACLModeEnabled, serf.StatusAlive, net.IP([]byte{127, 0, 0, 2})), + makeServer("primary", structs.ACLModeEnabled, serf.StatusAlive, net.IP([]byte{127, 0, 0, 3})), }, - numServers: 3, - leaderAddr: "127.0.0.1:10000", - datacenter: "", - minMode: structs.ACLModeEnabled, - leaderMode: structs.ACLModeEnabled, + foundServers: true, + leaderAddr: "127.0.0.1:10000", + datacenter: "primary", + minMode: structs.ACLModeEnabled, + leaderMode: structs.ACLModeEnabled, }, "failed-members": tcase{ - members: []serf.Member{ - makeMember("consul", "primary", structs.ACLModeLegacy, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), - makeMember("consul", "primary", structs.ACLModeUnknown, serf.StatusFailed, net.IP([]byte{127, 0, 0, 2})), - makeMember("consul", "primary", structs.ACLModeLegacy, serf.StatusFailed, net.IP([]byte{127, 0, 0, 3})), + servers: testServersProvider{ + makeServer("primary", structs.ACLModeLegacy, serf.StatusAlive, net.IP([]byte{127, 0, 0, 1})), + makeServer("primary", structs.ACLModeUnknown, serf.StatusFailed, net.IP([]byte{127, 0, 0, 2})), + makeServer("primary", structs.ACLModeLegacy, serf.StatusFailed, net.IP([]byte{127, 0, 0, 3})), }, - numServers: 3, - leaderAddr: "127.0.0.1:10000", - datacenter: "", - minMode: structs.ACLModeUnknown, - leaderMode: structs.ACLModeLegacy, + foundServers: true, + leaderAddr: "127.0.0.1:10000", + datacenter: "primary", + minMode: structs.ACLModeUnknown, + leaderMode: structs.ACLModeLegacy, }, } @@ -741,11 +684,11 @@ func TestServersGetACLMode(t *testing.T) { name := name tc := tc t.Run(name, func(t *testing.T) { - actualServers, actualMinMode, actualLeaderMode := ServersGetACLMode(tc.members, tc.leaderAddr, tc.datacenter) + actualServers, actualMinMode, actualLeaderMode := ServersGetACLMode(tc.servers, tc.leaderAddr, tc.datacenter) - require.Equal(t, tc.numServers, actualServers) require.Equal(t, tc.minMode, actualMinMode) require.Equal(t, tc.leaderMode, actualLeaderMode) + require.Equal(t, tc.foundServers, actualServers) }) } }