etcd: Connection options (socket file, ipv6)

- Allow unix domain socket to be used as client url
- Using ::1 as clienturl should not create default local ipv4 listener
- Add shell tests
This commit is contained in:
Johan Bloemberg
2019-02-06 16:51:13 +01:00
parent 4c8086977a
commit f7a06c1da9
11 changed files with 163 additions and 8 deletions

View File

@@ -194,6 +194,7 @@ type EmbdEtcd struct { // EMBeddeD etcd
advertiseClientURLs etcdtypes.URLs // client urls to advertise
advertiseServerURLs etcdtypes.URLs // server urls to advertise
noServer bool // disable all server peering if true
noNetwork bool // use unix:// sockets instead of TCP for clients/servers
// local tracked state
nominated etcdtypes.URLsMap // copy of who's nominated to locally track state
@@ -220,7 +221,7 @@ type EmbdEtcd struct { // EMBeddeD etcd
}
// NewEmbdEtcd creates the top level embedded etcd struct client and server obj.
func NewEmbdEtcd(hostname string, seeds, clientURLs, serverURLs, advertiseClientURLs, advertiseServerURLs etcdtypes.URLs, noServer bool, idealClusterSize uint16, flags Flags, prefix string, converger converger.Converger) *EmbdEtcd {
func NewEmbdEtcd(hostname string, seeds, clientURLs, serverURLs, advertiseClientURLs, advertiseServerURLs etcdtypes.URLs, noServer bool, noNetwork bool, idealClusterSize uint16, flags Flags, prefix string, converger converger.Converger) *EmbdEtcd {
endpoints := make(etcdtypes.URLsMap)
if hostname == seedSentinel { // safety
return nil
@@ -229,6 +230,15 @@ func NewEmbdEtcd(hostname string, seeds, clientURLs, serverURLs, advertiseClient
log.Printf("Etcd: need at least one seed if running with --no-server!")
return nil
}
if noNetwork {
if len(clientURLs) != 0 || len(serverURLs) != 0 || len(seeds) != 0 {
log.Printf("--no-network is mutual exclusive with --seeds, --client-urls and --server-urls")
return nil
}
clientURLs, _ = etcdtypes.NewURLs([]string{"unix://clients.sock:0"})
serverURLs, _ = etcdtypes.NewURLs([]string{"unix://servers.sock:0"})
}
if len(seeds) > 0 {
endpoints[seedSentinel] = seeds
idealClusterSize = 0 // unset, get from running cluster
@@ -253,6 +263,7 @@ func NewEmbdEtcd(hostname string, seeds, clientURLs, serverURLs, advertiseClient
advertiseClientURLs: advertiseClientURLs,
advertiseServerURLs: advertiseServerURLs,
noServer: noServer,
noNetwork: noNetwork,
idealClusterSize: idealClusterSize,
converger: converger,
@@ -304,7 +315,7 @@ func (obj *EmbdEtcd) GetConfig() etcd.Config {
// XXX: filter out any urls which wouldn't resolve here ?
for _, eps := range obj.endpoints { // flatten map
for _, u := range eps {
endpoints = append(endpoints, u.Host) // remove http:// prefix
endpoints = append(endpoints, u.String()) // use full url including scheme
}
}
sort.Strings(endpoints) // sort for determinism
@@ -1692,8 +1703,12 @@ func (obj *EmbdEtcd) LocalhostClientURLs() etcdtypes.URLs {
// look through obj.clientURLs and return the localhost ones
urls := etcdtypes.URLs{}
for _, x := range obj.clientURLs {
// "localhost" or anything in 127.0.0.0/8 is valid!
if s := x.Host; strings.HasPrefix(s, "localhost") || strings.HasPrefix(s, "127.") {
// "localhost", ::1 or anything in 127.0.0.0/8 is valid!
if s := x.Host; strings.HasPrefix(s, "localhost") || strings.HasPrefix(s, "127.") || strings.HasPrefix(s, "[::1]") {
urls = append(urls, x)
}
// or local unix domain socket
if x.Scheme == "unix" {
urls = append(urls, x)
}
}

View File

@@ -31,7 +31,7 @@ func TestNewEmbdEtcd(t *testing.T) {
noServer := false
var flags Flags
obj := NewEmbdEtcd("", nil, nil, nil, nil, nil, noServer, 0, flags, "", nil)
obj := NewEmbdEtcd("", nil, nil, nil, nil, nil, noServer, false, 0, flags, "", nil)
if obj == nil {
t.Fatal("failed to create server object")
}
@@ -44,7 +44,7 @@ func TestNewEmbdEtcdConfigValidation(t *testing.T) {
noServer := true
var flags Flags
obj := NewEmbdEtcd("", seeds, nil, nil, nil, nil, noServer, 0, flags, "", nil)
obj := NewEmbdEtcd("", seeds, nil, nil, nil, nil, noServer, false, 0, flags, "", nil)
if obj != nil {
t.Fatal("server initialization should fail on invalid configuration")
}

View File

@@ -171,7 +171,11 @@ func CLI(program, version string, flags Flags) error {
Name: "no-server",
Usage: "do not start embedded etcd server (do not promote from client to peer)",
},
cli.BoolFlag{
Name: "no-network",
Usage: "run single node instance without clustering or opening tcp ports to the outside",
EnvVar: "MGMT_NO_NETWORK",
},
cli.BoolFlag{
Name: "no-pgp",
Usage: "don't create pgp keys",

View File

@@ -89,6 +89,7 @@ type Main struct {
AdvertiseServerURLs []string // list of URLs to advertise for server (peer) traffic
IdealClusterSize int // ideal number of server peers in cluster; only read by initial server
NoServer bool // do not let other servers peer with me
NoNetwork bool // run single node instance without clustering or opening tcp ports to the outside
seeds etcdtypes.URLs // processed seeds value
clientURLs etcdtypes.URLs // processed client urls value
@@ -354,6 +355,7 @@ func (obj *Main) Run() error {
obj.advertiseClientURLs,
obj.advertiseServerURLs,
obj.NoServer,
obj.NoNetwork,
obj.idealClusterSize,
etcd.Flags{
Debug: obj.Flags.Debug,

View File

@@ -107,6 +107,7 @@ func run(c *cli.Context, name string, gapiObj gapi.GAPI) error {
obj.AdvertiseServerURLs = cliContext.StringSlice("advertise-server-urls")
obj.IdealClusterSize = cliContext.Int("ideal-cluster-size")
obj.NoServer = cliContext.Bool("no-server")
obj.NoNetwork = cliContext.Bool("no-network")
obj.NoPgp = cliContext.Bool("no-pgp")

40
test/shell/clustersize.sh Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
set -o errexit
set -o pipefail
if ! command -v etcdctl >/dev/null; then
echo "Missing etcdctl, skipping"
exit 0
fi
. "$(dirname "$0")/../util.sh"
mkdir /tmp/mgmt/{A..E}
# kill servers on error/exit
trap 'pkill -9 mgmt' EXIT
"$MGMT" run --hostname h1 --tmp-prefix --no-pgp empty &
"$MGMT" run --hostname h2 --tmp-prefix --no-pgp --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2381 --server-urls http://127.0.0.1:2382 empty &
"$MGMT" run --hostname h3 --tmp-prefix --no-pgp --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2383 --server-urls http://127.0.0.1:2384 empty &
# wait for everything to converge
sleep 10
ETCDCTL_API=3 etcdctl --endpoints 127.0.0.1:2379 put /_mgmt/idealClusterSize 3
"$MGMT" run --hostname h4 --tmp-prefix --no-pgp --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2385 --server-urls http://127.0.0.1:2386 empty &
"$MGMT" run --hostname h5 --tmp-prefix --no-pgp --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2387 --server-urls http://127.0.0.1:2388 empty &
# wait for everything to converge
sleep 10
test "$(ETCDCTL_API=3 etcdctl --endpoints 127.0.0.1:2379 member list | wc -l)" -eq 3
ETCDCTL_API=3 etcdctl --endpoints 127.0.0.1:2381 put /_mgmt/idealClusterSize 5
# wait for everything to converge
sleep 5
test "$(ETCDCTL_API=3 etcdctl --endpoints 127.0.0.1:2381 member list | wc -l)" -eq 5

22
test/shell/exchange.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
set -o errexit
set -o pipefail
. "$(dirname "$0")/../util.sh"
"$MGMT" run --hostname h1 --ideal-cluster-size 1 --tmp-prefix --no-pgp lang --lang exchange0.mcl &
"$MGMT" run --hostname h2 --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2381 --server-urls http://127.0.0.1:2382 --tmp-prefix --no-pgp lang --lang exchange0.mcl &
"$MGMT" run --hostname h3 --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2383 --server-urls http://127.0.0.1:2384 --tmp-prefix --no-pgp lang --lang exchange0.mcl &
"$MGMT" run --hostname h4 --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2385 --server-urls http://127.0.0.1:2386 --tmp-prefix --no-pgp lang --lang exchange0.mcl &
# kill servers on error/exit
trap 'pkill -9 mgmt' EXIT
# wait for everything to converge
sleep 10
test "$(cat /tmp/mgmt/exchange-* | grep -c h1)" -eq 4
test "$(cat /tmp/mgmt/exchange-* | grep -c h2)" -eq 4
test "$(cat /tmp/mgmt/exchange-* | grep -c h3)" -eq 4
test "$(cat /tmp/mgmt/exchange-* | grep -c h4)" -eq 4

16
test/shell/exchange0.mcl Normal file
View File

@@ -0,0 +1,16 @@
# run this example with these commands
# watch -n 0.1 'tail *' # run this in /tmp/mgmt/
# time ./mgmt run --hostname h1 --ideal-cluster-size 1 --tmp-prefix --no-pgp lang --lang examples/lang/exchange0.mcl
# time ./mgmt run --hostname h2 --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2381 --server-urls http://127.0.0.1:2382 --tmp-prefix --no-pgp lang --lang examples/lang/exchange0.mcl
# time ./mgmt run --hostname h3 --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2383 --server-urls http://127.0.0.1:2384 --tmp-prefix --no-pgp lang --lang examples/lang/exchange0.mcl
# time ./mgmt run --hostname h4 --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2385 --server-urls http://127.0.0.1:2386 --tmp-prefix --no-pgp lang --lang examples/lang/exchange0.mcl
import "sys"
import "world"
$rand = random1(8)
$exchanged = world.exchange("keyns", $rand)
file "/tmp/mgmt/exchange-${sys.hostname()}" {
content => template("Found: {{ . }}\n", $exchanged),
}

29
test/shell/ipv6-localhost.sh Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
set -o errexit
set -o pipefail
if ! ifconfig lo | grep 'inet6 ::1' >/dev/null; then
echo "No IPv6, skipping test"
exit 0
fi
. "$(dirname "$0")/../util.sh"
tmpdir="$($mktemp --tmpdir -d tmp.XXX)"
# run empty graph listing only to IPv6 addresses
"$MGMT" run --client-urls "http://[::1]:2379" --server-urls "http://[::1]:2380" --tmp-prefix empty &
pid=$!
# kill server on error/exit
trap 'pkill -9 mgmt' EXIT
# give mgmt a little time to startup
sleep 10
# mgmt configured for ipv6 only should not listen on any IPv4 ports
lsof -Pn -p "$pid" -a -i | grep '127.0.0.1' && false
# instead it should listen on IPv6
lsof -Pn -p "$pid" -a -i | grep '::1' || false

26
test/shell/no-network.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/bash
# Tests the behaviour of the --no-network
set -o errexit
set -o pipefail
. "$(dirname "$0")/../util.sh"
tmpdir="$($mktemp --tmpdir -d tmp.XXX)"
# run empty graph, with standalone enabled
"$MGMT" run --no-network --prefix "$tmpdir" empty &
pid=$!
# kill server on error/exit
trap 'kill -SIGINT "$pid"' EXIT
# give mgmt a little time to startup
sleep 10
# standalone mgmt should not listen on any tcp ports
lsof -i | grep "$pid" | grep TCP && false
# instead unix domain sockets should have been created
test -S "servers.sock:0"
test -S "clients.sock:0"

View File

@@ -21,7 +21,7 @@ fi
# As per https://github.com/travis-ci/docs-travis-ci-com/blob/master/user/docker.md
# Docker is not supported on Travis macOS test instances.
if [[ "$TRAVIS_OS_NAME" == "osx" ]];then
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
XTAGS+=('nodocker')
fi