Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -114,7 +114,7 @@ type ScopeClientCfg struct {
|
| 114 | 114 |
const ( |
| 115 | 115 |
// LocalScope indicates to store the KV object in local datastore such as boltdb |
| 116 | 116 |
LocalScope = "local" |
| 117 |
- // GlobalScope indicates to store the KV object in global datastore such as consul/etcd/zookeeper |
|
| 117 |
+ // GlobalScope indicates to store the KV object in global datastore such as consul/etcd |
|
| 118 | 118 |
GlobalScope = "global" |
| 119 | 119 |
// SwarmScope is not indicating a datastore location. It is defined here |
| 120 | 120 |
// along with the other two scopes just for consistency. |
| ... | ... |
@@ -27,7 +27,7 @@ Docker version 1.8.0-dev, build f39b9a0, experimental |
| 27 | 27 |
|
| 28 | 28 |
|
| 29 | 29 |
Multi-host networking uses a pluggable Key-Value store backend to distribute states using `libkv`. |
| 30 |
-`libkv` supports multiple pluggable backends such as `consul`, `etcd` & `zookeeper` (more to come). |
|
| 30 |
+`libkv` supports multiple pluggable backends such as `consul`, `etcd` (more to come). |
|
| 31 | 31 |
|
| 32 | 32 |
In this example we will use `consul` |
| 33 | 33 |
|
| ... | ... |
@@ -8,13 +8,11 @@ import ( |
| 8 | 8 |
"github.com/docker/libkv/store/boltdb" |
| 9 | 9 |
"github.com/docker/libkv/store/consul" |
| 10 | 10 |
"github.com/docker/libkv/store/etcd" |
| 11 |
- "github.com/docker/libkv/store/zookeeper" |
|
| 12 | 11 |
"github.com/sirupsen/logrus" |
| 13 | 12 |
) |
| 14 | 13 |
|
| 15 | 14 |
func registerKVStores() {
|
| 16 | 15 |
consul.Register() |
| 17 |
- zookeeper.Register() |
|
| 18 | 16 |
etcd.Register() |
| 19 | 17 |
boltdb.Register() |
| 20 | 18 |
} |
| ... | ... |
@@ -149,9 +149,7 @@ function start_dnet() {
|
| 149 | 149 |
# Try discovery URLs with or without path |
| 150 | 150 |
neigh_ip="" |
| 151 | 151 |
neighbors="" |
| 152 |
- if [ "$store" = "zookeeper" ]; then |
|
| 153 |
- read discovery provider address < <(parse_discovery_str zk://${bridge_ip}:2182)
|
|
| 154 |
- elif [ "$store" = "etcd" ]; then |
|
| 152 |
+ if [ "$store" = "etcd" ]; then |
|
| 155 | 153 |
read discovery provider address < <(parse_discovery_str etcd://${bridge_ip}:42000/custom_prefix)
|
| 156 | 154 |
elif [ "$store" = "consul" ]; then |
| 157 | 155 |
read discovery provider address < <(parse_discovery_str consul://${bridge_ip}:8500/custom_prefix)
|
| ... | ... |
@@ -288,21 +286,6 @@ function stop_etcd() {
|
| 288 | 288 |
docker rm -f dn_etcd || true |
| 289 | 289 |
} |
| 290 | 290 |
|
| 291 |
-function start_zookeeper() {
|
|
| 292 |
- stop_zookeeper |
|
| 293 |
- docker run -d \ |
|
| 294 |
- --name=zookeeper_server \ |
|
| 295 |
- -p 2182:2181 \ |
|
| 296 |
- -h zookeeper \ |
|
| 297 |
- dnephin/docker-zookeeper:3.4.6 |
|
| 298 |
- sleep 2 |
|
| 299 |
-} |
|
| 300 |
- |
|
| 301 |
-function stop_zookeeper() {
|
|
| 302 |
- echo "zookeeper started" |
|
| 303 |
- docker rm -f zookeeper_server || true |
|
| 304 |
-} |
|
| 305 |
- |
|
| 306 | 291 |
function test_overlay() {
|
| 307 | 292 |
dnet_suffix=$1 |
| 308 | 293 |
|
| ... | ... |
@@ -89,25 +89,6 @@ function run_overlay_consul_host_tests() {
|
| 89 | 89 |
unset _OVERLAY_HOST_MODE |
| 90 | 90 |
} |
| 91 | 91 |
|
| 92 |
-function run_overlay_zk_tests() {
|
|
| 93 |
- ## Test overlay network with zookeeper |
|
| 94 |
- start_dnet 1 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 95 |
- cmap[dnet - 1 - zookeeper]=dnet-1-zookeeper |
|
| 96 |
- start_dnet 2 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 97 |
- cmap[dnet - 2 - zookeeper]=dnet-2-zookeeper |
|
| 98 |
- start_dnet 3 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 99 |
- cmap[dnet - 3 - zookeeper]=dnet-3-zookeeper |
|
| 100 |
- |
|
| 101 |
- ./integration-tmp/bin/bats ./test/integration/dnet/overlay-zookeeper.bats |
|
| 102 |
- |
|
| 103 |
- stop_dnet 1 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 104 |
- unset cmap[dnet-1-zookeeper] |
|
| 105 |
- stop_dnet 2 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 106 |
- unset cmap[dnet-2-zookeeper] |
|
| 107 |
- stop_dnet 3 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 108 |
- unset cmap[dnet-3-zookeeper] |
|
| 109 |
-} |
|
| 110 |
- |
|
| 111 | 92 |
function run_overlay_etcd_tests() {
|
| 112 | 93 |
## Test overlay network with etcd |
| 113 | 94 |
start_dnet 1 etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
| ... | ... |
@@ -155,29 +136,6 @@ function run_multi_consul_tests() {
|
| 155 | 155 |
unset cmap[dnet-3-multi_consul] |
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 |
-function run_multi_zk_tests() {
|
|
| 159 |
- # Test multi node configuration with a global scope test driver backed by zookeeper |
|
| 160 |
- |
|
| 161 |
- ## Setup |
|
| 162 |
- start_dnet 1 multi_zk zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 163 |
- cmap[dnet - 1 - multi_zk]=dnet-1-multi_zk |
|
| 164 |
- start_dnet 2 multi_zk zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 165 |
- cmap[dnet - 2 - multi_zk]=dnet-2-multi_zk |
|
| 166 |
- start_dnet 3 multi_zk zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 167 |
- cmap[dnet - 3 - multi_zk]=dnet-3-multi_zk |
|
| 168 |
- |
|
| 169 |
- ## Run the test cases |
|
| 170 |
- ./integration-tmp/bin/bats ./test/integration/dnet/multi.bats |
|
| 171 |
- |
|
| 172 |
- ## Teardown |
|
| 173 |
- stop_dnet 1 multi_zk 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 174 |
- unset cmap[dnet-1-multi_zk] |
|
| 175 |
- stop_dnet 2 multi_zk 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 176 |
- unset cmap[dnet-2-multi_zk] |
|
| 177 |
- stop_dnet 3 multi_zk 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 178 |
- unset cmap[dnet-3-multi_zk] |
|
| 179 |
-} |
|
| 180 |
- |
|
| 181 | 158 |
function run_multi_etcd_tests() {
|
| 182 | 159 |
# Test multi node configuration with a global scope test driver backed by etcd |
| 183 | 160 |
|
| ... | ... |
@@ -220,7 +178,7 @@ fi |
| 220 | 220 |
# Suite setup |
| 221 | 221 |
|
| 222 | 222 |
if [ -z "$SUITES" ]; then |
| 223 |
- suites="dnet multi_consul multi_zk multi_etcd bridge overlay_consul overlay_consul_host overlay_zk overlay_etcd" |
|
| 223 |
+ suites="dnet multi_consul multi_etcd bridge overlay_consul overlay_consul_host overlay_etcd" |
|
| 224 | 224 |
else |
| 225 | 225 |
suites="$SUITES" |
| 226 | 226 |
fi |
| ... | ... |
@@ -231,12 +189,6 @@ if [[ ("$suites" =~ .*consul.*) || ("$suites" =~ .*bridge.*) ]]; then
|
| 231 | 231 |
cmap[pr_consul]=pr_consul |
| 232 | 232 |
fi |
| 233 | 233 |
|
| 234 |
-if [[ "$suites" =~ .*zk.* ]]; then |
|
| 235 |
- echo "Starting zookeeper ..." |
|
| 236 |
- start_zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
|
| 237 |
- cmap[zookeeper_server]=zookeeper_server |
|
| 238 |
-fi |
|
| 239 |
- |
|
| 240 | 234 |
if [[ "$suites" =~ .*etcd.* ]]; then |
| 241 | 235 |
echo "Starting etcd ..." |
| 242 | 236 |
start_etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1
|
| ... | ... |
@@ -60,7 +60,6 @@ github.com/vishvananda/netlink f049be6f391489d3f374498fe0c8 |
| 60 | 60 |
github.com/moby/ipvs 4566ccea0e08d68e9614c3e7a64a23b850c4bb35 # v1.0.1 |
| 61 | 61 |
github.com/google/btree 479b5e81b0a93ec038d201b0b33d17db599531d3 # v1.0.1 |
| 62 | 62 |
|
| 63 |
-github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374 |
|
| 64 | 63 |
github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d |
| 65 | 64 |
github.com/coreos/etcd 973882f697a8db3d59815bf132c6c506434334bd # v3.3.27 |
| 66 | 65 |
github.com/coreos/go-semver 8ab6407b697782a06568d4b7f1db25550ec2e4c6 # v0.2.0 |
| 67 | 66 |
deleted file mode 100644 |
| ... | ... |
@@ -1,429 +0,0 @@ |
| 1 |
-package zookeeper |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "strings" |
|
| 5 |
- "time" |
|
| 6 |
- |
|
| 7 |
- "github.com/docker/libkv" |
|
| 8 |
- "github.com/docker/libkv/store" |
|
| 9 |
- zk "github.com/samuel/go-zookeeper/zk" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-const ( |
|
| 13 |
- // SOH control character |
|
| 14 |
- SOH = "\x01" |
|
| 15 |
- |
|
| 16 |
- defaultTimeout = 10 * time.Second |
|
| 17 |
-) |
|
| 18 |
- |
|
| 19 |
-// Zookeeper is the receiver type for |
|
| 20 |
-// the Store interface |
|
| 21 |
-type Zookeeper struct {
|
|
| 22 |
- timeout time.Duration |
|
| 23 |
- client *zk.Conn |
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-type zookeeperLock struct {
|
|
| 27 |
- client *zk.Conn |
|
| 28 |
- lock *zk.Lock |
|
| 29 |
- key string |
|
| 30 |
- value []byte |
|
| 31 |
-} |
|
| 32 |
- |
|
| 33 |
-// Register registers zookeeper to libkv |
|
| 34 |
-func Register() {
|
|
| 35 |
- libkv.AddStore(store.ZK, New) |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-// New creates a new Zookeeper client given a |
|
| 39 |
-// list of endpoints and an optional tls config |
|
| 40 |
-func New(endpoints []string, options *store.Config) (store.Store, error) {
|
|
| 41 |
- s := &Zookeeper{}
|
|
| 42 |
- s.timeout = defaultTimeout |
|
| 43 |
- |
|
| 44 |
- // Set options |
|
| 45 |
- if options != nil {
|
|
| 46 |
- if options.ConnectionTimeout != 0 {
|
|
| 47 |
- s.setTimeout(options.ConnectionTimeout) |
|
| 48 |
- } |
|
| 49 |
- } |
|
| 50 |
- |
|
| 51 |
- // Connect to Zookeeper |
|
| 52 |
- conn, _, err := zk.Connect(endpoints, s.timeout) |
|
| 53 |
- if err != nil {
|
|
| 54 |
- return nil, err |
|
| 55 |
- } |
|
| 56 |
- s.client = conn |
|
| 57 |
- |
|
| 58 |
- return s, nil |
|
| 59 |
-} |
|
| 60 |
- |
|
| 61 |
-// setTimeout sets the timeout for connecting to Zookeeper |
|
| 62 |
-func (s *Zookeeper) setTimeout(time time.Duration) {
|
|
| 63 |
- s.timeout = time |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-// Get the value at "key", returns the last modified index |
|
| 67 |
-// to use in conjunction to Atomic calls |
|
| 68 |
-func (s *Zookeeper) Get(key string) (pair *store.KVPair, err error) {
|
|
| 69 |
- resp, meta, err := s.client.Get(s.normalize(key)) |
|
| 70 |
- |
|
| 71 |
- if err != nil {
|
|
| 72 |
- if err == zk.ErrNoNode {
|
|
| 73 |
- return nil, store.ErrKeyNotFound |
|
| 74 |
- } |
|
| 75 |
- return nil, err |
|
| 76 |
- } |
|
| 77 |
- |
|
| 78 |
- // FIXME handle very rare cases where Get returns the |
|
| 79 |
- // SOH control character instead of the actual value |
|
| 80 |
- if string(resp) == SOH {
|
|
| 81 |
- return s.Get(store.Normalize(key)) |
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- pair = &store.KVPair{
|
|
| 85 |
- Key: key, |
|
| 86 |
- Value: resp, |
|
| 87 |
- LastIndex: uint64(meta.Version), |
|
| 88 |
- } |
|
| 89 |
- |
|
| 90 |
- return pair, nil |
|
| 91 |
-} |
|
| 92 |
- |
|
| 93 |
-// createFullPath creates the entire path for a directory |
|
| 94 |
-// that does not exist |
|
| 95 |
-func (s *Zookeeper) createFullPath(path []string, ephemeral bool) error {
|
|
| 96 |
- for i := 1; i <= len(path); i++ {
|
|
| 97 |
- newpath := "/" + strings.Join(path[:i], "/") |
|
| 98 |
- if i == len(path) && ephemeral {
|
|
| 99 |
- _, err := s.client.Create(newpath, []byte{}, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
|
|
| 100 |
- return err |
|
| 101 |
- } |
|
| 102 |
- _, err := s.client.Create(newpath, []byte{}, 0, zk.WorldACL(zk.PermAll))
|
|
| 103 |
- if err != nil {
|
|
| 104 |
- // Skip if node already exists |
|
| 105 |
- if err != zk.ErrNodeExists {
|
|
| 106 |
- return err |
|
| 107 |
- } |
|
| 108 |
- } |
|
| 109 |
- } |
|
| 110 |
- return nil |
|
| 111 |
-} |
|
| 112 |
- |
|
| 113 |
-// Put a value at "key" |
|
| 114 |
-func (s *Zookeeper) Put(key string, value []byte, opts *store.WriteOptions) error {
|
|
| 115 |
- fkey := s.normalize(key) |
|
| 116 |
- |
|
| 117 |
- exists, err := s.Exists(key) |
|
| 118 |
- if err != nil {
|
|
| 119 |
- return err |
|
| 120 |
- } |
|
| 121 |
- |
|
| 122 |
- if !exists {
|
|
| 123 |
- if opts != nil && opts.TTL > 0 {
|
|
| 124 |
- s.createFullPath(store.SplitKey(strings.TrimSuffix(key, "/")), true) |
|
| 125 |
- } else {
|
|
| 126 |
- s.createFullPath(store.SplitKey(strings.TrimSuffix(key, "/")), false) |
|
| 127 |
- } |
|
| 128 |
- } |
|
| 129 |
- |
|
| 130 |
- _, err = s.client.Set(fkey, value, -1) |
|
| 131 |
- return err |
|
| 132 |
-} |
|
| 133 |
- |
|
| 134 |
-// Delete a value at "key" |
|
| 135 |
-func (s *Zookeeper) Delete(key string) error {
|
|
| 136 |
- err := s.client.Delete(s.normalize(key), -1) |
|
| 137 |
- if err == zk.ErrNoNode {
|
|
| 138 |
- return store.ErrKeyNotFound |
|
| 139 |
- } |
|
| 140 |
- return err |
|
| 141 |
-} |
|
| 142 |
- |
|
| 143 |
-// Exists checks if the key exists inside the store |
|
| 144 |
-func (s *Zookeeper) Exists(key string) (bool, error) {
|
|
| 145 |
- exists, _, err := s.client.Exists(s.normalize(key)) |
|
| 146 |
- if err != nil {
|
|
| 147 |
- return false, err |
|
| 148 |
- } |
|
| 149 |
- return exists, nil |
|
| 150 |
-} |
|
| 151 |
- |
|
| 152 |
-// Watch for changes on a "key" |
|
| 153 |
-// It returns a channel that will receive changes or pass |
|
| 154 |
-// on errors. Upon creation, the current value will first |
|
| 155 |
-// be sent to the channel. Providing a non-nil stopCh can |
|
| 156 |
-// be used to stop watching. |
|
| 157 |
-func (s *Zookeeper) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) {
|
|
| 158 |
- // Get the key first |
|
| 159 |
- pair, err := s.Get(key) |
|
| 160 |
- if err != nil {
|
|
| 161 |
- return nil, err |
|
| 162 |
- } |
|
| 163 |
- |
|
| 164 |
- // Catch zk notifications and fire changes into the channel. |
|
| 165 |
- watchCh := make(chan *store.KVPair) |
|
| 166 |
- go func() {
|
|
| 167 |
- defer close(watchCh) |
|
| 168 |
- |
|
| 169 |
- // Get returns the current value to the channel prior |
|
| 170 |
- // to listening to any event that may occur on that key |
|
| 171 |
- watchCh <- pair |
|
| 172 |
- for {
|
|
| 173 |
- _, _, eventCh, err := s.client.GetW(s.normalize(key)) |
|
| 174 |
- if err != nil {
|
|
| 175 |
- return |
|
| 176 |
- } |
|
| 177 |
- select {
|
|
| 178 |
- case e := <-eventCh: |
|
| 179 |
- if e.Type == zk.EventNodeDataChanged {
|
|
| 180 |
- if entry, err := s.Get(key); err == nil {
|
|
| 181 |
- watchCh <- entry |
|
| 182 |
- } |
|
| 183 |
- } |
|
| 184 |
- case <-stopCh: |
|
| 185 |
- // There is no way to stop GetW so just quit |
|
| 186 |
- return |
|
| 187 |
- } |
|
| 188 |
- } |
|
| 189 |
- }() |
|
| 190 |
- |
|
| 191 |
- return watchCh, nil |
|
| 192 |
-} |
|
| 193 |
- |
|
| 194 |
-// WatchTree watches for changes on a "directory" |
|
| 195 |
-// It returns a channel that will receive changes or pass |
|
| 196 |
-// on errors. Upon creating a watch, the current childs values |
|
| 197 |
-// will be sent to the channel .Providing a non-nil stopCh can |
|
| 198 |
-// be used to stop watching. |
|
| 199 |
-func (s *Zookeeper) WatchTree(directory string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) {
|
|
| 200 |
- // List the childrens first |
|
| 201 |
- entries, err := s.List(directory) |
|
| 202 |
- if err != nil {
|
|
| 203 |
- return nil, err |
|
| 204 |
- } |
|
| 205 |
- |
|
| 206 |
- // Catch zk notifications and fire changes into the channel. |
|
| 207 |
- watchCh := make(chan []*store.KVPair) |
|
| 208 |
- go func() {
|
|
| 209 |
- defer close(watchCh) |
|
| 210 |
- |
|
| 211 |
- // List returns the children values to the channel |
|
| 212 |
- // prior to listening to any events that may occur |
|
| 213 |
- // on those keys |
|
| 214 |
- watchCh <- entries |
|
| 215 |
- |
|
| 216 |
- for {
|
|
| 217 |
- _, _, eventCh, err := s.client.ChildrenW(s.normalize(directory)) |
|
| 218 |
- if err != nil {
|
|
| 219 |
- return |
|
| 220 |
- } |
|
| 221 |
- select {
|
|
| 222 |
- case e := <-eventCh: |
|
| 223 |
- if e.Type == zk.EventNodeChildrenChanged {
|
|
| 224 |
- if kv, err := s.List(directory); err == nil {
|
|
| 225 |
- watchCh <- kv |
|
| 226 |
- } |
|
| 227 |
- } |
|
| 228 |
- case <-stopCh: |
|
| 229 |
- // There is no way to stop GetW so just quit |
|
| 230 |
- return |
|
| 231 |
- } |
|
| 232 |
- } |
|
| 233 |
- }() |
|
| 234 |
- |
|
| 235 |
- return watchCh, nil |
|
| 236 |
-} |
|
| 237 |
- |
|
| 238 |
-// List child nodes of a given directory |
|
| 239 |
-func (s *Zookeeper) List(directory string) ([]*store.KVPair, error) {
|
|
| 240 |
- keys, stat, err := s.client.Children(s.normalize(directory)) |
|
| 241 |
- if err != nil {
|
|
| 242 |
- if err == zk.ErrNoNode {
|
|
| 243 |
- return nil, store.ErrKeyNotFound |
|
| 244 |
- } |
|
| 245 |
- return nil, err |
|
| 246 |
- } |
|
| 247 |
- |
|
| 248 |
- kv := []*store.KVPair{}
|
|
| 249 |
- |
|
| 250 |
- // FIXME Costly Get request for each child key.. |
|
| 251 |
- for _, key := range keys {
|
|
| 252 |
- pair, err := s.Get(strings.TrimSuffix(directory, "/") + s.normalize(key)) |
|
| 253 |
- if err != nil {
|
|
| 254 |
- // If node is not found: List is out of date, retry |
|
| 255 |
- if err == store.ErrKeyNotFound {
|
|
| 256 |
- return s.List(directory) |
|
| 257 |
- } |
|
| 258 |
- return nil, err |
|
| 259 |
- } |
|
| 260 |
- |
|
| 261 |
- kv = append(kv, &store.KVPair{
|
|
| 262 |
- Key: key, |
|
| 263 |
- Value: []byte(pair.Value), |
|
| 264 |
- LastIndex: uint64(stat.Version), |
|
| 265 |
- }) |
|
| 266 |
- } |
|
| 267 |
- |
|
| 268 |
- return kv, nil |
|
| 269 |
-} |
|
| 270 |
- |
|
| 271 |
-// DeleteTree deletes a range of keys under a given directory |
|
| 272 |
-func (s *Zookeeper) DeleteTree(directory string) error {
|
|
| 273 |
- pairs, err := s.List(directory) |
|
| 274 |
- if err != nil {
|
|
| 275 |
- return err |
|
| 276 |
- } |
|
| 277 |
- |
|
| 278 |
- var reqs []interface{}
|
|
| 279 |
- |
|
| 280 |
- for _, pair := range pairs {
|
|
| 281 |
- reqs = append(reqs, &zk.DeleteRequest{
|
|
| 282 |
- Path: s.normalize(directory + "/" + pair.Key), |
|
| 283 |
- Version: -1, |
|
| 284 |
- }) |
|
| 285 |
- } |
|
| 286 |
- |
|
| 287 |
- _, err = s.client.Multi(reqs...) |
|
| 288 |
- return err |
|
| 289 |
-} |
|
| 290 |
- |
|
| 291 |
-// AtomicPut put a value at "key" if the key has not been |
|
| 292 |
-// modified in the meantime, throws an error if this is the case |
|
| 293 |
-func (s *Zookeeper) AtomicPut(key string, value []byte, previous *store.KVPair, _ *store.WriteOptions) (bool, *store.KVPair, error) {
|
|
| 294 |
- var lastIndex uint64 |
|
| 295 |
- |
|
| 296 |
- if previous != nil {
|
|
| 297 |
- meta, err := s.client.Set(s.normalize(key), value, int32(previous.LastIndex)) |
|
| 298 |
- if err != nil {
|
|
| 299 |
- // Compare Failed |
|
| 300 |
- if err == zk.ErrBadVersion {
|
|
| 301 |
- return false, nil, store.ErrKeyModified |
|
| 302 |
- } |
|
| 303 |
- return false, nil, err |
|
| 304 |
- } |
|
| 305 |
- lastIndex = uint64(meta.Version) |
|
| 306 |
- } else {
|
|
| 307 |
- // Interpret previous == nil as create operation. |
|
| 308 |
- _, err := s.client.Create(s.normalize(key), value, 0, zk.WorldACL(zk.PermAll)) |
|
| 309 |
- if err != nil {
|
|
| 310 |
- // Directory does not exist |
|
| 311 |
- if err == zk.ErrNoNode {
|
|
| 312 |
- |
|
| 313 |
- // Create the directory |
|
| 314 |
- parts := store.SplitKey(strings.TrimSuffix(key, "/")) |
|
| 315 |
- parts = parts[:len(parts)-1] |
|
| 316 |
- if err = s.createFullPath(parts, false); err != nil {
|
|
| 317 |
- // Failed to create the directory. |
|
| 318 |
- return false, nil, err |
|
| 319 |
- } |
|
| 320 |
- |
|
| 321 |
- // Create the node |
|
| 322 |
- if _, err := s.client.Create(s.normalize(key), value, 0, zk.WorldACL(zk.PermAll)); err != nil {
|
|
| 323 |
- // Node exist error (when previous nil) |
|
| 324 |
- if err == zk.ErrNodeExists {
|
|
| 325 |
- return false, nil, store.ErrKeyExists |
|
| 326 |
- } |
|
| 327 |
- return false, nil, err |
|
| 328 |
- } |
|
| 329 |
- |
|
| 330 |
- } else {
|
|
| 331 |
- // Node Exists error (when previous nil) |
|
| 332 |
- if err == zk.ErrNodeExists {
|
|
| 333 |
- return false, nil, store.ErrKeyExists |
|
| 334 |
- } |
|
| 335 |
- |
|
| 336 |
- // Unhandled error |
|
| 337 |
- return false, nil, err |
|
| 338 |
- } |
|
| 339 |
- } |
|
| 340 |
- lastIndex = 0 // Newly created nodes have version 0. |
|
| 341 |
- } |
|
| 342 |
- |
|
| 343 |
- pair := &store.KVPair{
|
|
| 344 |
- Key: key, |
|
| 345 |
- Value: value, |
|
| 346 |
- LastIndex: lastIndex, |
|
| 347 |
- } |
|
| 348 |
- |
|
| 349 |
- return true, pair, nil |
|
| 350 |
-} |
|
| 351 |
- |
|
| 352 |
-// AtomicDelete deletes a value at "key" if the key |
|
| 353 |
-// has not been modified in the meantime, throws an |
|
| 354 |
-// error if this is the case |
|
| 355 |
-func (s *Zookeeper) AtomicDelete(key string, previous *store.KVPair) (bool, error) {
|
|
| 356 |
- if previous == nil {
|
|
| 357 |
- return false, store.ErrPreviousNotSpecified |
|
| 358 |
- } |
|
| 359 |
- |
|
| 360 |
- err := s.client.Delete(s.normalize(key), int32(previous.LastIndex)) |
|
| 361 |
- if err != nil {
|
|
| 362 |
- // Key not found |
|
| 363 |
- if err == zk.ErrNoNode {
|
|
| 364 |
- return false, store.ErrKeyNotFound |
|
| 365 |
- } |
|
| 366 |
- // Compare failed |
|
| 367 |
- if err == zk.ErrBadVersion {
|
|
| 368 |
- return false, store.ErrKeyModified |
|
| 369 |
- } |
|
| 370 |
- // General store error |
|
| 371 |
- return false, err |
|
| 372 |
- } |
|
| 373 |
- return true, nil |
|
| 374 |
-} |
|
| 375 |
- |
|
| 376 |
-// NewLock returns a handle to a lock struct which can |
|
| 377 |
-// be used to provide mutual exclusion on a key |
|
| 378 |
-func (s *Zookeeper) NewLock(key string, options *store.LockOptions) (lock store.Locker, err error) {
|
|
| 379 |
- value := []byte("")
|
|
| 380 |
- |
|
| 381 |
- // Apply options |
|
| 382 |
- if options != nil {
|
|
| 383 |
- if options.Value != nil {
|
|
| 384 |
- value = options.Value |
|
| 385 |
- } |
|
| 386 |
- } |
|
| 387 |
- |
|
| 388 |
- lock = &zookeeperLock{
|
|
| 389 |
- client: s.client, |
|
| 390 |
- key: s.normalize(key), |
|
| 391 |
- value: value, |
|
| 392 |
- lock: zk.NewLock(s.client, s.normalize(key), zk.WorldACL(zk.PermAll)), |
|
| 393 |
- } |
|
| 394 |
- |
|
| 395 |
- return lock, err |
|
| 396 |
-} |
|
| 397 |
- |
|
| 398 |
-// Lock attempts to acquire the lock and blocks while |
|
| 399 |
-// doing so. It returns a channel that is closed if our |
|
| 400 |
-// lock is lost or if an error occurs |
|
| 401 |
-func (l *zookeeperLock) Lock(stopChan chan struct{}) (<-chan struct{}, error) {
|
|
| 402 |
- err := l.lock.Lock() |
|
| 403 |
- |
|
| 404 |
- if err == nil {
|
|
| 405 |
- // We hold the lock, we can set our value |
|
| 406 |
- // FIXME: The value is left behind |
|
| 407 |
- // (problematic for leader election) |
|
| 408 |
- _, err = l.client.Set(l.key, l.value, -1) |
|
| 409 |
- } |
|
| 410 |
- |
|
| 411 |
- return make(chan struct{}), err
|
|
| 412 |
-} |
|
| 413 |
- |
|
| 414 |
-// Unlock the "key". Calling unlock while |
|
| 415 |
-// not holding the lock will throw an error |
|
| 416 |
-func (l *zookeeperLock) Unlock() error {
|
|
| 417 |
- return l.lock.Unlock() |
|
| 418 |
-} |
|
| 419 |
- |
|
| 420 |
-// Close closes the client connection |
|
| 421 |
-func (s *Zookeeper) Close() {
|
|
| 422 |
- s.client.Close() |
|
| 423 |
-} |
|
| 424 |
- |
|
| 425 |
-// Normalize the key for usage in Zookeeper |
|
| 426 |
-func (s *Zookeeper) normalize(key string) string {
|
|
| 427 |
- key = store.Normalize(key) |
|
| 428 |
- return strings.TrimSuffix(key, "/") |
|
| 429 |
-} |
| 430 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,25 +0,0 @@ |
| 1 |
-Copyright (c) 2013, Samuel Stauffer <samuel@descolada.com> |
|
| 2 |
-All rights reserved. |
|
| 3 |
- |
|
| 4 |
-Redistribution and use in source and binary forms, with or without |
|
| 5 |
-modification, are permitted provided that the following conditions are met: |
|
| 6 |
- |
|
| 7 |
-* Redistributions of source code must retain the above copyright |
|
| 8 |
- notice, this list of conditions and the following disclaimer. |
|
| 9 |
-* Redistributions in binary form must reproduce the above copyright |
|
| 10 |
- notice, this list of conditions and the following disclaimer in the |
|
| 11 |
- documentation and/or other materials provided with the distribution. |
|
| 12 |
-* Neither the name of the author nor the |
|
| 13 |
- names of its contributors may be used to endorse or promote products |
|
| 14 |
- derived from this software without specific prior written permission. |
|
| 15 |
- |
|
| 16 |
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|
| 17 |
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
| 18 |
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
| 19 |
-DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY |
|
| 20 |
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
| 21 |
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
| 22 |
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
| 23 |
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 24 |
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
| 25 |
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,11 +0,0 @@ |
| 1 |
-Native Go Zookeeper Client Library |
|
| 2 |
-=================================== |
|
| 3 |
- |
|
| 4 |
-[](https://travis-ci.org/samuel/go-zookeeper) |
|
| 5 |
- |
|
| 6 |
-Documentation: http://godoc.org/github.com/samuel/go-zookeeper/zk |
|
| 7 |
- |
|
| 8 |
-License |
|
| 9 |
- |
|
| 10 |
-3-clause BSD. See LICENSE file. |
| 11 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,844 +0,0 @@ |
| 1 |
-package zk |
|
| 2 |
- |
|
| 3 |
-/* |
|
| 4 |
-TODO: |
|
| 5 |
-* make sure a ping response comes back in a reasonable time |
|
| 6 |
- |
|
| 7 |
-Possible watcher events: |
|
| 8 |
-* Event{Type: EventNotWatching, State: StateDisconnected, Path: path, Err: err}
|
|
| 9 |
-*/ |
|
| 10 |
- |
|
| 11 |
-import ( |
|
| 12 |
- "crypto/rand" |
|
| 13 |
- "encoding/binary" |
|
| 14 |
- "errors" |
|
| 15 |
- "fmt" |
|
| 16 |
- "io" |
|
| 17 |
- "log" |
|
| 18 |
- "net" |
|
| 19 |
- "strconv" |
|
| 20 |
- "strings" |
|
| 21 |
- "sync" |
|
| 22 |
- "sync/atomic" |
|
| 23 |
- "time" |
|
| 24 |
-) |
|
| 25 |
- |
|
| 26 |
-var ErrNoServer = errors.New("zk: could not connect to a server")
|
|
| 27 |
- |
|
| 28 |
-const ( |
|
| 29 |
- bufferSize = 1536 * 1024 |
|
| 30 |
- eventChanSize = 6 |
|
| 31 |
- sendChanSize = 16 |
|
| 32 |
- protectedPrefix = "_c_" |
|
| 33 |
-) |
|
| 34 |
- |
|
| 35 |
-type watchType int |
|
| 36 |
- |
|
| 37 |
-const ( |
|
| 38 |
- watchTypeData = iota |
|
| 39 |
- watchTypeExist = iota |
|
| 40 |
- watchTypeChild = iota |
|
| 41 |
-) |
|
| 42 |
- |
|
| 43 |
-type watchPathType struct {
|
|
| 44 |
- path string |
|
| 45 |
- wType watchType |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-type Dialer func(network, address string, timeout time.Duration) (net.Conn, error) |
|
| 49 |
- |
|
| 50 |
-type Conn struct {
|
|
| 51 |
- lastZxid int64 |
|
| 52 |
- sessionID int64 |
|
| 53 |
- state State // must be 32-bit aligned |
|
| 54 |
- xid uint32 |
|
| 55 |
- timeout int32 // session timeout in milliseconds |
|
| 56 |
- passwd []byte |
|
| 57 |
- |
|
| 58 |
- dialer Dialer |
|
| 59 |
- servers []string |
|
| 60 |
- serverIndex int // remember last server that was tried during connect to round-robin attempts to servers |
|
| 61 |
- lastServerIndex int // index of the last server that was successfully connected to and authenticated with |
|
| 62 |
- conn net.Conn |
|
| 63 |
- eventChan chan Event |
|
| 64 |
- shouldQuit chan struct{}
|
|
| 65 |
- pingInterval time.Duration |
|
| 66 |
- recvTimeout time.Duration |
|
| 67 |
- connectTimeout time.Duration |
|
| 68 |
- |
|
| 69 |
- sendChan chan *request |
|
| 70 |
- requests map[int32]*request // Xid -> pending request |
|
| 71 |
- requestsLock sync.Mutex |
|
| 72 |
- watchers map[watchPathType][]chan Event |
|
| 73 |
- watchersLock sync.Mutex |
|
| 74 |
- |
|
| 75 |
- // Debug (used by unit tests) |
|
| 76 |
- reconnectDelay time.Duration |
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 |
-type request struct {
|
|
| 80 |
- xid int32 |
|
| 81 |
- opcode int32 |
|
| 82 |
- pkt interface{}
|
|
| 83 |
- recvStruct interface{}
|
|
| 84 |
- recvChan chan response |
|
| 85 |
- |
|
| 86 |
- // Because sending and receiving happen in separate go routines, there's |
|
| 87 |
- // a possible race condition when creating watches from outside the read |
|
| 88 |
- // loop. We must ensure that a watcher gets added to the list synchronously |
|
| 89 |
- // with the response from the server on any request that creates a watch. |
|
| 90 |
- // In order to not hard code the watch logic for each opcode in the recv |
|
| 91 |
- // loop the caller can use recvFunc to insert some synchronously code |
|
| 92 |
- // after a response. |
|
| 93 |
- recvFunc func(*request, *responseHeader, error) |
|
| 94 |
-} |
|
| 95 |
- |
|
| 96 |
-type response struct {
|
|
| 97 |
- zxid int64 |
|
| 98 |
- err error |
|
| 99 |
-} |
|
| 100 |
- |
|
| 101 |
-type Event struct {
|
|
| 102 |
- Type EventType |
|
| 103 |
- State State |
|
| 104 |
- Path string // For non-session events, the path of the watched node. |
|
| 105 |
- Err error |
|
| 106 |
- Server string // For connection events |
|
| 107 |
-} |
|
| 108 |
- |
|
| 109 |
-// Connect establishes a new connection to a pool of zookeeper servers |
|
| 110 |
-// using the default net.Dialer. See ConnectWithDialer for further |
|
| 111 |
-// information about session timeout. |
|
| 112 |
-func Connect(servers []string, sessionTimeout time.Duration) (*Conn, <-chan Event, error) {
|
|
| 113 |
- return ConnectWithDialer(servers, sessionTimeout, nil) |
|
| 114 |
-} |
|
| 115 |
- |
|
| 116 |
-// ConnectWithDialer establishes a new connection to a pool of zookeeper |
|
| 117 |
-// servers. The provided session timeout sets the amount of time for which |
|
| 118 |
-// a session is considered valid after losing connection to a server. Within |
|
| 119 |
-// the session timeout it's possible to reestablish a connection to a different |
|
| 120 |
-// server and keep the same session. This is means any ephemeral nodes and |
|
| 121 |
-// watches are maintained. |
|
| 122 |
-func ConnectWithDialer(servers []string, sessionTimeout time.Duration, dialer Dialer) (*Conn, <-chan Event, error) {
|
|
| 123 |
- if len(servers) == 0 {
|
|
| 124 |
- return nil, nil, errors.New("zk: server list must not be empty")
|
|
| 125 |
- } |
|
| 126 |
- |
|
| 127 |
- recvTimeout := sessionTimeout * 2 / 3 |
|
| 128 |
- |
|
| 129 |
- srvs := make([]string, len(servers)) |
|
| 130 |
- |
|
| 131 |
- for i, addr := range servers {
|
|
| 132 |
- if strings.Contains(addr, ":") {
|
|
| 133 |
- srvs[i] = addr |
|
| 134 |
- } else {
|
|
| 135 |
- srvs[i] = addr + ":" + strconv.Itoa(DefaultPort) |
|
| 136 |
- } |
|
| 137 |
- } |
|
| 138 |
- |
|
| 139 |
- // Randomize the order of the servers to avoid creating hotspots |
|
| 140 |
- stringShuffle(srvs) |
|
| 141 |
- |
|
| 142 |
- ec := make(chan Event, eventChanSize) |
|
| 143 |
- if dialer == nil {
|
|
| 144 |
- dialer = net.DialTimeout |
|
| 145 |
- } |
|
| 146 |
- conn := Conn{
|
|
| 147 |
- dialer: dialer, |
|
| 148 |
- servers: srvs, |
|
| 149 |
- serverIndex: 0, |
|
| 150 |
- lastServerIndex: -1, |
|
| 151 |
- conn: nil, |
|
| 152 |
- state: StateDisconnected, |
|
| 153 |
- eventChan: ec, |
|
| 154 |
- shouldQuit: make(chan struct{}),
|
|
| 155 |
- recvTimeout: recvTimeout, |
|
| 156 |
- pingInterval: recvTimeout / 2, |
|
| 157 |
- connectTimeout: 1 * time.Second, |
|
| 158 |
- sendChan: make(chan *request, sendChanSize), |
|
| 159 |
- requests: make(map[int32]*request), |
|
| 160 |
- watchers: make(map[watchPathType][]chan Event), |
|
| 161 |
- passwd: emptyPassword, |
|
| 162 |
- timeout: int32(sessionTimeout.Nanoseconds() / 1e6), |
|
| 163 |
- |
|
| 164 |
- // Debug |
|
| 165 |
- reconnectDelay: 0, |
|
| 166 |
- } |
|
| 167 |
- go func() {
|
|
| 168 |
- conn.loop() |
|
| 169 |
- conn.flushRequests(ErrClosing) |
|
| 170 |
- conn.invalidateWatches(ErrClosing) |
|
| 171 |
- close(conn.eventChan) |
|
| 172 |
- }() |
|
| 173 |
- return &conn, ec, nil |
|
| 174 |
-} |
|
| 175 |
- |
|
| 176 |
-func (c *Conn) Close() {
|
|
| 177 |
- close(c.shouldQuit) |
|
| 178 |
- |
|
| 179 |
- select {
|
|
| 180 |
- case <-c.queueRequest(opClose, &closeRequest{}, &closeResponse{}, nil):
|
|
| 181 |
- case <-time.After(time.Second): |
|
| 182 |
- } |
|
| 183 |
-} |
|
| 184 |
- |
|
| 185 |
-func (c *Conn) State() State {
|
|
| 186 |
- return State(atomic.LoadInt32((*int32)(&c.state))) |
|
| 187 |
-} |
|
| 188 |
- |
|
| 189 |
-func (c *Conn) setState(state State) {
|
|
| 190 |
- atomic.StoreInt32((*int32)(&c.state), int32(state)) |
|
| 191 |
- select {
|
|
| 192 |
- case c.eventChan <- Event{Type: EventSession, State: state, Server: c.servers[c.serverIndex]}:
|
|
| 193 |
- default: |
|
| 194 |
- // panic("zk: event channel full - it must be monitored and never allowed to be full")
|
|
| 195 |
- } |
|
| 196 |
-} |
|
| 197 |
- |
|
| 198 |
-func (c *Conn) connect() error {
|
|
| 199 |
- c.setState(StateConnecting) |
|
| 200 |
- for {
|
|
| 201 |
- c.serverIndex = (c.serverIndex + 1) % len(c.servers) |
|
| 202 |
- if c.serverIndex == c.lastServerIndex {
|
|
| 203 |
- c.flushUnsentRequests(ErrNoServer) |
|
| 204 |
- select {
|
|
| 205 |
- case <-time.After(time.Second): |
|
| 206 |
- // pass |
|
| 207 |
- case <-c.shouldQuit: |
|
| 208 |
- c.setState(StateDisconnected) |
|
| 209 |
- c.flushUnsentRequests(ErrClosing) |
|
| 210 |
- return ErrClosing |
|
| 211 |
- } |
|
| 212 |
- } else if c.lastServerIndex < 0 {
|
|
| 213 |
- // lastServerIndex defaults to -1 to avoid a delay on the initial connect |
|
| 214 |
- c.lastServerIndex = 0 |
|
| 215 |
- } |
|
| 216 |
- |
|
| 217 |
- zkConn, err := c.dialer("tcp", c.servers[c.serverIndex], c.connectTimeout)
|
|
| 218 |
- if err == nil {
|
|
| 219 |
- c.conn = zkConn |
|
| 220 |
- c.setState(StateConnected) |
|
| 221 |
- return nil |
|
| 222 |
- } |
|
| 223 |
- |
|
| 224 |
- log.Printf("Failed to connect to %s: %+v", c.servers[c.serverIndex], err)
|
|
| 225 |
- } |
|
| 226 |
-} |
|
| 227 |
- |
|
| 228 |
-func (c *Conn) loop() {
|
|
| 229 |
- for {
|
|
| 230 |
- if err := c.connect(); err != nil {
|
|
| 231 |
- // c.Close() was called |
|
| 232 |
- return |
|
| 233 |
- } |
|
| 234 |
- |
|
| 235 |
- err := c.authenticate() |
|
| 236 |
- switch {
|
|
| 237 |
- case err == ErrSessionExpired: |
|
| 238 |
- c.invalidateWatches(err) |
|
| 239 |
- case err != nil && c.conn != nil: |
|
| 240 |
- c.conn.Close() |
|
| 241 |
- case err == nil: |
|
| 242 |
- c.lastServerIndex = c.serverIndex |
|
| 243 |
- closeChan := make(chan struct{}) // channel to tell send loop stop
|
|
| 244 |
- var wg sync.WaitGroup |
|
| 245 |
- |
|
| 246 |
- wg.Add(1) |
|
| 247 |
- go func() {
|
|
| 248 |
- c.sendLoop(c.conn, closeChan) |
|
| 249 |
- c.conn.Close() // causes recv loop to EOF/exit |
|
| 250 |
- wg.Done() |
|
| 251 |
- }() |
|
| 252 |
- |
|
| 253 |
- wg.Add(1) |
|
| 254 |
- go func() {
|
|
| 255 |
- err = c.recvLoop(c.conn) |
|
| 256 |
- if err == nil {
|
|
| 257 |
- panic("zk: recvLoop should never return nil error")
|
|
| 258 |
- } |
|
| 259 |
- close(closeChan) // tell send loop to exit |
|
| 260 |
- wg.Done() |
|
| 261 |
- }() |
|
| 262 |
- |
|
| 263 |
- wg.Wait() |
|
| 264 |
- } |
|
| 265 |
- |
|
| 266 |
- c.setState(StateDisconnected) |
|
| 267 |
- |
|
| 268 |
- // Yeesh |
|
| 269 |
- if err != io.EOF && err != ErrSessionExpired && !strings.Contains(err.Error(), "use of closed network connection") {
|
|
| 270 |
- log.Println(err) |
|
| 271 |
- } |
|
| 272 |
- |
|
| 273 |
- select {
|
|
| 274 |
- case <-c.shouldQuit: |
|
| 275 |
- c.flushRequests(ErrClosing) |
|
| 276 |
- return |
|
| 277 |
- default: |
|
| 278 |
- } |
|
| 279 |
- |
|
| 280 |
- if err != ErrSessionExpired {
|
|
| 281 |
- err = ErrConnectionClosed |
|
| 282 |
- } |
|
| 283 |
- c.flushRequests(err) |
|
| 284 |
- |
|
| 285 |
- if c.reconnectDelay > 0 {
|
|
| 286 |
- select {
|
|
| 287 |
- case <-c.shouldQuit: |
|
| 288 |
- return |
|
| 289 |
- case <-time.After(c.reconnectDelay): |
|
| 290 |
- } |
|
| 291 |
- } |
|
| 292 |
- } |
|
| 293 |
-} |
|
| 294 |
- |
|
| 295 |
-func (c *Conn) flushUnsentRequests(err error) {
|
|
| 296 |
- for {
|
|
| 297 |
- select {
|
|
| 298 |
- default: |
|
| 299 |
- return |
|
| 300 |
- case req := <-c.sendChan: |
|
| 301 |
- req.recvChan <- response{-1, err}
|
|
| 302 |
- } |
|
| 303 |
- } |
|
| 304 |
-} |
|
| 305 |
- |
|
| 306 |
-// Send error to all pending requests and clear request map |
|
| 307 |
-func (c *Conn) flushRequests(err error) {
|
|
| 308 |
- c.requestsLock.Lock() |
|
| 309 |
- for _, req := range c.requests {
|
|
| 310 |
- req.recvChan <- response{-1, err}
|
|
| 311 |
- } |
|
| 312 |
- c.requests = make(map[int32]*request) |
|
| 313 |
- c.requestsLock.Unlock() |
|
| 314 |
-} |
|
| 315 |
- |
|
| 316 |
-// Send error to all watchers and clear watchers map |
|
| 317 |
-func (c *Conn) invalidateWatches(err error) {
|
|
| 318 |
- c.watchersLock.Lock() |
|
| 319 |
- defer c.watchersLock.Unlock() |
|
| 320 |
- |
|
| 321 |
- if len(c.watchers) >= 0 {
|
|
| 322 |
- for pathType, watchers := range c.watchers {
|
|
| 323 |
- ev := Event{Type: EventNotWatching, State: StateDisconnected, Path: pathType.path, Err: err}
|
|
| 324 |
- for _, ch := range watchers {
|
|
| 325 |
- ch <- ev |
|
| 326 |
- close(ch) |
|
| 327 |
- } |
|
| 328 |
- } |
|
| 329 |
- c.watchers = make(map[watchPathType][]chan Event) |
|
| 330 |
- } |
|
| 331 |
-} |
|
| 332 |
- |
|
| 333 |
-func (c *Conn) sendSetWatches() {
|
|
| 334 |
- c.watchersLock.Lock() |
|
| 335 |
- defer c.watchersLock.Unlock() |
|
| 336 |
- |
|
| 337 |
- if len(c.watchers) == 0 {
|
|
| 338 |
- return |
|
| 339 |
- } |
|
| 340 |
- |
|
| 341 |
- req := &setWatchesRequest{
|
|
| 342 |
- RelativeZxid: c.lastZxid, |
|
| 343 |
- DataWatches: make([]string, 0), |
|
| 344 |
- ExistWatches: make([]string, 0), |
|
| 345 |
- ChildWatches: make([]string, 0), |
|
| 346 |
- } |
|
| 347 |
- n := 0 |
|
| 348 |
- for pathType, watchers := range c.watchers {
|
|
| 349 |
- if len(watchers) == 0 {
|
|
| 350 |
- continue |
|
| 351 |
- } |
|
| 352 |
- switch pathType.wType {
|
|
| 353 |
- case watchTypeData: |
|
| 354 |
- req.DataWatches = append(req.DataWatches, pathType.path) |
|
| 355 |
- case watchTypeExist: |
|
| 356 |
- req.ExistWatches = append(req.ExistWatches, pathType.path) |
|
| 357 |
- case watchTypeChild: |
|
| 358 |
- req.ChildWatches = append(req.ChildWatches, pathType.path) |
|
| 359 |
- } |
|
| 360 |
- n++ |
|
| 361 |
- } |
|
| 362 |
- if n == 0 {
|
|
| 363 |
- return |
|
| 364 |
- } |
|
| 365 |
- |
|
| 366 |
- go func() {
|
|
| 367 |
- res := &setWatchesResponse{}
|
|
| 368 |
- _, err := c.request(opSetWatches, req, res, nil) |
|
| 369 |
- if err != nil {
|
|
| 370 |
- log.Printf("Failed to set previous watches: %s", err.Error())
|
|
| 371 |
- } |
|
| 372 |
- }() |
|
| 373 |
-} |
|
| 374 |
- |
|
| 375 |
-func (c *Conn) authenticate() error {
|
|
| 376 |
- buf := make([]byte, 256) |
|
| 377 |
- |
|
| 378 |
- // connect request |
|
| 379 |
- |
|
| 380 |
- n, err := encodePacket(buf[4:], &connectRequest{
|
|
| 381 |
- ProtocolVersion: protocolVersion, |
|
| 382 |
- LastZxidSeen: c.lastZxid, |
|
| 383 |
- TimeOut: c.timeout, |
|
| 384 |
- SessionID: c.sessionID, |
|
| 385 |
- Passwd: c.passwd, |
|
| 386 |
- }) |
|
| 387 |
- if err != nil {
|
|
| 388 |
- return err |
|
| 389 |
- } |
|
| 390 |
- |
|
| 391 |
- binary.BigEndian.PutUint32(buf[:4], uint32(n)) |
|
| 392 |
- |
|
| 393 |
- c.conn.SetWriteDeadline(time.Now().Add(c.recvTimeout * 10)) |
|
| 394 |
- _, err = c.conn.Write(buf[:n+4]) |
|
| 395 |
- c.conn.SetWriteDeadline(time.Time{})
|
|
| 396 |
- if err != nil {
|
|
| 397 |
- return err |
|
| 398 |
- } |
|
| 399 |
- |
|
| 400 |
- c.sendSetWatches() |
|
| 401 |
- |
|
| 402 |
- // connect response |
|
| 403 |
- |
|
| 404 |
- // package length |
|
| 405 |
- c.conn.SetReadDeadline(time.Now().Add(c.recvTimeout * 10)) |
|
| 406 |
- _, err = io.ReadFull(c.conn, buf[:4]) |
|
| 407 |
- c.conn.SetReadDeadline(time.Time{})
|
|
| 408 |
- if err != nil {
|
|
| 409 |
- // Sometimes zookeeper just drops connection on invalid session data, |
|
| 410 |
- // we prefer to drop session and start from scratch when that event |
|
| 411 |
- // occurs instead of dropping into loop of connect/disconnect attempts |
|
| 412 |
- c.sessionID = 0 |
|
| 413 |
- c.passwd = emptyPassword |
|
| 414 |
- c.lastZxid = 0 |
|
| 415 |
- c.setState(StateExpired) |
|
| 416 |
- return ErrSessionExpired |
|
| 417 |
- } |
|
| 418 |
- |
|
| 419 |
- blen := int(binary.BigEndian.Uint32(buf[:4])) |
|
| 420 |
- if cap(buf) < blen {
|
|
| 421 |
- buf = make([]byte, blen) |
|
| 422 |
- } |
|
| 423 |
- |
|
| 424 |
- _, err = io.ReadFull(c.conn, buf[:blen]) |
|
| 425 |
- if err != nil {
|
|
| 426 |
- return err |
|
| 427 |
- } |
|
| 428 |
- |
|
| 429 |
- r := connectResponse{}
|
|
| 430 |
- _, err = decodePacket(buf[:blen], &r) |
|
| 431 |
- if err != nil {
|
|
| 432 |
- return err |
|
| 433 |
- } |
|
| 434 |
- if r.SessionID == 0 {
|
|
| 435 |
- c.sessionID = 0 |
|
| 436 |
- c.passwd = emptyPassword |
|
| 437 |
- c.lastZxid = 0 |
|
| 438 |
- c.setState(StateExpired) |
|
| 439 |
- return ErrSessionExpired |
|
| 440 |
- } |
|
| 441 |
- |
|
| 442 |
- if c.sessionID != r.SessionID {
|
|
| 443 |
- atomic.StoreUint32(&c.xid, 0) |
|
| 444 |
- } |
|
| 445 |
- c.timeout = r.TimeOut |
|
| 446 |
- c.sessionID = r.SessionID |
|
| 447 |
- c.passwd = r.Passwd |
|
| 448 |
- c.setState(StateHasSession) |
|
| 449 |
- |
|
| 450 |
- return nil |
|
| 451 |
-} |
|
| 452 |
- |
|
| 453 |
-func (c *Conn) sendLoop(conn net.Conn, closeChan <-chan struct{}) error {
|
|
| 454 |
- pingTicker := time.NewTicker(c.pingInterval) |
|
| 455 |
- defer pingTicker.Stop() |
|
| 456 |
- |
|
| 457 |
- buf := make([]byte, bufferSize) |
|
| 458 |
- for {
|
|
| 459 |
- select {
|
|
| 460 |
- case req := <-c.sendChan: |
|
| 461 |
- header := &requestHeader{req.xid, req.opcode}
|
|
| 462 |
- n, err := encodePacket(buf[4:], header) |
|
| 463 |
- if err != nil {
|
|
| 464 |
- req.recvChan <- response{-1, err}
|
|
| 465 |
- continue |
|
| 466 |
- } |
|
| 467 |
- |
|
| 468 |
- n2, err := encodePacket(buf[4+n:], req.pkt) |
|
| 469 |
- if err != nil {
|
|
| 470 |
- req.recvChan <- response{-1, err}
|
|
| 471 |
- continue |
|
| 472 |
- } |
|
| 473 |
- |
|
| 474 |
- n += n2 |
|
| 475 |
- |
|
| 476 |
- binary.BigEndian.PutUint32(buf[:4], uint32(n)) |
|
| 477 |
- |
|
| 478 |
- c.requestsLock.Lock() |
|
| 479 |
- select {
|
|
| 480 |
- case <-closeChan: |
|
| 481 |
- req.recvChan <- response{-1, ErrConnectionClosed}
|
|
| 482 |
- c.requestsLock.Unlock() |
|
| 483 |
- return ErrConnectionClosed |
|
| 484 |
- default: |
|
| 485 |
- } |
|
| 486 |
- c.requests[req.xid] = req |
|
| 487 |
- c.requestsLock.Unlock() |
|
| 488 |
- |
|
| 489 |
- conn.SetWriteDeadline(time.Now().Add(c.recvTimeout)) |
|
| 490 |
- _, err = conn.Write(buf[:n+4]) |
|
| 491 |
- conn.SetWriteDeadline(time.Time{})
|
|
| 492 |
- if err != nil {
|
|
| 493 |
- req.recvChan <- response{-1, err}
|
|
| 494 |
- conn.Close() |
|
| 495 |
- return err |
|
| 496 |
- } |
|
| 497 |
- case <-pingTicker.C: |
|
| 498 |
- n, err := encodePacket(buf[4:], &requestHeader{Xid: -2, Opcode: opPing})
|
|
| 499 |
- if err != nil {
|
|
| 500 |
- panic("zk: opPing should never fail to serialize")
|
|
| 501 |
- } |
|
| 502 |
- |
|
| 503 |
- binary.BigEndian.PutUint32(buf[:4], uint32(n)) |
|
| 504 |
- |
|
| 505 |
- conn.SetWriteDeadline(time.Now().Add(c.recvTimeout)) |
|
| 506 |
- _, err = conn.Write(buf[:n+4]) |
|
| 507 |
- conn.SetWriteDeadline(time.Time{})
|
|
| 508 |
- if err != nil {
|
|
| 509 |
- conn.Close() |
|
| 510 |
- return err |
|
| 511 |
- } |
|
| 512 |
- case <-closeChan: |
|
| 513 |
- return nil |
|
| 514 |
- } |
|
| 515 |
- } |
|
| 516 |
-} |
|
| 517 |
- |
|
| 518 |
-func (c *Conn) recvLoop(conn net.Conn) error {
|
|
| 519 |
- buf := make([]byte, bufferSize) |
|
| 520 |
- for {
|
|
| 521 |
- // package length |
|
| 522 |
- conn.SetReadDeadline(time.Now().Add(c.recvTimeout)) |
|
| 523 |
- _, err := io.ReadFull(conn, buf[:4]) |
|
| 524 |
- if err != nil {
|
|
| 525 |
- return err |
|
| 526 |
- } |
|
| 527 |
- |
|
| 528 |
- blen := int(binary.BigEndian.Uint32(buf[:4])) |
|
| 529 |
- if cap(buf) < blen {
|
|
| 530 |
- buf = make([]byte, blen) |
|
| 531 |
- } |
|
| 532 |
- |
|
| 533 |
- _, err = io.ReadFull(conn, buf[:blen]) |
|
| 534 |
- conn.SetReadDeadline(time.Time{})
|
|
| 535 |
- if err != nil {
|
|
| 536 |
- return err |
|
| 537 |
- } |
|
| 538 |
- |
|
| 539 |
- res := responseHeader{}
|
|
| 540 |
- _, err = decodePacket(buf[:16], &res) |
|
| 541 |
- if err != nil {
|
|
| 542 |
- return err |
|
| 543 |
- } |
|
| 544 |
- |
|
| 545 |
- if res.Xid == -1 {
|
|
| 546 |
- res := &watcherEvent{}
|
|
| 547 |
- _, err := decodePacket(buf[16:16+blen], res) |
|
| 548 |
- if err != nil {
|
|
| 549 |
- return err |
|
| 550 |
- } |
|
| 551 |
- ev := Event{
|
|
| 552 |
- Type: res.Type, |
|
| 553 |
- State: res.State, |
|
| 554 |
- Path: res.Path, |
|
| 555 |
- Err: nil, |
|
| 556 |
- } |
|
| 557 |
- select {
|
|
| 558 |
- case c.eventChan <- ev: |
|
| 559 |
- default: |
|
| 560 |
- } |
|
| 561 |
- wTypes := make([]watchType, 0, 2) |
|
| 562 |
- switch res.Type {
|
|
| 563 |
- case EventNodeCreated: |
|
| 564 |
- wTypes = append(wTypes, watchTypeExist) |
|
| 565 |
- case EventNodeDeleted, EventNodeDataChanged: |
|
| 566 |
- wTypes = append(wTypes, watchTypeExist, watchTypeData, watchTypeChild) |
|
| 567 |
- case EventNodeChildrenChanged: |
|
| 568 |
- wTypes = append(wTypes, watchTypeChild) |
|
| 569 |
- } |
|
| 570 |
- c.watchersLock.Lock() |
|
| 571 |
- for _, t := range wTypes {
|
|
| 572 |
- wpt := watchPathType{res.Path, t}
|
|
| 573 |
- if watchers := c.watchers[wpt]; watchers != nil && len(watchers) > 0 {
|
|
| 574 |
- for _, ch := range watchers {
|
|
| 575 |
- ch <- ev |
|
| 576 |
- close(ch) |
|
| 577 |
- } |
|
| 578 |
- delete(c.watchers, wpt) |
|
| 579 |
- } |
|
| 580 |
- } |
|
| 581 |
- c.watchersLock.Unlock() |
|
| 582 |
- } else if res.Xid == -2 {
|
|
| 583 |
- // Ping response. Ignore. |
|
| 584 |
- } else if res.Xid < 0 {
|
|
| 585 |
- log.Printf("Xid < 0 (%d) but not ping or watcher event", res.Xid)
|
|
| 586 |
- } else {
|
|
| 587 |
- if res.Zxid > 0 {
|
|
| 588 |
- c.lastZxid = res.Zxid |
|
| 589 |
- } |
|
| 590 |
- |
|
| 591 |
- c.requestsLock.Lock() |
|
| 592 |
- req, ok := c.requests[res.Xid] |
|
| 593 |
- if ok {
|
|
| 594 |
- delete(c.requests, res.Xid) |
|
| 595 |
- } |
|
| 596 |
- c.requestsLock.Unlock() |
|
| 597 |
- |
|
| 598 |
- if !ok {
|
|
| 599 |
- log.Printf("Response for unknown request with xid %d", res.Xid)
|
|
| 600 |
- } else {
|
|
| 601 |
- if res.Err != 0 {
|
|
| 602 |
- err = res.Err.toError() |
|
| 603 |
- } else {
|
|
| 604 |
- _, err = decodePacket(buf[16:16+blen], req.recvStruct) |
|
| 605 |
- } |
|
| 606 |
- if req.recvFunc != nil {
|
|
| 607 |
- req.recvFunc(req, &res, err) |
|
| 608 |
- } |
|
| 609 |
- req.recvChan <- response{res.Zxid, err}
|
|
| 610 |
- if req.opcode == opClose {
|
|
| 611 |
- return io.EOF |
|
| 612 |
- } |
|
| 613 |
- } |
|
| 614 |
- } |
|
| 615 |
- } |
|
| 616 |
-} |
|
| 617 |
- |
|
| 618 |
-func (c *Conn) nextXid() int32 {
|
|
| 619 |
- return int32(atomic.AddUint32(&c.xid, 1) & 0x7fffffff) |
|
| 620 |
-} |
|
| 621 |
- |
|
| 622 |
-func (c *Conn) addWatcher(path string, watchType watchType) <-chan Event {
|
|
| 623 |
- c.watchersLock.Lock() |
|
| 624 |
- defer c.watchersLock.Unlock() |
|
| 625 |
- |
|
| 626 |
- ch := make(chan Event, 1) |
|
| 627 |
- wpt := watchPathType{path, watchType}
|
|
| 628 |
- c.watchers[wpt] = append(c.watchers[wpt], ch) |
|
| 629 |
- return ch |
|
| 630 |
-} |
|
| 631 |
- |
|
| 632 |
-func (c *Conn) queueRequest(opcode int32, req interface{}, res interface{}, recvFunc func(*request, *responseHeader, error)) <-chan response {
|
|
| 633 |
- rq := &request{
|
|
| 634 |
- xid: c.nextXid(), |
|
| 635 |
- opcode: opcode, |
|
| 636 |
- pkt: req, |
|
| 637 |
- recvStruct: res, |
|
| 638 |
- recvChan: make(chan response, 1), |
|
| 639 |
- recvFunc: recvFunc, |
|
| 640 |
- } |
|
| 641 |
- c.sendChan <- rq |
|
| 642 |
- return rq.recvChan |
|
| 643 |
-} |
|
| 644 |
- |
|
| 645 |
-func (c *Conn) request(opcode int32, req interface{}, res interface{}, recvFunc func(*request, *responseHeader, error)) (int64, error) {
|
|
| 646 |
- r := <-c.queueRequest(opcode, req, res, recvFunc) |
|
| 647 |
- return r.zxid, r.err |
|
| 648 |
-} |
|
| 649 |
- |
|
| 650 |
-func (c *Conn) AddAuth(scheme string, auth []byte) error {
|
|
| 651 |
- _, err := c.request(opSetAuth, &setAuthRequest{Type: 0, Scheme: scheme, Auth: auth}, &setAuthResponse{}, nil)
|
|
| 652 |
- return err |
|
| 653 |
-} |
|
| 654 |
- |
|
| 655 |
-func (c *Conn) Children(path string) ([]string, *Stat, error) {
|
|
| 656 |
- res := &getChildren2Response{}
|
|
| 657 |
- _, err := c.request(opGetChildren2, &getChildren2Request{Path: path, Watch: false}, res, nil)
|
|
| 658 |
- return res.Children, &res.Stat, err |
|
| 659 |
-} |
|
| 660 |
- |
|
| 661 |
-func (c *Conn) ChildrenW(path string) ([]string, *Stat, <-chan Event, error) {
|
|
| 662 |
- var ech <-chan Event |
|
| 663 |
- res := &getChildren2Response{}
|
|
| 664 |
- _, err := c.request(opGetChildren2, &getChildren2Request{Path: path, Watch: true}, res, func(req *request, res *responseHeader, err error) {
|
|
| 665 |
- if err == nil {
|
|
| 666 |
- ech = c.addWatcher(path, watchTypeChild) |
|
| 667 |
- } |
|
| 668 |
- }) |
|
| 669 |
- if err != nil {
|
|
| 670 |
- return nil, nil, nil, err |
|
| 671 |
- } |
|
| 672 |
- return res.Children, &res.Stat, ech, err |
|
| 673 |
-} |
|
| 674 |
- |
|
| 675 |
-func (c *Conn) Get(path string) ([]byte, *Stat, error) {
|
|
| 676 |
- res := &getDataResponse{}
|
|
| 677 |
- _, err := c.request(opGetData, &getDataRequest{Path: path, Watch: false}, res, nil)
|
|
| 678 |
- return res.Data, &res.Stat, err |
|
| 679 |
-} |
|
| 680 |
- |
|
| 681 |
-// GetW returns the contents of a znode and sets a watch |
|
| 682 |
-func (c *Conn) GetW(path string) ([]byte, *Stat, <-chan Event, error) {
|
|
| 683 |
- var ech <-chan Event |
|
| 684 |
- res := &getDataResponse{}
|
|
| 685 |
- _, err := c.request(opGetData, &getDataRequest{Path: path, Watch: true}, res, func(req *request, res *responseHeader, err error) {
|
|
| 686 |
- if err == nil {
|
|
| 687 |
- ech = c.addWatcher(path, watchTypeData) |
|
| 688 |
- } |
|
| 689 |
- }) |
|
| 690 |
- if err != nil {
|
|
| 691 |
- return nil, nil, nil, err |
|
| 692 |
- } |
|
| 693 |
- return res.Data, &res.Stat, ech, err |
|
| 694 |
-} |
|
| 695 |
- |
|
| 696 |
-func (c *Conn) Set(path string, data []byte, version int32) (*Stat, error) {
|
|
| 697 |
- res := &setDataResponse{}
|
|
| 698 |
- _, err := c.request(opSetData, &SetDataRequest{path, data, version}, res, nil)
|
|
| 699 |
- return &res.Stat, err |
|
| 700 |
-} |
|
| 701 |
- |
|
| 702 |
-func (c *Conn) Create(path string, data []byte, flags int32, acl []ACL) (string, error) {
|
|
| 703 |
- res := &createResponse{}
|
|
| 704 |
- _, err := c.request(opCreate, &CreateRequest{path, data, acl, flags}, res, nil)
|
|
| 705 |
- return res.Path, err |
|
| 706 |
-} |
|
| 707 |
- |
|
| 708 |
-// CreateProtectedEphemeralSequential fixes a race condition if the server crashes |
|
| 709 |
-// after it creates the node. On reconnect the session may still be valid so the |
|
| 710 |
-// ephemeral node still exists. Therefore, on reconnect we need to check if a node |
|
| 711 |
-// with a GUID generated on create exists. |
|
| 712 |
-func (c *Conn) CreateProtectedEphemeralSequential(path string, data []byte, acl []ACL) (string, error) {
|
|
| 713 |
- var guid [16]byte |
|
| 714 |
- _, err := io.ReadFull(rand.Reader, guid[:16]) |
|
| 715 |
- if err != nil {
|
|
| 716 |
- return "", err |
|
| 717 |
- } |
|
| 718 |
- guidStr := fmt.Sprintf("%x", guid)
|
|
| 719 |
- |
|
| 720 |
- parts := strings.Split(path, "/") |
|
| 721 |
- parts[len(parts)-1] = fmt.Sprintf("%s%s-%s", protectedPrefix, guidStr, parts[len(parts)-1])
|
|
| 722 |
- rootPath := strings.Join(parts[:len(parts)-1], "/") |
|
| 723 |
- protectedPath := strings.Join(parts, "/") |
|
| 724 |
- |
|
| 725 |
- var newPath string |
|
| 726 |
- for i := 0; i < 3; i++ {
|
|
| 727 |
- newPath, err = c.Create(protectedPath, data, FlagEphemeral|FlagSequence, acl) |
|
| 728 |
- switch err {
|
|
| 729 |
- case ErrSessionExpired: |
|
| 730 |
- // No need to search for the node since it can't exist. Just try again. |
|
| 731 |
- case ErrConnectionClosed: |
|
| 732 |
- children, _, err := c.Children(rootPath) |
|
| 733 |
- if err != nil {
|
|
| 734 |
- return "", err |
|
| 735 |
- } |
|
| 736 |
- for _, p := range children {
|
|
| 737 |
- parts := strings.Split(p, "/") |
|
| 738 |
- if pth := parts[len(parts)-1]; strings.HasPrefix(pth, protectedPrefix) {
|
|
| 739 |
- if g := pth[len(protectedPrefix) : len(protectedPrefix)+32]; g == guidStr {
|
|
| 740 |
- return rootPath + "/" + p, nil |
|
| 741 |
- } |
|
| 742 |
- } |
|
| 743 |
- } |
|
| 744 |
- case nil: |
|
| 745 |
- return newPath, nil |
|
| 746 |
- default: |
|
| 747 |
- return "", err |
|
| 748 |
- } |
|
| 749 |
- } |
|
| 750 |
- return "", err |
|
| 751 |
-} |
|
| 752 |
- |
|
| 753 |
-func (c *Conn) Delete(path string, version int32) error {
|
|
| 754 |
- _, err := c.request(opDelete, &DeleteRequest{path, version}, &deleteResponse{}, nil)
|
|
| 755 |
- return err |
|
| 756 |
-} |
|
| 757 |
- |
|
| 758 |
-func (c *Conn) Exists(path string) (bool, *Stat, error) {
|
|
| 759 |
- res := &existsResponse{}
|
|
| 760 |
- _, err := c.request(opExists, &existsRequest{Path: path, Watch: false}, res, nil)
|
|
| 761 |
- exists := true |
|
| 762 |
- if err == ErrNoNode {
|
|
| 763 |
- exists = false |
|
| 764 |
- err = nil |
|
| 765 |
- } |
|
| 766 |
- return exists, &res.Stat, err |
|
| 767 |
-} |
|
| 768 |
- |
|
| 769 |
-func (c *Conn) ExistsW(path string) (bool, *Stat, <-chan Event, error) {
|
|
| 770 |
- var ech <-chan Event |
|
| 771 |
- res := &existsResponse{}
|
|
| 772 |
- _, err := c.request(opExists, &existsRequest{Path: path, Watch: true}, res, func(req *request, res *responseHeader, err error) {
|
|
| 773 |
- if err == nil {
|
|
| 774 |
- ech = c.addWatcher(path, watchTypeData) |
|
| 775 |
- } else if err == ErrNoNode {
|
|
| 776 |
- ech = c.addWatcher(path, watchTypeExist) |
|
| 777 |
- } |
|
| 778 |
- }) |
|
| 779 |
- exists := true |
|
| 780 |
- if err == ErrNoNode {
|
|
| 781 |
- exists = false |
|
| 782 |
- err = nil |
|
| 783 |
- } |
|
| 784 |
- if err != nil {
|
|
| 785 |
- return false, nil, nil, err |
|
| 786 |
- } |
|
| 787 |
- return exists, &res.Stat, ech, err |
|
| 788 |
-} |
|
| 789 |
- |
|
| 790 |
-func (c *Conn) GetACL(path string) ([]ACL, *Stat, error) {
|
|
| 791 |
- res := &getAclResponse{}
|
|
| 792 |
- _, err := c.request(opGetAcl, &getAclRequest{Path: path}, res, nil)
|
|
| 793 |
- return res.Acl, &res.Stat, err |
|
| 794 |
-} |
|
| 795 |
- |
|
| 796 |
-func (c *Conn) SetACL(path string, acl []ACL, version int32) (*Stat, error) {
|
|
| 797 |
- res := &setAclResponse{}
|
|
| 798 |
- _, err := c.request(opSetAcl, &setAclRequest{Path: path, Acl: acl, Version: version}, res, nil)
|
|
| 799 |
- return &res.Stat, err |
|
| 800 |
-} |
|
| 801 |
- |
|
| 802 |
-func (c *Conn) Sync(path string) (string, error) {
|
|
| 803 |
- res := &syncResponse{}
|
|
| 804 |
- _, err := c.request(opSync, &syncRequest{Path: path}, res, nil)
|
|
| 805 |
- return res.Path, err |
|
| 806 |
-} |
|
| 807 |
- |
|
| 808 |
-type MultiResponse struct {
|
|
| 809 |
- Stat *Stat |
|
| 810 |
- String string |
|
| 811 |
-} |
|
| 812 |
- |
|
| 813 |
-// Multi executes multiple ZooKeeper operations or none of them. The provided |
|
| 814 |
-// ops must be one of *CreateRequest, *DeleteRequest, *SetDataRequest, or |
|
| 815 |
-// *CheckVersionRequest. |
|
| 816 |
-func (c *Conn) Multi(ops ...interface{}) ([]MultiResponse, error) {
|
|
| 817 |
- req := &multiRequest{
|
|
| 818 |
- Ops: make([]multiRequestOp, 0, len(ops)), |
|
| 819 |
- DoneHeader: multiHeader{Type: -1, Done: true, Err: -1},
|
|
| 820 |
- } |
|
| 821 |
- for _, op := range ops {
|
|
| 822 |
- var opCode int32 |
|
| 823 |
- switch op.(type) {
|
|
| 824 |
- case *CreateRequest: |
|
| 825 |
- opCode = opCreate |
|
| 826 |
- case *SetDataRequest: |
|
| 827 |
- opCode = opSetData |
|
| 828 |
- case *DeleteRequest: |
|
| 829 |
- opCode = opDelete |
|
| 830 |
- case *CheckVersionRequest: |
|
| 831 |
- opCode = opCheck |
|
| 832 |
- default: |
|
| 833 |
- return nil, fmt.Errorf("uknown operation type %T", op)
|
|
| 834 |
- } |
|
| 835 |
- req.Ops = append(req.Ops, multiRequestOp{multiHeader{opCode, false, -1}, op})
|
|
| 836 |
- } |
|
| 837 |
- res := &multiResponse{}
|
|
| 838 |
- _, err := c.request(opMulti, req, res, nil) |
|
| 839 |
- mr := make([]MultiResponse, len(res.Ops)) |
|
| 840 |
- for i, op := range res.Ops {
|
|
| 841 |
- mr[i] = MultiResponse{Stat: op.Stat, String: op.String}
|
|
| 842 |
- } |
|
| 843 |
- return mr, err |
|
| 844 |
-} |
| 845 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,242 +0,0 @@ |
| 1 |
-package zk |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-const ( |
|
| 8 |
- protocolVersion = 0 |
|
| 9 |
- |
|
| 10 |
- DefaultPort = 2181 |
|
| 11 |
-) |
|
| 12 |
- |
|
| 13 |
-const ( |
|
| 14 |
- opNotify = 0 |
|
| 15 |
- opCreate = 1 |
|
| 16 |
- opDelete = 2 |
|
| 17 |
- opExists = 3 |
|
| 18 |
- opGetData = 4 |
|
| 19 |
- opSetData = 5 |
|
| 20 |
- opGetAcl = 6 |
|
| 21 |
- opSetAcl = 7 |
|
| 22 |
- opGetChildren = 8 |
|
| 23 |
- opSync = 9 |
|
| 24 |
- opPing = 11 |
|
| 25 |
- opGetChildren2 = 12 |
|
| 26 |
- opCheck = 13 |
|
| 27 |
- opMulti = 14 |
|
| 28 |
- opClose = -11 |
|
| 29 |
- opSetAuth = 100 |
|
| 30 |
- opSetWatches = 101 |
|
| 31 |
- // Not in protocol, used internally |
|
| 32 |
- opWatcherEvent = -2 |
|
| 33 |
-) |
|
| 34 |
- |
|
| 35 |
-const ( |
|
| 36 |
- EventNodeCreated = EventType(1) |
|
| 37 |
- EventNodeDeleted = EventType(2) |
|
| 38 |
- EventNodeDataChanged = EventType(3) |
|
| 39 |
- EventNodeChildrenChanged = EventType(4) |
|
| 40 |
- |
|
| 41 |
- EventSession = EventType(-1) |
|
| 42 |
- EventNotWatching = EventType(-2) |
|
| 43 |
-) |
|
| 44 |
- |
|
| 45 |
-var ( |
|
| 46 |
- eventNames = map[EventType]string{
|
|
| 47 |
- EventNodeCreated: "EventNodeCreated", |
|
| 48 |
- EventNodeDeleted: "EventNodeDeleted", |
|
| 49 |
- EventNodeDataChanged: "EventNodeDataChanged", |
|
| 50 |
- EventNodeChildrenChanged: "EventNodeChildrenChanged", |
|
| 51 |
- EventSession: "EventSession", |
|
| 52 |
- EventNotWatching: "EventNotWatching", |
|
| 53 |
- } |
|
| 54 |
-) |
|
| 55 |
- |
|
| 56 |
-const ( |
|
| 57 |
- StateUnknown = State(-1) |
|
| 58 |
- StateDisconnected = State(0) |
|
| 59 |
- StateConnecting = State(1) |
|
| 60 |
- StateSyncConnected = State(3) |
|
| 61 |
- StateAuthFailed = State(4) |
|
| 62 |
- StateConnectedReadOnly = State(5) |
|
| 63 |
- StateSaslAuthenticated = State(6) |
|
| 64 |
- StateExpired = State(-112) |
|
| 65 |
- // StateAuthFailed = State(-113) |
|
| 66 |
- |
|
| 67 |
- StateConnected = State(100) |
|
| 68 |
- StateHasSession = State(101) |
|
| 69 |
-) |
|
| 70 |
- |
|
| 71 |
-const ( |
|
| 72 |
- FlagEphemeral = 1 |
|
| 73 |
- FlagSequence = 2 |
|
| 74 |
-) |
|
| 75 |
- |
|
| 76 |
-var ( |
|
| 77 |
- stateNames = map[State]string{
|
|
| 78 |
- StateUnknown: "StateUnknown", |
|
| 79 |
- StateDisconnected: "StateDisconnected", |
|
| 80 |
- StateSyncConnected: "StateSyncConnected", |
|
| 81 |
- StateConnectedReadOnly: "StateConnectedReadOnly", |
|
| 82 |
- StateSaslAuthenticated: "StateSaslAuthenticated", |
|
| 83 |
- StateExpired: "StateExpired", |
|
| 84 |
- StateAuthFailed: "StateAuthFailed", |
|
| 85 |
- StateConnecting: "StateConnecting", |
|
| 86 |
- StateConnected: "StateConnected", |
|
| 87 |
- StateHasSession: "StateHasSession", |
|
| 88 |
- } |
|
| 89 |
-) |
|
| 90 |
- |
|
| 91 |
-type State int32 |
|
| 92 |
- |
|
| 93 |
-func (s State) String() string {
|
|
| 94 |
- if name := stateNames[s]; name != "" {
|
|
| 95 |
- return name |
|
| 96 |
- } |
|
| 97 |
- return "Unknown" |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-type ErrCode int32 |
|
| 101 |
- |
|
| 102 |
-var ( |
|
| 103 |
- ErrConnectionClosed = errors.New("zk: connection closed")
|
|
| 104 |
- ErrUnknown = errors.New("zk: unknown error")
|
|
| 105 |
- ErrAPIError = errors.New("zk: api error")
|
|
| 106 |
- ErrNoNode = errors.New("zk: node does not exist")
|
|
| 107 |
- ErrNoAuth = errors.New("zk: not authenticated")
|
|
| 108 |
- ErrBadVersion = errors.New("zk: version conflict")
|
|
| 109 |
- ErrNoChildrenForEphemerals = errors.New("zk: ephemeral nodes may not have children")
|
|
| 110 |
- ErrNodeExists = errors.New("zk: node already exists")
|
|
| 111 |
- ErrNotEmpty = errors.New("zk: node has children")
|
|
| 112 |
- ErrSessionExpired = errors.New("zk: session has been expired by the server")
|
|
| 113 |
- ErrInvalidACL = errors.New("zk: invalid ACL specified")
|
|
| 114 |
- ErrAuthFailed = errors.New("zk: client authentication failed")
|
|
| 115 |
- ErrClosing = errors.New("zk: zookeeper is closing")
|
|
| 116 |
- ErrNothing = errors.New("zk: no server responsees to process")
|
|
| 117 |
- ErrSessionMoved = errors.New("zk: session moved to another server, so operation is ignored")
|
|
| 118 |
- |
|
| 119 |
- // ErrInvalidCallback = errors.New("zk: invalid callback specified")
|
|
| 120 |
- errCodeToError = map[ErrCode]error{
|
|
| 121 |
- 0: nil, |
|
| 122 |
- errAPIError: ErrAPIError, |
|
| 123 |
- errNoNode: ErrNoNode, |
|
| 124 |
- errNoAuth: ErrNoAuth, |
|
| 125 |
- errBadVersion: ErrBadVersion, |
|
| 126 |
- errNoChildrenForEphemerals: ErrNoChildrenForEphemerals, |
|
| 127 |
- errNodeExists: ErrNodeExists, |
|
| 128 |
- errNotEmpty: ErrNotEmpty, |
|
| 129 |
- errSessionExpired: ErrSessionExpired, |
|
| 130 |
- // errInvalidCallback: ErrInvalidCallback, |
|
| 131 |
- errInvalidAcl: ErrInvalidACL, |
|
| 132 |
- errAuthFailed: ErrAuthFailed, |
|
| 133 |
- errClosing: ErrClosing, |
|
| 134 |
- errNothing: ErrNothing, |
|
| 135 |
- errSessionMoved: ErrSessionMoved, |
|
| 136 |
- } |
|
| 137 |
-) |
|
| 138 |
- |
|
| 139 |
-func (e ErrCode) toError() error {
|
|
| 140 |
- if err, ok := errCodeToError[e]; ok {
|
|
| 141 |
- return err |
|
| 142 |
- } |
|
| 143 |
- return ErrUnknown |
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-const ( |
|
| 147 |
- errOk = 0 |
|
| 148 |
- // System and server-side errors |
|
| 149 |
- errSystemError = -1 |
|
| 150 |
- errRuntimeInconsistency = -2 |
|
| 151 |
- errDataInconsistency = -3 |
|
| 152 |
- errConnectionLoss = -4 |
|
| 153 |
- errMarshallingError = -5 |
|
| 154 |
- errUnimplemented = -6 |
|
| 155 |
- errOperationTimeout = -7 |
|
| 156 |
- errBadArguments = -8 |
|
| 157 |
- errInvalidState = -9 |
|
| 158 |
- // API errors |
|
| 159 |
- errAPIError = ErrCode(-100) |
|
| 160 |
- errNoNode = ErrCode(-101) // * |
|
| 161 |
- errNoAuth = ErrCode(-102) |
|
| 162 |
- errBadVersion = ErrCode(-103) // * |
|
| 163 |
- errNoChildrenForEphemerals = ErrCode(-108) |
|
| 164 |
- errNodeExists = ErrCode(-110) // * |
|
| 165 |
- errNotEmpty = ErrCode(-111) |
|
| 166 |
- errSessionExpired = ErrCode(-112) |
|
| 167 |
- errInvalidCallback = ErrCode(-113) |
|
| 168 |
- errInvalidAcl = ErrCode(-114) |
|
| 169 |
- errAuthFailed = ErrCode(-115) |
|
| 170 |
- errClosing = ErrCode(-116) |
|
| 171 |
- errNothing = ErrCode(-117) |
|
| 172 |
- errSessionMoved = ErrCode(-118) |
|
| 173 |
-) |
|
| 174 |
- |
|
| 175 |
-// Constants for ACL permissions |
|
| 176 |
-const ( |
|
| 177 |
- PermRead = 1 << iota |
|
| 178 |
- PermWrite |
|
| 179 |
- PermCreate |
|
| 180 |
- PermDelete |
|
| 181 |
- PermAdmin |
|
| 182 |
- PermAll = 0x1f |
|
| 183 |
-) |
|
| 184 |
- |
|
| 185 |
-var ( |
|
| 186 |
- emptyPassword = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
|
| 187 |
- opNames = map[int32]string{
|
|
| 188 |
- opNotify: "notify", |
|
| 189 |
- opCreate: "create", |
|
| 190 |
- opDelete: "delete", |
|
| 191 |
- opExists: "exists", |
|
| 192 |
- opGetData: "getData", |
|
| 193 |
- opSetData: "setData", |
|
| 194 |
- opGetAcl: "getACL", |
|
| 195 |
- opSetAcl: "setACL", |
|
| 196 |
- opGetChildren: "getChildren", |
|
| 197 |
- opSync: "sync", |
|
| 198 |
- opPing: "ping", |
|
| 199 |
- opGetChildren2: "getChildren2", |
|
| 200 |
- opCheck: "check", |
|
| 201 |
- opMulti: "multi", |
|
| 202 |
- opClose: "close", |
|
| 203 |
- opSetAuth: "setAuth", |
|
| 204 |
- opSetWatches: "setWatches", |
|
| 205 |
- |
|
| 206 |
- opWatcherEvent: "watcherEvent", |
|
| 207 |
- } |
|
| 208 |
-) |
|
| 209 |
- |
|
| 210 |
-type EventType int32 |
|
| 211 |
- |
|
| 212 |
-func (t EventType) String() string {
|
|
| 213 |
- if name := eventNames[t]; name != "" {
|
|
| 214 |
- return name |
|
| 215 |
- } |
|
| 216 |
- return "Unknown" |
|
| 217 |
-} |
|
| 218 |
- |
|
| 219 |
-// Mode is used to build custom server modes (leader|follower|standalone). |
|
| 220 |
-type Mode uint8 |
|
| 221 |
- |
|
| 222 |
-func (m Mode) String() string {
|
|
| 223 |
- if name := modeNames[m]; name != "" {
|
|
| 224 |
- return name |
|
| 225 |
- } |
|
| 226 |
- return "unknown" |
|
| 227 |
-} |
|
| 228 |
- |
|
| 229 |
-const ( |
|
| 230 |
- ModeUnknown Mode = iota |
|
| 231 |
- ModeLeader Mode = iota |
|
| 232 |
- ModeFollower Mode = iota |
|
| 233 |
- ModeStandalone Mode = iota |
|
| 234 |
-) |
|
| 235 |
- |
|
| 236 |
-var ( |
|
| 237 |
- modeNames = map[Mode]string{
|
|
| 238 |
- ModeLeader: "leader", |
|
| 239 |
- ModeFollower: "follower", |
|
| 240 |
- ModeStandalone: "standalone", |
|
| 241 |
- } |
|
| 242 |
-) |
| 243 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,288 +0,0 @@ |
| 1 |
-package zk |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "bytes" |
|
| 6 |
- "fmt" |
|
| 7 |
- "io/ioutil" |
|
| 8 |
- "math/big" |
|
| 9 |
- "net" |
|
| 10 |
- "regexp" |
|
| 11 |
- "strconv" |
|
| 12 |
- "time" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-// FLWSrvr is a FourLetterWord helper function. In particular, this function pulls the srvr output |
|
| 16 |
-// from the zookeeper instances and parses the output. A slice of *ServerStats structs are returned |
|
| 17 |
-// as well as a boolean value to indicate whether this function processed successfully. |
|
| 18 |
-// |
|
| 19 |
-// If the boolean value is false there was a problem. If the *ServerStats slice is empty or nil, |
|
| 20 |
-// then the error happened before we started to obtain 'srvr' values. Otherwise, one of the |
|
| 21 |
-// servers had an issue and the "Error" value in the struct should be inspected to determine |
|
| 22 |
-// which server had the issue. |
|
| 23 |
-func FLWSrvr(servers []string, timeout time.Duration) ([]*ServerStats, bool) {
|
|
| 24 |
- // different parts of the regular expression that are required to parse the srvr output |
|
| 25 |
- var ( |
|
| 26 |
- zrVer = `^Zookeeper version: ([A-Za-z0-9\.\-]+), built on (\d\d/\d\d/\d\d\d\d \d\d:\d\d [A-Za-z0-9:\+\-]+)` |
|
| 27 |
- zrLat = `^Latency min/avg/max: (\d+)/(\d+)/(\d+)` |
|
| 28 |
- zrNet = `^Received: (\d+).*\n^Sent: (\d+).*\n^Connections: (\d+).*\n^Outstanding: (\d+)` |
|
| 29 |
- zrState = `^Zxid: (0x[A-Za-z0-9]+).*\n^Mode: (\w+).*\n^Node count: (\d+)` |
|
| 30 |
- ) |
|
| 31 |
- |
|
| 32 |
- // build the regex from the pieces above |
|
| 33 |
- re, err := regexp.Compile(fmt.Sprintf(`(?m:\A%v.*\n%v.*\n%v.*\n%v)`, zrVer, zrLat, zrNet, zrState)) |
|
| 34 |
- |
|
| 35 |
- if err != nil {
|
|
| 36 |
- return nil, false |
|
| 37 |
- } |
|
| 38 |
- |
|
| 39 |
- imOk := true |
|
| 40 |
- servers = FormatServers(servers) |
|
| 41 |
- ss := make([]*ServerStats, len(servers)) |
|
| 42 |
- |
|
| 43 |
- for i := range ss {
|
|
| 44 |
- response, err := fourLetterWord(servers[i], "srvr", timeout) |
|
| 45 |
- |
|
| 46 |
- if err != nil {
|
|
| 47 |
- ss[i] = &ServerStats{Error: err}
|
|
| 48 |
- imOk = false |
|
| 49 |
- continue |
|
| 50 |
- } |
|
| 51 |
- |
|
| 52 |
- match := re.FindAllStringSubmatch(string(response), -1)[0][1:] |
|
| 53 |
- |
|
| 54 |
- if match == nil {
|
|
| 55 |
- err := fmt.Errorf("unable to parse fields from zookeeper response (no regex matches)")
|
|
| 56 |
- ss[i] = &ServerStats{Error: err}
|
|
| 57 |
- imOk = false |
|
| 58 |
- continue |
|
| 59 |
- } |
|
| 60 |
- |
|
| 61 |
- // determine current server |
|
| 62 |
- var srvrMode Mode |
|
| 63 |
- switch match[10] {
|
|
| 64 |
- case "leader": |
|
| 65 |
- srvrMode = ModeLeader |
|
| 66 |
- case "follower": |
|
| 67 |
- srvrMode = ModeFollower |
|
| 68 |
- case "standalone": |
|
| 69 |
- srvrMode = ModeStandalone |
|
| 70 |
- default: |
|
| 71 |
- srvrMode = ModeUnknown |
|
| 72 |
- } |
|
| 73 |
- |
|
| 74 |
- buildTime, err := time.Parse("01/02/2006 15:04 MST", match[1])
|
|
| 75 |
- |
|
| 76 |
- if err != nil {
|
|
| 77 |
- ss[i] = &ServerStats{Error: err}
|
|
| 78 |
- imOk = false |
|
| 79 |
- continue |
|
| 80 |
- } |
|
| 81 |
- |
|
| 82 |
- parsedInt, err := strconv.ParseInt(match[9], 0, 64) |
|
| 83 |
- |
|
| 84 |
- if err != nil {
|
|
| 85 |
- ss[i] = &ServerStats{Error: err}
|
|
| 86 |
- imOk = false |
|
| 87 |
- continue |
|
| 88 |
- } |
|
| 89 |
- |
|
| 90 |
- // the ZxID value is an int64 with two int32s packed inside |
|
| 91 |
- // the high int32 is the epoch (i.e., number of leader elections) |
|
| 92 |
- // the low int32 is the counter |
|
| 93 |
- epoch := int32(parsedInt >> 32) |
|
| 94 |
- counter := int32(parsedInt & 0xFFFFFFFF) |
|
| 95 |
- |
|
| 96 |
- // within the regex above, these values must be numerical |
|
| 97 |
- // so we can avoid useless checking of the error return value |
|
| 98 |
- minLatency, _ := strconv.ParseInt(match[2], 0, 64) |
|
| 99 |
- avgLatency, _ := strconv.ParseInt(match[3], 0, 64) |
|
| 100 |
- maxLatency, _ := strconv.ParseInt(match[4], 0, 64) |
|
| 101 |
- recv, _ := strconv.ParseInt(match[5], 0, 64) |
|
| 102 |
- sent, _ := strconv.ParseInt(match[6], 0, 64) |
|
| 103 |
- cons, _ := strconv.ParseInt(match[7], 0, 64) |
|
| 104 |
- outs, _ := strconv.ParseInt(match[8], 0, 64) |
|
| 105 |
- ncnt, _ := strconv.ParseInt(match[11], 0, 64) |
|
| 106 |
- |
|
| 107 |
- ss[i] = &ServerStats{
|
|
| 108 |
- Sent: sent, |
|
| 109 |
- Received: recv, |
|
| 110 |
- NodeCount: ncnt, |
|
| 111 |
- MinLatency: minLatency, |
|
| 112 |
- AvgLatency: avgLatency, |
|
| 113 |
- MaxLatency: maxLatency, |
|
| 114 |
- Connections: cons, |
|
| 115 |
- Outstanding: outs, |
|
| 116 |
- Epoch: epoch, |
|
| 117 |
- Counter: counter, |
|
| 118 |
- BuildTime: buildTime, |
|
| 119 |
- Mode: srvrMode, |
|
| 120 |
- Version: match[0], |
|
| 121 |
- } |
|
| 122 |
- } |
|
| 123 |
- |
|
| 124 |
- return ss, imOk |
|
| 125 |
-} |
|
| 126 |
- |
|
| 127 |
-// FLWRuok is a FourLetterWord helper function. In particular, this function |
|
| 128 |
-// pulls the ruok output from each server. |
|
| 129 |
-func FLWRuok(servers []string, timeout time.Duration) []bool {
|
|
| 130 |
- servers = FormatServers(servers) |
|
| 131 |
- oks := make([]bool, len(servers)) |
|
| 132 |
- |
|
| 133 |
- for i := range oks {
|
|
| 134 |
- response, err := fourLetterWord(servers[i], "ruok", timeout) |
|
| 135 |
- |
|
| 136 |
- if err != nil {
|
|
| 137 |
- continue |
|
| 138 |
- } |
|
| 139 |
- |
|
| 140 |
- if bytes.Equal(response[:4], []byte("imok")) {
|
|
| 141 |
- oks[i] = true |
|
| 142 |
- } |
|
| 143 |
- } |
|
| 144 |
- return oks |
|
| 145 |
-} |
|
| 146 |
- |
|
| 147 |
-// FLWCons is a FourLetterWord helper function. In particular, this function |
|
| 148 |
-// pulls the ruok output from each server. |
|
| 149 |
-// |
|
| 150 |
-// As with FLWSrvr, the boolean value indicates whether one of the requests had |
|
| 151 |
-// an issue. The Clients struct has an Error value that can be checked. |
|
| 152 |
-func FLWCons(servers []string, timeout time.Duration) ([]*ServerClients, bool) {
|
|
| 153 |
- var ( |
|
| 154 |
- zrAddr = `^ /((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):(?:\d+))\[\d+\]`
|
|
| 155 |
- zrPac = `\(queued=(\d+),recved=(\d+),sent=(\d+),sid=(0x[A-Za-z0-9]+),lop=(\w+),est=(\d+),to=(\d+),` |
|
| 156 |
- zrSesh = `lcxid=(0x[A-Za-z0-9]+),lzxid=(0x[A-Za-z0-9]+),lresp=(\d+),llat=(\d+),minlat=(\d+),avglat=(\d+),maxlat=(\d+)\)` |
|
| 157 |
- ) |
|
| 158 |
- |
|
| 159 |
- re, err := regexp.Compile(fmt.Sprintf("%v%v%v", zrAddr, zrPac, zrSesh))
|
|
| 160 |
- |
|
| 161 |
- if err != nil {
|
|
| 162 |
- return nil, false |
|
| 163 |
- } |
|
| 164 |
- |
|
| 165 |
- servers = FormatServers(servers) |
|
| 166 |
- sc := make([]*ServerClients, len(servers)) |
|
| 167 |
- imOk := true |
|
| 168 |
- |
|
| 169 |
- for i := range sc {
|
|
| 170 |
- response, err := fourLetterWord(servers[i], "cons", timeout) |
|
| 171 |
- |
|
| 172 |
- if err != nil {
|
|
| 173 |
- sc[i] = &ServerClients{Error: err}
|
|
| 174 |
- imOk = false |
|
| 175 |
- continue |
|
| 176 |
- } |
|
| 177 |
- |
|
| 178 |
- scan := bufio.NewScanner(bytes.NewReader(response)) |
|
| 179 |
- |
|
| 180 |
- var clients []*ServerClient |
|
| 181 |
- |
|
| 182 |
- for scan.Scan() {
|
|
| 183 |
- line := scan.Bytes() |
|
| 184 |
- |
|
| 185 |
- if len(line) == 0 {
|
|
| 186 |
- continue |
|
| 187 |
- } |
|
| 188 |
- |
|
| 189 |
- m := re.FindAllStringSubmatch(string(line), -1) |
|
| 190 |
- |
|
| 191 |
- if m == nil {
|
|
| 192 |
- err := fmt.Errorf("unable to parse fields from zookeeper response (no regex matches)")
|
|
| 193 |
- sc[i] = &ServerClients{Error: err}
|
|
| 194 |
- imOk = false |
|
| 195 |
- continue |
|
| 196 |
- } |
|
| 197 |
- |
|
| 198 |
- match := m[0][1:] |
|
| 199 |
- |
|
| 200 |
- queued, _ := strconv.ParseInt(match[1], 0, 64) |
|
| 201 |
- recvd, _ := strconv.ParseInt(match[2], 0, 64) |
|
| 202 |
- sent, _ := strconv.ParseInt(match[3], 0, 64) |
|
| 203 |
- sid, _ := strconv.ParseInt(match[4], 0, 64) |
|
| 204 |
- est, _ := strconv.ParseInt(match[6], 0, 64) |
|
| 205 |
- timeout, _ := strconv.ParseInt(match[7], 0, 32) |
|
| 206 |
- lresp, _ := strconv.ParseInt(match[10], 0, 64) |
|
| 207 |
- llat, _ := strconv.ParseInt(match[11], 0, 32) |
|
| 208 |
- minlat, _ := strconv.ParseInt(match[12], 0, 32) |
|
| 209 |
- avglat, _ := strconv.ParseInt(match[13], 0, 32) |
|
| 210 |
- maxlat, _ := strconv.ParseInt(match[14], 0, 32) |
|
| 211 |
- |
|
| 212 |
- // zookeeper returns a value, '0xffffffffffffffff', as the |
|
| 213 |
- // Lzxid for PING requests in the 'cons' output. |
|
| 214 |
- // unfortunately, in Go that is an invalid int64 and is not represented |
|
| 215 |
- // as -1. |
|
| 216 |
- // However, converting the string value to a big.Int and then back to |
|
| 217 |
- // and int64 properly sets the value to -1 |
|
| 218 |
- lzxid, ok := new(big.Int).SetString(match[9], 0) |
|
| 219 |
- |
|
| 220 |
- var errVal error |
|
| 221 |
- |
|
| 222 |
- if !ok {
|
|
| 223 |
- errVal = fmt.Errorf("failed to convert lzxid value to big.Int")
|
|
| 224 |
- imOk = false |
|
| 225 |
- } |
|
| 226 |
- |
|
| 227 |
- lcxid, ok := new(big.Int).SetString(match[8], 0) |
|
| 228 |
- |
|
| 229 |
- if !ok && errVal == nil {
|
|
| 230 |
- errVal = fmt.Errorf("failed to convert lcxid value to big.Int")
|
|
| 231 |
- imOk = false |
|
| 232 |
- } |
|
| 233 |
- |
|
| 234 |
- clients = append(clients, &ServerClient{
|
|
| 235 |
- Queued: queued, |
|
| 236 |
- Received: recvd, |
|
| 237 |
- Sent: sent, |
|
| 238 |
- SessionID: sid, |
|
| 239 |
- Lcxid: lcxid.Int64(), |
|
| 240 |
- Lzxid: lzxid.Int64(), |
|
| 241 |
- Timeout: int32(timeout), |
|
| 242 |
- LastLatency: int32(llat), |
|
| 243 |
- MinLatency: int32(minlat), |
|
| 244 |
- AvgLatency: int32(avglat), |
|
| 245 |
- MaxLatency: int32(maxlat), |
|
| 246 |
- Established: time.Unix(est, 0), |
|
| 247 |
- LastResponse: time.Unix(lresp, 0), |
|
| 248 |
- Addr: match[0], |
|
| 249 |
- LastOperation: match[5], |
|
| 250 |
- Error: errVal, |
|
| 251 |
- }) |
|
| 252 |
- } |
|
| 253 |
- |
|
| 254 |
- sc[i] = &ServerClients{Clients: clients}
|
|
| 255 |
- } |
|
| 256 |
- |
|
| 257 |
- return sc, imOk |
|
| 258 |
-} |
|
| 259 |
- |
|
| 260 |
-func fourLetterWord(server, command string, timeout time.Duration) ([]byte, error) {
|
|
| 261 |
- conn, err := net.DialTimeout("tcp", server, timeout)
|
|
| 262 |
- |
|
| 263 |
- if err != nil {
|
|
| 264 |
- return nil, err |
|
| 265 |
- } |
|
| 266 |
- |
|
| 267 |
- // the zookeeper server should automatically close this socket |
|
| 268 |
- // once the command has been processed, but better safe than sorry |
|
| 269 |
- defer conn.Close() |
|
| 270 |
- |
|
| 271 |
- conn.SetWriteDeadline(time.Now().Add(timeout)) |
|
| 272 |
- |
|
| 273 |
- _, err = conn.Write([]byte(command)) |
|
| 274 |
- |
|
| 275 |
- if err != nil {
|
|
| 276 |
- return nil, err |
|
| 277 |
- } |
|
| 278 |
- |
|
| 279 |
- conn.SetReadDeadline(time.Now().Add(timeout)) |
|
| 280 |
- |
|
| 281 |
- resp, err := ioutil.ReadAll(conn) |
|
| 282 |
- |
|
| 283 |
- if err != nil {
|
|
| 284 |
- return nil, err |
|
| 285 |
- } |
|
| 286 |
- |
|
| 287 |
- return resp, nil |
|
| 288 |
-} |
| 289 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,131 +0,0 @@ |
| 1 |
-package zk |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "fmt" |
|
| 6 |
- "strconv" |
|
| 7 |
- "strings" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-var ( |
|
| 11 |
- ErrDeadlock = errors.New("zk: trying to acquire a lock twice")
|
|
| 12 |
- ErrNotLocked = errors.New("zk: not locked")
|
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-type Lock struct {
|
|
| 16 |
- c *Conn |
|
| 17 |
- path string |
|
| 18 |
- acl []ACL |
|
| 19 |
- lockPath string |
|
| 20 |
- seq int |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-func NewLock(c *Conn, path string, acl []ACL) *Lock {
|
|
| 24 |
- return &Lock{
|
|
| 25 |
- c: c, |
|
| 26 |
- path: path, |
|
| 27 |
- acl: acl, |
|
| 28 |
- } |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-func parseSeq(path string) (int, error) {
|
|
| 32 |
- parts := strings.Split(path, "-") |
|
| 33 |
- return strconv.Atoi(parts[len(parts)-1]) |
|
| 34 |
-} |
|
| 35 |
- |
|
| 36 |
-func (l *Lock) Lock() error {
|
|
| 37 |
- if l.lockPath != "" {
|
|
| 38 |
- return ErrDeadlock |
|
| 39 |
- } |
|
| 40 |
- |
|
| 41 |
- prefix := fmt.Sprintf("%s/lock-", l.path)
|
|
| 42 |
- |
|
| 43 |
- path := "" |
|
| 44 |
- var err error |
|
| 45 |
- for i := 0; i < 3; i++ {
|
|
| 46 |
- path, err = l.c.CreateProtectedEphemeralSequential(prefix, []byte{}, l.acl)
|
|
| 47 |
- if err == ErrNoNode {
|
|
| 48 |
- // Create parent node. |
|
| 49 |
- parts := strings.Split(l.path, "/") |
|
| 50 |
- pth := "" |
|
| 51 |
- for _, p := range parts[1:] {
|
|
| 52 |
- pth += "/" + p |
|
| 53 |
- _, err := l.c.Create(pth, []byte{}, 0, l.acl)
|
|
| 54 |
- if err != nil && err != ErrNodeExists {
|
|
| 55 |
- return err |
|
| 56 |
- } |
|
| 57 |
- } |
|
| 58 |
- } else if err == nil {
|
|
| 59 |
- break |
|
| 60 |
- } else {
|
|
| 61 |
- return err |
|
| 62 |
- } |
|
| 63 |
- } |
|
| 64 |
- if err != nil {
|
|
| 65 |
- return err |
|
| 66 |
- } |
|
| 67 |
- |
|
| 68 |
- seq, err := parseSeq(path) |
|
| 69 |
- if err != nil {
|
|
| 70 |
- return err |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- for {
|
|
| 74 |
- children, _, err := l.c.Children(l.path) |
|
| 75 |
- if err != nil {
|
|
| 76 |
- return err |
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- lowestSeq := seq |
|
| 80 |
- prevSeq := 0 |
|
| 81 |
- prevSeqPath := "" |
|
| 82 |
- for _, p := range children {
|
|
| 83 |
- s, err := parseSeq(p) |
|
| 84 |
- if err != nil {
|
|
| 85 |
- return err |
|
| 86 |
- } |
|
| 87 |
- if s < lowestSeq {
|
|
| 88 |
- lowestSeq = s |
|
| 89 |
- } |
|
| 90 |
- if s < seq && s > prevSeq {
|
|
| 91 |
- prevSeq = s |
|
| 92 |
- prevSeqPath = p |
|
| 93 |
- } |
|
| 94 |
- } |
|
| 95 |
- |
|
| 96 |
- if seq == lowestSeq {
|
|
| 97 |
- // Acquired the lock |
|
| 98 |
- break |
|
| 99 |
- } |
|
| 100 |
- |
|
| 101 |
- // Wait on the node next in line for the lock |
|
| 102 |
- _, _, ch, err := l.c.GetW(l.path + "/" + prevSeqPath) |
|
| 103 |
- if err != nil && err != ErrNoNode {
|
|
| 104 |
- return err |
|
| 105 |
- } else if err != nil && err == ErrNoNode {
|
|
| 106 |
- // try again |
|
| 107 |
- continue |
|
| 108 |
- } |
|
| 109 |
- |
|
| 110 |
- ev := <-ch |
|
| 111 |
- if ev.Err != nil {
|
|
| 112 |
- return ev.Err |
|
| 113 |
- } |
|
| 114 |
- } |
|
| 115 |
- |
|
| 116 |
- l.seq = seq |
|
| 117 |
- l.lockPath = path |
|
| 118 |
- return nil |
|
| 119 |
-} |
|
| 120 |
- |
|
| 121 |
-func (l *Lock) Unlock() error {
|
|
| 122 |
- if l.lockPath == "" {
|
|
| 123 |
- return ErrNotLocked |
|
| 124 |
- } |
|
| 125 |
- if err := l.c.Delete(l.lockPath, -1); err != nil {
|
|
| 126 |
- return err |
|
| 127 |
- } |
|
| 128 |
- l.lockPath = "" |
|
| 129 |
- l.seq = 0 |
|
| 130 |
- return nil |
|
| 131 |
-} |
| 132 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,119 +0,0 @@ |
| 1 |
-package zk |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "io" |
|
| 6 |
- "io/ioutil" |
|
| 7 |
- "math/rand" |
|
| 8 |
- "os" |
|
| 9 |
- "path/filepath" |
|
| 10 |
- "time" |
|
| 11 |
-) |
|
| 12 |
- |
|
| 13 |
-type TestServer struct {
|
|
| 14 |
- Port int |
|
| 15 |
- Path string |
|
| 16 |
- Srv *Server |
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-type TestCluster struct {
|
|
| 20 |
- Path string |
|
| 21 |
- Servers []TestServer |
|
| 22 |
-} |
|
| 23 |
- |
|
| 24 |
-func StartTestCluster(size int, stdout, stderr io.Writer) (*TestCluster, error) {
|
|
| 25 |
- tmpPath, err := ioutil.TempDir("", "gozk")
|
|
| 26 |
- if err != nil {
|
|
| 27 |
- return nil, err |
|
| 28 |
- } |
|
| 29 |
- success := false |
|
| 30 |
- startPort := int(rand.Int31n(6000) + 10000) |
|
| 31 |
- cluster := &TestCluster{Path: tmpPath}
|
|
| 32 |
- defer func() {
|
|
| 33 |
- if !success {
|
|
| 34 |
- cluster.Stop() |
|
| 35 |
- } |
|
| 36 |
- }() |
|
| 37 |
- for serverN := 0; serverN < size; serverN++ {
|
|
| 38 |
- srvPath := filepath.Join(tmpPath, fmt.Sprintf("srv%d", serverN))
|
|
| 39 |
- if err := os.Mkdir(srvPath, 0700); err != nil {
|
|
| 40 |
- return nil, err |
|
| 41 |
- } |
|
| 42 |
- port := startPort + serverN*3 |
|
| 43 |
- cfg := ServerConfig{
|
|
| 44 |
- ClientPort: port, |
|
| 45 |
- DataDir: srvPath, |
|
| 46 |
- } |
|
| 47 |
- for i := 0; i < size; i++ {
|
|
| 48 |
- cfg.Servers = append(cfg.Servers, ServerConfigServer{
|
|
| 49 |
- ID: i + 1, |
|
| 50 |
- Host: "127.0.0.1", |
|
| 51 |
- PeerPort: startPort + i*3 + 1, |
|
| 52 |
- LeaderElectionPort: startPort + i*3 + 2, |
|
| 53 |
- }) |
|
| 54 |
- } |
|
| 55 |
- cfgPath := filepath.Join(srvPath, "zoo.cfg") |
|
| 56 |
- fi, err := os.Create(cfgPath) |
|
| 57 |
- if err != nil {
|
|
| 58 |
- return nil, err |
|
| 59 |
- } |
|
| 60 |
- err = cfg.Marshall(fi) |
|
| 61 |
- fi.Close() |
|
| 62 |
- if err != nil {
|
|
| 63 |
- return nil, err |
|
| 64 |
- } |
|
| 65 |
- |
|
| 66 |
- fi, err = os.Create(filepath.Join(srvPath, "myid")) |
|
| 67 |
- if err != nil {
|
|
| 68 |
- return nil, err |
|
| 69 |
- } |
|
| 70 |
- _, err = fmt.Fprintf(fi, "%d\n", serverN+1) |
|
| 71 |
- fi.Close() |
|
| 72 |
- if err != nil {
|
|
| 73 |
- return nil, err |
|
| 74 |
- } |
|
| 75 |
- |
|
| 76 |
- srv := &Server{
|
|
| 77 |
- ConfigPath: cfgPath, |
|
| 78 |
- Stdout: stdout, |
|
| 79 |
- Stderr: stderr, |
|
| 80 |
- } |
|
| 81 |
- if err := srv.Start(); err != nil {
|
|
| 82 |
- return nil, err |
|
| 83 |
- } |
|
| 84 |
- cluster.Servers = append(cluster.Servers, TestServer{
|
|
| 85 |
- Path: srvPath, |
|
| 86 |
- Port: cfg.ClientPort, |
|
| 87 |
- Srv: srv, |
|
| 88 |
- }) |
|
| 89 |
- } |
|
| 90 |
- success = true |
|
| 91 |
- time.Sleep(time.Second) // Give the server time to become active. Should probably actually attempt to connect to verify. |
|
| 92 |
- return cluster, nil |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 |
-func (ts *TestCluster) Connect(idx int) (*Conn, error) {
|
|
| 96 |
- zk, _, err := Connect([]string{fmt.Sprintf("127.0.0.1:%d", ts.Servers[idx].Port)}, time.Second*15)
|
|
| 97 |
- return zk, err |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-func (ts *TestCluster) ConnectAll() (*Conn, <-chan Event, error) {
|
|
| 101 |
- return ts.ConnectAllTimeout(time.Second * 15) |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-func (ts *TestCluster) ConnectAllTimeout(sessionTimeout time.Duration) (*Conn, <-chan Event, error) {
|
|
| 105 |
- hosts := make([]string, len(ts.Servers)) |
|
| 106 |
- for i, srv := range ts.Servers {
|
|
| 107 |
- hosts[i] = fmt.Sprintf("127.0.0.1:%d", srv.Port)
|
|
| 108 |
- } |
|
| 109 |
- zk, ch, err := Connect(hosts, sessionTimeout) |
|
| 110 |
- return zk, ch, err |
|
| 111 |
-} |
|
| 112 |
- |
|
| 113 |
-func (ts *TestCluster) Stop() error {
|
|
| 114 |
- for _, srv := range ts.Servers {
|
|
| 115 |
- srv.Srv.Stop() |
|
| 116 |
- } |
|
| 117 |
- defer os.RemoveAll(ts.Path) |
|
| 118 |
- return nil |
|
| 119 |
-} |
| 120 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,136 +0,0 @@ |
| 1 |
-package zk |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "io" |
|
| 6 |
- "os" |
|
| 7 |
- "os/exec" |
|
| 8 |
- "path/filepath" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-type ErrMissingServerConfigField string |
|
| 12 |
- |
|
| 13 |
-func (e ErrMissingServerConfigField) Error() string {
|
|
| 14 |
- return fmt.Sprintf("zk: missing server config field '%s'", string(e))
|
|
| 15 |
-} |
|
| 16 |
- |
|
| 17 |
-const ( |
|
| 18 |
- DefaultServerTickTime = 2000 |
|
| 19 |
- DefaultServerInitLimit = 10 |
|
| 20 |
- DefaultServerSyncLimit = 5 |
|
| 21 |
- DefaultServerAutoPurgeSnapRetainCount = 3 |
|
| 22 |
- DefaultPeerPort = 2888 |
|
| 23 |
- DefaultLeaderElectionPort = 3888 |
|
| 24 |
-) |
|
| 25 |
- |
|
| 26 |
-type ServerConfigServer struct {
|
|
| 27 |
- ID int |
|
| 28 |
- Host string |
|
| 29 |
- PeerPort int |
|
| 30 |
- LeaderElectionPort int |
|
| 31 |
-} |
|
| 32 |
- |
|
| 33 |
-type ServerConfig struct {
|
|
| 34 |
- TickTime int // Number of milliseconds of each tick |
|
| 35 |
- InitLimit int // Number of ticks that the initial synchronization phase can take |
|
| 36 |
- SyncLimit int // Number of ticks that can pass between sending a request and getting an acknowledgement |
|
| 37 |
- DataDir string // Direcrory where the snapshot is stored |
|
| 38 |
- ClientPort int // Port at which clients will connect |
|
| 39 |
- AutoPurgeSnapRetainCount int // Number of snapshots to retain in dataDir |
|
| 40 |
- AutoPurgePurgeInterval int // Purge task internal in hours (0 to disable auto purge) |
|
| 41 |
- Servers []ServerConfigServer |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-func (sc ServerConfig) Marshall(w io.Writer) error {
|
|
| 45 |
- if sc.DataDir == "" {
|
|
| 46 |
- return ErrMissingServerConfigField("dataDir")
|
|
| 47 |
- } |
|
| 48 |
- fmt.Fprintf(w, "dataDir=%s\n", sc.DataDir) |
|
| 49 |
- if sc.TickTime <= 0 {
|
|
| 50 |
- sc.TickTime = DefaultServerTickTime |
|
| 51 |
- } |
|
| 52 |
- fmt.Fprintf(w, "tickTime=%d\n", sc.TickTime) |
|
| 53 |
- if sc.InitLimit <= 0 {
|
|
| 54 |
- sc.InitLimit = DefaultServerInitLimit |
|
| 55 |
- } |
|
| 56 |
- fmt.Fprintf(w, "initLimit=%d\n", sc.InitLimit) |
|
| 57 |
- if sc.SyncLimit <= 0 {
|
|
| 58 |
- sc.SyncLimit = DefaultServerSyncLimit |
|
| 59 |
- } |
|
| 60 |
- fmt.Fprintf(w, "syncLimit=%d\n", sc.SyncLimit) |
|
| 61 |
- if sc.ClientPort <= 0 {
|
|
| 62 |
- sc.ClientPort = DefaultPort |
|
| 63 |
- } |
|
| 64 |
- fmt.Fprintf(w, "clientPort=%d\n", sc.ClientPort) |
|
| 65 |
- if sc.AutoPurgePurgeInterval > 0 {
|
|
| 66 |
- if sc.AutoPurgeSnapRetainCount <= 0 {
|
|
| 67 |
- sc.AutoPurgeSnapRetainCount = DefaultServerAutoPurgeSnapRetainCount |
|
| 68 |
- } |
|
| 69 |
- fmt.Fprintf(w, "autopurge.snapRetainCount=%d\n", sc.AutoPurgeSnapRetainCount) |
|
| 70 |
- fmt.Fprintf(w, "autopurge.purgeInterval=%d\n", sc.AutoPurgePurgeInterval) |
|
| 71 |
- } |
|
| 72 |
- if len(sc.Servers) > 0 {
|
|
| 73 |
- for _, srv := range sc.Servers {
|
|
| 74 |
- if srv.PeerPort <= 0 {
|
|
| 75 |
- srv.PeerPort = DefaultPeerPort |
|
| 76 |
- } |
|
| 77 |
- if srv.LeaderElectionPort <= 0 {
|
|
| 78 |
- srv.LeaderElectionPort = DefaultLeaderElectionPort |
|
| 79 |
- } |
|
| 80 |
- fmt.Fprintf(w, "server.%d=%s:%d:%d\n", srv.ID, srv.Host, srv.PeerPort, srv.LeaderElectionPort) |
|
| 81 |
- } |
|
| 82 |
- } |
|
| 83 |
- return nil |
|
| 84 |
-} |
|
| 85 |
- |
|
| 86 |
-var jarSearchPaths = []string{
|
|
| 87 |
- "zookeeper-*/contrib/fatjar/zookeeper-*-fatjar.jar", |
|
| 88 |
- "../zookeeper-*/contrib/fatjar/zookeeper-*-fatjar.jar", |
|
| 89 |
- "/usr/share/java/zookeeper-*.jar", |
|
| 90 |
- "/usr/local/zookeeper-*/contrib/fatjar/zookeeper-*-fatjar.jar", |
|
| 91 |
- "/usr/local/Cellar/zookeeper/*/libexec/contrib/fatjar/zookeeper-*-fatjar.jar", |
|
| 92 |
-} |
|
| 93 |
- |
|
| 94 |
-func findZookeeperFatJar() string {
|
|
| 95 |
- var paths []string |
|
| 96 |
- zkPath := os.Getenv("ZOOKEEPER_PATH")
|
|
| 97 |
- if zkPath == "" {
|
|
| 98 |
- paths = jarSearchPaths |
|
| 99 |
- } else {
|
|
| 100 |
- paths = []string{filepath.Join(zkPath, "contrib/fatjar/zookeeper-*-fatjar.jar")}
|
|
| 101 |
- } |
|
| 102 |
- for _, path := range paths {
|
|
| 103 |
- matches, _ := filepath.Glob(path) |
|
| 104 |
- // TODO: could sort by version and pick latest |
|
| 105 |
- if len(matches) > 0 {
|
|
| 106 |
- return matches[0] |
|
| 107 |
- } |
|
| 108 |
- } |
|
| 109 |
- return "" |
|
| 110 |
-} |
|
| 111 |
- |
|
| 112 |
-type Server struct {
|
|
| 113 |
- JarPath string |
|
| 114 |
- ConfigPath string |
|
| 115 |
- Stdout, Stderr io.Writer |
|
| 116 |
- |
|
| 117 |
- cmd *exec.Cmd |
|
| 118 |
-} |
|
| 119 |
- |
|
| 120 |
-func (srv *Server) Start() error {
|
|
| 121 |
- if srv.JarPath == "" {
|
|
| 122 |
- srv.JarPath = findZookeeperFatJar() |
|
| 123 |
- if srv.JarPath == "" {
|
|
| 124 |
- return fmt.Errorf("zk: unable to find server jar")
|
|
| 125 |
- } |
|
| 126 |
- } |
|
| 127 |
- srv.cmd = exec.Command("java", "-jar", srv.JarPath, "server", srv.ConfigPath)
|
|
| 128 |
- srv.cmd.Stdout = srv.Stdout |
|
| 129 |
- srv.cmd.Stderr = srv.Stderr |
|
| 130 |
- return srv.cmd.Start() |
|
| 131 |
-} |
|
| 132 |
- |
|
| 133 |
-func (srv *Server) Stop() error {
|
|
| 134 |
- srv.cmd.Process.Signal(os.Kill) |
|
| 135 |
- return srv.cmd.Wait() |
|
| 136 |
-} |
| 137 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,633 +0,0 @@ |
| 1 |
-package zk |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/binary" |
|
| 5 |
- "errors" |
|
| 6 |
- "reflect" |
|
| 7 |
- "runtime" |
|
| 8 |
- "time" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-var ( |
|
| 12 |
- ErrUnhandledFieldType = errors.New("zk: unhandled field type")
|
|
| 13 |
- ErrPtrExpected = errors.New("zk: encode/decode expect a non-nil pointer to struct")
|
|
| 14 |
- ErrShortBuffer = errors.New("zk: buffer too small")
|
|
| 15 |
-) |
|
| 16 |
- |
|
| 17 |
-type ACL struct {
|
|
| 18 |
- Perms int32 |
|
| 19 |
- Scheme string |
|
| 20 |
- ID string |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-type Stat struct {
|
|
| 24 |
- Czxid int64 // The zxid of the change that caused this znode to be created. |
|
| 25 |
- Mzxid int64 // The zxid of the change that last modified this znode. |
|
| 26 |
- Ctime int64 // The time in milliseconds from epoch when this znode was created. |
|
| 27 |
- Mtime int64 // The time in milliseconds from epoch when this znode was last modified. |
|
| 28 |
- Version int32 // The number of changes to the data of this znode. |
|
| 29 |
- Cversion int32 // The number of changes to the children of this znode. |
|
| 30 |
- Aversion int32 // The number of changes to the ACL of this znode. |
|
| 31 |
- EphemeralOwner int64 // The session id of the owner of this znode if the znode is an ephemeral node. If it is not an ephemeral node, it will be zero. |
|
| 32 |
- DataLength int32 // The length of the data field of this znode. |
|
| 33 |
- NumChildren int32 // The number of children of this znode. |
|
| 34 |
- Pzxid int64 // last modified children |
|
| 35 |
-} |
|
| 36 |
- |
|
| 37 |
-// ServerClient is the information for a single Zookeeper client and its session. |
|
| 38 |
-// This is used to parse/extract the output fo the `cons` command. |
|
| 39 |
-type ServerClient struct {
|
|
| 40 |
- Queued int64 |
|
| 41 |
- Received int64 |
|
| 42 |
- Sent int64 |
|
| 43 |
- SessionID int64 |
|
| 44 |
- Lcxid int64 |
|
| 45 |
- Lzxid int64 |
|
| 46 |
- Timeout int32 |
|
| 47 |
- LastLatency int32 |
|
| 48 |
- MinLatency int32 |
|
| 49 |
- AvgLatency int32 |
|
| 50 |
- MaxLatency int32 |
|
| 51 |
- Established time.Time |
|
| 52 |
- LastResponse time.Time |
|
| 53 |
- Addr string |
|
| 54 |
- LastOperation string // maybe? |
|
| 55 |
- Error error |
|
| 56 |
-} |
|
| 57 |
- |
|
| 58 |
-// ServerClients is a struct for the FLWCons() function. It's used to provide |
|
| 59 |
-// the list of Clients. |
|
| 60 |
-// |
|
| 61 |
-// This is needed because FLWCons() takes multiple servers. |
|
| 62 |
-type ServerClients struct {
|
|
| 63 |
- Clients []*ServerClient |
|
| 64 |
- Error error |
|
| 65 |
-} |
|
| 66 |
- |
|
| 67 |
-// ServerStats is the information pulled from the Zookeeper `stat` command. |
|
| 68 |
-type ServerStats struct {
|
|
| 69 |
- Sent int64 |
|
| 70 |
- Received int64 |
|
| 71 |
- NodeCount int64 |
|
| 72 |
- MinLatency int64 |
|
| 73 |
- AvgLatency int64 |
|
| 74 |
- MaxLatency int64 |
|
| 75 |
- Connections int64 |
|
| 76 |
- Outstanding int64 |
|
| 77 |
- Epoch int32 |
|
| 78 |
- Counter int32 |
|
| 79 |
- BuildTime time.Time |
|
| 80 |
- Mode Mode |
|
| 81 |
- Version string |
|
| 82 |
- Error error |
|
| 83 |
-} |
|
| 84 |
- |
|
| 85 |
-type requestHeader struct {
|
|
| 86 |
- Xid int32 |
|
| 87 |
- Opcode int32 |
|
| 88 |
-} |
|
| 89 |
- |
|
| 90 |
-type responseHeader struct {
|
|
| 91 |
- Xid int32 |
|
| 92 |
- Zxid int64 |
|
| 93 |
- Err ErrCode |
|
| 94 |
-} |
|
| 95 |
- |
|
| 96 |
-type multiHeader struct {
|
|
| 97 |
- Type int32 |
|
| 98 |
- Done bool |
|
| 99 |
- Err ErrCode |
|
| 100 |
-} |
|
| 101 |
- |
|
| 102 |
-type auth struct {
|
|
| 103 |
- Type int32 |
|
| 104 |
- Scheme string |
|
| 105 |
- Auth []byte |
|
| 106 |
-} |
|
| 107 |
- |
|
| 108 |
-// Generic request structs |
|
| 109 |
- |
|
| 110 |
-type pathRequest struct {
|
|
| 111 |
- Path string |
|
| 112 |
-} |
|
| 113 |
- |
|
| 114 |
-type PathVersionRequest struct {
|
|
| 115 |
- Path string |
|
| 116 |
- Version int32 |
|
| 117 |
-} |
|
| 118 |
- |
|
| 119 |
-type pathWatchRequest struct {
|
|
| 120 |
- Path string |
|
| 121 |
- Watch bool |
|
| 122 |
-} |
|
| 123 |
- |
|
| 124 |
-type pathResponse struct {
|
|
| 125 |
- Path string |
|
| 126 |
-} |
|
| 127 |
- |
|
| 128 |
-type statResponse struct {
|
|
| 129 |
- Stat Stat |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-// |
|
| 133 |
- |
|
| 134 |
-type CheckVersionRequest PathVersionRequest |
|
| 135 |
-type closeRequest struct{}
|
|
| 136 |
-type closeResponse struct{}
|
|
| 137 |
- |
|
| 138 |
-type connectRequest struct {
|
|
| 139 |
- ProtocolVersion int32 |
|
| 140 |
- LastZxidSeen int64 |
|
| 141 |
- TimeOut int32 |
|
| 142 |
- SessionID int64 |
|
| 143 |
- Passwd []byte |
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-type connectResponse struct {
|
|
| 147 |
- ProtocolVersion int32 |
|
| 148 |
- TimeOut int32 |
|
| 149 |
- SessionID int64 |
|
| 150 |
- Passwd []byte |
|
| 151 |
-} |
|
| 152 |
- |
|
| 153 |
-type CreateRequest struct {
|
|
| 154 |
- Path string |
|
| 155 |
- Data []byte |
|
| 156 |
- Acl []ACL |
|
| 157 |
- Flags int32 |
|
| 158 |
-} |
|
| 159 |
- |
|
| 160 |
-type createResponse pathResponse |
|
| 161 |
-type DeleteRequest PathVersionRequest |
|
| 162 |
-type deleteResponse struct{}
|
|
| 163 |
- |
|
| 164 |
-type errorResponse struct {
|
|
| 165 |
- Err int32 |
|
| 166 |
-} |
|
| 167 |
- |
|
| 168 |
-type existsRequest pathWatchRequest |
|
| 169 |
-type existsResponse statResponse |
|
| 170 |
-type getAclRequest pathRequest |
|
| 171 |
- |
|
| 172 |
-type getAclResponse struct {
|
|
| 173 |
- Acl []ACL |
|
| 174 |
- Stat Stat |
|
| 175 |
-} |
|
| 176 |
- |
|
| 177 |
-type getChildrenRequest pathRequest |
|
| 178 |
- |
|
| 179 |
-type getChildrenResponse struct {
|
|
| 180 |
- Children []string |
|
| 181 |
-} |
|
| 182 |
- |
|
| 183 |
-type getChildren2Request pathWatchRequest |
|
| 184 |
- |
|
| 185 |
-type getChildren2Response struct {
|
|
| 186 |
- Children []string |
|
| 187 |
- Stat Stat |
|
| 188 |
-} |
|
| 189 |
- |
|
| 190 |
-type getDataRequest pathWatchRequest |
|
| 191 |
- |
|
| 192 |
-type getDataResponse struct {
|
|
| 193 |
- Data []byte |
|
| 194 |
- Stat Stat |
|
| 195 |
-} |
|
| 196 |
- |
|
| 197 |
-type getMaxChildrenRequest pathRequest |
|
| 198 |
- |
|
| 199 |
-type getMaxChildrenResponse struct {
|
|
| 200 |
- Max int32 |
|
| 201 |
-} |
|
| 202 |
- |
|
| 203 |
-type getSaslRequest struct {
|
|
| 204 |
- Token []byte |
|
| 205 |
-} |
|
| 206 |
- |
|
| 207 |
-type pingRequest struct{}
|
|
| 208 |
-type pingResponse struct{}
|
|
| 209 |
- |
|
| 210 |
-type setAclRequest struct {
|
|
| 211 |
- Path string |
|
| 212 |
- Acl []ACL |
|
| 213 |
- Version int32 |
|
| 214 |
-} |
|
| 215 |
- |
|
| 216 |
-type setAclResponse statResponse |
|
| 217 |
- |
|
| 218 |
-type SetDataRequest struct {
|
|
| 219 |
- Path string |
|
| 220 |
- Data []byte |
|
| 221 |
- Version int32 |
|
| 222 |
-} |
|
| 223 |
- |
|
| 224 |
-type setDataResponse statResponse |
|
| 225 |
- |
|
| 226 |
-type setMaxChildren struct {
|
|
| 227 |
- Path string |
|
| 228 |
- Max int32 |
|
| 229 |
-} |
|
| 230 |
- |
|
| 231 |
-type setSaslRequest struct {
|
|
| 232 |
- Token string |
|
| 233 |
-} |
|
| 234 |
- |
|
| 235 |
-type setSaslResponse struct {
|
|
| 236 |
- Token string |
|
| 237 |
-} |
|
| 238 |
- |
|
| 239 |
-type setWatchesRequest struct {
|
|
| 240 |
- RelativeZxid int64 |
|
| 241 |
- DataWatches []string |
|
| 242 |
- ExistWatches []string |
|
| 243 |
- ChildWatches []string |
|
| 244 |
-} |
|
| 245 |
- |
|
| 246 |
-type setWatchesResponse struct{}
|
|
| 247 |
- |
|
| 248 |
-type syncRequest pathRequest |
|
| 249 |
-type syncResponse pathResponse |
|
| 250 |
- |
|
| 251 |
-type setAuthRequest auth |
|
| 252 |
-type setAuthResponse struct{}
|
|
| 253 |
- |
|
| 254 |
-type multiRequestOp struct {
|
|
| 255 |
- Header multiHeader |
|
| 256 |
- Op interface{}
|
|
| 257 |
-} |
|
| 258 |
-type multiRequest struct {
|
|
| 259 |
- Ops []multiRequestOp |
|
| 260 |
- DoneHeader multiHeader |
|
| 261 |
-} |
|
| 262 |
-type multiResponseOp struct {
|
|
| 263 |
- Header multiHeader |
|
| 264 |
- String string |
|
| 265 |
- Stat *Stat |
|
| 266 |
-} |
|
| 267 |
-type multiResponse struct {
|
|
| 268 |
- Ops []multiResponseOp |
|
| 269 |
- DoneHeader multiHeader |
|
| 270 |
-} |
|
| 271 |
- |
|
| 272 |
-func (r *multiRequest) Encode(buf []byte) (int, error) {
|
|
| 273 |
- total := 0 |
|
| 274 |
- for _, op := range r.Ops {
|
|
| 275 |
- op.Header.Done = false |
|
| 276 |
- n, err := encodePacketValue(buf[total:], reflect.ValueOf(op)) |
|
| 277 |
- if err != nil {
|
|
| 278 |
- return total, err |
|
| 279 |
- } |
|
| 280 |
- total += n |
|
| 281 |
- } |
|
| 282 |
- r.DoneHeader.Done = true |
|
| 283 |
- n, err := encodePacketValue(buf[total:], reflect.ValueOf(r.DoneHeader)) |
|
| 284 |
- if err != nil {
|
|
| 285 |
- return total, err |
|
| 286 |
- } |
|
| 287 |
- total += n |
|
| 288 |
- |
|
| 289 |
- return total, nil |
|
| 290 |
-} |
|
| 291 |
- |
|
| 292 |
-func (r *multiRequest) Decode(buf []byte) (int, error) {
|
|
| 293 |
- r.Ops = make([]multiRequestOp, 0) |
|
| 294 |
- r.DoneHeader = multiHeader{-1, true, -1}
|
|
| 295 |
- total := 0 |
|
| 296 |
- for {
|
|
| 297 |
- header := &multiHeader{}
|
|
| 298 |
- n, err := decodePacketValue(buf[total:], reflect.ValueOf(header)) |
|
| 299 |
- if err != nil {
|
|
| 300 |
- return total, err |
|
| 301 |
- } |
|
| 302 |
- total += n |
|
| 303 |
- if header.Done {
|
|
| 304 |
- r.DoneHeader = *header |
|
| 305 |
- break |
|
| 306 |
- } |
|
| 307 |
- |
|
| 308 |
- req := requestStructForOp(header.Type) |
|
| 309 |
- if req == nil {
|
|
| 310 |
- return total, ErrAPIError |
|
| 311 |
- } |
|
| 312 |
- n, err = decodePacketValue(buf[total:], reflect.ValueOf(req)) |
|
| 313 |
- if err != nil {
|
|
| 314 |
- return total, err |
|
| 315 |
- } |
|
| 316 |
- total += n |
|
| 317 |
- r.Ops = append(r.Ops, multiRequestOp{*header, req})
|
|
| 318 |
- } |
|
| 319 |
- return total, nil |
|
| 320 |
-} |
|
| 321 |
- |
|
| 322 |
-func (r *multiResponse) Decode(buf []byte) (int, error) {
|
|
| 323 |
- r.Ops = make([]multiResponseOp, 0) |
|
| 324 |
- r.DoneHeader = multiHeader{-1, true, -1}
|
|
| 325 |
- total := 0 |
|
| 326 |
- for {
|
|
| 327 |
- header := &multiHeader{}
|
|
| 328 |
- n, err := decodePacketValue(buf[total:], reflect.ValueOf(header)) |
|
| 329 |
- if err != nil {
|
|
| 330 |
- return total, err |
|
| 331 |
- } |
|
| 332 |
- total += n |
|
| 333 |
- if header.Done {
|
|
| 334 |
- r.DoneHeader = *header |
|
| 335 |
- break |
|
| 336 |
- } |
|
| 337 |
- |
|
| 338 |
- res := multiResponseOp{Header: *header}
|
|
| 339 |
- var w reflect.Value |
|
| 340 |
- switch header.Type {
|
|
| 341 |
- default: |
|
| 342 |
- return total, ErrAPIError |
|
| 343 |
- case opCreate: |
|
| 344 |
- w = reflect.ValueOf(&res.String) |
|
| 345 |
- case opSetData: |
|
| 346 |
- res.Stat = new(Stat) |
|
| 347 |
- w = reflect.ValueOf(res.Stat) |
|
| 348 |
- case opCheck, opDelete: |
|
| 349 |
- } |
|
| 350 |
- if w.IsValid() {
|
|
| 351 |
- n, err := decodePacketValue(buf[total:], w) |
|
| 352 |
- if err != nil {
|
|
| 353 |
- return total, err |
|
| 354 |
- } |
|
| 355 |
- total += n |
|
| 356 |
- } |
|
| 357 |
- r.Ops = append(r.Ops, res) |
|
| 358 |
- } |
|
| 359 |
- return total, nil |
|
| 360 |
-} |
|
| 361 |
- |
|
| 362 |
-type watcherEvent struct {
|
|
| 363 |
- Type EventType |
|
| 364 |
- State State |
|
| 365 |
- Path string |
|
| 366 |
-} |
|
| 367 |
- |
|
| 368 |
-type decoder interface {
|
|
| 369 |
- Decode(buf []byte) (int, error) |
|
| 370 |
-} |
|
| 371 |
- |
|
| 372 |
-type encoder interface {
|
|
| 373 |
- Encode(buf []byte) (int, error) |
|
| 374 |
-} |
|
| 375 |
- |
|
| 376 |
-func decodePacket(buf []byte, st interface{}) (n int, err error) {
|
|
| 377 |
- defer func() {
|
|
| 378 |
- if r := recover(); r != nil {
|
|
| 379 |
- if e, ok := r.(runtime.Error); ok && e.Error() == "runtime error: slice bounds out of range" {
|
|
| 380 |
- err = ErrShortBuffer |
|
| 381 |
- } else {
|
|
| 382 |
- panic(r) |
|
| 383 |
- } |
|
| 384 |
- } |
|
| 385 |
- }() |
|
| 386 |
- |
|
| 387 |
- v := reflect.ValueOf(st) |
|
| 388 |
- if v.Kind() != reflect.Ptr || v.IsNil() {
|
|
| 389 |
- return 0, ErrPtrExpected |
|
| 390 |
- } |
|
| 391 |
- return decodePacketValue(buf, v) |
|
| 392 |
-} |
|
| 393 |
- |
|
| 394 |
-func decodePacketValue(buf []byte, v reflect.Value) (int, error) {
|
|
| 395 |
- rv := v |
|
| 396 |
- kind := v.Kind() |
|
| 397 |
- if kind == reflect.Ptr {
|
|
| 398 |
- if v.IsNil() {
|
|
| 399 |
- v.Set(reflect.New(v.Type().Elem())) |
|
| 400 |
- } |
|
| 401 |
- v = v.Elem() |
|
| 402 |
- kind = v.Kind() |
|
| 403 |
- } |
|
| 404 |
- |
|
| 405 |
- n := 0 |
|
| 406 |
- switch kind {
|
|
| 407 |
- default: |
|
| 408 |
- return n, ErrUnhandledFieldType |
|
| 409 |
- case reflect.Struct: |
|
| 410 |
- if de, ok := rv.Interface().(decoder); ok {
|
|
| 411 |
- return de.Decode(buf) |
|
| 412 |
- } else if de, ok := v.Interface().(decoder); ok {
|
|
| 413 |
- return de.Decode(buf) |
|
| 414 |
- } else {
|
|
| 415 |
- for i := 0; i < v.NumField(); i++ {
|
|
| 416 |
- field := v.Field(i) |
|
| 417 |
- n2, err := decodePacketValue(buf[n:], field) |
|
| 418 |
- n += n2 |
|
| 419 |
- if err != nil {
|
|
| 420 |
- return n, err |
|
| 421 |
- } |
|
| 422 |
- } |
|
| 423 |
- } |
|
| 424 |
- case reflect.Bool: |
|
| 425 |
- v.SetBool(buf[n] != 0) |
|
| 426 |
- n++ |
|
| 427 |
- case reflect.Int32: |
|
| 428 |
- v.SetInt(int64(binary.BigEndian.Uint32(buf[n : n+4]))) |
|
| 429 |
- n += 4 |
|
| 430 |
- case reflect.Int64: |
|
| 431 |
- v.SetInt(int64(binary.BigEndian.Uint64(buf[n : n+8]))) |
|
| 432 |
- n += 8 |
|
| 433 |
- case reflect.String: |
|
| 434 |
- ln := int(binary.BigEndian.Uint32(buf[n : n+4])) |
|
| 435 |
- v.SetString(string(buf[n+4 : n+4+ln])) |
|
| 436 |
- n += 4 + ln |
|
| 437 |
- case reflect.Slice: |
|
| 438 |
- switch v.Type().Elem().Kind() {
|
|
| 439 |
- default: |
|
| 440 |
- count := int(binary.BigEndian.Uint32(buf[n : n+4])) |
|
| 441 |
- n += 4 |
|
| 442 |
- values := reflect.MakeSlice(v.Type(), count, count) |
|
| 443 |
- v.Set(values) |
|
| 444 |
- for i := 0; i < count; i++ {
|
|
| 445 |
- n2, err := decodePacketValue(buf[n:], values.Index(i)) |
|
| 446 |
- n += n2 |
|
| 447 |
- if err != nil {
|
|
| 448 |
- return n, err |
|
| 449 |
- } |
|
| 450 |
- } |
|
| 451 |
- case reflect.Uint8: |
|
| 452 |
- ln := int(int32(binary.BigEndian.Uint32(buf[n : n+4]))) |
|
| 453 |
- if ln < 0 {
|
|
| 454 |
- n += 4 |
|
| 455 |
- v.SetBytes(nil) |
|
| 456 |
- } else {
|
|
| 457 |
- bytes := make([]byte, ln) |
|
| 458 |
- copy(bytes, buf[n+4:n+4+ln]) |
|
| 459 |
- v.SetBytes(bytes) |
|
| 460 |
- n += 4 + ln |
|
| 461 |
- } |
|
| 462 |
- } |
|
| 463 |
- } |
|
| 464 |
- return n, nil |
|
| 465 |
-} |
|
| 466 |
- |
|
| 467 |
-func encodePacket(buf []byte, st interface{}) (n int, err error) {
|
|
| 468 |
- defer func() {
|
|
| 469 |
- if r := recover(); r != nil {
|
|
| 470 |
- if e, ok := r.(runtime.Error); ok && e.Error() == "runtime error: slice bounds out of range" {
|
|
| 471 |
- err = ErrShortBuffer |
|
| 472 |
- } else {
|
|
| 473 |
- panic(r) |
|
| 474 |
- } |
|
| 475 |
- } |
|
| 476 |
- }() |
|
| 477 |
- |
|
| 478 |
- v := reflect.ValueOf(st) |
|
| 479 |
- if v.Kind() != reflect.Ptr || v.IsNil() {
|
|
| 480 |
- return 0, ErrPtrExpected |
|
| 481 |
- } |
|
| 482 |
- return encodePacketValue(buf, v) |
|
| 483 |
-} |
|
| 484 |
- |
|
| 485 |
-func encodePacketValue(buf []byte, v reflect.Value) (int, error) {
|
|
| 486 |
- rv := v |
|
| 487 |
- for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
|
|
| 488 |
- v = v.Elem() |
|
| 489 |
- } |
|
| 490 |
- |
|
| 491 |
- n := 0 |
|
| 492 |
- switch v.Kind() {
|
|
| 493 |
- default: |
|
| 494 |
- return n, ErrUnhandledFieldType |
|
| 495 |
- case reflect.Struct: |
|
| 496 |
- if en, ok := rv.Interface().(encoder); ok {
|
|
| 497 |
- return en.Encode(buf) |
|
| 498 |
- } else if en, ok := v.Interface().(encoder); ok {
|
|
| 499 |
- return en.Encode(buf) |
|
| 500 |
- } else {
|
|
| 501 |
- for i := 0; i < v.NumField(); i++ {
|
|
| 502 |
- field := v.Field(i) |
|
| 503 |
- n2, err := encodePacketValue(buf[n:], field) |
|
| 504 |
- n += n2 |
|
| 505 |
- if err != nil {
|
|
| 506 |
- return n, err |
|
| 507 |
- } |
|
| 508 |
- } |
|
| 509 |
- } |
|
| 510 |
- case reflect.Bool: |
|
| 511 |
- if v.Bool() {
|
|
| 512 |
- buf[n] = 1 |
|
| 513 |
- } else {
|
|
| 514 |
- buf[n] = 0 |
|
| 515 |
- } |
|
| 516 |
- n++ |
|
| 517 |
- case reflect.Int32: |
|
| 518 |
- binary.BigEndian.PutUint32(buf[n:n+4], uint32(v.Int())) |
|
| 519 |
- n += 4 |
|
| 520 |
- case reflect.Int64: |
|
| 521 |
- binary.BigEndian.PutUint64(buf[n:n+8], uint64(v.Int())) |
|
| 522 |
- n += 8 |
|
| 523 |
- case reflect.String: |
|
| 524 |
- str := v.String() |
|
| 525 |
- binary.BigEndian.PutUint32(buf[n:n+4], uint32(len(str))) |
|
| 526 |
- copy(buf[n+4:n+4+len(str)], []byte(str)) |
|
| 527 |
- n += 4 + len(str) |
|
| 528 |
- case reflect.Slice: |
|
| 529 |
- switch v.Type().Elem().Kind() {
|
|
| 530 |
- default: |
|
| 531 |
- count := v.Len() |
|
| 532 |
- startN := n |
|
| 533 |
- n += 4 |
|
| 534 |
- for i := 0; i < count; i++ {
|
|
| 535 |
- n2, err := encodePacketValue(buf[n:], v.Index(i)) |
|
| 536 |
- n += n2 |
|
| 537 |
- if err != nil {
|
|
| 538 |
- return n, err |
|
| 539 |
- } |
|
| 540 |
- } |
|
| 541 |
- binary.BigEndian.PutUint32(buf[startN:startN+4], uint32(count)) |
|
| 542 |
- case reflect.Uint8: |
|
| 543 |
- if v.IsNil() {
|
|
| 544 |
- binary.BigEndian.PutUint32(buf[n:n+4], uint32(0xffffffff)) |
|
| 545 |
- n += 4 |
|
| 546 |
- } else {
|
|
| 547 |
- bytes := v.Bytes() |
|
| 548 |
- binary.BigEndian.PutUint32(buf[n:n+4], uint32(len(bytes))) |
|
| 549 |
- copy(buf[n+4:n+4+len(bytes)], bytes) |
|
| 550 |
- n += 4 + len(bytes) |
|
| 551 |
- } |
|
| 552 |
- } |
|
| 553 |
- } |
|
| 554 |
- return n, nil |
|
| 555 |
-} |
|
| 556 |
- |
|
| 557 |
-func requestStructForOp(op int32) interface{} {
|
|
| 558 |
- switch op {
|
|
| 559 |
- case opClose: |
|
| 560 |
- return &closeRequest{}
|
|
| 561 |
- case opCreate: |
|
| 562 |
- return &CreateRequest{}
|
|
| 563 |
- case opDelete: |
|
| 564 |
- return &DeleteRequest{}
|
|
| 565 |
- case opExists: |
|
| 566 |
- return &existsRequest{}
|
|
| 567 |
- case opGetAcl: |
|
| 568 |
- return &getAclRequest{}
|
|
| 569 |
- case opGetChildren: |
|
| 570 |
- return &getChildrenRequest{}
|
|
| 571 |
- case opGetChildren2: |
|
| 572 |
- return &getChildren2Request{}
|
|
| 573 |
- case opGetData: |
|
| 574 |
- return &getDataRequest{}
|
|
| 575 |
- case opPing: |
|
| 576 |
- return &pingRequest{}
|
|
| 577 |
- case opSetAcl: |
|
| 578 |
- return &setAclRequest{}
|
|
| 579 |
- case opSetData: |
|
| 580 |
- return &SetDataRequest{}
|
|
| 581 |
- case opSetWatches: |
|
| 582 |
- return &setWatchesRequest{}
|
|
| 583 |
- case opSync: |
|
| 584 |
- return &syncRequest{}
|
|
| 585 |
- case opSetAuth: |
|
| 586 |
- return &setAuthRequest{}
|
|
| 587 |
- case opCheck: |
|
| 588 |
- return &CheckVersionRequest{}
|
|
| 589 |
- case opMulti: |
|
| 590 |
- return &multiRequest{}
|
|
| 591 |
- } |
|
| 592 |
- return nil |
|
| 593 |
-} |
|
| 594 |
- |
|
| 595 |
-func responseStructForOp(op int32) interface{} {
|
|
| 596 |
- switch op {
|
|
| 597 |
- case opClose: |
|
| 598 |
- return &closeResponse{}
|
|
| 599 |
- case opCreate: |
|
| 600 |
- return &createResponse{}
|
|
| 601 |
- case opDelete: |
|
| 602 |
- return &deleteResponse{}
|
|
| 603 |
- case opExists: |
|
| 604 |
- return &existsResponse{}
|
|
| 605 |
- case opGetAcl: |
|
| 606 |
- return &getAclResponse{}
|
|
| 607 |
- case opGetChildren: |
|
| 608 |
- return &getChildrenResponse{}
|
|
| 609 |
- case opGetChildren2: |
|
| 610 |
- return &getChildren2Response{}
|
|
| 611 |
- case opGetData: |
|
| 612 |
- return &getDataResponse{}
|
|
| 613 |
- case opPing: |
|
| 614 |
- return &pingResponse{}
|
|
| 615 |
- case opSetAcl: |
|
| 616 |
- return &setAclResponse{}
|
|
| 617 |
- case opSetData: |
|
| 618 |
- return &setDataResponse{}
|
|
| 619 |
- case opSetWatches: |
|
| 620 |
- return &setWatchesResponse{}
|
|
| 621 |
- case opSync: |
|
| 622 |
- return &syncResponse{}
|
|
| 623 |
- case opWatcherEvent: |
|
| 624 |
- return &watcherEvent{}
|
|
| 625 |
- case opSetAuth: |
|
| 626 |
- return &setAuthResponse{}
|
|
| 627 |
- // case opCheck: |
|
| 628 |
- // return &checkVersionResponse{}
|
|
| 629 |
- case opMulti: |
|
| 630 |
- return &multiResponse{}
|
|
| 631 |
- } |
|
| 632 |
- return nil |
|
| 633 |
-} |
| 634 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,148 +0,0 @@ |
| 1 |
-package zk |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/binary" |
|
| 5 |
- "fmt" |
|
| 6 |
- "io" |
|
| 7 |
- "net" |
|
| 8 |
- "sync" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-var ( |
|
| 12 |
- requests = make(map[int32]int32) // Map of Xid -> Opcode |
|
| 13 |
- requestsLock = &sync.Mutex{}
|
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-func trace(conn1, conn2 net.Conn, client bool) {
|
|
| 17 |
- defer conn1.Close() |
|
| 18 |
- defer conn2.Close() |
|
| 19 |
- buf := make([]byte, 10*1024) |
|
| 20 |
- init := true |
|
| 21 |
- for {
|
|
| 22 |
- _, err := io.ReadFull(conn1, buf[:4]) |
|
| 23 |
- if err != nil {
|
|
| 24 |
- fmt.Println("1>", client, err)
|
|
| 25 |
- return |
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- blen := int(binary.BigEndian.Uint32(buf[:4])) |
|
| 29 |
- |
|
| 30 |
- _, err = io.ReadFull(conn1, buf[4:4+blen]) |
|
| 31 |
- if err != nil {
|
|
| 32 |
- fmt.Println("2>", client, err)
|
|
| 33 |
- return |
|
| 34 |
- } |
|
| 35 |
- |
|
| 36 |
- var cr interface{}
|
|
| 37 |
- opcode := int32(-1) |
|
| 38 |
- readHeader := true |
|
| 39 |
- if client {
|
|
| 40 |
- if init {
|
|
| 41 |
- cr = &connectRequest{}
|
|
| 42 |
- readHeader = false |
|
| 43 |
- } else {
|
|
| 44 |
- xid := int32(binary.BigEndian.Uint32(buf[4:8])) |
|
| 45 |
- opcode = int32(binary.BigEndian.Uint32(buf[8:12])) |
|
| 46 |
- requestsLock.Lock() |
|
| 47 |
- requests[xid] = opcode |
|
| 48 |
- requestsLock.Unlock() |
|
| 49 |
- cr = requestStructForOp(opcode) |
|
| 50 |
- if cr == nil {
|
|
| 51 |
- fmt.Printf("Unknown opcode %d\n", opcode)
|
|
| 52 |
- } |
|
| 53 |
- } |
|
| 54 |
- } else {
|
|
| 55 |
- if init {
|
|
| 56 |
- cr = &connectResponse{}
|
|
| 57 |
- readHeader = false |
|
| 58 |
- } else {
|
|
| 59 |
- xid := int32(binary.BigEndian.Uint32(buf[4:8])) |
|
| 60 |
- zxid := int64(binary.BigEndian.Uint64(buf[8:16])) |
|
| 61 |
- errnum := int32(binary.BigEndian.Uint32(buf[16:20])) |
|
| 62 |
- if xid != -1 || zxid != -1 {
|
|
| 63 |
- requestsLock.Lock() |
|
| 64 |
- found := false |
|
| 65 |
- opcode, found = requests[xid] |
|
| 66 |
- if !found {
|
|
| 67 |
- opcode = 0 |
|
| 68 |
- } |
|
| 69 |
- delete(requests, xid) |
|
| 70 |
- requestsLock.Unlock() |
|
| 71 |
- } else {
|
|
| 72 |
- opcode = opWatcherEvent |
|
| 73 |
- } |
|
| 74 |
- cr = responseStructForOp(opcode) |
|
| 75 |
- if cr == nil {
|
|
| 76 |
- fmt.Printf("Unknown opcode %d\n", opcode)
|
|
| 77 |
- } |
|
| 78 |
- if errnum != 0 {
|
|
| 79 |
- cr = &struct{}{}
|
|
| 80 |
- } |
|
| 81 |
- } |
|
| 82 |
- } |
|
| 83 |
- opname := "." |
|
| 84 |
- if opcode != -1 {
|
|
| 85 |
- opname = opNames[opcode] |
|
| 86 |
- } |
|
| 87 |
- if cr == nil {
|
|
| 88 |
- fmt.Printf("%+v %s %+v\n", client, opname, buf[4:4+blen])
|
|
| 89 |
- } else {
|
|
| 90 |
- n := 4 |
|
| 91 |
- hdrStr := "" |
|
| 92 |
- if readHeader {
|
|
| 93 |
- var hdr interface{}
|
|
| 94 |
- if client {
|
|
| 95 |
- hdr = &requestHeader{}
|
|
| 96 |
- } else {
|
|
| 97 |
- hdr = &responseHeader{}
|
|
| 98 |
- } |
|
| 99 |
- if n2, err := decodePacket(buf[n:n+blen], hdr); err != nil {
|
|
| 100 |
- fmt.Println(err) |
|
| 101 |
- } else {
|
|
| 102 |
- n += n2 |
|
| 103 |
- } |
|
| 104 |
- hdrStr = fmt.Sprintf(" %+v", hdr)
|
|
| 105 |
- } |
|
| 106 |
- if _, err := decodePacket(buf[n:n+blen], cr); err != nil {
|
|
| 107 |
- fmt.Println(err) |
|
| 108 |
- } |
|
| 109 |
- fmt.Printf("%+v %s%s %+v\n", client, opname, hdrStr, cr)
|
|
| 110 |
- } |
|
| 111 |
- |
|
| 112 |
- init = false |
|
| 113 |
- |
|
| 114 |
- written, err := conn2.Write(buf[:4+blen]) |
|
| 115 |
- if err != nil {
|
|
| 116 |
- fmt.Println("3>", client, err)
|
|
| 117 |
- return |
|
| 118 |
- } else if written != 4+blen {
|
|
| 119 |
- fmt.Printf("Written != read: %d != %d\n", written, blen)
|
|
| 120 |
- return |
|
| 121 |
- } |
|
| 122 |
- } |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-func handleConnection(addr string, conn net.Conn) {
|
|
| 126 |
- zkConn, err := net.Dial("tcp", addr)
|
|
| 127 |
- if err != nil {
|
|
| 128 |
- fmt.Println(err) |
|
| 129 |
- return |
|
| 130 |
- } |
|
| 131 |
- go trace(conn, zkConn, true) |
|
| 132 |
- trace(zkConn, conn, false) |
|
| 133 |
-} |
|
| 134 |
- |
|
| 135 |
-func StartTracer(listenAddr, serverAddr string) {
|
|
| 136 |
- ln, err := net.Listen("tcp", listenAddr)
|
|
| 137 |
- if err != nil {
|
|
| 138 |
- panic(err) |
|
| 139 |
- } |
|
| 140 |
- for {
|
|
| 141 |
- conn, err := ln.Accept() |
|
| 142 |
- if err != nil {
|
|
| 143 |
- fmt.Println(err) |
|
| 144 |
- continue |
|
| 145 |
- } |
|
| 146 |
- go handleConnection(serverAddr, conn) |
|
| 147 |
- } |
|
| 148 |
-} |
| 149 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,54 +0,0 @@ |
| 1 |
-package zk |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "crypto/sha1" |
|
| 5 |
- "encoding/base64" |
|
| 6 |
- "fmt" |
|
| 7 |
- "math/rand" |
|
| 8 |
- "strconv" |
|
| 9 |
- "strings" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-// AuthACL produces an ACL list containing a single ACL which uses the |
|
| 13 |
-// provided permissions, with the scheme "auth", and ID "", which is used |
|
| 14 |
-// by ZooKeeper to represent any authenticated user. |
|
| 15 |
-func AuthACL(perms int32) []ACL {
|
|
| 16 |
- return []ACL{{perms, "auth", ""}}
|
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-// WorldACL produces an ACL list containing a single ACL which uses the |
|
| 20 |
-// provided permissions, with the scheme "world", and ID "anyone", which |
|
| 21 |
-// is used by ZooKeeper to represent any user at all. |
|
| 22 |
-func WorldACL(perms int32) []ACL {
|
|
| 23 |
- return []ACL{{perms, "world", "anyone"}}
|
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-func DigestACL(perms int32, user, password string) []ACL {
|
|
| 27 |
- userPass := []byte(fmt.Sprintf("%s:%s", user, password))
|
|
| 28 |
- h := sha1.New() |
|
| 29 |
- if n, err := h.Write(userPass); err != nil || n != len(userPass) {
|
|
| 30 |
- panic("SHA1 failed")
|
|
| 31 |
- } |
|
| 32 |
- digest := base64.StdEncoding.EncodeToString(h.Sum(nil)) |
|
| 33 |
- return []ACL{{perms, "digest", fmt.Sprintf("%s:%s", user, digest)}}
|
|
| 34 |
-} |
|
| 35 |
- |
|
| 36 |
-// FormatServers takes a slice of addresses, and makes sure they are in a format |
|
| 37 |
-// that resembles <addr>:<port>. If the server has no port provided, the |
|
| 38 |
-// DefaultPort constant is added to the end. |
|
| 39 |
-func FormatServers(servers []string) []string {
|
|
| 40 |
- for i := range servers {
|
|
| 41 |
- if !strings.Contains(servers[i], ":") {
|
|
| 42 |
- servers[i] = servers[i] + ":" + strconv.Itoa(DefaultPort) |
|
| 43 |
- } |
|
| 44 |
- } |
|
| 45 |
- return servers |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-// stringShuffle performs a Fisher-Yates shuffle on a slice of strings |
|
| 49 |
-func stringShuffle(s []string) {
|
|
| 50 |
- for i := len(s) - 1; i > 0; i-- {
|
|
| 51 |
- j := rand.Intn(i + 1) |
|
| 52 |
- s[i], s[j] = s[j], s[i] |
|
| 53 |
- } |
|
| 54 |
-} |