pax_global_header00006660000000000000000000000064122146662200014513gustar00rootroot0000000000000052 comment=5de93f059a863ac6d0d12acd97fdc4b334c675a4 golang-doozer-0.0~git20130909/000077500000000000000000000000001221466622000156535ustar00rootroot00000000000000golang-doozer-0.0~git20130909/.gitignore000066400000000000000000000000601221466622000176370ustar00rootroot00000000000000*.[568] [568].out _obj _test* cmd/doozer/doozer golang-doozer-0.0~git20130909/LICENSE000066400000000000000000000021011221466622000166520ustar00rootroot00000000000000The MIT License Copyright (c) 2010 Blake Mizerany, Keith Rarick Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-doozer-0.0~git20130909/README.md000066400000000000000000000022121221466622000171270ustar00rootroot00000000000000# Doozer Client **Note:** [doozerd](https://github.com/4ad/doozerd) is the server. This is the Go client driver for doozer. ## How to use To install the Go client: $ go get github.com/4ad/doozer To install the CLI client: $ go get github.com/4ad/doozer/cmd/doozer To use: package main import ( "github.com/4ad/doozer" "os" ) func main() { doozer, err := doozer.Dial("localhost:8046") if err != nil { panic(err) } myfile, _, _ := doozer.Get("/myfile", nil) os.Stdout.Write(myfile) } ## Hacking You can create a workspace for hacking on the doozer library and command by doing the following: $ mkdir doozer $ cd doozer $ export GOPATH=`pwd` $ go get github.com/4ad/doozer/... # ...hack...hack..hack... $ vim src/github.com/4ad/doozer/cmd/doozer/help.go # rebuild ./bin/doozer $ go install github.com/4ad/doozer/... ## License and Authors Doozer is distributed under the terms of the MIT License. See [LICENSE](LICENSE) for details. Doozer was created by Blake Mizerany and Keith Rarick. Type `git shortlog -s` for a full list of contributors. golang-doozer-0.0~git20130909/cmd/000077500000000000000000000000001221466622000164165ustar00rootroot00000000000000golang-doozer-0.0~git20130909/cmd/doozer/000077500000000000000000000000001221466622000177205ustar00rootroot00000000000000golang-doozer-0.0~git20130909/cmd/doozer/add.go000066400000000000000000000006531221466622000210030ustar00rootroot00000000000000package main func init() { cmds["add"] = cmd{add, "", "add a key only if it isn't already set"} cmdHelp["add"] = `Sets the body of the file located at only if it didn't already exist. Equivalent to set 0 The body is read from stdin. If the file already exists, no change will be made. Prints the new revision on stdout, or an error message on stderr. ` } func add(path string) { set(path, "0") } golang-doozer-0.0~git20130909/cmd/doozer/del.go000066400000000000000000000005521221466622000210150ustar00rootroot00000000000000package main func init() { cmds["del"] = cmd{del, " ", "delete a file"} cmdHelp["del"] = `Deletes the file at . If is not greater than or equal to the revision of the file, no change will be made. ` } func del(path, rev string) { oldRev := mustAtoi64(rev) c := dial() err := c.Del(path, oldRev) if err != nil { bail(err) } } golang-doozer-0.0~git20130909/cmd/doozer/doozer.go000066400000000000000000000054001221466622000215500ustar00rootroot00000000000000package main import ( "flag" "fmt" "github.com/4ad/doozer" "os" "reflect" "sort" "strconv" ) var ( uri = flag.String("a", "doozer:?ca=127.0.0.1:8046", "the address to bind to") buri = flag.String("b", "", "the DzNS uri") rrev = flag.Int64("r", -1, "request rev") showHelp = flag.Bool("h", false, "show help") showVersion = flag.Bool("v", false, "print version string") ) type cmd struct { f interface{} a string // args d string // short description } var ( selfName = os.Args[0] cmds = map[string]cmd{} cmdHelp = map[string]string{} ) const ( usage1 = ` Each command takes zero or more options and zero or more arguments. In addition, there are some global options that can be used with any command. The exit status is 0 on success, 1 for a rev mismatch, and 2 otherwise. Global Options: ` usage2 = `Environment: DOOZER_URI - The doozer cluster to bind to; overriden by -a. DOOZER_BOOT_URI - The DzNS to lookup address in; overriden by -b. Commands: ` ) func usage() { fmt.Fprintf(os.Stderr, "Use: %s [options] [options] [args]\n", selfName) fmt.Fprint(os.Stderr, usage1) flag.PrintDefaults() fmt.Fprintln(os.Stderr) fmt.Fprint(os.Stderr, usage2) var max int var names []string us := make(map[string]string) for k := range cmds { u := k + " " + cmds[k].a if len(u) > max { max = len(u) } names = append(names, k) us[k] = u } sort.Strings(names) for _, k := range names { fmt.Fprintf(os.Stderr, " %-*s - %s\n", max, us[k], cmds[k].d) } } func bail(e error) { fmt.Fprintln(os.Stderr, "Error:", e) if e == doozer.ErrOldRev { os.Exit(1) } os.Exit(2) } func mustAtoi64(arg string) int64 { n, err := strconv.ParseInt(arg, 10, 64) if err != nil { bail(err) } return n } func dial() *doozer.Conn { c, err := doozer.DialUri(*uri, *buri) if err != nil { bail(err) } return c } func main() { if e := os.Getenv("DOOZER_URI"); e != "" { *uri = e } if e := os.Getenv("DOOZER_BOOT_URI"); e != "" { *buri = e } flag.Usage = usage flag.Parse() if *showHelp { usage() return } if *showVersion { fmt.Println("doozer", version) return } if flag.NArg() < 1 { fmt.Fprintf(os.Stderr, "%s: missing command\n", os.Args[0]) usage() os.Exit(127) } cmd := flag.Arg(0) c, ok := cmds[cmd] if !ok { fmt.Fprintln(os.Stderr, "Unknown command:", cmd) usage() os.Exit(127) } os.Args = flag.Args() flag.Parse() if *showHelp { help(cmd) return } args := flag.Args() ft := reflect.TypeOf(c.f) if len(args) != ft.NumIn() { fmt.Fprintf(os.Stderr, "%s: wrong number of arguments\n", cmd) help(cmd) os.Exit(127) } vals := make([]reflect.Value, len(args)) for i, s := range args { vals[i] = reflect.ValueOf(s) } fv := reflect.ValueOf(c.f) fv.Call(vals) } golang-doozer-0.0~git20130909/cmd/doozer/find.go000066400000000000000000000014741221466622000211750ustar00rootroot00000000000000package main import ( "fmt" "github.com/4ad/doozer" "os" ) func init() { cmds["find"] = cmd{find, "", "list files"} cmdHelp["find"] = `Prints the tree rooted at Prints the path for each file or directory, one per line. ` } func find(path string) { c := dial() if *rrev == -1 { var err error *rrev, err = c.Rev() if err != nil { bail(err) } } v := make(vis) errs := make(chan error) go func() { doozer.Walk(c, *rrev, path, v, errs) close(v) }() for { select { case path, ok := <-v: if !ok { return } fmt.Println(path) case err := <-errs: fmt.Fprintln(os.Stderr, err) } } } type vis chan string func (v vis) VisitDir(path string, f *doozer.FileInfo) bool { v <- path return true } func (v vis) VisitFile(path string, f *doozer.FileInfo) { v <- path } golang-doozer-0.0~git20130909/cmd/doozer/get.go000066400000000000000000000004411221466622000210250ustar00rootroot00000000000000package main import ( "os" ) func init() { cmds["get"] = cmd{get, "", "read a file"} cmdHelp["get"] = "Prints the body of the file at .\n" } func get(path string) { c := dial() body, _, err := c.Get(path, nil) if err != nil { bail(err) } os.Stdout.Write(body) } golang-doozer-0.0~git20130909/cmd/doozer/help.go000066400000000000000000000004261221466622000212010ustar00rootroot00000000000000package main import "fmt" func init() { cmds["help"] = cmd{help, "", "provide detailed help on a command"} } func help(command string) { c := cmds[command] fmt.Printf("Use: %s [options] %s [options] %s\n\n", selfName, command, c.a) fmt.Print(cmdHelp[command]) } golang-doozer-0.0~git20130909/cmd/doozer/nop.go000066400000000000000000000003651221466622000210470ustar00rootroot00000000000000package main func init() { cmds["nop"] = cmd{nop, "", "consensus"} cmdHelp["nop"] = `Performs a consensus operation. No change will be made to the data store. ` } func nop() { c := dial() err := c.Nop() if err != nil { bail(err) } } golang-doozer-0.0~git20130909/cmd/doozer/rev.go000066400000000000000000000004141221466622000210420ustar00rootroot00000000000000package main import ( "fmt" ) func init() { cmds["rev"] = cmd{rev, "", "show current revision"} cmdHelp["rev"] = "Prints the current revision of the store.\n" } func rev() { c := dial() rev, err := c.Rev() if err != nil { bail(err) } fmt.Println(rev) } golang-doozer-0.0~git20130909/cmd/doozer/self.go000066400000000000000000000003701221466622000212000ustar00rootroot00000000000000package main import ( "os" ) func init() { cmds["self"] = cmd{self, "", "identify a node"} cmdHelp["self"] = "Prints the node's ID.\n" } func self() { c := dial() id, err := c.Self() if err != nil { bail(err) } os.Stdout.Write(id) } golang-doozer-0.0~git20130909/cmd/doozer/set.go000066400000000000000000000011441221466622000210420ustar00rootroot00000000000000package main import ( "fmt" "io/ioutil" "os" ) func init() { cmds["set"] = cmd{set, " ", "write a file"} cmdHelp["set"] = `Sets the body of the file at . The body is read from stdin. If is not greater than or equal to the revision of the file, no change will be made. Prints the new revision on stdout, or an error message on stderr. ` } func set(path, rev string) { oldRev := mustAtoi64(rev) c := dial() body, err := ioutil.ReadAll(os.Stdin) if err != nil { bail(err) } newRev, err := c.Set(path, oldRev, body) if err != nil { bail(err) } fmt.Println(newRev) } golang-doozer-0.0~git20130909/cmd/doozer/stat.go000066400000000000000000000010521221466622000212200ustar00rootroot00000000000000package main import ( "fmt" "os" ) func init() { cmds["stat"] = cmd{stat, "", "print file status"} cmdHelp["stat"] = `Print the status for each . If path is a directory, prints "d" and the number of entries. Otherwise, prints its revision and length. ` } func stat(path string) { c := dial() len, rev, err := c.Stat(path, nil) if err != nil { bail(err) } switch rev { case 0: fmt.Fprintln(os.Stderr, "No such file or directory:", path) os.Exit(2) case -2: fmt.Println("d", len) default: fmt.Println(rev, len) } } golang-doozer-0.0~git20130909/cmd/doozer/touch.go000066400000000000000000000006361221466622000213760ustar00rootroot00000000000000package main func init() { cmds["touch"] = cmd{touch, "", "update rev of a file"} cmdHelp["touch"] = `Attempts to update the rev of a file to a value greater than the current rev. If a file does not exist, it will be created. ` } func touch(path string) { c := dial() body, rev, err := c.Get(path, nil) if err != nil { bail(err) } _, err = c.Set(path, rev, body) if err != nil { bail(err) } } golang-doozer-0.0~git20130909/cmd/doozer/version.go000066400000000000000000000000541221466622000217330ustar00rootroot00000000000000package main const version = `unknown-4ad` golang-doozer-0.0~git20130909/cmd/doozer/wait.go000066400000000000000000000026121221466622000212140ustar00rootroot00000000000000package main import ( "flag" "fmt" "os" ) var timeout = flag.Duration("t", 0, "wait timeout") func init() { flag.Parse() cmds["wait"] = cmd{wait, "", "wait for a change"} cmdHelp["wait"] = `Prints the next change to a file matching . If flag -r is given, prints the next change on or after . if flag -t is given, the wait will timeout after the specified duration (i.e. -t 1m30s will wait 1 minute and 30 seconds, valid time units: "ns", "us" (or "µs"), "ms", "s", "m", "h"). Rules for pattern-matching: - '?' matches a single char in a single path component - '*' matches zero or more chars in a single path component - '**' matches zero or more chars in zero or more components - any other sequence matches itself Output is a sequence of records, one for each change. Format of each record: LF LF Here, is the file's path, is the revision, is the number of bytes in the body, and LF is an ASCII line-feed char. ` } func wait(path string) { c := dial() if *rrev == -1 { var err error *rrev, err = c.Rev() if err != nil { bail(err) } } ev, err := c.WaitTimeout(path, *rrev, *timeout) if err != nil { bail(err) } var sd string switch { case ev.IsSet(): sd = "set" case ev.IsDel(): sd = "del" } fmt.Println(ev.Path, ev.Rev, sd, len(ev.Body)) os.Stdout.Write(ev.Body) fmt.Println() } golang-doozer-0.0~git20130909/cmd/doozer/watch.go000066400000000000000000000022461221466622000213610ustar00rootroot00000000000000package main import ( "fmt" "os" ) func init() { cmds["watch"] = cmd{watch, "", "watch for a change"} cmdHelp["watch"] = `Prints changes to any file matching . If flag -r is given, prints changes beginning on . Rules for pattern-matching: - '?' matches a single char in a single path component - '*' matches zero or more chars in a single path component - '**' matches zero or more chars in zero or more components - any other sequence matches itself Output is a sequence of records, one for each change. Format of each record: LF LF Here, is the file's path, is the revision of the change, is the number of bytes in the body, and LF is an ASCII line-feed char. ` } func watch(glob string) { c := dial() if *rrev == -1 { var err error *rrev, err = c.Rev() if err != nil { bail(err) } } for { ev, err := c.Wait(glob, *rrev) if err != nil { bail(err) } *rrev = ev.Rev + 1 var sd string switch { case ev.IsSet(): sd = "set" case ev.IsDel(): sd = "del" } fmt.Println(ev.Path, ev.Rev, sd, len(ev.Body)) os.Stdout.Write(ev.Body) fmt.Println() } } golang-doozer-0.0~git20130909/conn.go000066400000000000000000000247031221466622000171450ustar00rootroot00000000000000package doozer import ( "code.google.com/p/goprotobuf/proto" "encoding/binary" "errors" "github.com/kr/pretty" "time" "io" "log" "math/rand" "net" "net/url" "strings" ) var ( uriPrefix = "doozer:?" ) var ( ErrInvalidUri = errors.New("invalid uri") ) type txn struct { req request resp *response err error done chan bool } type Conn struct { addr string conn net.Conn send chan *txn msg chan []byte err error stop chan bool stopped chan bool } func init() { rand.Seed(time.Now().UnixNano()) } // Dial connects to a single doozer server. func Dial(addr string) (*Conn, error) { return dial(addr, -1) } // DialTimeout acts like Dial but takes a timeout. func DialTimeout(addr string, timeout time.Duration) (*Conn, error) { return dial(addr, timeout) } func dial(addr string, timeout time.Duration) (*Conn, error) { var c Conn var err error c.addr = addr if timeout > 0 { c.conn, err = net.DialTimeout("tcp", addr, timeout) } else { c.conn, err = net.Dial("tcp", addr) } if err != nil { return nil, err } c.send = make(chan *txn) c.msg = make(chan []byte) c.stop = make(chan bool, 1) c.stopped = make(chan bool) errch := make(chan error, 1) go c.mux(errch) go c.readAll(errch) return &c, nil } // DialUri connects to one of the doozer servers given in `uri`. If `uri` // contains a cluster name, it will lookup addrs to try in `buri`. If `uri` // contains a secret key, then DialUri will call `Access` with the secret. func DialUri(uri, buri string) (*Conn, error) { return dialUri(uri, buri, -1) } // DialUriTimeout acts like DialUri but takes a timeout. func DialUriTimeout(uri, buri string, timeout time.Duration) (*Conn, error) { return dialUri(uri, buri, -1) } func dialUri(uri, buri string, timeout time.Duration) (*Conn, error) { if !strings.HasPrefix(uri, uriPrefix) { return nil, ErrInvalidUri } q := uri[len(uriPrefix):] p, err := url.ParseQuery(q) if err != nil { return nil, err } addrs := make([]string, 0) name, ok := p["cn"] if ok && buri != "" { c, err := DialUri(buri, "") if err != nil { return nil, err } addrs, err = lookup(c, name[0]) if err != nil { return nil, err } } else { var ok bool addrs, ok = p["ca"] if !ok { return nil, ErrInvalidUri } } c, err := dial(addrs[rand.Int()%len(addrs)], timeout) if err != nil { return nil, err } secret, ok := p["sk"] if ok { err = c.Access(secret[0]) if err != nil { c.Close() return nil, err } } return c, nil } // Find possible addresses for cluster named name. func lookup(b *Conn, name string) (as []string, err error) { rev, err := b.Rev() if err != nil { return nil, err } path := "/ctl/ns/" + name names, err := b.Getdir(path, rev, 0, -1) if err, ok := err.(*Error); ok && err.Err == ErrNoEnt { return nil, nil } else if err != nil { return nil, err } path += "/" for _, name := range names { body, _, err := b.Get(path+name, &rev) if err != nil { return nil, err } as = append(as, string(body)) } return as, nil } func (c *Conn) call(t *txn) error { t.done = make(chan bool) select { case <-c.stopped: return c.err case c.send <- t: select { case <-c.stopped: return c.err case <-t.done: if t.err != nil { return t.err } if t.resp.ErrCode != nil { return newError(t) } } } return nil } // After Close is called, operations on c will return ErrClosed. func (c *Conn) Close() { select { case c.stop <- true: default: } } func (c *Conn) mux(errch chan error) { txns := make(map[int32]*txn) var n int32 // next tag var err error for { select { case t := <-c.send: // find an unused tag for t := txns[n]; t != nil; t = txns[n] { n++ } txns[n] = t // don't take n's address; it will change tag := n t.req.Tag = &tag var buf []byte buf, err = proto.Marshal(&t.req) if err != nil { txns[n] = nil t.err = err t.done <- true continue } err = c.write(buf) if err != nil { goto error } case buf := <-c.msg: var r response err = proto.Unmarshal(buf, &r) if err != nil { log.Print(err) continue } if r.Tag == nil { log.Printf("nil tag: %# v", pretty.Formatter(r)) continue } t := txns[*r.Tag] if t == nil { log.Printf("unexpected: %# v", pretty.Formatter(r)) continue } delete(txns, *r.Tag) t.resp = &r t.done <- true case err = <-errch: goto error case <-c.stop: err = ErrClosed goto error } } error: c.err = err for _, t := range txns { t.err = err t.done <- true } c.conn.Close() close(c.stopped) } func (c *Conn) readAll(errch chan error) { for { buf, err := c.read() if err != nil { errch <- err return } c.msg <- buf } } func (c *Conn) read() ([]byte, error) { var size int32 err := binary.Read(c.conn, binary.BigEndian, &size) if err != nil { return nil, err } buf := make([]byte, size) _, err = io.ReadFull(c.conn, buf) if err != nil { return nil, err } return buf, nil } func (c *Conn) write(buf []byte) error { err := binary.Write(c.conn, binary.BigEndian, int32(len(buf))) if err != nil { return err } _, err = c.conn.Write(buf) return err } // Attempts access to the store func (c *Conn) Access(token string) error { var t txn t.req.Verb = request_ACCESS.Enum() t.req.Value = []byte(token) return c.call(&t) } // Sets the contents of file to body, if it hasn't been modified since oldRev. func (c *Conn) Set(file string, oldRev int64, body []byte) (newRev int64, err error) { var t txn t.req.Verb = request_SET.Enum() t.req.Path = &file t.req.Value = body t.req.Rev = &oldRev err = c.call(&t) if err != nil { return } return t.resp.GetRev(), nil } // Deletes file, if it hasn't been modified since rev. func (c *Conn) Del(file string, rev int64) error { var t txn t.req.Verb = request_DEL.Enum() t.req.Path = &file t.req.Rev = &rev return c.call(&t) } func (c *Conn) Nop() error { var t txn t.req.Verb = request_NOP.Enum() return c.call(&t) } // Returns the body and revision of the file at path, // as of store revision *rev. // If rev is nil, uses the current state. func (c *Conn) Get(file string, rev *int64) ([]byte, int64, error) { var t txn t.req.Verb = request_GET.Enum() t.req.Path = &file t.req.Rev = rev err := c.call(&t) if err != nil { return nil, 0, err } return t.resp.Value, t.resp.GetRev(), nil } // Getdir reads up to lim names from dir, at revision rev, into an array. // Names are read in lexicographical order, starting at position off. // A negative lim means to read until the end. func (c *Conn) Getdir(dir string, rev int64, off, lim int) (names []string, err error) { for lim != 0 { var t txn t.req.Verb = request_GETDIR.Enum() t.req.Rev = &rev t.req.Path = &dir t.req.Offset = proto.Int32(int32(off)) err = c.call(&t) if err, ok := err.(*Error); ok && err.Err == ErrRange { return names, nil } if err != nil { return nil, err } names = append(names, *t.resp.Path) off++ lim-- } return } // Getdirinfo reads metadata for up to lim files from dir, at revision rev, // into an array. // Files are read in lexicographical order, starting at position off. // A negative lim means to read until the end. // Getdirinfo returns the array and an error, if any. func (c *Conn) Getdirinfo(dir string, rev int64, off, lim int) (a []FileInfo, err error) { names, err := c.Getdir(dir, rev, off, lim) if err != nil { return nil, err } if dir != "/" { dir += "/" } a = make([]FileInfo, len(names)) for i, name := range names { var fp *FileInfo fp, err = c.Statinfo(rev, dir+name) if err != nil { a[i].Name = name } else { a[i] = *fp } } return } // Statinfo returns metadata about the file or directory at path, // in revision *storeRev. If storeRev is nil, uses the current // revision. func (c *Conn) Statinfo(rev int64, path string) (f *FileInfo, err error) { f = new(FileInfo) f.Len, f.Rev, err = c.Stat(path, &rev) if err != nil { return nil, err } if f.Rev == missing { return nil, ErrNoEnt } f.Name = basename(path) f.IsSet = true f.IsDir = f.Rev == dir return f, nil } // Stat returns metadata about the file or directory at path, // in revision *storeRev. If storeRev is nil, uses the current // revision. func (c *Conn) Stat(path string, storeRev *int64) (len int, fileRev int64, err error) { var t txn t.req.Verb = request_STAT.Enum() t.req.Path = &path t.req.Rev = storeRev err = c.call(&t) if err != nil { return 0, 0, err } return int(t.resp.GetLen()), t.resp.GetRev(), nil } // Walk reads up to lim entries matching glob, in revision rev, into an array. // Entries are read in lexicographical order, starting at position off. // A negative lim means to read until the end. // Conn.Walk will be removed in a future release. Use Walk instead. func (c *Conn) Walk(glob string, rev int64, off, lim int) (info []Event, err error) { for lim != 0 { var t txn t.req.Verb = request_WALK.Enum() t.req.Rev = &rev t.req.Path = &glob t.req.Offset = proto.Int32(int32(off)) err = c.call(&t) if err, ok := err.(*Error); ok && err.Err == ErrRange { return info, nil } if err != nil { return nil, err } info = append(info, Event{ *t.resp.Rev, *t.resp.Path, t.resp.Value, *t.resp.Flags, }) off++ lim-- } return } // Waits for the first change, on or after rev, to any file matching glob. func (c *Conn) Wait(glob string, rev int64) (ev Event, err error) { var t txn t.req.Verb = request_WAIT.Enum() t.req.Path = &glob t.req.Rev = &rev err = c.call(&t) if err != nil { return } ev.Rev = *t.resp.Rev ev.Path = *t.resp.Path ev.Body = t.resp.Value ev.Flag = *t.resp.Flags & (set | del) return } // Waits for the first change, on or after rev, to any file matching glob, // within the specific time expressed as time.Duration func (c *Conn) WaitTimeout(glob string, rev int64, timeout time.Duration) (ev Event, err error) { var timer *time.Timer if timeout > 0 { timer = time.AfterFunc(timeout, func() { c.err = ErrWaitTimeout c.stopped <- true }) } ev, err = c.Wait(glob, rev) if timer != nil { timer.Stop() } return } // Rev returns the current revision of the store. func (c *Conn) Rev() (int64, error) { var t txn t.req.Verb = request_REV.Enum() err := c.call(&t) if err != nil { return 0, err } return *t.resp.Rev, nil } // Self returns the node's identifier func (c *Conn) Self() ([]byte, error) { var t txn t.req.Verb = request_SELF.Enum() err := c.call(&t) if err != nil { return nil, err } return t.resp.Value, nil } golang-doozer-0.0~git20130909/err.go000066400000000000000000000015431221466622000167750ustar00rootroot00000000000000package doozer import ( "errors" ) var ( ErrNoAddrs = errors.New("no known address") ErrBadTag = errors.New("bad tag") ErrClosed = errors.New("closed") ErrWaitTimeout = errors.New("wait timeout") ) var ( ErrOther response_Err = response_OTHER ErrNotDir response_Err = response_NOTDIR ErrIsDir response_Err = response_ISDIR ErrNoEnt response_Err = response_NOENT ErrRange response_Err = response_RANGE ErrOldRev response_Err = response_REV_MISMATCH ErrTooLate response_Err = response_TOO_LATE ErrReadonly response_Err = response_READONLY ) type Error struct { Err error Detail string } func newError(t *txn) *Error { return &Error{ Err: *t.resp.ErrCode, Detail: t.resp.GetErrDetail(), } } func (e *Error) Error() (s string) { s = e.Err.Error() if e.Detail != "" { s += ": " + e.Detail } return s } golang-doozer-0.0~git20130909/event.go000066400000000000000000000003571221466622000173300ustar00rootroot00000000000000package doozer const ( _ = 1 << iota _ set del ) type Event struct { Rev int64 Path string Body []byte Flag int32 } func (e Event) IsSet() bool { return e.Flag&set > 0 } func (e Event) IsDel() bool { return e.Flag&del > 0 } golang-doozer-0.0~git20130909/file.go000066400000000000000000000004521221466622000171220ustar00rootroot00000000000000package doozer const ( missing = int64(-iota) clobber dir nop ) type FileInfo struct { Name string Len int Rev int64 IsSet bool IsDir bool } func basename(path string) string { for i := len(path) - 1; i >= 0; i-- { if path[i] == '/' { return path[i+1:] } } return path } golang-doozer-0.0~git20130909/msg.pb.go000066400000000000000000000156361221466622000174030ustar00rootroot00000000000000// Code generated by protoc-gen-go. // source: msg.proto // DO NOT EDIT! package doozer import proto "code.google.com/p/goprotobuf/proto" import json "encoding/json" import math "math" // Reference proto, json, and math imports to suppress error if they are not otherwise used. var _ = proto.Marshal var _ = &json.SyntaxError{} var _ = math.Inf type request_Verb int32 const ( request_GET request_Verb = 1 request_SET request_Verb = 2 request_DEL request_Verb = 3 request_REV request_Verb = 5 request_WAIT request_Verb = 6 request_NOP request_Verb = 7 request_WALK request_Verb = 9 request_GETDIR request_Verb = 14 request_STAT request_Verb = 16 request_SELF request_Verb = 20 request_ACCESS request_Verb = 99 ) var request_Verb_name = map[int32]string{ 1: "GET", 2: "SET", 3: "DEL", 5: "REV", 6: "WAIT", 7: "NOP", 9: "WALK", 14: "GETDIR", 16: "STAT", 20: "SELF", 99: "ACCESS", } var request_Verb_value = map[string]int32{ "GET": 1, "SET": 2, "DEL": 3, "REV": 5, "WAIT": 6, "NOP": 7, "WALK": 9, "GETDIR": 14, "STAT": 16, "SELF": 20, "ACCESS": 99, } func (x request_Verb) Enum() *request_Verb { p := new(request_Verb) *p = x return p } func (x request_Verb) String() string { return proto.EnumName(request_Verb_name, int32(x)) } func (x request_Verb) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *request_Verb) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(request_Verb_value, data, "request_Verb") if err != nil { return err } *x = request_Verb(value) return nil } type response_Err int32 const ( response_OTHER response_Err = 127 response_TAG_IN_USE response_Err = 1 response_UNKNOWN_VERB response_Err = 2 response_READONLY response_Err = 3 response_TOO_LATE response_Err = 4 response_REV_MISMATCH response_Err = 5 response_BAD_PATH response_Err = 6 response_MISSING_ARG response_Err = 7 response_RANGE response_Err = 8 response_NOTDIR response_Err = 20 response_ISDIR response_Err = 21 response_NOENT response_Err = 22 ) var response_Err_name = map[int32]string{ 127: "OTHER", 1: "TAG_IN_USE", 2: "UNKNOWN_VERB", 3: "READONLY", 4: "TOO_LATE", 5: "REV_MISMATCH", 6: "BAD_PATH", 7: "MISSING_ARG", 8: "RANGE", 20: "NOTDIR", 21: "ISDIR", 22: "NOENT", } var response_Err_value = map[string]int32{ "OTHER": 127, "TAG_IN_USE": 1, "UNKNOWN_VERB": 2, "READONLY": 3, "TOO_LATE": 4, "REV_MISMATCH": 5, "BAD_PATH": 6, "MISSING_ARG": 7, "RANGE": 8, "NOTDIR": 20, "ISDIR": 21, "NOENT": 22, } func (x response_Err) Enum() *response_Err { p := new(response_Err) *p = x return p } func (x response_Err) String() string { return proto.EnumName(response_Err_name, int32(x)) } func (x response_Err) Error() string { return x.String() } func (x response_Err) MarshalJSON() ([]byte, error) { return json.Marshal(x.String()) } func (x *response_Err) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(response_Err_value, data, "response_Err") if err != nil { return err } *x = response_Err(value) return nil } type request struct { Tag *int32 `protobuf:"varint,1,opt,name=tag" json:"tag,omitempty"` Verb *request_Verb `protobuf:"varint,2,opt,name=verb,enum=doozer.request_Verb" json:"verb,omitempty"` Path *string `protobuf:"bytes,4,opt,name=path" json:"path,omitempty"` Value []byte `protobuf:"bytes,5,opt,name=value" json:"value,omitempty"` OtherTag *int32 `protobuf:"varint,6,opt,name=other_tag" json:"other_tag,omitempty"` Offset *int32 `protobuf:"varint,7,opt,name=offset" json:"offset,omitempty"` Rev *int64 `protobuf:"varint,9,opt,name=rev" json:"rev,omitempty"` XXX_unrecognized []byte `json:"-"` } func (this *request) Reset() { *this = request{} } func (this *request) String() string { return proto.CompactTextString(this) } func (*request) ProtoMessage() {} func (this *request) GetTag() int32 { if this != nil && this.Tag != nil { return *this.Tag } return 0 } func (this *request) GetVerb() request_Verb { if this != nil && this.Verb != nil { return *this.Verb } return 0 } func (this *request) GetPath() string { if this != nil && this.Path != nil { return *this.Path } return "" } func (this *request) GetValue() []byte { if this != nil { return this.Value } return nil } func (this *request) GetOtherTag() int32 { if this != nil && this.OtherTag != nil { return *this.OtherTag } return 0 } func (this *request) GetOffset() int32 { if this != nil && this.Offset != nil { return *this.Offset } return 0 } func (this *request) GetRev() int64 { if this != nil && this.Rev != nil { return *this.Rev } return 0 } type response struct { Tag *int32 `protobuf:"varint,1,opt,name=tag" json:"tag,omitempty"` Flags *int32 `protobuf:"varint,2,opt,name=flags" json:"flags,omitempty"` Rev *int64 `protobuf:"varint,3,opt,name=rev" json:"rev,omitempty"` Path *string `protobuf:"bytes,5,opt,name=path" json:"path,omitempty"` Value []byte `protobuf:"bytes,6,opt,name=value" json:"value,omitempty"` Len *int32 `protobuf:"varint,8,opt,name=len" json:"len,omitempty"` ErrCode *response_Err `protobuf:"varint,100,opt,name=err_code,enum=doozer.response_Err" json:"err_code,omitempty"` ErrDetail *string `protobuf:"bytes,101,opt,name=err_detail" json:"err_detail,omitempty"` XXX_unrecognized []byte `json:"-"` } func (this *response) Reset() { *this = response{} } func (this *response) String() string { return proto.CompactTextString(this) } func (*response) ProtoMessage() {} func (this *response) GetTag() int32 { if this != nil && this.Tag != nil { return *this.Tag } return 0 } func (this *response) GetFlags() int32 { if this != nil && this.Flags != nil { return *this.Flags } return 0 } func (this *response) GetRev() int64 { if this != nil && this.Rev != nil { return *this.Rev } return 0 } func (this *response) GetPath() string { if this != nil && this.Path != nil { return *this.Path } return "" } func (this *response) GetValue() []byte { if this != nil { return this.Value } return nil } func (this *response) GetLen() int32 { if this != nil && this.Len != nil { return *this.Len } return 0 } func (this *response) GetErrCode() response_Err { if this != nil && this.ErrCode != nil { return *this.ErrCode } return 0 } func (this *response) GetErrDetail() string { if this != nil && this.ErrDetail != nil { return *this.ErrDetail } return "" } func init() { proto.RegisterEnum("doozer.request_Verb", request_Verb_name, request_Verb_value) proto.RegisterEnum("doozer.response_Err", response_Err_name, response_Err_value) } golang-doozer-0.0~git20130909/msg.proto000066400000000000000000000020721221466622000175270ustar00rootroot00000000000000package doozer; // see doc/proto.md message Request { optional int32 tag = 1; enum Verb { GET = 1; SET = 2; DEL = 3; REV = 5; WAIT = 6; NOP = 7; WALK = 9; GETDIR = 14; STAT = 16; SELF = 20; ACCESS = 99; } optional Verb verb = 2; optional string path = 4; optional bytes value = 5; optional int32 other_tag = 6; optional int32 offset = 7; optional int64 rev = 9; } // see doc/proto.md message Response { optional int32 tag = 1; optional int32 flags = 2; optional int64 rev = 3; optional string path = 5; optional bytes value = 6; optional int32 len = 8; enum Err { // don't use value 0 OTHER = 127; TAG_IN_USE = 1; UNKNOWN_VERB = 2; READONLY = 3; TOO_LATE = 4; REV_MISMATCH = 5; BAD_PATH = 6; MISSING_ARG = 7; RANGE = 8; NOTDIR = 20; ISDIR = 21; NOENT = 22; } optional Err err_code = 100; optional string err_detail = 101; } golang-doozer-0.0~git20130909/walk.go000066400000000000000000000015121221466622000171370ustar00rootroot00000000000000package doozer type Visitor interface { VisitDir(path string, f *FileInfo) bool VisitFile(path string, f *FileInfo) } // Walk walks the file tree in revision rev, rooted at root, // analogously to Walk in package path/filepath. func Walk(c *Conn, rev int64, root string, v Visitor, errors chan<- error) { f, err := c.Statinfo(rev, root) if err != nil { if errors != nil { errors <- err } return } walk(c, rev, root, f, v, errors) } func walk(c *Conn, r int64, path string, f *FileInfo, v Visitor, errors chan<- error) { if !f.IsDir { v.VisitFile(path, f) return } if !v.VisitDir(path, f) { return } list, err := c.Getdirinfo(path, r, 0, -1) if err != nil && errors != nil { errors <- err } if path != "/" { path += "/" } for i := range list { walk(c, r, path+list[i].Name, &list[i], v, errors) } }