1
0
Fork 0
forked from forgejo/forgejo

Integrate public as bindata optionally (#293)

* Dropped unused codekit config

* Integrated dynamic and static bindata for public

* Ignore public bindata

* Add a general generate make task

* Integrated flexible public assets into web command

* Updated vendoring, added all missiong govendor deps

* Made the linter happy with the bindata and dynamic code

* Moved public bindata definition to modules directory

* Ignoring the new bindata path now

* Updated to the new public modules import path

* Updated public bindata command and drop the new prefix
This commit is contained in:
Thomas Boerger 2016-11-29 17:26:36 +01:00 committed by Lunny Xiao
parent 4680c349dd
commit b6a95a8cb3
691 changed files with 305318 additions and 1272 deletions

4
vendor/github.com/ngaut/zkhelper/README.md generated vendored Normal file
View file

@ -0,0 +1,4 @@
Coordinator
========
Support both zookeeper and etcd

53
vendor/github.com/ngaut/zkhelper/conn.go generated vendored Normal file
View file

@ -0,0 +1,53 @@
package zkhelper
import (
zk "github.com/ngaut/go-zookeeper/zk"
)
/*
type Stat interface {
Czxid() int64
Mzxid() int64
CTime() time.Time
MTime() time.Time
Version() int
CVersion() int
AVersion() int
EphemeralOwner() int64
DataLength() int
NumChildren() int
Pzxid() int64
}
*/
// This interface is really close to the zookeeper connection
// interface. It uses the Stat interface defined here instead of the
// zookeeper.Stat structure for stats. Everything else is the same as
// in zookeeper. So refer to the zookeeper docs for the conventions
// used here (for instance, using -1 as version to specify any
// version)
type Conn interface {
Get(path string) (data []byte, stat zk.Stat, err error)
GetW(path string) (data []byte, stat zk.Stat, watch <-chan zk.Event, err error)
Children(path string) (children []string, stat zk.Stat, err error)
ChildrenW(path string) (children []string, stat zk.Stat, watch <-chan zk.Event, err error)
Exists(path string) (exist bool, stat zk.Stat, err error)
ExistsW(path string) (exist bool, stat zk.Stat, watch <-chan zk.Event, err error)
Create(path string, value []byte, flags int32, aclv []zk.ACL) (pathCreated string, err error)
Set(path string, value []byte, version int32) (stat zk.Stat, err error)
Delete(path string, version int32) (err error)
Close()
//RetryChange(path string, flags int, acl []ACL, changeFunc ChangeFunc) error
GetACL(path string) ([]zk.ACL, zk.Stat, error)
SetACL(path string, aclv []zk.ACL, version int32) (zk.Stat, error)
Seq2Str(seq int64) string
}

472
vendor/github.com/ngaut/zkhelper/etcd.go generated vendored Normal file
View file

@ -0,0 +1,472 @@
package zkhelper
import (
"errors"
"fmt"
"path"
"strings"
"sync"
"time"
etcderr "github.com/coreos/etcd/error"
"github.com/coreos/go-etcd/etcd"
zk "github.com/ngaut/go-zookeeper/zk"
"github.com/ngaut/log"
"github.com/ngaut/pools"
)
var (
singleInstanceLock sync.Mutex
etcdInstance *etcdImpl
)
type PooledEtcdClient struct {
c *etcd.Client
}
func (c *PooledEtcdClient) Close() {
}
func (e *etcdImpl) Seq2Str(seq int64) string {
return fmt.Sprintf("%d", seq)
}
type etcdImpl struct {
sync.Mutex
cluster string
pool *pools.ResourcePool
indexMap map[string]uint64
}
func convertToZkError(err error) error {
//todo: convert other errors
if ec, ok := err.(*etcd.EtcdError); ok {
switch ec.ErrorCode {
case etcderr.EcodeKeyNotFound:
return zk.ErrNoNode
case etcderr.EcodeNotFile:
case etcderr.EcodeNotDir:
case etcderr.EcodeNodeExist:
return zk.ErrNodeExists
case etcderr.EcodeDirNotEmpty:
return zk.ErrNotEmpty
}
}
return err
}
func convertToZkEvent(watchPath string, resp *etcd.Response, err error) zk.Event {
//log.Infof("convert event from path:%s, %+v, %+v", watchPath, resp, resp.Node.Key)
var e zk.Event
if err != nil {
e.Err = convertToZkError(err)
e.State = zk.StateDisconnected
return e
}
e.State = zk.StateConnected
e.Path = resp.Node.Key
if len(resp.Node.Key) > len(watchPath) {
e.Type = zk.EventNodeChildrenChanged
return e
}
switch resp.Action {
case "set":
e.Type = zk.EventNodeDataChanged
case "delete":
e.Type = zk.EventNodeDeleted
case "update":
e.Type = zk.EventNodeDataChanged
case "create":
e.Type = zk.EventNodeCreated
case "expire":
e.Type = zk.EventNotWatching
}
return e
}
func NewEtcdConn(zkAddr string) (Conn, error) {
singleInstanceLock.Lock()
defer singleInstanceLock.Unlock()
if etcdInstance != nil {
return etcdInstance, nil
}
p := pools.NewResourcePool(func() (pools.Resource, error) {
cluster := strings.Split(zkAddr, ",")
for i, addr := range cluster {
if !strings.HasPrefix(addr, "http://") {
cluster[i] = "http://" + addr
}
}
newClient := etcd.NewClient(cluster)
newClient.SetConsistency(etcd.STRONG_CONSISTENCY)
return &PooledEtcdClient{c: newClient}, nil
}, 10, 10, 0)
etcdInstance = &etcdImpl{
cluster: zkAddr,
pool: p,
indexMap: make(map[string]uint64),
}
log.Infof("new etcd %s", zkAddr)
if etcdInstance == nil {
return nil, errors.New("unknown error")
}
return etcdInstance, nil
}
func (e *etcdImpl) Get(key string) (data []byte, stat zk.Stat, err error) {
conn, err := e.pool.Get()
if err != nil {
return nil, nil, err
}
defer e.pool.Put(conn)
c := conn.(*PooledEtcdClient).c
resp, err := c.Get(key, true, false)
if resp == nil {
return nil, nil, convertToZkError(err)
}
return []byte(resp.Node.Value), nil, nil
}
func (e *etcdImpl) setIndex(key string, index uint64) {
e.Lock()
defer e.Unlock()
e.indexMap[key] = index
}
func (e *etcdImpl) getIndex(key string) uint64 {
e.Lock()
defer e.Unlock()
index := e.indexMap[key]
return index
}
func (e *etcdImpl) watch(key string, children bool) (resp *etcd.Response, stat zk.Stat, watch <-chan zk.Event, err error) {
conn, err := e.pool.Get()
if err != nil {
return nil, nil, nil, err
}
defer e.pool.Put(conn)
c := conn.(*PooledEtcdClient).c
index := e.getIndex(key)
resp, err = c.Get(key, true, true)
if resp == nil {
return nil, nil, nil, convertToZkError(err)
}
if index < resp.Node.ModifiedIndex {
index = resp.Node.ModifiedIndex
}
for _, n := range resp.Node.Nodes {
if n.ModifiedIndex > index {
index = n.ModifiedIndex
}
}
log.Info("try watch", key)
ch := make(chan zk.Event, 100)
originVal := resp.Node.Value
go func() {
defer func() {
e.setIndex(key, index)
}()
for {
conn, err := e.pool.Get()
if err != nil {
log.Error(err)
return
}
c := conn.(*PooledEtcdClient).c
resp, err := c.Watch(key, index, children, nil, nil)
e.pool.Put(conn)
if err != nil {
if ec, ok := err.(*etcd.EtcdError); ok {
if ec.ErrorCode == etcderr.EcodeEventIndexCleared {
index++
continue
}
}
log.Warning("watch", err)
ch <- convertToZkEvent(key, resp, err)
return
}
if key == resp.Node.Key && originVal == string(resp.Node.Value) { //keep alive event
index++
continue
}
ch <- convertToZkEvent(key, resp, err)
//update index
if index <= resp.Node.ModifiedIndex {
index = resp.Node.ModifiedIndex + 1
} else {
index++
}
return
}
}()
return resp, nil, ch, nil
}
func (e *etcdImpl) GetW(key string) (data []byte, stat zk.Stat, watch <-chan zk.Event, err error) {
resp, stat, watch, err := e.watch(key, false)
if err != nil {
return
}
return []byte(resp.Node.Value), stat, watch, nil
}
func (e *etcdImpl) Children(key string) (children []string, stat zk.Stat, err error) {
conn, err := e.pool.Get()
if err != nil {
return nil, nil, err
}
defer e.pool.Put(conn)
c := conn.(*PooledEtcdClient).c
resp, err := c.Get(key, true, false)
if resp == nil {
return nil, nil, convertToZkError(err)
}
for _, c := range resp.Node.Nodes {
children = append(children, path.Base(c.Key))
}
return
}
func (e *etcdImpl) ChildrenW(key string) (children []string, stat zk.Stat, watch <-chan zk.Event, err error) {
resp, stat, watch, err := e.watch(key, true)
if err != nil {
return nil, stat, nil, convertToZkError(err)
}
for _, c := range resp.Node.Nodes {
children = append(children, path.Base(c.Key))
}
return children, stat, watch, nil
}
func (e *etcdImpl) Exists(key string) (exist bool, stat zk.Stat, err error) {
conn, err := e.pool.Get()
if err != nil {
return false, nil, err
}
defer e.pool.Put(conn)
c := conn.(*PooledEtcdClient).c
_, err = c.Get(key, true, false)
if err == nil {
return true, nil, nil
}
if ec, ok := err.(*etcd.EtcdError); ok {
if ec.ErrorCode == etcderr.EcodeKeyNotFound {
return false, nil, nil
}
}
return false, nil, convertToZkError(err)
}
func (e *etcdImpl) ExistsW(key string) (exist bool, stat zk.Stat, watch <-chan zk.Event, err error) {
_, stat, watch, err = e.watch(key, false)
if err != nil {
return false, nil, nil, convertToZkError(err)
}
return true, nil, watch, nil
}
const MAX_TTL = 365 * 24 * 60 * 60
func (e *etcdImpl) doKeepAlive(key string, ttl uint64) error {
conn, err := e.pool.Get()
if err != nil {
return err
}
defer e.pool.Put(conn)
c := conn.(*PooledEtcdClient).c
resp, err := c.Get(key, false, false)
if err != nil {
log.Error(err)
return err
}
if resp.Node.Dir {
return fmt.Errorf("can not set ttl to directory", key)
}
//log.Info("keep alive ", key)
resp, err = c.CompareAndSwap(key, resp.Node.Value, ttl, resp.Node.Value, resp.Node.ModifiedIndex)
if err == nil {
return nil
}
if ec, ok := err.(*etcd.EtcdError); ok && ec.ErrorCode == etcderr.EcodeTestFailed {
return nil
}
return err
}
//todo:add test for keepAlive
func (e *etcdImpl) keepAlive(key string, ttl uint64) {
go func() {
for {
time.Sleep(1 * time.Second)
err := e.doKeepAlive(key, ttl)
if err != nil {
log.Error(err)
return
}
}
}()
}
func (e *etcdImpl) Create(wholekey string, value []byte, flags int32, aclv []zk.ACL) (keyCreated string, err error) {
seq := (flags & zk.FlagSequence) != 0
tmp := (flags & zk.FlagEphemeral) != 0
ttl := uint64(MAX_TTL)
if tmp {
ttl = 5
}
var resp *etcd.Response
conn, err := e.pool.Get()
if err != nil {
return "", err
}
defer e.pool.Put(conn)
c := conn.(*PooledEtcdClient).c
fn := c.Create
log.Info("create", wholekey)
if seq {
wholekey = path.Dir(wholekey)
fn = c.CreateInOrder
} else {
for _, v := range aclv {
if v.Perms == PERM_DIRECTORY {
log.Info("etcdImpl:create directory", wholekey)
fn = nil
resp, err = c.CreateDir(wholekey, uint64(ttl))
if err != nil {
return "", convertToZkError(err)
}
}
}
}
if fn == nil {
if tmp {
e.keepAlive(wholekey, ttl)
}
return resp.Node.Key, nil
}
resp, err = fn(wholekey, string(value), uint64(ttl))
if err != nil {
return "", convertToZkError(err)
}
if tmp {
e.keepAlive(resp.Node.Key, ttl)
}
return resp.Node.Key, nil
}
func (e *etcdImpl) Set(key string, value []byte, version int32) (stat zk.Stat, err error) {
if version == 0 {
return nil, errors.New("invalid version")
}
conn, err := e.pool.Get()
if err != nil {
return nil, err
}
defer e.pool.Put(conn)
c := conn.(*PooledEtcdClient).c
resp, err := c.Get(key, true, false)
if resp == nil {
return nil, convertToZkError(err)
}
_, err = c.Set(key, string(value), uint64(resp.Node.TTL))
return nil, convertToZkError(err)
}
func (e *etcdImpl) Delete(key string, version int32) (err error) {
//todo: handle version
conn, err := e.pool.Get()
if err != nil {
return err
}
defer e.pool.Put(conn)
c := conn.(*PooledEtcdClient).c
resp, err := c.Get(key, true, false)
if resp == nil {
return convertToZkError(err)
}
if resp.Node.Dir {
_, err = c.DeleteDir(key)
} else {
_, err = c.Delete(key, false)
}
return convertToZkError(err)
}
func (e *etcdImpl) GetACL(key string) ([]zk.ACL, zk.Stat, error) {
return nil, nil, nil
}
func (e *etcdImpl) SetACL(key string, aclv []zk.ACL, version int32) (zk.Stat, error) {
return nil, nil
}
func (e *etcdImpl) Close() {
//how to implement this
}

519
vendor/github.com/ngaut/zkhelper/fakezk.go generated vendored Normal file
View file

@ -0,0 +1,519 @@
// Copyright 2013, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package fakezk is a pretty complete mock implementation of a
// Zookeper connection (see go/zk/zk.Conn). All operations
// work as expected with the exceptions of zk.Conn.ACL and
// zk.Conn.SetACL. zk.Conn.SetACL will succeed, but it is a noop (and
// the ACLs won't be respected). zk.Conn.ACL will panic. It is OK to
// access the connection from multiple goroutines, but the locking is
// very naive (every operation locks the whole connection).
package zkhelper
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"sync"
"time"
"github.com/ngaut/go-zookeeper/zk"
)
type zconn struct {
mu sync.Mutex
root *stat
zxid int64
existWatches map[string][]chan zk.Event
}
func (conn *zconn) getZxid() int64 {
conn.zxid++
return conn.zxid
}
func (conn *zconn) Seq2Str(seq int64) string {
return fmt.Sprintf("%0.10d", seq)
}
// NewConn returns a fake zk.Conn implementation. Data is stored in
// memory, and there's a global connection lock for concurrent access.
func NewConn() Conn {
return &zconn{
root: &stat{
name: "/",
children: make(map[string]*stat),
},
existWatches: make(map[string][]chan zk.Event)}
}
// NewConnFromFile returns a fake zk.Conn implementation, that is seeded
// with the json data extracted from the input file.
func NewConnFromFile(filename string) Conn {
result := &zconn{
root: &stat{
name: "/",
children: make(map[string]*stat),
},
existWatches: make(map[string][]chan zk.Event)}
data, err := ioutil.ReadFile(filename)
if err != nil {
panic(fmt.Errorf("NewConnFromFile failed to read file %v: %v", filename, err))
}
values := make(map[string]interface{})
if err := json.Unmarshal(data, &values); err != nil {
panic(fmt.Errorf("NewConnFromFile failed to json.Unmarshal file %v: %v", filename, err))
}
for k, v := range values {
jv, err := json.Marshal(v)
if err != nil {
panic(fmt.Errorf("NewConnFromFile failed to json.Marshal value %v: %v", k, err))
}
// CreateRecursive will work for a leaf node where the parent
// doesn't exist, but not for a node in the middle of a tree
// that already exists. So have to use 'Set' as a backup.
if _, err := CreateRecursive(result, k, string(jv), 0, nil); err != nil {
if ZkErrorEqual(err, zk.ErrNodeExists) {
_, err = result.Set(k, jv, -1)
}
if err != nil {
panic(fmt.Errorf("NewConnFromFile failed to zk.CreateRecursive value %v: %v", k, err))
}
}
}
return result
}
func (conn *zconn) GetACL(path string) ([]zk.ACL, zk.Stat, error) {
return nil, nil, nil
}
func (conn *zconn) Get(zkPath string) (data []byte, stat zk.Stat, err error) {
conn.mu.Lock()
defer conn.mu.Unlock()
node, _, rest, err := conn.getNode(zkPath, "get")
if err != nil {
return nil, nil, err
}
if len(rest) != 0 {
return nil, nil, zkError(zk.ErrNoNode, "get", zkPath)
}
return []byte(node.content), node, nil
}
func (conn *zconn) GetW(zkPath string) (data []byte, stat zk.Stat, watch <-chan zk.Event, err error) {
conn.mu.Lock()
defer conn.mu.Unlock()
node, _, rest, err := conn.getNode(zkPath, "getw")
if err != nil {
return nil, nil, nil, err
}
if len(rest) != 0 {
return nil, nil, nil, zkError(zk.ErrNoNode, "getw", zkPath)
}
c := make(chan zk.Event, 1)
node.changeWatches = append(node.changeWatches, c)
return []byte(node.content), node, c, nil
}
func (conn *zconn) Children(zkPath string) (children []string, stat zk.Stat, err error) {
conn.mu.Lock()
defer conn.mu.Unlock()
//println("Children:", conn.String())
node, _, rest, err := conn.getNode(zkPath, "children")
if err != nil {
return nil, nil, err
}
if len(rest) != 0 {
return nil, nil, zkError(zk.ErrNoNode, "children", zkPath)
}
for name := range node.children {
children = append(children, name)
}
return children, node, nil
}
func (conn *zconn) ChildrenW(zkPath string) (children []string, stat zk.Stat, watch <-chan zk.Event, err error) {
conn.mu.Lock()
defer conn.mu.Unlock()
//println("ChildrenW:", conn.String())
node, _, rest, err := conn.getNode(zkPath, "childrenw")
if err != nil {
return nil, nil, nil, err
}
if len(rest) != 0 {
return nil, nil, nil, zkError(zk.ErrNoNode, "childrenw", zkPath)
}
c := make(chan zk.Event, 1)
node.childrenWatches = append(node.childrenWatches, c)
for name := range node.children {
children = append(children, name)
}
return children, node, c, nil
}
func (conn *zconn) Exists(zkPath string) (exist bool, stat zk.Stat, err error) {
// FIXME(szopa): if the path is bad, Op will be "get."
exist = false
_, stat, err = conn.Get(zkPath)
if err != nil {
if ZkErrorEqual(err, zk.ErrNoNode) {
err = nil
}
} else {
exist = true
}
return exist, stat, err
}
func (conn *zconn) ExistsW(zkPath string) (exist bool, stat zk.Stat, watch <-chan zk.Event, err error) {
conn.mu.Lock()
defer conn.mu.Unlock()
exist = false
c := make(chan zk.Event, 1)
node, _, rest, err := conn.getNode(zkPath, "existsw")
if err != nil {
return exist, nil, nil, err
}
if len(rest) != 0 {
watches, ok := conn.existWatches[zkPath]
if !ok {
watches = make([]chan zk.Event, 0)
conn.existWatches[zkPath] = watches
}
conn.existWatches[zkPath] = append(watches, c)
return exist, nil, c, nil
}
exist = true
node.existWatches = append(node.existWatches, c)
return exist, node, c, nil
}
func (conn *zconn) Create(zkPath string, value []byte, flags int32, aclv []zk.ACL) (zkPathCreated string, err error) {
conn.mu.Lock()
defer conn.mu.Unlock()
node, _, rest, err := conn.getNode(zkPath, "create")
if err != nil {
return "", err
}
if len(rest) == 0 {
return "", zkError(zk.ErrNodeExists, "create", zkPath)
}
if len(rest) > 1 {
return "", zkError(zk.ErrNoNode, "create", zkPath)
}
zxid := conn.getZxid()
name := rest[0]
if (flags & zk.FlagSequence) != 0 {
sequence := node.nextSequence()
name += sequence
zkPath = zkPath + sequence
}
stat := &stat{
name: name,
content: string(value),
children: make(map[string]*stat),
acl: aclv,
mtime: time.Now(),
ctime: time.Now(),
czxid: zxid,
mzxid: zxid,
existWatches: make([]chan zk.Event, 0),
}
node.children[name] = stat
event := zk.Event{
Type: zk.EventNodeCreated,
Path: zkPath,
State: zk.StateConnected,
}
if watches, ok := conn.existWatches[zkPath]; ok {
delete(conn.existWatches, zkPath)
for _, watch := range watches {
watch <- event
}
}
childrenEvent := zk.Event{
Type: zk.EventNodeChildrenChanged,
Path: zkPath,
State: zk.StateConnected,
}
for _, watch := range node.childrenWatches {
watch <- childrenEvent
close(watch)
}
node.childrenWatches = nil
node.cversion++
return zkPath, nil
}
func (conn *zconn) Set(zkPath string, value []byte, version int32) (stat zk.Stat, err error) {
conn.mu.Lock()
defer conn.mu.Unlock()
node, _, rest, err := conn.getNode(zkPath, "set")
if err != nil {
return nil, err
}
if len(rest) != 0 {
return nil, zkError(zk.ErrNoNode, "set", zkPath)
}
if version != -1 && node.version != int(version) {
return nil, zkError(zk.ErrBadVersion, "set", zkPath)
}
node.content = string(value)
node.version++
for _, watch := range node.changeWatches {
watch <- zk.Event{
Type: zk.EventNodeDataChanged,
Path: zkPath,
State: zk.StateConnected,
}
}
node.changeWatches = nil
return node, nil
}
func (conn *zconn) Delete(zkPath string, version int32) (err error) {
conn.mu.Lock()
defer conn.mu.Unlock()
node, parent, rest, err := conn.getNode(zkPath, "delete")
if err != nil {
return err
}
if len(rest) > 0 {
return zkError(zk.ErrNoNode, "delete", zkPath)
}
if len(node.children) > 0 {
return zkError(zk.ErrNotEmpty, "delete", zkPath)
}
delete(parent.children, node.name)
event := zk.Event{
Type: zk.EventNodeDeleted,
Path: zkPath,
State: zk.StateConnected,
}
for _, watch := range node.existWatches {
watch <- event
}
for _, watch := range node.changeWatches {
watch <- event
}
node.existWatches = nil
node.changeWatches = nil
childrenEvent := zk.Event{
Type: zk.EventNodeChildrenChanged,
Path: zkPath,
State: zk.StateConnected}
for _, watch := range parent.childrenWatches {
watch <- childrenEvent
}
return nil
}
func (conn *zconn) Close() {
conn.mu.Lock()
defer conn.mu.Unlock()
for _, watches := range conn.existWatches {
for _, c := range watches {
close(c)
}
}
conn.root.closeAllWatches()
}
/*
func (conn *zconn) RetryChange(path string, flags int, acl []zk.ACL, changeFunc zk.ChangeFunc) error {
for {
oldValue, oldStat, err := conn.Get(path)
if err != nil && !ZkErrorEqual(err, zk.ErrNoNode) {
return err
}
newValue, err := changeFunc(oldValue, oldStat)
if err != nil {
return err
}
if oldStat == nil {
_, err := conn.Create(path, newValue, flags, acl)
if err == nil || !ZkErrorEqual(err, zk.ZNODEEXISTS) {
return err
}
continue
}
if newValue == oldValue {
return nil // Nothing to do.
}
_, err = conn.Set(path, newValue, oldStat.Version())
if err == nil || !ZkErrorEqual(err, zk.ZBADVERSION) && !ZkErrorEqual(err, zk.ErrNoNode) {
return err
}
}
}
*/
func (conn *zconn) SetACL(zkPath string, aclv []zk.ACL, version int32) (zk.Stat, error) {
return nil, nil
}
func (conn *zconn) getNode(zkPath string, op string) (node *stat, parent *stat, rest []string, err error) {
// FIXME(szopa): Make sure the path starts with /.
parts := strings.Split(zkPath, "/")
if parts[0] != "" {
//todo: fix this, error bad arguments
return nil, nil, nil, zkError(zk.ErrUnknown, op, zkPath)
}
elements := parts[1:]
parent = nil
current := conn.root
for i, el := range elements {
candidateParent := current
candidate, ok := current.children[el]
if !ok {
return current, parent, elements[i:], nil
}
current, parent = candidate, candidateParent
}
return current, parent, []string{}, nil
}
type ZkError struct {
Code error
Op string
Path string
}
func (ze *ZkError) Error() string {
return ze.Code.Error()
}
// zkError creates an appropriate error return from
// a ZooKeeper status
func zkError(code error, op, path string) error {
return &ZkError{
Op: op,
Code: code,
Path: path,
}
}
type stat struct {
name string
content string
children map[string]*stat
acl []zk.ACL
mtime time.Time
ctime time.Time
czxid int64
mzxid int64
pzxid int64
version int
cversion int
aversion int
sequence int
existWatches []chan zk.Event
changeWatches []chan zk.Event
childrenWatches []chan zk.Event
}
func (st stat) closeAllWatches() {
for _, c := range st.existWatches {
close(c)
}
for _, c := range st.changeWatches {
close(c)
}
for _, c := range st.childrenWatches {
close(c)
}
for _, child := range st.children {
child.closeAllWatches()
}
}
func (st stat) Czxid() int64 {
return st.czxid
}
func (st stat) Mzxid() int64 {
return st.mzxid
}
func (st stat) CTime() time.Time {
return st.ctime
}
func (st stat) MTime() time.Time {
return st.mtime
}
func (st stat) Version() int {
return st.version
}
func (st stat) CVersion() int {
return st.cversion
}
func (st stat) AVersion() int {
return st.aversion
}
func (st stat) EphemeralOwner() int64 {
return 0
}
func (st stat) DataLength() int {
return len(st.content)
}
func (st stat) NumChildren() int {
return len(st.children)
}
func (st stat) Pzxid() int64 {
return st.pzxid
}
func (st *stat) nextSequence() string {
st.sequence++
return fmt.Sprintf("%010d", st.sequence)
}
func (st stat) fprintRecursive(level int, buf *bytes.Buffer) {
start := strings.Repeat(" ", level)
fmt.Fprintf(buf, "%v-%v:\n", start, st.name)
if st.content != "" {
fmt.Fprintf(buf, "%v content: %q\n\n", start, st.content)
}
if len(st.children) > 0 {
for _, child := range st.children {
child.fprintRecursive(level+1, buf)
}
}
}
func (conn *zconn) String() string {
b := new(bytes.Buffer)
conn.root.fprintRecursive(0, b)
return b.String()
}

