Allow specification of initial IPAM ring via --ipam-seed
This commit is contained in:
1 parent
18ddf50b4c
commit
e9081c6607
8 files changed
+126
-28
No files matched your search
+6
-3
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
type DB interface {
|
||||
Load(string, interface{}) error
|
||||
Load(string, interface{}) (bool, error)
|
||||
Save(string, interface{}) error
|
||||
}
|
||||
|
||||
@@ -50,11 +50,13 @@ func NewBoltDB(dbPathname string) (*BoltDB, error) {
|
||||
return &BoltDB{db: db}, err
|
||||
}
|
||||
|
||||
func (d *BoltDB) Load(ident string, data interface{}) error {
|
||||
return d.db.View(func(tx *bolt.Tx) error {
|
||||
func (d *BoltDB) Load(ident string, data interface{}) (bool, error) {
|
||||
found := true
|
||||
err := d.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(topBucket)
|
||||
v := b.Get([]byte(ident))
|
||||
if v == nil {
|
||||
found = false
|
||||
return nil
|
||||
}
|
||||
reader := bytes.NewReader(v)
|
||||
@@ -62,6 +64,7 @@ func (d *BoltDB) Load(ident string, data interface{}) error {
|
||||
err := decoder.Decode(data)
|
||||
return err
|
||||
})
|
||||
return found, err
|
||||
}
|
||||
|
||||
func (d *BoltDB) Save(ident string, data interface{}) error {
|
||||
|
||||
+39
-18
@@ -47,6 +47,7 @@ type operation interface {
|
||||
type Allocator struct {
|
||||
actionChan chan<- func()
|
||||
ourName mesh.PeerName
|
||||
seed []mesh.PeerName // optional user supplied ring seed
|
||||
universe address.Range // superset of all ranges
|
||||
ring *ring.Ring // information on ranges owned by all peers
|
||||
space space.Space // more detail on ranges owned by us
|
||||
@@ -70,6 +71,7 @@ type Config struct {
|
||||
OurName mesh.PeerName
|
||||
OurUID mesh.PeerUID
|
||||
OurNickname string
|
||||
Seed []mesh.PeerName
|
||||
Universe address.Range
|
||||
Quorum uint
|
||||
Db db.DB
|
||||
@@ -80,6 +82,7 @@ type Config struct {
|
||||
func NewAllocator(config Config) *Allocator {
|
||||
return &Allocator{
|
||||
ourName: config.OurName,
|
||||
seed: config.Seed,
|
||||
universe: config.Universe,
|
||||
ring: ring.New(config.Universe.Start, config.Universe.End, config.OurName),
|
||||
owned: make(map[string][]address.CIDR),
|
||||
@@ -835,31 +838,49 @@ func (alloc *Allocator) persistRing() {
|
||||
}
|
||||
|
||||
func (alloc *Allocator) loadPersistedData() {
|
||||
checkPeerName := alloc.ourName
|
||||
if err := alloc.db.Load(nameIdent, &checkPeerName); err != nil {
|
||||
var checkPeerName mesh.PeerName
|
||||
nameFound, err := alloc.db.Load(nameIdent, &checkPeerName)
|
||||
if err != nil {
|
||||
alloc.fatalf("Error loading persisted peer name: %s", err)
|
||||
return
|
||||
}
|
||||
if checkPeerName != alloc.ourName {
|
||||
ringFound, err := alloc.db.Load(ringIdent, &alloc.ring)
|
||||
if err != nil {
|
||||
alloc.fatalf("Error loading persisted IPAM data: %s", err)
|
||||
}
|
||||
ownedFound, err := alloc.db.Load(ownedIdent, &alloc.owned)
|
||||
if err != nil {
|
||||
alloc.fatalf("Error loading persisted address data: %s", err)
|
||||
}
|
||||
|
||||
if nameFound {
|
||||
if checkPeerName == alloc.ourName {
|
||||
if ringFound {
|
||||
if len(alloc.seed) != 0 {
|
||||
alloc.infof("Found persisted IPAM data, ignoring supplied IPAM seed")
|
||||
}
|
||||
alloc.space.UpdateRanges(alloc.ring.OwnedRanges())
|
||||
}
|
||||
if ownedFound {
|
||||
for _, cidrs := range alloc.owned {
|
||||
for _, cidr := range cidrs {
|
||||
alloc.space.Claim(cidr.Addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
alloc.infof("Deleting persisted data for peername %s", checkPeerName)
|
||||
alloc.persistRing()
|
||||
alloc.persistOwned()
|
||||
return
|
||||
}
|
||||
if err := alloc.db.Load(ringIdent, &alloc.ring); err != nil {
|
||||
alloc.fatalf("Error loading persisted ring data: %s", err)
|
||||
}
|
||||
if alloc.ring != nil {
|
||||
alloc.space.UpdateRanges(alloc.ring.OwnedRanges())
|
||||
}
|
||||
if err := alloc.db.Load(ownedIdent, &alloc.owned); err != nil {
|
||||
alloc.fatalf("Error loading persisted address data: %s", err)
|
||||
}
|
||||
for _, cidrs := range alloc.owned {
|
||||
for _, cidr := range cidrs {
|
||||
alloc.space.Claim(cidr.Addr)
|
||||
}
|
||||
|
||||
if len(alloc.seed) != 0 {
|
||||
alloc.infof("Initialising with supplied IPAM seed")
|
||||
alloc.createRing(alloc.seed)
|
||||
} else {
|
||||
alloc.infof("Initialising via deferred consensus")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (alloc *Allocator) persistOwned() {
|
||||
|
||||
@@ -125,8 +125,8 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
|
||||
|
||||
type mockDB struct{}
|
||||
|
||||
func (d *mockDB) Load(_ string, _ interface{}) error { return nil }
|
||||
func (d *mockDB) Save(_ string, _ interface{}) error { return nil }
|
||||
func (d *mockDB) Load(_ string, _ interface{}) (bool, error) { return false, nil }
|
||||
func (d *mockDB) Save(_ string, _ interface{}) error { return nil }
|
||||
|
||||
func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.CIDR) {
|
||||
peername, err := mesh.PeerNameFromString(name)
|
||||
|
||||
+25
-4
@@ -91,6 +91,7 @@ func main() {
|
||||
ipsubnetCIDR string
|
||||
peerCount int
|
||||
observer bool
|
||||
ipamSeedStr string
|
||||
dockerAPI string
|
||||
peers []string
|
||||
noDNS bool
|
||||
@@ -125,6 +126,7 @@ func main() {
|
||||
mflag.StringVar(&ipsubnetCIDR, []string{"#ipsubnet", "#-ipsubnet", "-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
|
||||
mflag.IntVar(&peerCount, []string{"#initpeercount", "#-initpeercount", "-init-peer-count"}, 0, "number of peers in network (for IP address allocation)")
|
||||
mflag.BoolVar(&observer, []string{"-observer"}, false, "do not participate in paxos, observe only")
|
||||
mflag.StringVar(&ipamSeedStr, []string{"-ipam-seed"}, "", "Comma-separated list of peer names amongst which address space is shared initially")
|
||||
mflag.StringVar(&dockerAPI, []string{"#api", "#-api", "-docker-api"}, defaultDockerHost, "Docker API endpoint")
|
||||
mflag.BoolVar(&noDNS, []string{"-no-dns"}, false, "disable DNS server")
|
||||
mflag.StringVar(&dnsConfig.Domain, []string{"-dns-domain"}, nameserver.DefaultDomain, "local domain to server requests for")
|
||||
@@ -229,11 +231,11 @@ func main() {
|
||||
allocator *ipam.Allocator
|
||||
defaultSubnet address.CIDR
|
||||
)
|
||||
if observer && peerCount > 0 {
|
||||
Log.Fatal("At most one of --observer and --init-peer-count must be specified.")
|
||||
if observer && peerCount > 0 || peerCount > 0 && len(ipamSeedStr) > 0 || len(ipamSeedStr) > 0 && observer {
|
||||
Log.Fatal("At most one of --observer, --ipam-seed or --init-peer-count may be specified.")
|
||||
}
|
||||
if iprangeCIDR != "" {
|
||||
allocator, defaultSubnet = createAllocator(router.Router, iprangeCIDR, ipsubnetCIDR, determineQuorum(observer, peerCount, peers), db, isKnownPeer)
|
||||
allocator, defaultSubnet = createAllocator(router.Router, ipamSeedStr, iprangeCIDR, ipsubnetCIDR, determineQuorum(observer, peerCount, peers), db, isKnownPeer)
|
||||
observeContainers(allocator)
|
||||
ids, err := dockerCli.AllContainerIDs()
|
||||
checkFatal(err)
|
||||
@@ -359,7 +361,8 @@ func parseAndCheckCIDR(cidrStr string) address.CIDR {
|
||||
return cidr
|
||||
}
|
||||
|
||||
func createAllocator(router *mesh.Router, ipRangeStr string, defaultSubnetStr string, quorum uint, db db.DB, isKnownPeer func(mesh.PeerName) bool) (*ipam.Allocator, address.CIDR) {
|
||||
func createAllocator(router *mesh.Router, ipamSeedStr, ipRangeStr string, defaultSubnetStr string, quorum uint, db db.DB, isKnownPeer func(mesh.PeerName) bool) (*ipam.Allocator, address.CIDR) {
|
||||
seed := parseIPAMSeed(ipamSeedStr)
|
||||
ipRange := parseAndCheckCIDR(ipRangeStr)
|
||||
defaultSubnet := ipRange
|
||||
if defaultSubnetStr != "" {
|
||||
@@ -373,6 +376,7 @@ func createAllocator(router *mesh.Router, ipRangeStr string, defaultSubnetStr st
|
||||
OurName: router.Ourself.Peer.Name,
|
||||
OurUID: router.Ourself.Peer.UID,
|
||||
OurNickname: router.Ourself.Peer.NickName,
|
||||
Seed: seed,
|
||||
Universe: ipRange.Range(),
|
||||
Quorum: quorum,
|
||||
Db: db,
|
||||
@@ -469,6 +473,23 @@ func parseTrustedSubnets(trustedSubnetStr string) []*net.IPNet {
|
||||
return trustedSubnets
|
||||
}
|
||||
|
||||
func parseIPAMSeed(ipamSeed string) []mesh.PeerName {
|
||||
peerNames := []mesh.PeerName{}
|
||||
if ipamSeed == "" {
|
||||
return peerNames
|
||||
}
|
||||
|
||||
for _, peerNameStr := range strings.Split(ipamSeed, ",") {
|
||||
peerName, err := mesh.PeerNameFromUserInput(peerNameStr)
|
||||
if err != nil {
|
||||
Log.Fatal("Unable to parse IPAM seed: ", err)
|
||||
}
|
||||
peerNames = append(peerNames, peerName)
|
||||
}
|
||||
|
||||
return peerNames
|
||||
}
|
||||
|
||||
func listenAndServeHTTP(httpAddr string, muxRouter *mux.Router) {
|
||||
protocol := "tcp"
|
||||
if strings.HasPrefix(httpAddr, "/") {
|
||||
|
||||
@@ -230,7 +230,7 @@ func (router *NetworkRouter) ForgetConnections(peers []string) {
|
||||
|
||||
func (router *NetworkRouter) InitialPeers(peers []string) ([]string, error) {
|
||||
var storedPeers []string
|
||||
if err := router.db.Load(peersIdent, &storedPeers); err != nil {
|
||||
if _, err := router.db.Load(peersIdent, &storedPeers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,36 @@ The following automatic IP address managment topics are discussed:
|
||||
|
||||
### <a name="initialization"></a>Initializing Peers on a Weave Network
|
||||
|
||||
There are two options for initialising IPAM - with a statically
|
||||
configured seed or automatically via one-off consensus. The two
|
||||
options have different tradeoffs, so pick the one that suits your
|
||||
deployment best.
|
||||
|
||||
#### <a name="seed"></a>Via Seed
|
||||
|
||||
Configuration via seed requires you to provide a list of peer names
|
||||
(via the `--ipam-seed` parameter) amongst which the address space will
|
||||
be shared initially - this means that you will also have to specify
|
||||
peer names explicitly on launch:
|
||||
|
||||
host1$ weave launch --name ::1 --ipam-seed ::1,::2,::3
|
||||
host2$ weave launch --name ::2 --ipam-seed ::1,::2,::3
|
||||
host3$ weave launch --name ::3 --ipam-seed ::1,::2,::3
|
||||
|
||||
In this configuration each peer knows in advance how the address space
|
||||
has been divided up, and will be able to perform allocations from the
|
||||
outset even under conditions of partition - no consensus is required.
|
||||
|
||||
#### <a name="consensus"></a>Via One-off Consensus
|
||||
|
||||
Alternatively, you can let weave determine the seed automatically via
|
||||
a consensus algorithm; since you don't need to provide it with a list
|
||||
of peer names anymore you can also let weave generate those randomly
|
||||
for you too. However, in order for weave to form a single consensus
|
||||
reliably you must now instead tell each peer how many peers there are
|
||||
in total either by listing them as target peers or via the
|
||||
`--init-peer-count` parameter.
|
||||
|
||||
Just once, when the first automatic IP address allocation is requested
|
||||
in the whole network, Weave Net needs a majority of peers to be present in
|
||||
order to avoid formation of isolated groups, which can lead to
|
||||
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
start_suite "Specify initial IPAM seed"
|
||||
|
||||
# Launch two disconnected routers
|
||||
weave_on $HOST1 launch --name ::1 --ipam-seed ::1,::2
|
||||
weave_on $HOST2 launch --name ::2 --ipam-seed ::1,::2
|
||||
|
||||
# Ensure allocations can proceed
|
||||
assert_raises "timeout 10 cat <( start_container $HOST1 --name c1)"
|
||||
assert_raises "timeout 10 cat <( start_container $HOST2 --name c2)"
|
||||
|
||||
# Connect routers
|
||||
weave_on $HOST2 connect $HOST1
|
||||
|
||||
# Check connectivity
|
||||
assert_raises "exec_on $HOST1 c1 $PING c2"
|
||||
assert_raises "exec_on $HOST2 c2 $PING c1"
|
||||
|
||||
end_suite
|
||||
@@ -40,6 +40,7 @@ weave --help | help
|
||||
|
||||
weave launch <same arguments as 'weave launch-router'>
|
||||
launch-router [--password <pass>] [--name <mac>] [--nickname <nickname>]
|
||||
[--ipam-seed <mac>,...]
|
||||
[--ipalloc-range <cidr> [--ipalloc-default-subnet <cidr>]]
|
||||
[--init-peer-count <count>] [--no-discovery]
|
||||
[--trusted-subnets <cidr>,...] <peer> ...
|
||||
|
||||
Reference in new issue
Block a user