ソースを参照

libnetwork/diagnostic: use standard http.Handler

We don't need C-style callback functions which accept a void* context
parameter: Go has closures. Drop the unnecessary httpHandlerCustom type
and refactor the diagnostic server handler functions into closures which
capture whatever context they need implicitly.

If the node leaves and rejoins a swarm, the cluster agent and its
associated NetworkDB are discarded and replaced with new instances. Upon
rejoin, the agent registers its NetworkDB instance with the diagnostic
server. These handlers would all conflict with the handlers registered
by the previous NetworkDB instance. Attempting to register a second
handler on a http.ServeMux with the same pattern will panic, which the
diagnostic server would historically deal with by ignoring the duplicate
handler registration. Consequently, the first NetworkDB instance to be
registered would "stick" to the diagnostic server for the lifetime of
the process, even after it is replaced with another instance. Improve
duplicate-handler registration such that the most recently-registered
handler for a pattern is used for all subsequent requests.

Signed-off-by: Cory Snider <csnider@mirantis.com>
Cory Snider 1 年間 前
コミット
424ae36046

+ 1 - 1
libnetwork/agent.go

@@ -332,7 +332,7 @@ func (c *Controller) agentInit(listenAddr, bindAddrOrInterface, advertiseAddr, d
 	}
 
 	// Register the diagnostic handlers
-	c.DiagnosticServer.RegisterHandler(nDB, networkdb.NetDbPaths2Func)
+	nDB.RegisterDiagnosticHandlers(c.DiagnosticServer)
 
 	var cancelList []func()
 	ch, cancel := nDB.Watch(libnetworkEPTable, "")

+ 4 - 8
libnetwork/cmd/networkdb-test/dbserver/ndbServer.go

@@ -21,11 +21,7 @@ var (
 	ipAddr string
 )
 
