You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
609 lines
12 KiB
609 lines
12 KiB
package zk |
|
|
|
import ( |
|
"encoding/binary" |
|
"errors" |
|
"log" |
|
"reflect" |
|
"runtime" |
|
"time" |
|
) |
|
|
|
var ( |
|
ErrUnhandledFieldType = errors.New("zk: unhandled field type") |
|
ErrPtrExpected = errors.New("zk: encode/decode expect a non-nil pointer to struct") |
|
ErrShortBuffer = errors.New("zk: buffer too small") |
|
) |
|
|
|
type defaultLogger struct{} |
|
|
|
func (defaultLogger) Printf(format string, a ...interface{}) { |
|
log.Printf(format, a...) |
|
} |
|
|
|
type ACL struct { |
|
Perms int32 |
|
Scheme string |
|
ID string |
|
} |
|
|
|
type Stat struct { |
|
Czxid int64 // The zxid of the change that caused this znode to be created. |
|
Mzxid int64 // The zxid of the change that last modified this znode. |
|
Ctime int64 // The time in milliseconds from epoch when this znode was created. |
|
Mtime int64 // The time in milliseconds from epoch when this znode was last modified. |
|
Version int32 // The number of changes to the data of this znode. |
|
Cversion int32 // The number of changes to the children of this znode. |
|
Aversion int32 // The number of changes to the ACL of this znode. |
|
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. |
|
DataLength int32 // The length of the data field of this znode. |
|
NumChildren int32 // The number of children of this znode. |
|
Pzxid int64 // last modified children |
|
} |
|
|
|
// ServerClient is the information for a single Zookeeper client and its session. |
|
// This is used to parse/extract the output fo the `cons` command. |
|
type ServerClient struct { |
|
Queued int64 |
|
Received int64 |
|
Sent int64 |
|
SessionID int64 |
|
Lcxid int64 |
|
Lzxid int64 |
|
Timeout int32 |
|
LastLatency int32 |
|
MinLatency int32 |
|
AvgLatency int32 |
|
MaxLatency int32 |
|
Established time.Time |
|
LastResponse time.Time |
|
Addr string |
|
LastOperation string // maybe? |
|
Error error |
|
} |
|
|
|
// ServerClients is a struct for the FLWCons() function. It's used to provide |
|
// the list of Clients. |
|
// |
|
// This is needed because FLWCons() takes multiple servers. |
|
type ServerClients struct { |
|
Clients []*ServerClient |
|
Error error |
|
} |
|
|
|
// ServerStats is the information pulled from the Zookeeper `stat` command. |
|
type ServerStats struct { |
|
Sent int64 |
|
Received int64 |
|
NodeCount int64 |
|
MinLatency int64 |
|
AvgLatency int64 |
|
MaxLatency int64 |
|
Connections int64 |
|
Outstanding int64 |
|
Epoch int32 |
|
Counter int32 |
|
BuildTime time.Time |
|
Mode Mode |
|
Version string |
|
Error error |
|
} |
|
|
|
type requestHeader struct { |
|
Xid int32 |
|
Opcode int32 |
|
} |
|
|
|
type responseHeader struct { |
|
Xid int32 |
|
Zxid int64 |
|
Err ErrCode |
|
} |
|
|
|
type multiHeader struct { |
|
Type int32 |
|
Done bool |
|
Err ErrCode |
|
} |
|
|
|
type auth struct { |
|
Type int32 |
|
Scheme string |
|
Auth []byte |
|
} |
|
|
|
// Generic request structs |
|
|
|
type pathRequest struct { |
|
Path string |
|
} |
|
|
|
type PathVersionRequest struct { |
|
Path string |
|
Version int32 |
|
} |
|
|
|
type pathWatchRequest struct { |
|
Path string |
|
Watch bool |
|
} |
|
|
|
type pathResponse struct { |
|
Path string |
|
} |
|
|
|
type statResponse struct { |
|
Stat Stat |
|
} |
|
|
|
// |
|
|
|
type CheckVersionRequest PathVersionRequest |
|
type closeRequest struct{} |
|
type closeResponse struct{} |
|
|
|
type connectRequest struct { |
|
ProtocolVersion int32 |
|
LastZxidSeen int64 |
|
TimeOut int32 |
|
SessionID int64 |
|
Passwd []byte |
|
} |
|
|
|
type connectResponse struct { |
|
ProtocolVersion int32 |
|
TimeOut int32 |
|
SessionID int64 |
|
Passwd []byte |
|
} |
|
|
|
type CreateRequest struct { |
|
Path string |
|
Data []byte |
|
Acl []ACL |
|
Flags int32 |
|
} |
|
|
|
type createResponse pathResponse |
|
type DeleteRequest PathVersionRequest |
|
type deleteResponse struct{} |
|
|
|
type errorResponse struct { |
|
Err int32 |
|
} |
|
|
|
type existsRequest pathWatchRequest |
|
type existsResponse statResponse |
|
type getAclRequest pathRequest |
|
|
|
type getAclResponse struct { |
|
Acl []ACL |
|
Stat Stat |
|
} |
|
|
|
type getChildrenRequest pathRequest |
|
|
|
type getChildrenResponse struct { |
|
Children []string |
|
} |
|
|
|
type getChildren2Request pathWatchRequest |
|
|
|
type getChildren2Response struct { |
|
Children []string |
|
Stat Stat |
|
} |
|
|
|
type getDataRequest pathWatchRequest |
|
|
|
type getDataResponse struct { |
|
Data []byte |
|
Stat Stat |
|
} |
|
|
|
type getMaxChildrenRequest pathRequest |
|
|
|
type getMaxChildrenResponse struct { |
|
Max int32 |
|
} |
|
|
|
type getSaslRequest struct { |
|
Token []byte |
|
} |
|
|
|
type pingRequest struct{} |
|
type pingResponse struct{} |
|
|
|
type setAclRequest struct { |
|
Path string |
|
Acl []ACL |
|
Version int32 |
|
} |
|
|
|
type setAclResponse statResponse |
|
|
|
type SetDataRequest struct { |
|
Path string |
|
Data []byte |
|
Version int32 |
|
} |
|
|
|
type setDataResponse statResponse |
|
|
|
type setMaxChildren struct { |
|
Path string |
|
Max int32 |
|
} |
|
|
|
type setSaslRequest struct { |
|
Token string |
|
} |
|
|
|
type setSaslResponse struct { |
|
Token string |
|
} |
|
|
|
type setWatchesRequest struct { |
|
RelativeZxid int64 |
|
DataWatches []string |
|
ExistWatches []string |
|
ChildWatches []string |
|
} |
|
|
|
type setWatchesResponse struct{} |
|
|
|
type syncRequest pathRequest |
|
type syncResponse pathResponse |
|
|
|
type setAuthRequest auth |
|
type setAuthResponse struct{} |
|
|
|
type multiRequestOp struct { |
|
Header multiHeader |
|
Op interface{} |
|
} |
|
type multiRequest struct { |
|
Ops []multiRequestOp |
|
DoneHeader multiHeader |
|
} |
|
type multiResponseOp struct { |
|
Header multiHeader |
|
String string |
|
Stat *Stat |
|
Err ErrCode |
|
} |
|
type multiResponse struct { |
|
Ops []multiResponseOp |
|
DoneHeader multiHeader |
|
} |
|
|
|
func (r *multiRequest) Encode(buf []byte) (int, error) { |
|
total := 0 |
|
for _, op := range r.Ops { |
|
op.Header.Done = false |
|
n, err := encodePacketValue(buf[total:], reflect.ValueOf(op)) |
|
if err != nil { |
|
return total, err |
|
} |
|
total += n |
|
} |
|
r.DoneHeader.Done = true |
|
n, err := encodePacketValue(buf[total:], reflect.ValueOf(r.DoneHeader)) |
|
if err != nil { |
|
return total, err |
|
} |
|
total += n |
|
|
|
return total, nil |
|
} |
|
|
|
func (r *multiRequest) Decode(buf []byte) (int, error) { |
|
r.Ops = make([]multiRequestOp, 0) |
|
r.DoneHeader = multiHeader{-1, true, -1} |
|
total := 0 |
|
for { |
|
header := &multiHeader{} |
|
n, err := decodePacketValue(buf[total:], reflect.ValueOf(header)) |
|
if err != nil { |
|
return total, err |
|
} |
|
total += n |
|
if header.Done { |
|
r.DoneHeader = *header |
|
break |
|
} |
|
|
|
req := requestStructForOp(header.Type) |
|
if req == nil { |
|
return total, ErrAPIError |
|
} |
|
n, err = decodePacketValue(buf[total:], reflect.ValueOf(req)) |
|
if err != nil { |
|
return total, err |
|
} |
|
total += n |
|
r.Ops = append(r.Ops, multiRequestOp{*header, req}) |
|
} |
|
return total, nil |
|
} |
|
|
|
func (r *multiResponse) Decode(buf []byte) (int, error) { |
|
var multiErr error |
|
|
|
r.Ops = make([]multiResponseOp, 0) |
|
r.DoneHeader = multiHeader{-1, true, -1} |
|
total := 0 |
|
for { |
|
header := &multiHeader{} |
|
n, err := decodePacketValue(buf[total:], reflect.ValueOf(header)) |
|
if err != nil { |
|
return total, err |
|
} |
|
total += n |
|
if header.Done { |
|
r.DoneHeader = *header |
|
break |
|
} |
|
|
|
res := multiResponseOp{Header: *header} |
|
var w reflect.Value |
|
switch header.Type { |
|
default: |
|
return total, ErrAPIError |
|
case opError: |
|
w = reflect.ValueOf(&res.Err) |
|
case opCreate: |
|
w = reflect.ValueOf(&res.String) |
|
case opSetData: |
|
res.Stat = new(Stat) |
|
w = reflect.ValueOf(res.Stat) |
|
case opCheck, opDelete: |
|
} |
|
if w.IsValid() { |
|
n, err := decodePacketValue(buf[total:], w) |
|
if err != nil { |
|
return total, err |
|
} |
|
total += n |
|
} |
|
r.Ops = append(r.Ops, res) |
|
if multiErr == nil && res.Err != errOk { |
|
// Use the first error as the error returned from Multi(). |
|
multiErr = res.Err.toError() |
|
} |
|
} |
|
return total, multiErr |
|
} |
|
|
|
type watcherEvent struct { |
|
Type EventType |
|
State State |
|
Path string |
|
} |
|
|
|
type decoder interface { |
|
Decode(buf []byte) (int, error) |
|
} |
|
|
|
type encoder interface { |
|
Encode(buf []byte) (int, error) |
|
} |
|
|
|
func decodePacket(buf []byte, st interface{}) (n int, err error) { |
|
defer func() { |
|
if r := recover(); r != nil { |
|
if e, ok := r.(runtime.Error); ok && e.Error() == "runtime error: slice bounds out of range" { |
|
err = ErrShortBuffer |
|
} else { |
|
panic(r) |
|
} |
|
} |
|
}() |
|
|
|
v := reflect.ValueOf(st) |
|
if v.Kind() != reflect.Ptr || v.IsNil() { |
|
return 0, ErrPtrExpected |
|
} |
|
return decodePacketValue(buf, v) |
|
} |
|
|
|
func decodePacketValue(buf []byte, v reflect.Value) (int, error) { |
|
rv := v |
|
kind := v.Kind() |
|
if kind == reflect.Ptr { |
|
if v.IsNil() { |
|
v.Set(reflect.New(v.Type().Elem())) |
|
} |
|
v = v.Elem() |
|
kind = v.Kind() |
|
} |
|
|
|
n := 0 |
|
switch kind { |
|
default: |
|
return n, ErrUnhandledFieldType |
|
case reflect.Struct: |
|
if de, ok := rv.Interface().(decoder); ok { |
|
return de.Decode(buf) |
|
} else if de, ok := v.Interface().(decoder); ok { |
|
return de.Decode(buf) |
|
} else { |
|
for i := 0; i < v.NumField(); i++ { |
|
field := v.Field(i) |
|
n2, err := decodePacketValue(buf[n:], field) |
|
n += n2 |
|
if err != nil { |
|
return n, err |
|
} |
|
} |
|
} |
|
case reflect.Bool: |
|
v.SetBool(buf[n] != 0) |
|
n++ |
|
case reflect.Int32: |
|
v.SetInt(int64(binary.BigEndian.Uint32(buf[n : n+4]))) |
|
n += 4 |
|
case reflect.Int64: |
|
v.SetInt(int64(binary.BigEndian.Uint64(buf[n : n+8]))) |
|
n += 8 |
|
case reflect.String: |
|
ln := int(binary.BigEndian.Uint32(buf[n : n+4])) |
|
v.SetString(string(buf[n+4 : n+4+ln])) |
|
n += 4 + ln |
|
case reflect.Slice: |
|
switch v.Type().Elem().Kind() { |
|
default: |
|
count := int(binary.BigEndian.Uint32(buf[n : n+4])) |
|
n += 4 |
|
values := reflect.MakeSlice(v.Type(), count, count) |
|
v.Set(values) |
|
for i := 0; i < count; i++ { |
|
n2, err := decodePacketValue(buf[n:], values.Index(i)) |
|
n += n2 |
|
if err != nil { |
|
return n, err |
|
} |
|
} |
|
case reflect.Uint8: |
|
ln := int(int32(binary.BigEndian.Uint32(buf[n : n+4]))) |
|
if ln < 0 { |
|
n += 4 |
|
v.SetBytes(nil) |
|
} else { |
|
bytes := make([]byte, ln) |
|
copy(bytes, buf[n+4:n+4+ln]) |
|
v.SetBytes(bytes) |
|
n += 4 + ln |
|
} |
|
} |
|
} |
|
return n, nil |
|
} |
|
|
|
func encodePacket(buf []byte, st interface{}) (n int, err error) { |
|
defer func() { |
|
if r := recover(); r != nil { |
|
if e, ok := r.(runtime.Error); ok && e.Error() == "runtime error: slice bounds out of range" { |
|
err = ErrShortBuffer |
|
} else { |
|
panic(r) |
|
} |
|
} |
|
}() |
|
|
|
v := reflect.ValueOf(st) |
|
if v.Kind() != reflect.Ptr || v.IsNil() { |
|
return 0, ErrPtrExpected |
|
} |
|
return encodePacketValue(buf, v) |
|
} |
|
|
|
func encodePacketValue(buf []byte, v reflect.Value) (int, error) { |
|
rv := v |
|
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { |
|
v = v.Elem() |
|
} |
|
|
|
n := 0 |
|
switch v.Kind() { |
|
default: |
|
return n, ErrUnhandledFieldType |
|
case reflect.Struct: |
|
if en, ok := rv.Interface().(encoder); ok { |
|
return en.Encode(buf) |
|
} else if en, ok := v.Interface().(encoder); ok { |
|
return en.Encode(buf) |
|
} else { |
|
for i := 0; i < v.NumField(); i++ { |
|
field := v.Field(i) |
|
n2, err := encodePacketValue(buf[n:], field) |
|
n += n2 |
|
if err != nil { |
|
return n, err |
|
} |
|
} |
|
} |
|
case reflect.Bool: |
|
if v.Bool() { |
|
buf[n] = 1 |
|
} else { |
|
buf[n] = 0 |
|
} |
|
n++ |
|
case reflect.Int32: |
|
binary.BigEndian.PutUint32(buf[n:n+4], uint32(v.Int())) |
|
n += 4 |
|
case reflect.Int64: |
|
binary.BigEndian.PutUint64(buf[n:n+8], uint64(v.Int())) |
|
n += 8 |
|
case reflect.String: |
|
str := v.String() |
|
binary.BigEndian.PutUint32(buf[n:n+4], uint32(len(str))) |
|
copy(buf[n+4:n+4+len(str)], []byte(str)) |
|
n += 4 + len(str) |
|
case reflect.Slice: |
|
switch v.Type().Elem().Kind() { |
|
default: |
|
count := v.Len() |
|
startN := n |
|
n += 4 |
|
for i := 0; i < count; i++ { |
|
n2, err := encodePacketValue(buf[n:], v.Index(i)) |
|
n += n2 |
|
if err != nil { |
|
return n, err |
|
} |
|
} |
|
binary.BigEndian.PutUint32(buf[startN:startN+4], uint32(count)) |
|
case reflect.Uint8: |
|
if v.IsNil() { |
|
binary.BigEndian.PutUint32(buf[n:n+4], uint32(0xffffffff)) |
|
n += 4 |
|
} else { |
|
bytes := v.Bytes() |
|
binary.BigEndian.PutUint32(buf[n:n+4], uint32(len(bytes))) |
|
copy(buf[n+4:n+4+len(bytes)], bytes) |
|
n += 4 + len(bytes) |
|
} |
|
} |
|
} |
|
return n, nil |
|
} |
|
|
|
func requestStructForOp(op int32) interface{} { |
|
switch op { |
|
case opClose: |
|
return &closeRequest{} |
|
case opCreate: |
|
return &CreateRequest{} |
|
case opDelete: |
|
return &DeleteRequest{} |
|
case opExists: |
|
return &existsRequest{} |
|
case opGetAcl: |
|
return &getAclRequest{} |
|
case opGetChildren: |
|
return &getChildrenRequest{} |
|
case opGetChildren2: |
|
return &getChildren2Request{} |
|
case opGetData: |
|
return &getDataRequest{} |
|
case opPing: |
|
return &pingRequest{} |
|
case opSetAcl: |
|
return &setAclRequest{} |
|
case opSetData: |
|
return &SetDataRequest{} |
|
case opSetWatches: |
|
return &setWatchesRequest{} |
|
case opSync: |
|
return &syncRequest{} |
|
case opSetAuth: |
|
return &setAuthRequest{} |
|
case opCheck: |
|
return &CheckVersionRequest{} |
|
case opMulti: |
|
return &multiRequest{} |
|
} |
|
return nil |
|
}
|
|
|