899
vendor/github.com/ngaut/zkhelper/zk.go generated vendored Normal file
View file

@ -0,0 +1,899 @@
// zk helper functions
// modified from Vitess project
package zkhelper
import (
"encoding/json"
"errors"
"fmt"
"math/rand"
"os"
"path"
"sort"
"strings"
"sync"
"time"
"github.com/ngaut/go-zookeeper/zk"
"github.com/ngaut/log"
)
var (
// This error is returned by functions that wait for a result
// when they are interrupted.
ErrInterrupted = errors.New("zkutil: obtaining lock was interrupted")
// This error is returned by functions that wait for a result
// when the timeout value is reached.
ErrTimeout = errors.New("zkutil: obtaining lock timed out")
)
const (
// PERM_DIRECTORY are default permissions for a node.
PERM_DIRECTORY = zk.PermAdmin | zk.PermCreate | zk.PermDelete | zk.PermRead | zk.PermWrite
// PERM_FILE allows a zk node to emulate file behavior by disallowing child nodes.
PERM_FILE = zk.PermAdmin | zk.PermRead | zk.PermWrite
MagicPrefix = "zk"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
type MyZkConn struct {
*zk.Conn
}
func (conn *MyZkConn) Seq2Str(seq int64) string {
return fmt.Sprintf("%0.10d", seq)
}
func ConnectToZk(zkAddr string) (Conn, error) {
zkConn, _, err := zk.Connect(strings.Split(zkAddr, ","), 3*time.Second)
if err != nil {
return nil, err
}
return &MyZkConn{Conn: zkConn}, nil
}
func ConnectToZkWithTimeout(zkAddr string, recvTime time.Duration) (Conn, error) {
zkConn, _, err := zk.Connect(strings.Split(zkAddr, ","), recvTime)
if err != nil {
return nil, err
}
return &MyZkConn{Conn: zkConn}, nil
}
func DefaultACLs() []zk.ACL {
return zk.WorldACL(zk.PermAll)
}
func DefaultDirACLs() []zk.ACL {
return zk.WorldACL(PERM_DIRECTORY)
}
func DefaultFileACLs() []zk.ACL {
return zk.WorldACL(PERM_FILE)
}
// IsDirectory returns if this node should be treated as a directory.
func IsDirectory(aclv []zk.ACL) bool {
for _, acl := range aclv {
if acl.Perms != PERM_DIRECTORY {
return false
}
}
return true
}
func ZkErrorEqual(a, b error) bool {
if a != nil && b != nil {
return a.Error() == b.Error()
}
return a == b
}
// Create a path and any pieces required, think mkdir -p.
// Intermediate znodes are always created empty.
func CreateRecursive(zconn Conn, zkPath, value string, flags int, aclv []zk.ACL) (pathCreated string, err error) {
parts := strings.Split(zkPath, "/")
if parts[1] != MagicPrefix {
return "", fmt.Errorf("zkutil: non /%v path: %v", MagicPrefix, zkPath)
}
pathCreated, err = zconn.Create(zkPath, []byte(value), int32(flags), aclv)
if ZkErrorEqual(err, zk.ErrNoNode) {
// Make sure that nodes are either "file" or "directory" to mirror file system
// semantics.
dirAclv := make([]zk.ACL, len(aclv))
for i, acl := range aclv {
dirAclv[i] = acl
dirAclv[i].Perms = PERM_DIRECTORY
}
_, err = CreateRecursive(zconn, path.Dir(zkPath), "", flags, dirAclv)
if err != nil && !ZkErrorEqual(err, zk.ErrNodeExists) {
return "", err
}
pathCreated, err = zconn.Create(zkPath, []byte(value), int32(flags), aclv)
}
return
}
func CreateOrUpdate(zconn Conn, zkPath, value string, flags int, aclv []zk.ACL, recursive bool) (pathCreated string, err error) {
if recursive {
pathCreated, err = CreateRecursive(zconn, zkPath, value, 0, aclv)
} else {
pathCreated, err = zconn.Create(zkPath, []byte(value), 0, aclv)
}
if err != nil && ZkErrorEqual(err, zk.ErrNodeExists) {
pathCreated = ""
_, err = zconn.Set(zkPath, []byte(value), -1)
}
return
}
type pathItem struct {
path string
err error
}
func ChildrenRecursive(zconn Conn, zkPath string) ([]string, error) {
var err error
mutex := sync.Mutex{}
wg := sync.WaitGroup{}
pathList := make([]string, 0, 32)
children, _, err := zconn.Children(zkPath)
if err != nil {
return nil, err
}
for _, child := range children {
wg.Add(1)
go func(child string) {
childPath := path.Join(zkPath, child)
rChildren, zkErr := ChildrenRecursive(zconn, childPath)
if zkErr != nil {
// If other processes are deleting nodes, we need to ignore
// the missing nodes.
if !ZkErrorEqual(zkErr, zk.ErrNoNode) {
mutex.Lock()
err = zkErr
mutex.Unlock()
}
} else {
mutex.Lock()
pathList = append(pathList, child)
for _, rChild := range rChildren {
pathList = append(pathList, path.Join(child, rChild))
}
mutex.Unlock()
}
wg.Done()
}(child)
}
wg.Wait()
mutex.Lock()
defer mutex.Unlock()
if err != nil {
return nil, err
}
return pathList, nil
}
func HasWildcard(path string) bool {
for i := 0; i < len(path); i++ {
switch path[i] {
case '\\':
if i+1 >= len(path) {
return true
} else {
i++
}
case '*', '?', '[':
return true
}
}
return false
}
func resolveRecursive(zconn Conn, parts []string, toplevel bool) ([]string, error) {
for i, part := range parts {
if HasWildcard(part) {
var children []string
zkParentPath := strings.Join(parts[:i], "/")
var err error
children, _, err = zconn.Children(zkParentPath)
if err != nil {
// we asked for something like
// /zk/cell/aaa/* and
// /zk/cell/aaa doesn't exist
// -> return empty list, no error
// (note we check both a regular zk
// error and the error the test
// produces)
if ZkErrorEqual(err, zk.ErrNoNode) {
return nil, nil
}
// otherwise we return the error
return nil, err
}
sort.Strings(children)
results := make([][]string, len(children))
wg := &sync.WaitGroup{}
mu := &sync.Mutex{}
var firstError error
for j, child := range children {
matched, err := path.Match(part, child)
if err != nil {
return nil, err
}
if matched {
// we have a match!
wg.Add(1)
newParts := make([]string, len(parts))
copy(newParts, parts)
newParts[i] = child
go func(j int) {
defer wg.Done()
subResult, err := resolveRecursive(zconn, newParts, false)
if err != nil {
mu.Lock()
if firstError != nil {
log.Infof("Multiple error: %v", err)
} else {
firstError = err
}
mu.Unlock()
} else {
results[j] = subResult
}
}(j)
}
}
wg.Wait()
if firstError != nil {
return nil, firstError
}
result := make([]string, 0, 32)
for j := 0; j < len(children); j++ {
subResult := results[j]
if subResult != nil {
result = append(result, subResult...)
}
}
// we found a part that is a wildcard, we
// added the children already, we're done
return result, nil
}
}
// no part contains a wildcard, add the path if it exists, and done
path := strings.Join(parts, "/")
if toplevel {
// for whatever the user typed at the toplevel, we don't
// check it exists or not, we just return it
return []string{path}, nil
}
// this is an expanded path, we need to check if it exists
_, stat, err := zconn.Exists(path)
if err != nil {
return nil, err
}
if stat != nil {
return []string{path}, nil
}
return nil, nil
}
// resolve paths like:
// /zk/nyc/vt/tablets/*/action
// /zk/global/vt/keyspaces/*/shards/*/action
// /zk/*/vt/tablets/*/action
// into real existing paths
//
// If you send paths that don't contain any wildcard and
// don't exist, this function will return an empty array.
func ResolveWildcards(zconn Conn, zkPaths []string) ([]string, error) {
// check all the paths start with /zk/ before doing anything
// time consuming
// relax this in case we are not talking to a metaconn and
// just want to talk to a specified instance.
// for _, zkPath := range zkPaths {
// if _, err := ZkCellFromZkPath(zkPath); err != nil {
// return nil, err
// }
// }
results := make([][]string, len(zkPaths))
wg := &sync.WaitGroup{}
mu := &sync.Mutex{}
var firstError error
for i, zkPath := range zkPaths {
wg.Add(1)
parts := strings.Split(zkPath, "/")
go func(i int) {
defer wg.Done()
subResult, err := resolveRecursive(zconn, parts, true)
if err != nil {
mu.Lock()
if firstError != nil {
log.Infof("Multiple error: %v", err)
} else {
firstError = err
}
mu.Unlock()
} else {
results[i] = subResult
}
}(i)
}
wg.Wait()
if firstError != nil {
return nil, firstError
}
result := make([]string, 0, 32)
for i := 0; i < len(zkPaths); i++ {
subResult := results[i]
if subResult != nil {
result = append(result, subResult...)
}
}
return result, nil
}
func DeleteRecursive(zconn Conn, zkPath string, version int) error {
// version: -1 delete any version of the node at path - only applies to the top node
err := zconn.Delete(zkPath, int32(version))
if err == nil {
return nil
}
if !ZkErrorEqual(err, zk.ErrNotEmpty) {
return err
}
// Remove the ability for other nodes to get created while we are trying to delete.
// Otherwise, you can enter a race condition, or get starved out from deleting.
_, err = zconn.SetACL(zkPath, zk.WorldACL(zk.PermAdmin|zk.PermDelete|zk.PermRead), int32(version))
if err != nil {
return err
}
children, _, err := zconn.Children(zkPath)
if err != nil {
return err
}
for _, child := range children {
err := DeleteRecursive(zconn, path.Join(zkPath, child), -1)
if err != nil && !ZkErrorEqual(err, zk.ErrNoNode) {
return fmt.Errorf("zkutil: recursive delete failed: %v", err)
}
}
err = zconn.Delete(zkPath, int32(version))
if err != nil && !ZkErrorEqual(err, zk.ErrNotEmpty) {
err = fmt.Errorf("zkutil: nodes getting recreated underneath delete (app race condition): %v", zkPath)
}
return err
}
// The lexically lowest node is the lock holder - verify that this
// path holds the lock. Call this queue-lock because the semantics are
// a hybrid. Normal zk locks make assumptions about sequential
// numbering that don't hold when the data in a lock is modified.
// if the provided 'interrupted' chan is closed, we'll just stop waiting
// and return an interruption error
func ObtainQueueLock(zconn Conn, zkPath string, wait time.Duration, interrupted chan struct{}) error {
queueNode := path.Dir(zkPath)
lockNode := path.Base(zkPath)
timer := time.NewTimer(wait)
trylock:
children, _, err := zconn.Children(queueNode)
if err != nil {
return fmt.Errorf("zkutil: trylock failed %v", err)
}
sort.Strings(children)
if len(children) > 0 {
if children[0] == lockNode {
return nil
}
if wait > 0 {
prevLock := ""
for i := 1; i < len(children); i++ {
if children[i] == lockNode {
prevLock = children[i-1]
break
}
}
if prevLock == "" {
return fmt.Errorf("zkutil: no previous queue node found: %v", zkPath)
}
zkPrevLock := path.Join(queueNode, prevLock)
_, stat, watch, err := zconn.ExistsW(zkPrevLock)
if err != nil {
return fmt.Errorf("zkutil: unable to watch queued node %v %v", zkPrevLock, err)
}
if stat == nil {
goto trylock
}
select {
case <-timer.C:
break
case <-interrupted:
return ErrInterrupted
case <-watch:
// The precise event doesn't matter - try to read again regardless.
goto trylock
}
}
return ErrTimeout
}
return fmt.Errorf("zkutil: empty queue node: %v", queueNode)
}
func ZkEventOk(e zk.Event) bool {
return e.State == zk.StateConnected
}
func NodeExists(zconn Conn, zkPath string) (bool, error) {
b, _, err := zconn.Exists(zkPath)
return b, err
}
// Close the release channel when you want to clean up nicely.
func CreatePidNode(zconn Conn, zkPath string, contents string, done chan struct{}) error {
// On the first try, assume the cluster is up and running, that will
// help hunt down any config issues present at startup
if _, err := zconn.Create(zkPath, []byte(contents), zk.FlagEphemeral, zk.WorldACL(PERM_FILE)); err != nil {
if ZkErrorEqual(err, zk.ErrNodeExists) {
err = zconn.Delete(zkPath, -1)
}
if err != nil {
return fmt.Errorf("zkutil: failed deleting pid node: %v: %v", zkPath, err)
}
_, err = zconn.Create(zkPath, []byte(contents), zk.FlagEphemeral, zk.WorldACL(PERM_FILE))
if err != nil {
return fmt.Errorf("zkutil: failed creating pid node: %v: %v", zkPath, err)
}
}
go func() {
for {
_, _, watch, err := zconn.GetW(zkPath)
if err != nil {
if ZkErrorEqual(err, zk.ErrNoNode) {
_, err = zconn.Create(zkPath, []byte(contents), zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
if err != nil {
log.Warningf("failed recreating pid node: %v: %v", zkPath, err)
} else {
log.Infof("recreated pid node: %v", zkPath)
continue
}
} else {
log.Warningf("failed reading pid node: %v", err)
}
} else {
select {
case event := <-watch:
if ZkEventOk(event) && event.Type == zk.EventNodeDeleted {
// Most likely another process has started up. However,
// there is a chance that an ephemeral node is deleted by
// the session expiring, yet that same session gets a watch
// notification. This seems like buggy behavior, but rather
// than race too hard on the node, just wait a bit and see
// if the situation resolves itself.
log.Warningf("pid deleted: %v", zkPath)
} else {
log.Infof("pid node event: %v", event)
}
// break here and wait for a bit before attempting
case <-done:
log.Infof("pid watcher stopped on done: %v", zkPath)
return
}
}
select {
// No one likes a thundering herd, least of all zk.
case <-time.After(5*time.Second + time.Duration(rand.Int63n(55e9))):
case <-done:
log.Infof("pid watcher stopped on done: %v", zkPath)
return
}
}
}()
return nil
}
// ZLocker is an interface for a lock that can fail.
type ZLocker interface {
Lock(desc string) error
LockWithTimeout(wait time.Duration, desc string) error
Unlock() error
Interrupt()
}
// Experiment with a little bit of abstraction.
// FIMXE(msolo) This object may need a mutex to ensure it can be shared
// across goroutines.
type zMutex struct {
mu sync.Mutex
zconn Conn
path string // Path under which we try to create lock nodes.
contents string
interrupted chan struct{}
name string // The name of the specific lock node we created.
ephemeral bool
}
// CreateMutex initializes an unaquired mutex. A mutex is released only
// by Unlock. You can clean up a mutex with delete, but you should be
// careful doing so.
func CreateMutex(zconn Conn, zkPath string) ZLocker {
zm, err := CreateMutexWithContents(zconn, zkPath, map[string]interface{}{})
if err != nil {
panic(err) // should never happen
}
return zm
}
// CreateMutex initializes an unaquired mutex with special content for this mutex.
// A mutex is released only by Unlock. You can clean up a mutex with delete, but you should be
// careful doing so.
func CreateMutexWithContents(zconn Conn, zkPath string, contents map[string]interface{}) (ZLocker, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
pid := os.Getpid()
contents["hostname"] = hostname
contents["pid"] = pid
data, err := json.Marshal(contents)
if err != nil {
return nil, err
}
return &zMutex{zconn: zconn, path: zkPath, contents: string(data), interrupted: make(chan struct{})}, nil
}
// Interrupt releases a lock that's held.
func (zm *zMutex) Interrupt() {
select {
case zm.interrupted <- struct{}{}:
default:
log.Warningf("zmutex interrupt blocked")
}
}
// Lock returns nil when the lock is acquired.
func (zm *zMutex) Lock(desc string) error {
return zm.LockWithTimeout(365*24*time.Hour, desc)
}
// LockWithTimeout returns nil when the lock is acquired. A lock is
// held if the file exists and you are the creator. Setting the wait
// to zero makes this a nonblocking lock check.
//
// FIXME(msolo) Disallow non-super users from removing the lock?
func (zm *zMutex) LockWithTimeout(wait time.Duration, desc string) (err error) {
timer := time.NewTimer(wait)
defer func() {
if panicErr := recover(); panicErr != nil || err != nil {
zm.deleteLock()
}
}()
// Ensure the rendezvous node is here.
// FIXME(msolo) Assuming locks are contended, it will be cheaper to assume this just
// exists.
_, err = CreateRecursive(zm.zconn, zm.path, "", 0, zk.WorldACL(PERM_DIRECTORY))
if err != nil && !ZkErrorEqual(err, zk.ErrNodeExists) {
return err
}
lockPrefix := path.Join(zm.path, "lock-")
zflags := zk.FlagSequence
if zm.ephemeral {
zflags = zflags | zk.FlagEphemeral
}
// update node content
var lockContent map[string]interface{}
err = json.Unmarshal([]byte(zm.contents), &lockContent)
if err != nil {
return err
}
lockContent["desc"] = desc
newContent, err := json.Marshal(lockContent)
if err != nil {
return err
}
createlock:
lockCreated, err := zm.zconn.Create(lockPrefix, newContent, int32(zflags), zk.WorldACL(PERM_FILE))
if err != nil {
return err
}
name := path.Base(lockCreated)
zm.mu.Lock()
zm.name = name
zm.mu.Unlock()
trylock:
children, _, err := zm.zconn.Children(zm.path)
if err != nil {
return fmt.Errorf("zkutil: trylock failed %v", err)
}
sort.Strings(children)
if len(children) == 0 {
return fmt.Errorf("zkutil: empty lock: %v", zm.path)
}
if children[0] == name {
// We are the lock owner.
return nil
}
// This is the degenerate case of a nonblocking lock check. It's not optimal, but
// also probably not worth optimizing.
if wait == 0 {
return ErrTimeout
}
prevLock := ""
for i := 1; i < len(children); i++ {
if children[i] == name {
prevLock = children[i-1]
break
}
}
if prevLock == "" {
// This is an interesting case. The node disappeared
// underneath us, probably due to a session loss. We can
// recreate the lock node (with a new sequence number) and
// keep trying.
log.Warningf("zkutil: no lock node found: %v/%v", zm.path, zm.name)
goto createlock
}
zkPrevLock := path.Join(zm.path, prevLock)
exist, stat, watch, err := zm.zconn.ExistsW(zkPrevLock)
if err != nil {
// FIXME(msolo) Should this be a retry?
return fmt.Errorf("zkutil: unable to watch previous lock node %v %v", zkPrevLock, err)
}
if stat == nil || !exist {
goto trylock
}
select {
case <-timer.C:
return ErrTimeout
case <-zm.interrupted:
return ErrInterrupted
case event := <-watch:
log.Infof("zkutil: lock event: %v", event)
// The precise event doesn't matter - try to read again regardless.
goto trylock
}
panic("unexpected")
}
// Unlock returns nil if the lock was successfully
// released. Otherwise, it is most likely a zk related error.
func (zm *zMutex) Unlock() error {
return zm.deleteLock()
}
func (zm *zMutex) deleteLock() error {
zm.mu.Lock()
zpath := path.Join(zm.path, zm.name)
zm.mu.Unlock()
err := zm.zconn.Delete(zpath, -1)
if err != nil && !ZkErrorEqual(err, zk.ErrNoNode) {
return err
}
return nil
}
// ZElector stores basic state for running an election.
type ZElector struct {
*zMutex
path string
leader string
}
func (ze *ZElector) isLeader() bool {
return ze.leader == ze.name
}
type electionEvent struct {
Event int
Err error
}
type backoffDelay struct {
min time.Duration
max time.Duration
delay time.Duration
}
func newBackoffDelay(min, max time.Duration) *backoffDelay {
return &backoffDelay{min, max, min}
}
func (bd *backoffDelay) NextDelay() time.Duration {
delay := bd.delay
bd.delay = 2 * bd.delay
if bd.delay > bd.max {
bd.delay = bd.max
}
return delay
}
func (bd *backoffDelay) Reset() {
bd.delay = bd.min
}
// ElectorTask is the interface for a task that runs essentially
// forever or until something bad happens. If a task must be stopped,
// it should be handled promptly - no second notification will be
// sent.
type ElectorTask interface {
Run() error
Stop()
// Return true if interrupted, false if it died of natural causes.
// An interrupted task indicates that the election should stop.
Interrupted() bool
}
// CreateElection returns an initialized elector. An election is
// really a cycle of events. You are flip-flopping between leader and
// candidate. It's better to think of this as a stream of events that
// one needs to react to.
func CreateElection(zconn Conn, zkPath string) ZElector {
zm, err := CreateElectionWithContents(zconn, zkPath, map[string]interface{}{})
if err != nil {
// should never happend
panic(err)
}
return zm
}
// CreateElection returns an initialized elector with special contents. An election is
// really a cycle of events. You are flip-flopping between leader and
// candidate. It's better to think of this as a stream of events that
// one needs to react to.
func CreateElectionWithContents(zconn Conn, zkPath string, contents map[string]interface{}) (ZElector, error) {
l, err := CreateMutexWithContents(zconn, path.Join(zkPath, "candidates"), contents)
if err != nil {
return ZElector{}, err
}
zm := l.(*zMutex)
zm.ephemeral = true
return ZElector{zMutex: zm, path: zkPath}, nil
}
// RunTask returns nil when the underlyingtask ends or the error it
// generated.
func (ze *ZElector) RunTask(task ElectorTask) error {
delay := newBackoffDelay(100*time.Millisecond, 1*time.Minute)
leaderPath := path.Join(ze.path, "leader")
for {
_, err := CreateRecursive(ze.zconn, leaderPath, "", 0, zk.WorldACL(PERM_FILE))
if err == nil || ZkErrorEqual(err, zk.ErrNodeExists) {
break
}
log.Warningf("election leader create failed: %v", err)
time.Sleep(delay.NextDelay())
}
for {
err := ze.Lock("RunTask")
if err != nil {
log.Warningf("election lock failed: %v", err)
if err == ErrInterrupted {
return ErrInterrupted
}
continue
}
// Confirm your win and deliver acceptance speech. This notifies
// listeners who will have been watching the leader node for
// changes.
_, err = ze.zconn.Set(leaderPath, []byte(ze.contents), -1)
if err != nil {
log.Warningf("election promotion failed: %v", err)
continue
}
log.Infof("election promote leader %v", leaderPath)
taskErrChan := make(chan error)
go func() {
taskErrChan <- task.Run()
}()
watchLeader:
// Watch the leader so we can get notified if something goes wrong.
data, _, watch, err := ze.zconn.GetW(leaderPath)
if err != nil {
log.Warningf("election unable to watch leader node %v %v", leaderPath, err)
// FIXME(msolo) Add delay
goto watchLeader
}
if string(data) != ze.contents {
log.Warningf("election unable to promote leader")
task.Stop()
// We won the election, but we didn't become the leader. How is that possible?
// (see Bush v. Gore for some inspiration)
// It means:
// 1. Someone isn't playing by the election rules (a bad actor).
// Hard to detect - let's assume we don't have this problem. :)
// 2. We lost our connection somehow and the ephemeral lock was cleared,
// allowing someone else to win the election.
continue
}
// This is where we start our target process and watch for its failure.
waitForEvent:
select {
case <-ze.interrupted:
log.Warning("election interrupted - stop child process")
task.Stop()
// Once the process dies from the signal, this will all tear down.
goto waitForEvent
case taskErr := <-taskErrChan:
// If our code fails, unlock to trigger an election.
log.Infof("election child process ended: %v", taskErr)
ze.Unlock()
if task.Interrupted() {
log.Warningf("election child process interrupted - stepping down")
return ErrInterrupted
}
continue
case zevent := <-watch:
// We had a zk connection hiccup. We have a few choices,
// but it depends on the constraints and the events.
//
// If we get SESSION_EXPIRED our connection loss triggered an
// election that we won't have won and the thus the lock was
// automatically freed. We have no choice but to start over.
if zevent.State == zk.StateExpired {
log.Warningf("election leader watch expired")
task.Stop()
continue
}
// Otherwise, we had an intermittent issue or something touched
// the node. Either we lost our position or someone broke
// protocol and touched the leader node. We just reconnect and
// revalidate. In the meantime, assume we are still the leader
// until we determine otherwise.
//
// On a reconnect we will be able to see the leader
// information. If we still hold the position, great. If not, we
// kill the associated process.
//
// On a leader node change, we need to perform the same
// validation. It's possible an election completes without the
// old leader realizing he is out of touch.
log.Warningf("election leader watch event %v", zevent)
goto watchLeader
}
}
panic("unreachable")
}