-var testerPaths2Func = map[string]diagnostic.HTTPHandlerFunc{
-	"/myip": ipaddress,
-}
-
-func ipaddress(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func ipaddress(w http.ResponseWriter, r *http.Request) {
 	fmt.Fprintf(w, "%s\n", ipAddr)
 }
 
@@ -63,9 +59,9 @@ func Server(args []string) {
 	}
 
 	// Register network db handlers
-	server.RegisterHandler(nDB, networkdb.NetDbPaths2Func)
-	server.RegisterHandler(nil, testerPaths2Func)
-	server.RegisterHandler(nDB, dummyclient.DummyClientPaths2Func)
+	nDB.RegisterDiagnosticHandlers(server)
+	server.HandleFunc("/myip", ipaddress)
+	dummyclient.RegisterDiagnosticHandlers(server, nDB)
 	server.EnableDiagnostic("", port)
 	// block here
 	select {}

+ 22 - 20
libnetwork/cmd/networkdb-test/dummyclient/dummyClient.go

@@ -11,10 +11,13 @@ import (
 	events "github.com/docker/go-events"
 )
 
-// DummyClientPaths2Func exported paths for the client
-var DummyClientPaths2Func = map[string]diagnostic.HTTPHandlerFunc{
-	"/watchtable":          watchTable,
-	"/watchedtableentries": watchTableEntries,
+type Mux interface {
+	HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
+}
+
+func RegisterDiagnosticHandlers(mux Mux, nDB *networkdb.NetworkDB) {
+	mux.HandleFunc("/watchtable", watchTable(nDB))
+	mux.HandleFunc("/watchedtableentries", watchTableEntries)
 }
 
 const (
@@ -28,23 +31,22 @@ type tableHandler struct {
 
 var clientWatchTable = map[string]tableHandler{}
 
-func watchTable(ctx interface{}, w http.ResponseWriter, r *http.Request) {
-	r.ParseForm() //nolint:errcheck
-	diagnostic.DebugHTTPForm(r)
-	if len(r.Form["tname"]) < 1 {
-		rsp := diagnostic.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name", r.URL.Path))
-		diagnostic.HTTPReply(w, rsp, &diagnostic.JSONOutput{}) //nolint:errcheck
-		return
-	}
+func watchTable(nDB *networkdb.NetworkDB) func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		r.ParseForm() //nolint:errcheck
+		diagnostic.DebugHTTPForm(r)
+		if len(r.Form["tname"]) < 1 {
+			rsp := diagnostic.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name", r.URL.Path))
+			diagnostic.HTTPReply(w, rsp, &diagnostic.JSONOutput{}) //nolint:errcheck
+			return
+		}
 
-	tableName := r.Form["tname"][0]
-	if _, ok := clientWatchTable[tableName]; ok {
-		fmt.Fprintf(w, "OK\n")
-		return
-	}
+		tableName := r.Form["tname"][0]
+		if _, ok := clientWatchTable[tableName]; ok {
+			fmt.Fprintf(w, "OK\n")
+			return
+		}
 
-	nDB, ok := ctx.(*networkdb.NetworkDB)
-	if ok {
 		ch, cancel := nDB.Watch(tableName, "")
 		clientWatchTable[tableName] = tableHandler{cancelWatch: cancel, entries: make(map[string]string)}
 		go handleTableEvents(tableName, ch)
@@ -53,7 +55,7 @@ func watchTable(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 	}
 }
 
-func watchTableEntries(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func watchTableEntries(w http.ResponseWriter, r *http.Request) {
 	r.ParseForm() //nolint:errcheck
 	diagnostic.DebugHTTPForm(r)
 	if len(r.Form["tname"]) < 1 {

+ 39 - 47
libnetwork/diagnostic/server.go

@@ -16,58 +16,53 @@ import (
 	"github.com/docker/docker/pkg/stack"
 )
 
-// HTTPHandlerFunc TODO
-type HTTPHandlerFunc func(interface{}, http.ResponseWriter, *http.Request)
-
-type httpHandlerCustom struct {
-	ctx interface{}
-	F   func(interface{}, http.ResponseWriter, *http.Request)
-}
-
-// ServeHTTP TODO
-func (h httpHandlerCustom) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	h.F(h.ctx, w, r)
-}
-
-var diagPaths2Func = map[string]HTTPHandlerFunc{
-	"/":          notImplemented,
-	"/help":      help,
-	"/ready":     ready,
-	"/stackdump": stackTrace,
-}
-
 // Server when the debug is enabled exposes a
 // This data structure is protected by the Agent mutex so does not require and additional mutex here
 type Server struct {
-	mu                sync.Mutex
-	enable            int32
-	srv               *http.Server
-	port              int
-	mux               *http.ServeMux
-	registeredHanders map[string]bool
+	mu       sync.Mutex
+	enable   int32
+	srv      *http.Server
+	port     int
+	mux      *http.ServeMux
+	handlers map[string]http.Handler
 }
 
 // New creates a new diagnostic server
 func New() *Server {
 	s := &Server{
-		mux:               http.NewServeMux(),
-		registeredHanders: make(map[string]bool),
+		mux:      http.NewServeMux(),
+		handlers: make(map[string]http.Handler),
 	}
-	s.RegisterHandler(s, diagPaths2Func)
+	s.HandleFunc("/", notImplemented)
+	s.HandleFunc("/help", s.help)
+	s.HandleFunc("/ready", ready)
+	s.HandleFunc("/stackdump", stackTrace)
 	return s
 }
 
-// RegisterHandler allows to register new handlers to the mux and to a specific path
-func (s *Server) RegisterHandler(ctx interface{}, hdlrs map[string]HTTPHandlerFunc) {
+// Handle registers the handler for the given pattern,
+// replacing any existing handler.
+func (s *Server) Handle(pattern string, handler http.Handler) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	for path, fun := range hdlrs {
-		if _, ok := s.registeredHanders[path]; ok {
-			continue
-		}
-		s.mux.Handle(path, httpHandlerCustom{ctx, fun})
-		s.registeredHanders[path] = true
+	if _, ok := s.handlers[pattern]; !ok {
+		// Register a handler on the mux which allows the underlying handler to
+		// be dynamically switched out. The http.ServeMux will panic if one
+		// attempts to register a handler for the same pattern twice.
+		s.mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
+			s.mu.Lock()
+			h := s.handlers[pattern]
+			s.mu.Unlock()
+			h.ServeHTTP(w, r)
+		})
 	}
+	s.handlers[pattern] = handler
+}
+
+// Handle registers the handler function for the given pattern,
+// replacing any existing handler.
+func (s *Server) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
+	s.Handle(pattern, http.HandlerFunc(handler))
 }
 
 // ServeHTTP this is the method called bu the ListenAndServe, and is needed to allow us to
@@ -123,7 +118,7 @@ func (s *Server) IsDiagnosticEnabled() bool {
 	return s.enable == 1
 }
 
-func notImplemented(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func notImplemented(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	_, jsonOutput := ParseHTTPFormOptions(r)
 	rsp := WrongCommand("not implemented", fmt.Sprintf("URL path: %s no method implemented check /help\n", r.URL.Path))
@@ -139,7 +134,7 @@ func notImplemented(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 	_, _ = HTTPReply(w, rsp, jsonOutput)
 }
 
-func help(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (s *Server) help(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	_, jsonOutput := ParseHTTPFormOptions(r)
 
@@ -151,17 +146,14 @@ func help(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 		"url":       r.URL.String(),
 	}).Info("help done")
 
-	n, ok := ctx.(*Server)
 	var result string
-	if ok {
-		for path := range n.registeredHanders {
-			result += fmt.Sprintf("%s\n", path)
-		}
-		_, _ = HTTPReply(w, CommandSucceed(&StringCmd{Info: result}), jsonOutput)
+	for path := range s.handlers {
+		result += fmt.Sprintf("%s\n", path)
 	}
+	_, _ = HTTPReply(w, CommandSucceed(&StringCmd{Info: result}), jsonOutput)
 }
 
-func ready(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func ready(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	_, jsonOutput := ParseHTTPFormOptions(r)
 
@@ -175,7 +167,7 @@ func ready(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 	_, _ = HTTPReply(w, CommandSucceed(&StringCmd{Info: "OK"}), jsonOutput)
 }
 
-func stackTrace(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func stackTrace(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	_, jsonOutput := ParseHTTPFormOptions(r)
 

+ 124 - 176
libnetwork/networkdb/networkdbdiagnostic.go

@@ -17,22 +17,25 @@ const (
 	dbNotAvailable   = "database not available"
 )
 
-// NetDbPaths2Func TODO
-var NetDbPaths2Func = map[string]diagnostic.HTTPHandlerFunc{
-	"/join":         dbJoin,
-	"/networkpeers": dbPeers,
-	"/clusterpeers": dbClusterPeers,
-	"/joinnetwork":  dbJoinNetwork,
-	"/leavenetwork": dbLeaveNetwork,
-	"/createentry":  dbCreateEntry,
-	"/updateentry":  dbUpdateEntry,
-	"/deleteentry":  dbDeleteEntry,
-	"/getentry":     dbGetEntry,
-	"/gettable":     dbGetTable,
-	"/networkstats": dbNetworkStats,
+type Mux interface {
+	HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
 }
 
-func dbJoin(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) RegisterDiagnosticHandlers(m Mux) {
+	m.HandleFunc("/join", nDB.dbJoin)
+	m.HandleFunc("/networkpeers", nDB.dbPeers)
+	m.HandleFunc("/clusterpeers", nDB.dbClusterPeers)
+	m.HandleFunc("/joinnetwork", nDB.dbJoinNetwork)
+	m.HandleFunc("/leavenetwork", nDB.dbLeaveNetwork)
+	m.HandleFunc("/createentry", nDB.dbCreateEntry)
+	m.HandleFunc("/updateentry", nDB.dbUpdateEntry)
+	m.HandleFunc("/deleteentry", nDB.dbDeleteEntry)
+	m.HandleFunc("/getentry", nDB.dbGetEntry)
+	m.HandleFunc("/gettable", nDB.dbGetTable)
+	m.HandleFunc("/networkstats", nDB.dbNetworkStats)
+}
+
+func (nDB *NetworkDB) dbJoin(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	_, json := diagnostic.ParseHTTPFormOptions(r)
@@ -53,24 +56,19 @@ func dbJoin(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		err := nDB.Join(strings.Split(r.Form["members"][0], ","))
-		if err != nil {
-			rsp := diagnostic.FailCommand(fmt.Errorf("%s error in the DB join %s", r.URL.Path, err))
-			logger.WithError(err).Error("join cluster failed")
-			diagnostic.HTTPReply(w, rsp, json)
-			return
-		}
-
-		logger.Info("join cluster done")
-		diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
+	err := nDB.Join(strings.Split(r.Form["members"][0], ","))
+	if err != nil {
+		rsp := diagnostic.FailCommand(fmt.Errorf("%s error in the DB join %s", r.URL.Path, err))
+		logger.WithError(err).Error("join cluster failed")
+		diagnostic.HTTPReply(w, rsp, json)
 		return
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+
+	logger.Info("join cluster done")
+	diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
 }
 
-func dbPeers(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) dbPeers(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	_, json := diagnostic.ParseHTTPFormOptions(r)
@@ -91,25 +89,20 @@ func dbPeers(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		peers := nDB.Peers(r.Form["nid"][0])
-		rsp := &diagnostic.TableObj{Length: len(peers)}
-		for i, peerInfo := range peers {
-			if peerInfo.IP == "unknown" {
-				rsp.Elements = append(rsp.Elements, &diagnostic.PeerEntryObj{Index: i, Name: "orphan-" + peerInfo.Name, IP: peerInfo.IP})
-			} else {
-				rsp.Elements = append(rsp.Elements, &diagnostic.PeerEntryObj{Index: i, Name: peerInfo.Name, IP: peerInfo.IP})
-			}
+	peers := nDB.Peers(r.Form["nid"][0])
+	rsp := &diagnostic.TableObj{Length: len(peers)}
+	for i, peerInfo := range peers {
+		if peerInfo.IP == "unknown" {
+			rsp.Elements = append(rsp.Elements, &diagnostic.PeerEntryObj{Index: i, Name: "orphan-" + peerInfo.Name, IP: peerInfo.IP})
+		} else {
+			rsp.Elements = append(rsp.Elements, &diagnostic.PeerEntryObj{Index: i, Name: peerInfo.Name, IP: peerInfo.IP})
 		}
-		logger.WithField("response", fmt.Sprintf("%+v", rsp)).Info("network peers done")
-		diagnostic.HTTPReply(w, diagnostic.CommandSucceed(rsp), json)
-		return
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+	logger.WithField("response", fmt.Sprintf("%+v", rsp)).Info("network peers done")
+	diagnostic.HTTPReply(w, diagnostic.CommandSucceed(rsp), json)
 }
 
-func dbClusterPeers(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) dbClusterPeers(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	_, json := diagnostic.ParseHTTPFormOptions(r)
@@ -123,21 +116,16 @@ func dbClusterPeers(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 	})
 	logger.Info("cluster peers")
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		peers := nDB.ClusterPeers()
-		rsp := &diagnostic.TableObj{Length: len(peers)}
-		for i, peerInfo := range peers {
-			rsp.Elements = append(rsp.Elements, &diagnostic.PeerEntryObj{Index: i, Name: peerInfo.Name, IP: peerInfo.IP})
-		}
-		logger.WithField("response", fmt.Sprintf("%+v", rsp)).Info("cluster peers done")
-		diagnostic.HTTPReply(w, diagnostic.CommandSucceed(rsp), json)
-		return
+	peers := nDB.ClusterPeers()
+	rsp := &diagnostic.TableObj{Length: len(peers)}
+	for i, peerInfo := range peers {
+		rsp.Elements = append(rsp.Elements, &diagnostic.PeerEntryObj{Index: i, Name: peerInfo.Name, IP: peerInfo.IP})
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+	logger.WithField("response", fmt.Sprintf("%+v", rsp)).Info("cluster peers done")
+	diagnostic.HTTPReply(w, diagnostic.CommandSucceed(rsp), json)
 }
 
-func dbCreateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) dbCreateEntry(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	unsafe, json := diagnostic.ParseHTTPFormOptions(r)
@@ -176,22 +164,17 @@ func dbCreateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		if err := nDB.CreateEntry(tname, nid, key, decodedValue); err != nil {
-			rsp := diagnostic.FailCommand(err)
-			diagnostic.HTTPReply(w, rsp, json)
-			logger.WithError(err).Error("create entry failed")
-			return
-		}
-		logger.Info("create entry done")
-		diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
+	if err := nDB.CreateEntry(tname, nid, key, decodedValue); err != nil {
+		rsp := diagnostic.FailCommand(err)
+		diagnostic.HTTPReply(w, rsp, json)
+		logger.WithError(err).Error("create entry failed")
 		return
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+	logger.Info("create entry done")
+	diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
 }
 
-func dbUpdateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) dbUpdateEntry(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	unsafe, json := diagnostic.ParseHTTPFormOptions(r)
@@ -230,21 +213,16 @@ func dbUpdateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		if err := nDB.UpdateEntry(tname, nid, key, decodedValue); err != nil {
-			logger.WithError(err).Error("update entry failed")
-			diagnostic.HTTPReply(w, diagnostic.FailCommand(err), json)
-			return
-		}
-		logger.Info("update entry done")
-		diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
+	if err := nDB.UpdateEntry(tname, nid, key, decodedValue); err != nil {
+		logger.WithError(err).Error("update entry failed")
+		diagnostic.HTTPReply(w, diagnostic.FailCommand(err), json)
 		return
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+	logger.Info("update entry done")
+	diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
 }
 
-func dbDeleteEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) dbDeleteEntry(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	_, json := diagnostic.ParseHTTPFormOptions(r)
@@ -271,22 +249,17 @@ func dbDeleteEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 	nid := r.Form["nid"][0]
 	key := r.Form["key"][0]
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		err := nDB.DeleteEntry(tname, nid, key)
-		if err != nil {
-			logger.WithError(err).Error("delete entry failed")
-			diagnostic.HTTPReply(w, diagnostic.FailCommand(err), json)
-			return
-		}
-		logger.Info("delete entry done")
-		diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
+	err := nDB.DeleteEntry(tname, nid, key)
+	if err != nil {
+		logger.WithError(err).Error("delete entry failed")
+		diagnostic.HTTPReply(w, diagnostic.FailCommand(err), json)
 		return
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+	logger.Info("delete entry done")
+	diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
 }
 
-func dbGetEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) dbGetEntry(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	unsafe, json := diagnostic.ParseHTTPFormOptions(r)
@@ -313,31 +286,26 @@ func dbGetEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 	nid := r.Form["nid"][0]
 	key := r.Form["key"][0]
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		value, err := nDB.GetEntry(tname, nid, key)
-		if err != nil {
-			logger.WithError(err).Error("get entry failed")
-			diagnostic.HTTPReply(w, diagnostic.FailCommand(err), json)
-			return
-		}
-
-		var encodedValue string
-		if unsafe {
-			encodedValue = string(value)
-		} else {
-			encodedValue = base64.StdEncoding.EncodeToString(value)
-		}
-
-		rsp := &diagnostic.TableEntryObj{Key: key, Value: encodedValue}
-		logger.WithField("response", fmt.Sprintf("%+v", rsp)).Info("get entry done")
-		diagnostic.HTTPReply(w, diagnostic.CommandSucceed(rsp), json)
+	value, err := nDB.GetEntry(tname, nid, key)
+	if err != nil {
+		logger.WithError(err).Error("get entry failed")
+		diagnostic.HTTPReply(w, diagnostic.FailCommand(err), json)
 		return
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+
+	var encodedValue string
+	if unsafe {
+		encodedValue = string(value)
+	} else {
+		encodedValue = base64.StdEncoding.EncodeToString(value)
+	}
+
+	rsp := &diagnostic.TableEntryObj{Key: key, Value: encodedValue}
+	logger.WithField("response", fmt.Sprintf("%+v", rsp)).Info("get entry done")
+	diagnostic.HTTPReply(w, diagnostic.CommandSucceed(rsp), json)
 }
 
-func dbJoinNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) dbJoinNetwork(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	_, json := diagnostic.ParseHTTPFormOptions(r)
@@ -360,21 +328,16 @@ func dbJoinNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 
 	nid := r.Form["nid"][0]
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		if err := nDB.JoinNetwork(nid); err != nil {
-			logger.WithError(err).Error("join network failed")
-			diagnostic.HTTPReply(w, diagnostic.FailCommand(err), json)
-			return
-		}
-		logger.Info("join network done")
-		diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
+	if err := nDB.JoinNetwork(nid); err != nil {
+		logger.WithError(err).Error("join network failed")
+		diagnostic.HTTPReply(w, diagnostic.FailCommand(err), json)
 		return
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+	logger.Info("join network done")
+	diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
 }
 
-func dbLeaveNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) dbLeaveNetwork(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	_, json := diagnostic.ParseHTTPFormOptions(r)
@@ -397,21 +360,16 @@ func dbLeaveNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 
 	nid := r.Form["nid"][0]
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		if err := nDB.LeaveNetwork(nid); err != nil {
-			logger.WithError(err).Error("leave network failed")
-			diagnostic.HTTPReply(w, diagnostic.FailCommand(err), json)
-			return
-		}
-		logger.Info("leave network done")
-		diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
+	if err := nDB.LeaveNetwork(nid); err != nil {
+		logger.WithError(err).Error("leave network failed")
+		diagnostic.HTTPReply(w, diagnostic.FailCommand(err), json)
 		return
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+	logger.Info("leave network done")
+	diagnostic.HTTPReply(w, diagnostic.CommandSucceed(nil), json)
 }
 
-func dbGetTable(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) dbGetTable(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	unsafe, json := diagnostic.ParseHTTPFormOptions(r)
@@ -436,35 +394,30 @@ func dbGetTable(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 	tname := r.Form["tname"][0]
 	nid := r.Form["nid"][0]
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		table := nDB.GetTableByNetwork(tname, nid)
-		rsp := &diagnostic.TableObj{Length: len(table)}
-		i := 0
-		for k, v := range table {
-			var encodedValue string
-			if unsafe {
-				encodedValue = string(v.Value)
-			} else {
-				encodedValue = base64.StdEncoding.EncodeToString(v.Value)
-			}
-			rsp.Elements = append(rsp.Elements,
-				&diagnostic.TableEntryObj{
-					Index: i,
-					Key:   k,
-					Value: encodedValue,
-					Owner: v.owner,
-				})
-			i++
+	table := nDB.GetTableByNetwork(tname, nid)
+	rsp := &diagnostic.TableObj{Length: len(table)}
+	i := 0
+	for k, v := range table {
+		var encodedValue string
+		if unsafe {
+			encodedValue = string(v.Value)
+		} else {
+			encodedValue = base64.StdEncoding.EncodeToString(v.Value)
 		}
-		logger.WithField("response", fmt.Sprintf("%+v", rsp)).Info("get table done")
-		diagnostic.HTTPReply(w, diagnostic.CommandSucceed(rsp), json)
-		return
+		rsp.Elements = append(rsp.Elements,
+			&diagnostic.TableEntryObj{
+				Index: i,
+				Key:   k,
+				Value: encodedValue,
+				Owner: v.owner,
+			})
+		i++
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+	logger.WithField("response", fmt.Sprintf("%+v", rsp)).Info("get table done")
+	diagnostic.HTTPReply(w, diagnostic.CommandSucceed(rsp), json)
 }
 
-func dbNetworkStats(ctx interface{}, w http.ResponseWriter, r *http.Request) {
+func (nDB *NetworkDB) dbNetworkStats(w http.ResponseWriter, r *http.Request) {
 	_ = r.ParseForm()
 	diagnostic.DebugHTTPForm(r)
 	_, json := diagnostic.ParseHTTPFormOptions(r)
@@ -485,24 +438,19 @@ func dbNetworkStats(ctx interface{}, w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	nDB, ok := ctx.(*NetworkDB)
-	if ok {
-		nDB.RLock()
-		networks := nDB.networks[nDB.config.NodeID]
-		network, ok := networks[r.Form["nid"][0]]
-
-		entries := -1
-		qLen := -1
-		if ok {
-			entries = int(network.entriesNumber.Load())
-			qLen = network.tableBroadcasts.NumQueued()
-		}
-		nDB.RUnlock()
+	nDB.RLock()
+	networks := nDB.networks[nDB.config.NodeID]
+	network, ok := networks[r.Form["nid"][0]]
 
-		rsp := diagnostic.CommandSucceed(&diagnostic.NetworkStatsResult{Entries: entries, QueueLen: qLen})
-		logger.WithField("response", fmt.Sprintf("%+v", rsp)).Info("network stats done")
-		diagnostic.HTTPReply(w, rsp, json)
-		return
+	entries := -1
+	qLen := -1
+	if ok {
+		entries = int(network.entriesNumber.Load())
+		qLen = network.tableBroadcasts.NumQueued()
 	}
-	diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf(dbNotAvailable)), json)
+	nDB.RUnlock()
+
+	rsp := diagnostic.CommandSucceed(&diagnostic.NetworkStatsResult{Entries: entries, QueueLen: qLen})
+	logger.WithField("response", fmt.Sprintf("%+v", rsp)).Info("network stats done")
+	diagnostic.HTTPReply(w, rsp, json)
 }