1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #19573 from sanimej/libn

Vendor in libnetwork v0.6.0-rc2
This commit is contained in:
Tibor Vass 2016-01-22 12:07:40 -05:00
commit a9fc4939df
9 changed files with 144 additions and 75 deletions

View file

@ -21,7 +21,7 @@ import (
runconfigopts "github.com/docker/docker/runconfig/opts" runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/docker/docker/utils" "github.com/docker/docker/utils"
"github.com/docker/docker/volume" "github.com/docker/docker/volume"
"github.com/docker/engine-api/types/container" containertypes "github.com/docker/engine-api/types/container"
"github.com/docker/engine-api/types/network" "github.com/docker/engine-api/types/network"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
"github.com/docker/libnetwork" "github.com/docker/libnetwork"
@ -290,7 +290,7 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network) ([]
} }
} }
if !container.HostConfig.NetworkMode.IsUserDefined() { if !containertypes.NetworkMode(n.Name()).IsUserDefined() {
createOptions = append(createOptions, libnetwork.CreateOptionDisableResolution()) createOptions = append(createOptions, libnetwork.CreateOptionDisableResolution())
} }
@ -577,7 +577,7 @@ func (container *Container) IpcMounts() []execdriver.Mount {
return mounts return mounts
} }
func updateCommand(c *execdriver.Command, resources container.Resources) { func updateCommand(c *execdriver.Command, resources containertypes.Resources) {
c.Resources.BlkioWeight = resources.BlkioWeight c.Resources.BlkioWeight = resources.BlkioWeight
c.Resources.CPUShares = resources.CPUShares c.Resources.CPUShares = resources.CPUShares
c.Resources.CPUPeriod = resources.CPUPeriod c.Resources.CPUPeriod = resources.CPUPeriod
@ -591,7 +591,7 @@ func updateCommand(c *execdriver.Command, resources container.Resources) {
} }
// UpdateContainer updates resources of a container. // UpdateContainer updates resources of a container.
func (container *Container) UpdateContainer(hostConfig *container.HostConfig) error { func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfig) error {
container.Lock() container.Lock()
resources := hostConfig.Resources resources := hostConfig.Resources

View file

@ -27,7 +27,7 @@ clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de
clone git github.com/imdario/mergo 0.2.1 clone git github.com/imdario/mergo 0.2.1
#get libnetwork packages #get libnetwork packages
clone git github.com/docker/libnetwork v0.6.0-rc1 clone git github.com/docker/libnetwork v0.6.0-rc2
clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4 clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4

View file

@ -1301,3 +1301,26 @@ func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectAlias(c *check.C) {
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar") _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
} }
func (s *DockerSuite) TestUserDefinedNetworkConnectivity(c *check.C) {
testRequires(c, DaemonIsLinux, NotUserNamespace)
dockerCmd(c, "network", "create", "-d", "bridge", "br.net1")
dockerCmd(c, "run", "-d", "--net=br.net1", "--name=c1.net1", "busybox", "top")
c.Assert(waitRun("c1.net1"), check.IsNil)
dockerCmd(c, "run", "-d", "--net=br.net1", "--name=c2.net1", "busybox", "top")
c.Assert(waitRun("c2.net1"), check.IsNil)
// ping first container by its unqualified name
_, _, err := dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1")
c.Assert(err, check.IsNil)
// ping first container by its qualified name
_, _, err = dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1.br.net1")
c.Assert(err, check.IsNil)
// ping with first qualified name masked by an additional domain. should fail
_, _, err = dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1.br.net1.google.com")
c.Assert(err, check.NotNil)
}

View file

@ -22,6 +22,7 @@ import (
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
"github.com/docker/docker/runconfig" "github.com/docker/docker/runconfig"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/resolvconf" "github.com/docker/libnetwork/resolvconf"
"github.com/go-check/check" "github.com/go-check/check"
) )
@ -1258,13 +1259,13 @@ func (s *DockerSuite) TestRunDnsOptionsBasedOnHostResolvConf(c *check.C) {
c.Fatalf("/etc/resolv.conf does not exist") c.Fatalf("/etc/resolv.conf does not exist")
} }
hostNamservers := resolvconf.GetNameservers(origResolvConf) hostNamservers := resolvconf.GetNameservers(origResolvConf, netutils.IP)
hostSearch := resolvconf.GetSearchDomains(origResolvConf) hostSearch := resolvconf.GetSearchDomains(origResolvConf)
var out string var out string
out, _ = dockerCmd(c, "run", "--dns=127.0.0.1", "busybox", "cat", "/etc/resolv.conf") out, _ = dockerCmd(c, "run", "--dns=127.0.0.1", "busybox", "cat", "/etc/resolv.conf")
if actualNameservers := resolvconf.GetNameservers([]byte(out)); string(actualNameservers[0]) != "127.0.0.1" { if actualNameservers := resolvconf.GetNameservers([]byte(out), netutils.IP); string(actualNameservers[0]) != "127.0.0.1" {
c.Fatalf("expected '127.0.0.1', but says: %q", string(actualNameservers[0])) c.Fatalf("expected '127.0.0.1', but says: %q", string(actualNameservers[0]))
} }
@ -1280,7 +1281,7 @@ func (s *DockerSuite) TestRunDnsOptionsBasedOnHostResolvConf(c *check.C) {
out, _ = dockerCmd(c, "run", "--dns-search=mydomain", "busybox", "cat", "/etc/resolv.conf") out, _ = dockerCmd(c, "run", "--dns-search=mydomain", "busybox", "cat", "/etc/resolv.conf")
actualNameservers := resolvconf.GetNameservers([]byte(out)) actualNameservers := resolvconf.GetNameservers([]byte(out), netutils.IP)
if len(actualNameservers) != len(hostNamservers) { if len(actualNameservers) != len(hostNamservers) {
c.Fatalf("expected %q nameserver(s), but it has: %q", len(hostNamservers), len(actualNameservers)) c.Fatalf("expected %q nameserver(s), but it has: %q", len(hostNamservers), len(actualNameservers))
} }
@ -1311,11 +1312,11 @@ func (s *DockerSuite) TestRunDnsOptionsBasedOnHostResolvConf(c *check.C) {
c.Fatalf("/etc/resolv.conf does not exist") c.Fatalf("/etc/resolv.conf does not exist")
} }
hostNamservers = resolvconf.GetNameservers(resolvConf) hostNamservers = resolvconf.GetNameservers(resolvConf, netutils.IP)
hostSearch = resolvconf.GetSearchDomains(resolvConf) hostSearch = resolvconf.GetSearchDomains(resolvConf)
out, _ = dockerCmd(c, "run", "busybox", "cat", "/etc/resolv.conf") out, _ = dockerCmd(c, "run", "busybox", "cat", "/etc/resolv.conf")
if actualNameservers = resolvconf.GetNameservers([]byte(out)); string(actualNameservers[0]) != "12.34.56.78" || len(actualNameservers) != 1 { if actualNameservers = resolvconf.GetNameservers([]byte(out), netutils.IP); string(actualNameservers[0]) != "12.34.56.78" || len(actualNameservers) != 1 {
c.Fatalf("expected '12.34.56.78', but has: %v", actualNameservers) c.Fatalf("expected '12.34.56.78', but has: %v", actualNameservers)
} }

