func zkResolveWildcards(wr *wrangler.Wrangler, args []string) ([]string, error) { zkts, ok := wr.TopoServer().(*zktopo.Server) if !ok { return args, nil } return zk.ResolveWildcards(zkts.GetZConn(), args) }
// HandlePath is part of the Explorer interface func (ex ZkExplorer) HandlePath(zkPath string, r *http.Request) *explorer.Result { result := &explorer.Result{} if zkPath == "/" { cells, err := zk.ResolveWildcards(ex.zconn, []string{"/zk/*"}) if err != nil { result.Error = err.Error() return result } for i, cell := range cells { cells[i] = cell[4:] // cut off "/zk/" } result.Children = cells sort.Strings(result.Children) return result } zkPath = "/zk" + zkPath data, _, err := ex.zconn.Get(zkPath) if err != nil { result.Error = err.Error() return result } result.Data = data children, _, err := ex.zconn.Children(zkPath) if err != nil { result.Error = err.Error() return result } result.Children = children sort.Strings(result.Children) return result }
func cmdChmod(subFlags *flag.FlagSet, args []string) error { subFlags.Parse(args) if subFlags.NArg() < 2 { return fmt.Errorf("chmod: no permission specified") } mode := subFlags.Arg(0) if mode[0] != 'n' { return fmt.Errorf("chmod: invalid mode") } addPerms := false if mode[1] == '+' { addPerms = true } else if mode[1] != '-' { return fmt.Errorf("chmod: invalid mode") } var permMask uint32 for _, c := range mode[2:] { permMask |= charPermMap[string(c)] } resolved, err := zk.ResolveWildcards(zconn, subFlags.Args()[1:]) if err != nil { return fmt.Errorf("chmod: 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) aclv, _, err := zconn.ACL(zkPath) if err != nil { hasError = true log.Warningf("chmod: cannot set access %v: %v", zkPath, err) continue } if addPerms { aclv[0].Perms |= permMask } else { aclv[0].Perms &= ^permMask } err = zconn.SetACL(zkPath, aclv, -1) if err != nil { hasError = true log.Warningf("chmod: cannot set access %v: %v", zkPath, err) continue } } if hasError { return fmt.Errorf("chmod: some paths had errors") } return nil }
func cmdChmod(args []string) { if len(args) < 2 { log.Fatal("chmod: no permission specified") } mode := args[0] if mode[0] != 'n' { log.Fatal("chmod: invalid mode") } addPerms := false if mode[1] == '+' { addPerms = true } else if mode[1] != '-' { log.Fatal("chmod: invalid mode") } var permMask uint32 for _, c := range mode[2:] { permMask |= charPermMap[string(c)] } args, err := zk.ResolveWildcards(zconn, args[1:]) if err != nil { log.Fatalf("chmod: invalid wildcards: %v", err) } if len(args) == 0 { // the wildcards didn't result in anything, we're done return } hasError := false for _, arg := range args { zkPath := fixZkPath(arg) aclv, _, err := zconn.ACL(zkPath) if err != nil { hasError = true log.Printf("chmod: cannot set access %v: %v", zkPath, err) continue } if addPerms { aclv[0].Perms |= permMask } else { aclv[0].Perms &= ^permMask } err = zconn.SetACL(zkPath, aclv, -1) if err != nil { hasError = true log.Printf("chmod: cannot set access %v: %v", zkPath, err) continue } } if hasError { os.Exit(1) } }
func cmdRm(subFlags *flag.FlagSet, args []string) error { var ( force = subFlags.Bool("f", false, "no warning on nonexistent node") recursiveDelete = subFlags.Bool("r", false, "recursive delete") forceAndRecursive = subFlags.Bool("rf", false, "shorthand for -r -f") ) subFlags.Parse(args) *force = *force || *forceAndRecursive *recursiveDelete = *recursiveDelete || *forceAndRecursive if subFlags.NArg() == 0 { return fmt.Errorf("rm: no path specified") } if *recursiveDelete { for _, arg := range subFlags.Args() { zkPath := fixZkPath(arg) if strings.Count(zkPath, "/") < 4 { return fmt.Errorf("rm: overly general path: %v", zkPath) } } } resolved, err := zk.ResolveWildcards(zconn, subFlags.Args()) if err != nil { return fmt.Errorf("rm: 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) var err error if *recursiveDelete { err = zk.DeleteRecursive(zconn, zkPath, -1) } else { err = zconn.Delete(zkPath, -1) } if err != nil && (!*force || !zookeeper.IsError(err, zookeeper.ZNONODE)) { hasError = true log.Warningf("rm: cannot delete %v: %v", zkPath, err) } } if hasError { // to be consistent with the command line 'rm -f', return // 0 if using 'zk rm -f' and the file doesn't exist. return fmt.Errorf("rm: some paths had errors") } return nil }
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 || !zookeeper.IsError(err, zookeeper.ZNONODE) { log.Warningf("stat: cannot access %v: %v", zkPath, err) } continue } fmt.Printf("Path: %s\n", zkPath) fmt.Printf("Created: %s\n", stat.CTime().Format(timeFmtMicro)) fmt.Printf("Modified: %s\n", 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 (ex ZkExplorer) HandlePath(actionRepo *ActionRepository, zkPath string, r *http.Request) interface{} { result := NewZkResult(zkPath) if zkPath == "/zk" { cells, err := zk.ResolveWildcards(ex.zconn, []string{"/zk/*"}) if err != nil { result.Error = err.Error() return result } for i, cell := range cells { cells[i] = cell[4:] // cut off "/zk/" } result.Children = cells sort.Strings(result.Children) return result } data, _, err := ex.zconn.Get(zkPath) if err != nil { result.Error = err.Error() return result } if m, _ := path.Match("/zk/global/vt/keyspaces/*", zkPath); m { keyspace := path.Base(zkPath) actionRepo.PopulateKeyspaceActions(result.Actions, keyspace) } else if m, _ := path.Match("/zk/global/vt/keyspaces/*/shards/*", zkPath); m { zkPathParts := strings.Split(zkPath, "/") keyspace := zkPathParts[5] shard := zkPathParts[7] actionRepo.PopulateShardActions(result.Actions, keyspace, shard) } else if m, _ := path.Match("/zk/*/vt/tablets/*", result.Path); m { zkPathParts := strings.Split(result.Path, "/") alias := zkPathParts[2] + "-" + zkPathParts[5] actionRepo.PopulateTabletActions(result.Actions, alias, r) ex.addTabletLinks(data, result) } result.Data = data children, _, err := ex.zconn.Children(zkPath) if err != nil { result.Error = err.Error() return result } result.Children = children sort.Strings(result.Children) return result }
func cmdRm(args []string) { if len(args) == 0 { log.Fatal("rm: no path specified") } if *recursiveDelete { for _, arg := range args { zkPath := fixZkPath(arg) if strings.Count(zkPath, "/") < 4 { log.Fatalf("rm: overly general path: %v", zkPath) } } } args, err := zk.ResolveWildcards(zconn, args) if err != nil { log.Fatalf("rm: invalid wildcards: %v", err) } if len(args) == 0 { // the wildcards didn't result in anything, we're done return } hasError := false for _, arg := range args { zkPath := fixZkPath(arg) var err error if *recursiveDelete { err = zk.DeleteRecursive(zconn, zkPath, -1) } else { err = zconn.Delete(zkPath, -1) } if err != nil { if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) { hasError = true log.Printf("rm: cannot delete %v: %v", zkPath, err) } } } if hasError { // to be consistent with the command line 'rm -f', return // 0 if using 'zk rm -f' and the file doesn't exist. os.Exit(1) } }
func cmdStat(args []string) { if len(args) == 0 { log.Fatal("stat: no path specified") } args, err := zk.ResolveWildcards(zconn, args) if err != nil { log.Fatalf("stat: invalid wildcards: %v", err) } if len(args) == 0 { // the wildcards didn't result in anything, we're done return } hasError := false for _, arg := range args { 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 || !zookeeper.IsError(err, zookeeper.ZNONODE) { log.Printf("stat: cannot access %v: %v", zkPath, err) } continue } fmt.Printf("Path: %s\n", zkPath) fmt.Printf("Created: %s\n", stat.CTime().Format(timeFmtMicro)) fmt.Printf("Modified: %s\n", 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 { os.Exit(1) } }
func cmdCat(subFlags *flag.FlagSet, args []string) error { var ( longListing = subFlags.Bool("l", false, "long listing") force = subFlags.Bool("f", false, "no warning on nonexistent node") ) subFlags.Parse(args) if subFlags.NArg() == 0 { return fmt.Errorf("cat: no path specified") } resolved, err := zk.ResolveWildcards(zconn, subFlags.Args()) if err != nil { return fmt.Errorf("cat: 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) data, _, err := zconn.Get(zkPath) if err != nil { hasError = true if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) { log.Warningf("cat: cannot access %v: %v", zkPath, err) } } else { if *longListing { fmt.Printf("%v:\n", zkPath) } fmt.Print(data) if len(data) > 0 && data[len(data)-1] != '\n' && (terminal.IsTerminal(int(os.Stdout.Fd())) || *longListing) { fmt.Print("\n") } } } if hasError { return fmt.Errorf("cat: some paths had errors") } return nil }
func cmdCat(args []string) { if len(args) == 0 { log.Fatal("cat: no path specified") } args, err := zk.ResolveWildcards(zconn, args) if err != nil { log.Fatalf("cat: invalid wildcards: %v", err) } if len(args) == 0 { // the wildcards didn't result in anything, we're done return } hasError := false for _, arg := range args { zkPath := fixZkPath(arg) data, _, err := zconn.Get(zkPath) if err != nil { hasError = true if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) { log.Printf("cat: cannot access %v: %v", zkPath, err) } } else { if *longListing { fmt.Printf("%v:\n", zkPath) } fmt.Print(data) if len(data) > 0 && data[len(data)-1] != '\n' && (terminal.IsTerminal(os.Stdout.Fd()) || *longListing) { fmt.Print("\n") } } } if hasError { os.Exit(1) } }
func init() { // handles /zk paths http.HandleFunc("/zk/", func(w http.ResponseWriter, r *http.Request) { zkTopoServ := topo.GetServerByName("zookeeper") if zkTopoServ == nil { http.Error(w, "can only look at zk with zktopo.Server", http.StatusInternalServerError) return } zconn := zkTopoServ.(*zktopo.Server).GetZConn() if err := r.ParseForm(); err != nil { httpError(w, "cannot parse form: %s", err) return } zkPath := r.URL.Path[strings.Index(r.URL.Path, "/zk"):] if cleanPath := path.Clean(zkPath); zkPath != cleanPath && zkPath != cleanPath+"/" { log.Infof("redirecting to %v", cleanPath) http.Redirect(w, r, cleanPath, http.StatusTemporaryRedirect) return } if strings.HasSuffix(zkPath, "/") { zkPath = zkPath[:len(zkPath)-1] } result := NewZkResult(zkPath) if zkPath == "/zk" { cells, err := zk.ResolveWildcards(zconn, []string{"/zk/*"}) if err != nil { httpError(w, "zk error: %v", err) return } for i, cell := range cells { cells[i] = cell[4:] // cut off "/zk/" } result.Children = cells sort.Strings(result.Children) } else { if data, _, err := zconn.Get(zkPath); err != nil { result.Error = err.Error() } else { if m, _ := path.Match("/zk/global/vt/keyspaces/*", zkPath); m { keyspace := path.Base(zkPath) actionRepo.PopulateKeyspaceActions(result.Actions, keyspace) } else if m, _ := path.Match("/zk/global/vt/keyspaces/*/shards/*", zkPath); m { zkPathParts := strings.Split(zkPath, "/") keyspace := zkPathParts[5] shard := zkPathParts[7] actionRepo.PopulateShardActions(result.Actions, keyspace, shard) } else if m, _ := path.Match("/zk/*/vt/tablets/*", result.Path); m { zkPathParts := strings.Split(result.Path, "/") alias := zkPathParts[2] + "-" + zkPathParts[5] actionRepo.PopulateTabletActions(result.Actions, alias) } result.Data = data if children, _, err := zconn.Children(zkPath); err != nil { result.Error = err.Error() } else { result.Children = children sort.Strings(result.Children) } } } templateLoader.ServeTemplate("zk.html", result, w, r) }) // adds links for keyspaces and shards funcMap["keyspace"] = func(keyspace string) template.HTML { return template.HTML("<a href=\"/zk/global/vt/keyspaces/" + keyspace + "\">" + keyspace + "</a>") } funcMap["shard"] = func(keyspace, shard string) template.HTML { return template.HTML("<a href=\"/zk/global/vt/keyspaces/" + keyspace + "/shards/" + shard + "\">" + shard + "</a>") } // add toplevel link for zookeeper indexContent.ToplevelLinks["Zookeeper Explorer"] = "/zk" }
func cmdLs(subFlags *flag.FlagSet, args []string) error { var ( longListing = subFlags.Bool("l", false, "long listing") directoryListing = subFlags.Bool("d", false, "list directory instead of contents") force = subFlags.Bool("f", false, "no warning on nonexistent node") recursiveListing = subFlags.Bool("R", false, "recursive listing") ) subFlags.Parse(args) if subFlags.NArg() == 0 { return fmt.Errorf("ls: no path specified") } // FIXME(szopa): shadowing? resolved, err := zk.ResolveWildcards(zconn, subFlags.Args()) if err != nil { return fmt.Errorf("ls: invalid wildcards: %v", err) } if len(resolved) == 0 { // the wildcards didn't result in anything, we're // done. return nil } hasError := false needsHeader := len(resolved) > 1 && !*directoryListing for _, arg := range resolved { zkPath := fixZkPath(arg) var children []string var err error isDir := true if *directoryListing { children = []string{""} isDir = false } else if *recursiveListing { children, err = zk.ChildrenRecursive(zconn, zkPath) } else { children, _, err = zconn.Children(zkPath) // Assume this is a file node if it has no children. if len(children) == 0 { children = []string{""} isDir = false } } if err != nil { hasError = true if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) { log.Warningf("ls: cannot access %v: %v", zkPath, err) } } // Show the full path when it helps. showFullPath := false if *recursiveListing { showFullPath = true } else if *longListing && (*directoryListing || !isDir) { showFullPath = true } if needsHeader { fmt.Printf("%v:\n", zkPath) } if len(children) > 0 { if *longListing && isDir { fmt.Printf("total: %v\n", len(children)) } sort.Strings(children) stats := make([]zk.Stat, len(children)) wg := sync.WaitGroup{} f := func(i int) { localPath := path.Join(zkPath, children[i]) stat, err := zconn.Exists(localPath) if err != nil { if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) { log.Warningf("ls: cannot access: %v: %v", localPath, err) } } else { stats[i] = stat } wg.Done() } for i := range children { wg.Add(1) go f(i) } wg.Wait() for i, child := range children { localPath := path.Join(zkPath, child) if stat := stats[i]; stat != nil { fmtPath(stat, localPath, showFullPath, *longListing) } } } if needsHeader { fmt.Println() } } if hasError { return fmt.Errorf("ls: some paths had errors") } return nil }
func cmdLs(args []string) { if len(args) == 0 { log.Fatal("ls: no path specified") } args, err := zk.ResolveWildcards(zconn, args) if err != nil { log.Fatalf("ls: invalid wildcards: %v", err) } if len(args) == 0 { // the wildcards didn't result in anything, we're done return } hasError := false needsHeader := len(args) > 1 && !*directoryListing for _, arg := range args { zkPath := fixZkPath(arg) var children []string var err error isDir := true if *directoryListing { children = []string{""} isDir = false } else if *recursiveListing { children, err = zk.ChildrenRecursive(zconn, zkPath) } else { children, _, err = zconn.Children(zkPath) // Assume this is a file node if it has no children. if len(children) == 0 { children = []string{""} isDir = false } } if err != nil { hasError = true if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) { log.Printf("ls: cannot access %v: %v", zkPath, err) } } // Show the full path when it helps. showFullPath := false if *recursiveListing { showFullPath = true } else if *longListing && (*directoryListing || !isDir) { showFullPath = true } if needsHeader { fmt.Printf("%v:\n", zkPath) } if len(children) > 0 { if *longListing && isDir { fmt.Printf("total: %v\n", len(children)) } sort.Strings(children) stats := make([]zk.Stat, len(children)) wg := sync.WaitGroup{} f := func(i int) { localPath := path.Join(zkPath, children[i]) stat, err := zconn.Exists(localPath) if err != nil { if !*force || !zookeeper.IsError(err, zookeeper.ZNONODE) { log.Printf("ls: cannot access: %v: %v", localPath, err) } } else { stats[i] = stat } wg.Done() } for i := range children { wg.Add(1) go f(i) } wg.Wait() for i, child := range children { localPath := path.Join(zkPath, child) if stat := stats[i]; stat != nil { fmtPath(stat, localPath, showFullPath) } } } if needsHeader { fmt.Println() } } if hasError { os.Exit(1) } }
func main() { flag.Parse() templateLoader := NewTemplateLoader(*templateDir, dummyTemplate, *debug) ts := topo.GetServer() defer topo.CloseServers() wr := wrangler.New(ts, 30*time.Second, 30*time.Second) actionRepo := NewActionRepository(wr) const ( keyspacePath = "/zk/global/vt/keyspaces/*" shardPath = "/zk/global/vt/keyspaces/*/shards/*" tabletPath = "/zk/*/vt/tablets/*" ) actionRepo.Register(keyspacePath, "ValidateKeyspace", func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) { return "", wr.ValidateKeyspace(path.Base(zkPath), false) }) actionRepo.Register(keyspacePath, "ValidateSchemaKeyspace", func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) { return "", wr.ValidateSchemaKeyspace(path.Base(zkPath), false) }) actionRepo.Register(keyspacePath, "ValidateVersionKeyspace", func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) { return "", wr.ValidateVersionKeyspace(path.Base(zkPath)) }) actionRepo.Register(keyspacePath, "ValidatePermissionsKeyspace", func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) { return "", wr.ValidatePermissionsKeyspace(path.Base(zkPath)) }) actionRepo.Register(shardPath, "ValidateShard", func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) { zkPathParts := strings.Split(zkPath, "/") return "", wr.ValidateShard(zkPathParts[5], zkPathParts[7], false) }) actionRepo.Register(shardPath, "ValidateSchemaShard", func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) { zkPathParts := strings.Split(zkPath, "/") return "", wr.ValidateSchemaShard(zkPathParts[5], zkPathParts[7], false) }) actionRepo.Register(shardPath, "ValidateVersionShard", func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) { zkPathParts := strings.Split(zkPath, "/") return "", wr.ValidateVersionShard(zkPathParts[5], zkPathParts[7]) }) actionRepo.Register(shardPath, "ValidatePermissionsShard", func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) { zkPathParts := strings.Split(zkPath, "/") return "", wr.ValidatePermissionsShard(zkPathParts[5], zkPathParts[7]) }) actionRepo.Register(tabletPath, "RpcPing", func(wr *wrangler.Wrangler, zkPath string, r *http.Request) (string, error) { zkPathParts := strings.Split(zkPath, "/") alias, err := topo.ParseTabletAliasString(zkPathParts[2] + "-" + zkPathParts[5]) if err != nil { return "", err } return "", wr.ActionInitiator().RpcPing(alias, 10*time.Second) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { tmpl, err := templateLoader.Lookup("index.html") if err != nil { httpError(w, "error in template loader: %v", err) return } if err := tmpl.Execute(w, nil); err != nil { httpError(w, "error executing template", err) } }) http.HandleFunc("/actions", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpError(w, "cannot parse form: %s", err) return } action := r.FormValue("action") if action == "" { http.Error(w, "no action provided", http.StatusBadRequest) return } zkPath := r.FormValue("zkpath") if zkPath == "" { http.Error(w, "no zookeeper path provided", http.StatusBadRequest) return } result := actionRepo.Apply(action, zkPath, r) templateLoader.ServeTemplate("action.html", result, w, r) }) http.HandleFunc("/dbtopo", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpError(w, "cannot parse form: %s", err) return } result := DbTopologyResult{} topology, err := wr.DbTopology() if err != nil { result.Error = err.Error() } else { result.Topology = topology } templateLoader.ServeTemplate("dbtopo.html", result, w, r) }) http.HandleFunc("/zk/", func(w http.ResponseWriter, r *http.Request) { zkTopoServ := topo.GetServerByName("zookeeper") if zkTopoServ == nil { http.Error(w, "can only look at zk with zktopo.Server", http.StatusInternalServerError) return } zconn := zkTopoServ.(*zktopo.Server).GetZConn() if err := r.ParseForm(); err != nil { httpError(w, "cannot parse form: %s", err) return } zkPath := r.URL.Path[strings.Index(r.URL.Path, "/zk"):] if cleanPath := path.Clean(zkPath); zkPath != cleanPath && zkPath != cleanPath+"/" { relog.Info("redirecting to %v", cleanPath) http.Redirect(w, r, cleanPath, http.StatusTemporaryRedirect) return } if strings.HasSuffix(zkPath, "/") { zkPath = zkPath[:len(zkPath)-1] } result := NewZkResult(zkPath) if zkPath == "/zk" { cells, err := zk.ResolveWildcards(zconn, []string{"/zk/*"}) if err != nil { httpError(w, "zk error: %v", err) return } for i, cell := range cells { cells[i] = cell[4:] // cut off "/zk/" } result.Children = cells } else { if data, _, err := zconn.Get(zkPath); err != nil { result.Error = err.Error() } else { actionRepo.PopulateAvailableActions(result) result.Data = data if children, _, err := zconn.Children(zkPath); err != nil { result.Error = err.Error() } else { result.Children = children } } } templateLoader.ServeTemplate("zk.html", result, w, r) }) relog.Fatal("%s", http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)) }