func cmdStat(subFlags *flag.FlagSet, args []string) error { var ( force = subFlags.Bool("f", false, "no warning on nonexistent node") ) subFlags.Parse(args) if subFlags.NArg() == 0 { return fmt.Errorf("stat: no path specified") } resolved, err := zk.ResolveWildcards(zconn, subFlags.Args()) if err != nil { return fmt.Errorf("stat: invalid wildcards: %v", err) } if len(resolved) == 0 { // the wildcards didn't result in anything, we're done return nil } hasError := false for _, arg := range resolved { zkPath := fixZkPath(arg) acls, stat, err := zconn.ACL(zkPath) if stat == nil { err = fmt.Errorf("no such node") } if err != nil { hasError = true if !*force || err != zookeeper.ErrNoNode { log.Warningf("stat: cannot access %v: %v", zkPath, err) } continue } fmt.Printf("Path: %s\n", zkPath) fmt.Printf("Created: %s\n", zk.Time(stat.Ctime).Format(timeFmtMicro)) fmt.Printf("Modified: %s\n", zk.Time(stat.Mtime).Format(timeFmtMicro)) fmt.Printf("Size: %v\n", stat.DataLength) fmt.Printf("Children: %v\n", stat.NumChildren) fmt.Printf("Version: %v\n", stat.Version) fmt.Printf("Ephemeral: %v\n", stat.EphemeralOwner) fmt.Printf("ACL:\n") for _, acl := range acls { fmt.Printf(" %v:%v %v\n", acl.Scheme, acl.ID, fmtAcl(acl)) } } if hasError { return fmt.Errorf("stat: some paths had errors") } return nil }
func fmtPath(stat *zookeeper.Stat, zkPath string, showFullPath bool, longListing bool) { var name, perms string if !showFullPath { name = path.Base(zkPath) } else { name = zkPath } if longListing { if stat.NumChildren > 0 { // FIXME(msolomon) do permissions check? perms = "drwxrwxrwx" if stat.DataLength > 0 { // give a visual indication that this node has data as well as children perms = "nrw-rw-rw-" } } else if stat.EphemeralOwner != 0 { perms = "erw-rw-rw-" } else { perms = "-rw-rw-rw-" } // always print the Local version of the time. zookeeper's // go / C library would return a local time anyway, but // might as well be sure. fmt.Printf("%v %v %v % 8v % 20v %v\n", perms, "zk", "zk", stat.DataLength, zk.Time(stat.Mtime).Local().Format(timeFmt), name) } else { fmt.Printf("%v\n", name) } }
// Store a zk tree in a zip archive. This won't be immediately useful to // zip tools since even "directories" can contain data. func cmdZip(subFlags *flag.FlagSet, args []string) error { subFlags.Parse(args) if subFlags.NArg() < 2 { return fmt.Errorf("zip: need to specify source and destination paths") } dstPath := subFlags.Arg(subFlags.NArg() - 1) paths := subFlags.Args()[:len(args)-1] if !strings.HasSuffix(dstPath, ".zip") { return fmt.Errorf("zip: need to specify destination .zip path: %v", dstPath) } zipFile, err := os.Create(dstPath) if err != nil { return fmt.Errorf("zip: error %v", err) } wg := sync.WaitGroup{} items := make(chan *zkItem, 64) for _, arg := range paths { zkPath := fixZkPath(arg) children, err := zk.ChildrenRecursive(zconn, zkPath) if err != nil { return fmt.Errorf("zip: error %v", err) } for _, child := range children { toAdd := path.Join(zkPath, child) wg.Add(1) go func() { data, stat, err := zconn.Get(toAdd) items <- &zkItem{toAdd, data, stat, err} wg.Done() }() } } go func() { wg.Wait() close(items) }() zipWriter := zip.NewWriter(zipFile) for item := range items { path, data, stat, err := item.path, item.data, item.stat, item.err if err != nil { return fmt.Errorf("zip: get failed: %v", err) } // Skip ephemerals - not sure why you would archive them. if stat.EphemeralOwner > 0 { continue } fi := &zip.FileHeader{Name: path, Method: zip.Deflate} fi.SetModTime(zk.Time(stat.Mtime)) f, err := zipWriter.CreateHeader(fi) if err != nil { return fmt.Errorf("zip: create failed: %v", err) } _, err = f.Write(data) if err != nil { return fmt.Errorf("zip: create failed: %v", err) } } err = zipWriter.Close() if err != nil { return fmt.Errorf("zip: close failed: %v", err) } zipFile.Close() return nil }