View file

@ -1,5 +1,11 @@
# Changelog # Changelog
## 0.6.0-rc2 (2016-01-21)
- Fixes docker/docker#19376
- Fixes docker/docker#15819
- Fixes libnetwork/#885, Not filter v6 DNS servers from resolv.conf
- Fixes docker/docker #19448, also handles the . in service and network names correctly.
## 0.6.0-rc1 (2016-01-14) ## 0.6.0-rc1 (2016-01-14)
- Fixes docker/docker#19404 - Fixes docker/docker#19404
- Fixes the ungraceful daemon restart issue in systemd with remote network plugin - Fixes the ungraceful daemon restart issue in systemd with remote network plugin

View file

@ -14,6 +14,13 @@ import (
"github.com/docker/libnetwork/types" "github.com/docker/libnetwork/types"
) )
// constants for the IP address type
const (
IP = iota // IPv4 and IPv6
IPv4
IPv6
)
var ( var (
// ErrNetworkOverlapsWithNameservers preformatted error // ErrNetworkOverlapsWithNameservers preformatted error
ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver") ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver")

View file

@ -10,6 +10,7 @@ import (
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/resolvconf/dns" "github.com/docker/libnetwork/resolvconf/dns"
) )
@ -29,6 +30,8 @@ var (
localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IPLocalhost + `\s*\n*`) localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IPLocalhost + `\s*\n*`)
nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`) nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`)
nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`)
nsIPv6Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv6Address + `))\s*$`)
nsIPv4Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `))\s*$`)
searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`)
optionsRegexp = regexp.MustCompile(`^\s*options\s*(([^\s]+\s*)*)$`) optionsRegexp = regexp.MustCompile(`^\s*options\s*(([^\s]+\s*)*)$`)
) )
@ -119,7 +122,7 @@ func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
} }
// if the resulting resolvConf has no more nameservers defined, add appropriate // if the resulting resolvConf has no more nameservers defined, add appropriate
// default DNS servers for IPv4 and (optionally) IPv6 // default DNS servers for IPv4 and (optionally) IPv6
if len(GetNameservers(cleanedResolvConf)) == 0 { if len(GetNameservers(cleanedResolvConf, netutils.IP)) == 0 {
logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers : %v", defaultIPv4Dns) logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers : %v", defaultIPv4Dns)
dns := defaultIPv4Dns dns := defaultIPv4Dns
if ipv6Enabled { if ipv6Enabled {
@ -151,10 +154,17 @@ func getLines(input []byte, commentMarker []byte) [][]byte {
} }
// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf // GetNameservers returns nameservers (if any) listed in /etc/resolv.conf
func GetNameservers(resolvConf []byte) []string { func GetNameservers(resolvConf []byte, kind int) []string {
nameservers := []string{} nameservers := []string{}
for _, line := range getLines(resolvConf, []byte("#")) { for _, line := range getLines(resolvConf, []byte("#")) {
var ns = nsRegexp.FindSubmatch(line) var ns [][]byte
if kind == netutils.IP {
ns = nsRegexp.FindSubmatch(line)
} else if kind == netutils.IPv4 {
ns = nsIPv4Regexpmatch.FindSubmatch(line)
} else if kind == netutils.IPv6 {
ns = nsIPv6Regexpmatch.FindSubmatch(line)
}
if len(ns) > 0 { if len(ns) > 0 {
nameservers = append(nameservers, string(ns[1])) nameservers = append(nameservers, string(ns[1]))
} }
@ -167,7 +177,7 @@ func GetNameservers(resolvConf []byte) []string {
// This function's output is intended for net.ParseCIDR // This function's output is intended for net.ParseCIDR
func GetNameserversAsCIDR(resolvConf []byte) []string { func GetNameserversAsCIDR(resolvConf []byte) []string {
nameservers := []string{} nameservers := []string{}
for _, nameserver := range GetNameservers(resolvConf) { for _, nameserver := range GetNameservers(resolvConf, netutils.IP) {
nameservers = append(nameservers, nameserver+"/32") nameservers = append(nameservers, nameserver+"/32")
} }
return nameservers return nameservers

View file

@ -36,6 +36,7 @@ const (
ptrIPv4domain = ".in-addr.arpa." ptrIPv4domain = ".in-addr.arpa."
ptrIPv6domain = ".ip6.arpa." ptrIPv6domain = ".ip6.arpa."
respTTL = 1800 respTTL = 1800
maxExtDNS = 3 //max number of external servers to try
) )
// resolver implements the Resolver interface // resolver implements the Resolver interface
@ -188,15 +189,24 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
if len(r.extDNS) == 0 { if len(r.extDNS) == 0 {
return return
} }
log.Debugf("Querying ext dns %s for %s[%d]", r.extDNS[0], name, query.Question[0].Qtype)
c := &dns.Client{Net: "udp"} num := maxExtDNS
addr := fmt.Sprintf("%s:%d", r.extDNS[0], 53) if len(r.extDNS) < maxExtDNS {
num = len(r.extDNS)
}
for i := 0; i < num; i++ {
log.Debugf("Querying ext dns %s for %s[%d]", r.extDNS[i], name, query.Question[0].Qtype)
// TODO: iterate over avilable servers in case of error c := &dns.Client{Net: "udp"}
resp, _, err = c.Exchange(query, addr) addr := fmt.Sprintf("%s:%d", r.extDNS[i], 53)
if err != nil {
resp, _, err = c.Exchange(query, addr)
if err == nil {
break
}
log.Errorf("external resolution failed, %s", err) log.Errorf("external resolution failed, %s", err)
}
if resp == nil {
return return
} }
} }

View file

@ -14,6 +14,7 @@ import (
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/etchosts" "github.com/docker/libnetwork/etchosts"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/osl"
"github.com/docker/libnetwork/resolvconf" "github.com/docker/libnetwork/resolvconf"
"github.com/docker/libnetwork/types" "github.com/docker/libnetwork/types"
@ -322,11 +323,15 @@ func (sb *sandbox) startResolver() {
} }
}() }()
sb.rebuildDNS() err = sb.rebuildDNS()
if err != nil {
log.Errorf("Updating resolv.conf failed for container %s, %q", sb.ContainerID(), err)
return
}
sb.resolver.SetExtServers(sb.extDNS) sb.resolver.SetExtServers(sb.extDNS)
sb.osSbox.InvokeFunc(sb.resolver.SetupFunc()) sb.osSbox.InvokeFunc(sb.resolver.SetupFunc())
if err := sb.resolver.Start(); err != nil { if err = sb.resolver.Start(); err != nil {
log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err) log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err)
} }
}) })
@ -427,23 +432,51 @@ func (sb *sandbox) ResolveIP(ip string) string {
func (sb *sandbox) ResolveName(name string) net.IP { func (sb *sandbox) ResolveName(name string) net.IP {
var ip net.IP var ip net.IP
parts := strings.Split(name, ".")
log.Debugf("To resolve %v", parts)
reqName := parts[0] // Embedded server owns the docker network domain. Resolution should work
networkName := "" // for both container_name and container_name.network_name
if len(parts) > 1 { // We allow '.' in service name and network name. For a name a.b.c.d the
networkName = parts[1] // following have to tried;
// {a.b.c.d in the networks container is connected to}
// {a.b.c in network d},
// {a.b in network c.d},
// {a in network b.c.d},
name = strings.TrimSuffix(name, ".")
reqName := []string{name}
networkName := []string{""}
if strings.Contains(name, ".") {
var i int
dup := name
for {
if i = strings.LastIndex(dup, "."); i == -1 {
break
}
networkName = append(networkName, name[i+1:])
reqName = append(reqName, name[:i])
dup = dup[:i]
}
} }
epList := sb.getConnectedEndpoints() epList := sb.getConnectedEndpoints()
// First check for local container alias for i := 0; i < len(reqName); i++ {
ip = sb.resolveName(reqName, networkName, epList, true) log.Debugf("To resolve: %v in %v", reqName[i], networkName[i])
if ip != nil {
return ip
}
// Resolve the actual container name // First check for local container alias
return sb.resolveName(reqName, networkName, epList, false) ip = sb.resolveName(reqName[i], networkName[i], epList, true)
if ip != nil {
return ip
}
// Resolve the actual container name
ip = sb.resolveName(reqName[i], networkName[i], epList, false)
if ip != nil {
return ip
}
}
return nil
} }
func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool) net.IP { func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool) net.IP {
@ -823,7 +856,7 @@ func (sb *sandbox) setupDNS() error {
if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 { if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
var ( var (
err error err error
dnsList = resolvconf.GetNameservers(currRC.Content) dnsList = resolvconf.GetNameservers(currRC.Content, netutils.IP)
dnsSearchList = resolvconf.GetSearchDomains(currRC.Content) dnsSearchList = resolvconf.GetSearchDomains(currRC.Content)
dnsOptionsList = resolvconf.GetOptions(currRC.Content) dnsOptionsList = resolvconf.GetOptions(currRC.Content)
) )
@ -865,6 +898,11 @@ func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
hashFile = sb.config.resolvConfHashFile hashFile = sb.config.resolvConfHashFile
) )
// This is for the host mode networking
if sb.config.originResolvConfPath != "" {
return nil
}
if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 { if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
return nil return nil
} }
@ -897,36 +935,21 @@ func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, 0644)
if err != nil {
return err
}
// for atomic updates to these files, use temporary files with os.Rename: // write the new hash in a temp file and rename it to make the update atomic
dir := path.Dir(sb.config.resolvConfPath) dir := path.Dir(sb.config.resolvConfPath)
tmpHashFile, err := ioutil.TempFile(dir, "hash") tmpHashFile, err := ioutil.TempFile(dir, "hash")
if err != nil { if err != nil {
return err return err
} }
tmpResolvFile, err := ioutil.TempFile(dir, "resolv")
if err != nil {
return err
}
// Change the perms to filePerm (0644) since ioutil.TempFile creates it by default as 0600
if err := os.Chmod(tmpResolvFile.Name(), filePerm); err != nil {
return err
}
// write the updates to the temp files
if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newRC.Hash), filePerm); err != nil { if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newRC.Hash), filePerm); err != nil {
return err return err
} }
if err = ioutil.WriteFile(tmpResolvFile.Name(), newRC.Content, filePerm); err != nil { return os.Rename(tmpHashFile.Name(), hashFile)
return err
}
// rename the temp files for atomic replace
if err = os.Rename(tmpHashFile.Name(), hashFile); err != nil {
return err
}
return os.Rename(tmpResolvFile.Name(), sb.config.resolvConfPath)
} }
// Embedded DNS server has to be enabled for this sandbox. Rebuild the container's // Embedded DNS server has to be enabled for this sandbox. Rebuild the container's
@ -941,7 +964,8 @@ func (sb *sandbox) rebuildDNS() error {
} }
// localhost entries have already been filtered out from the list // localhost entries have already been filtered out from the list
sb.extDNS = resolvconf.GetNameservers(currRC.Content) // retain only the v4 servers in sb for forwarding the DNS queries
sb.extDNS = resolvconf.GetNameservers(currRC.Content, netutils.IPv4)
var ( var (
dnsList = []string{sb.resolver.NameServer()} dnsList = []string{sb.resolver.NameServer()}
@ -949,26 +973,14 @@ func (sb *sandbox) rebuildDNS() error {
dnsSearchList = resolvconf.GetSearchDomains(currRC.Content) dnsSearchList = resolvconf.GetSearchDomains(currRC.Content)
) )
// external v6 DNS servers has to be listed in resolv.conf
dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, netutils.IPv6)...)
// Resolver returns the options in the format resolv.conf expects // Resolver returns the options in the format resolv.conf expects
dnsOptionsList = append(dnsOptionsList, sb.resolver.ResolverOptions()...) dnsOptionsList = append(dnsOptionsList, sb.resolver.ResolverOptions()...)
dir := path.Dir(sb.config.resolvConfPath) _, err = resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList)
tmpResolvFile, err := ioutil.TempFile(dir, "resolv") return err
if err != nil {
return err
}
// Change the perms to filePerm (0644) since ioutil.TempFile creates it by default as 0600
if err := os.Chmod(tmpResolvFile.Name(), filePerm); err != nil {
return err
}
_, err = resolvconf.Build(tmpResolvFile.Name(), dnsList, dnsSearchList, dnsOptionsList)
if err != nil {
return err
}
return os.Rename(tmpResolvFile.Name(), sb.config.resolvConfPath)
} }
// joinLeaveStart waits to ensure there are no joins or leaves in progress and // joinLeaveStart waits to ensure there are no joins or leaves in progress and