From feab565a1a3d955b4599cb787929601cbd38db1a Mon Sep 17 00:00:00 2001 From: Janos Guljas Date: Mon, 1 Mar 2021 19:21:51 +0100 Subject: [PATCH 1/3] debug api basic and full routing --- pkg/debugapi/debugapi.go | 107 ++++++++++++++++++++-------------- pkg/debugapi/debugapi_test.go | 73 ++++++++++++++++++++++- pkg/debugapi/router.go | 43 ++++++++++---- pkg/debugapi/status.go | 2 +- pkg/node/node.go | 60 ++++++++++--------- 5 files changed, 201 insertions(+), 84 deletions(-) diff --git a/pkg/debugapi/debugapi.go b/pkg/debugapi/debugapi.go index ec290aa8f91..239ebd5d5c5 100644 --- a/pkg/debugapi/debugapi.go +++ b/pkg/debugapi/debugapi.go @@ -10,6 +10,7 @@ package debugapi import ( "crypto/ecdsa" "net/http" + "sync" "unicode/utf8" "github.com/ethereum/go-ethereum/common" @@ -30,33 +31,32 @@ import ( type Service interface { http.Handler + Configure(overlay swarm.Address, publicKey, pssPublicKey ecdsa.PublicKey, ethereumAddress common.Address, p2p p2p.DebugService, pingpong pingpong.Interface, topologyDriver topology.Driver, storer storage.Storer, tags *tags.Tags, accounting accounting.Interface, settlement settlement.Interface, chequebookEnabled bool, swap swap.ApiInterface, chequebook chequebook.Service) MustRegisterMetrics(cs ...prometheus.Collector) } type server struct { - Overlay swarm.Address - PublicKey ecdsa.PublicKey - PSSPublicKey ecdsa.PublicKey - EthereumAddress common.Address - P2P p2p.DebugService - Pingpong pingpong.Interface - TopologyDriver topology.Driver - Storer storage.Storer - Logger logging.Logger - Tracer *tracing.Tracer - Tags *tags.Tags - Accounting accounting.Interface - Settlement settlement.Interface - ChequebookEnabled bool - Chequebook chequebook.Service - Swap swap.ApiInterface - metricsRegistry *prometheus.Registry - Options - http.Handler -} - -type Options struct { + Overlay swarm.Address + PublicKey ecdsa.PublicKey + PSSPublicKey ecdsa.PublicKey + EthereumAddress common.Address + P2P p2p.DebugService + Pingpong pingpong.Interface + TopologyDriver topology.Driver + Storer storage.Storer + Logger logging.Logger + Tracer *tracing.Tracer + Tags *tags.Tags + Accounting accounting.Interface + Settlement settlement.Interface + ChequebookEnabled bool + Chequebook chequebook.Service + Swap swap.ApiInterface CORSAllowedOrigins []string + metricsRegistry *prometheus.Registry + // handler is changed in the Configure method + handler http.Handler + handlerMu sync.RWMutex } // checkOrigin returns true if the origin is not set or is equal to the request host. @@ -103,28 +103,49 @@ func equalASCIIFold(s, t string) bool { return s == t } -func New(overlay swarm.Address, publicKey, pssPublicKey ecdsa.PublicKey, ethereumAddress common.Address, p2p p2p.DebugService, pingpong pingpong.Interface, topologyDriver topology.Driver, storer storage.Storer, logger logging.Logger, tracer *tracing.Tracer, tags *tags.Tags, accounting accounting.Interface, settlement settlement.Interface, chequebookEnabled bool, swap swap.ApiInterface, chequebook chequebook.Service, o Options) Service { - s := &server{ - Overlay: overlay, - PublicKey: publicKey, - PSSPublicKey: pssPublicKey, - EthereumAddress: ethereumAddress, - P2P: p2p, - Pingpong: pingpong, - TopologyDriver: topologyDriver, - Storer: storer, - Logger: logger, - Tracer: tracer, - Tags: tags, - Accounting: accounting, - Settlement: settlement, - metricsRegistry: newMetricsRegistry(), - ChequebookEnabled: chequebookEnabled, - Chequebook: chequebook, - Swap: swap, - } +// New creates a new Debug API Service with only basic routers enabled in order +// to expose /health endpoint, Go metrics and pprof. It is useful to expose +// these endpoints before all dependencies are configured and injected to have +// access to basic debugging tools and /health endpoint. +func New(logger logging.Logger, tracer *tracing.Tracer, corsAllowedOrigins []string) Service { + s := new(server) + s.Logger = logger + s.Tracer = tracer + s.CORSAllowedOrigins = corsAllowedOrigins + s.metricsRegistry = newMetricsRegistry() - s.setupRouting() + s.setRouter(s.newBasicRouter()) return s } + +// Configure injects required dependencies and configuration parameters and +// constructs HTTP routes that depend on them. It is intended and safe to call +// this method only once. +func (s *server) Configure(overlay swarm.Address, publicKey, pssPublicKey ecdsa.PublicKey, ethereumAddress common.Address, p2p p2p.DebugService, pingpong pingpong.Interface, topologyDriver topology.Driver, storer storage.Storer, tags *tags.Tags, accounting accounting.Interface, settlement settlement.Interface, chequebookEnabled bool, swap swap.ApiInterface, chequebook chequebook.Service) { + s.Overlay = overlay + s.PublicKey = publicKey + s.PSSPublicKey = pssPublicKey + s.EthereumAddress = ethereumAddress + s.P2P = p2p + s.Pingpong = pingpong + s.TopologyDriver = topologyDriver + s.Storer = storer + s.Tags = tags + s.Accounting = accounting + s.Settlement = settlement + s.ChequebookEnabled = chequebookEnabled + s.Chequebook = chequebook + s.Swap = swap + + s.setRouter(s.newRouter()) +} + +func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // protect handler as it is changed by the Configure method + s.handlerMu.RLock() + h := s.handler + s.handlerMu.RUnlock() + + h.ServeHTTP(w, r) +} diff --git a/pkg/debugapi/debugapi_test.go b/pkg/debugapi/debugapi_test.go index ed4606ebe13..81f25072dd5 100644 --- a/pkg/debugapi/debugapi_test.go +++ b/pkg/debugapi/debugapi_test.go @@ -13,8 +13,11 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethersphere/bee" accountingmock "github.com/ethersphere/bee/pkg/accounting/mock" "github.com/ethersphere/bee/pkg/debugapi" + "github.com/ethersphere/bee/pkg/jsonhttp" + "github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest" "github.com/ethersphere/bee/pkg/logging" p2pmock "github.com/ethersphere/bee/pkg/p2p/mock" "github.com/ethersphere/bee/pkg/pingpong" @@ -57,7 +60,8 @@ func newTestServer(t *testing.T, o testServerOptions) *testServer { settlement := swapmock.New(o.SettlementOpts...) chequebook := chequebookmock.NewChequebook(o.ChequebookOpts...) swapserv := swapmock.NewApiInterface(o.SwapOpts...) - s := debugapi.New(o.Overlay, o.PublicKey, o.PSSPublicKey, o.EthereumAddress, o.P2P, o.Pingpong, topologyDriver, o.Storer, logging.New(ioutil.Discard, 0), nil, o.Tags, acc, settlement, true, swapserv, chequebook, debugapi.Options{}) + s := debugapi.New(logging.New(ioutil.Discard, 0), nil, nil) + s.Configure(o.Overlay, o.PublicKey, o.PSSPublicKey, o.EthereumAddress, o.P2P, o.Pingpong, topologyDriver, o.Storer, o.Tags, acc, settlement, true, swapserv, chequebook) ts := httptest.NewServer(s) t.Cleanup(ts.Close) @@ -86,3 +90,70 @@ func mustMultiaddr(t *testing.T, s string) multiaddr.Multiaddr { } return a } + +// TestServer_Configure validates that http routes are correct when server is +// constructed with only basic routes and after it is configured with +// dependencies. +func TestServer_Configure(t *testing.T) { + var o testServerOptions + topologyDriver := topologymock.NewTopologyDriver(o.TopologyOpts...) + acc := accountingmock.NewAccounting(o.AccountingOpts...) + settlement := swapmock.New(o.SettlementOpts...) + chequebook := chequebookmock.NewChequebook(o.ChequebookOpts...) + swapserv := swapmock.NewApiInterface(o.SwapOpts...) + s := debugapi.New(logging.New(ioutil.Discard, 0), nil, nil) + ts := httptest.NewServer(s) + t.Cleanup(ts.Close) + + client := &http.Client{ + Transport: web.RoundTripperFunc(func(r *http.Request) (*http.Response, error) { + u, err := url.Parse(ts.URL + r.URL.String()) + if err != nil { + return nil, err + } + r.URL = u + return ts.Client().Transport.RoundTrip(r) + }), + } + + testBasicRouter(t, client) + jsonhttptest.Request(t, client, http.MethodGet, "/readiness", http.StatusNotFound, + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: http.StatusText(http.StatusNotFound), + Code: http.StatusNotFound, + }), + ) + + s.Configure(o.Overlay, o.PublicKey, o.PSSPublicKey, o.EthereumAddress, o.P2P, o.Pingpong, topologyDriver, o.Storer, o.Tags, acc, settlement, true, swapserv, chequebook) + + testBasicRouter(t, client) + jsonhttptest.Request(t, client, http.MethodGet, "/readiness", http.StatusOK, + jsonhttptest.WithExpectedJSONResponse(debugapi.StatusResponse{ + Status: "ok", + Version: bee.Version, + }), + ) +} + +func testBasicRouter(t *testing.T, client *http.Client) { + t.Helper() + + jsonhttptest.Request(t, client, http.MethodGet, "/health", http.StatusOK, + jsonhttptest.WithExpectedJSONResponse(debugapi.StatusResponse{ + Status: "ok", + Version: bee.Version, + }), + ) + + for _, path := range []string{ + "/metrics", + "/debug/pprof", + "/debug/pprof/cmdline", + "/debug/pprof/profile?seconds=1", // profile for only 1 second to check only the status code + "/debug/pprof/symbol", + "/debug/pprof/trace", + "/debug/vars", + } { + jsonhttptest.Request(t, client, http.MethodGet, path, http.StatusOK) + } +} diff --git a/pkg/debugapi/router.go b/pkg/debugapi/router.go index f9b8dbdfdcf..a656855debd 100644 --- a/pkg/debugapi/router.go +++ b/pkg/debugapi/router.go @@ -19,10 +19,16 @@ import ( "github.com/ethersphere/bee/pkg/logging/httpaccess" ) -func (s *server) setupRouting() { - baseRouter := http.NewServeMux() +// newBasicRouter constructs only the routes that do not depend on the injected dependencies: +// - /health +// - pprof +// - vars +// - metrics +func (s *server) newBasicRouter() *mux.Router { + router := mux.NewRouter() + router.NotFoundHandler = http.HandlerFunc(jsonhttp.NotFoundHandler) - baseRouter.Handle("/metrics", web.ChainHandlers( + router.Path("/metrics").Handler(web.ChainHandlers( httpaccess.SetAccessLogLevelHandler(0), // suppress access log messages web.FinalHandler(promhttp.InstrumentMetricHandler( s.metricsRegistry, @@ -30,9 +36,6 @@ func (s *server) setupRouting() { )), )) - router := mux.NewRouter() - router.NotFoundHandler = http.HandlerFunc(jsonhttp.NotFoundHandler) - router.Handle("/debug/pprof", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { u := r.URL u.Path += "/" @@ -48,11 +51,21 @@ func (s *server) setupRouting() { router.Handle("/health", web.ChainHandlers( httpaccess.SetAccessLogLevelHandler(0), // suppress access log messages - web.FinalHandlerFunc(s.statusHandler), + web.FinalHandlerFunc(statusHandler), )) + + return router +} + +// newRouter construct the complete set of routes after all of the dependencies +// are injected and exposes /readiness endpoint to provide information that +// Debug API is fully active. +func (s *server) newRouter() *mux.Router { + router := s.newBasicRouter() + router.Handle("/readiness", web.ChainHandlers( httpaccess.SetAccessLogLevelHandler(0), // suppress access log messages - web.FinalHandlerFunc(s.statusHandler), + web.FinalHandlerFunc(statusHandler), )) router.Handle("/pingpong/{peer-id}", jsonhttp.MethodHandler{ @@ -149,7 +162,13 @@ func (s *server) setupRouting() { "GET": http.HandlerFunc(s.getTagHandler), }) - baseRouter.Handle("/", web.ChainHandlers( + return router +} + +// setRouter sets the base Debug API handler with common middlewares. +func (s *server) setRouter(router http.Handler) { + h := http.NewServeMux() + h.Handle("/", web.ChainHandlers( httpaccess.NewHTTPAccessLogHandler(s.Logger, logrus.InfoLevel, s.Tracer, "debug api access"), handlers.CompressHandler, func(h http.Handler) http.Handler { @@ -164,10 +183,12 @@ func (s *server) setupRouting() { h.ServeHTTP(w, r) }) }, - // todo: add recovery handler web.NoCacheHeadersHandler, web.FinalHandler(router), )) - s.Handler = baseRouter + s.handlerMu.Lock() + defer s.handlerMu.Unlock() + + s.handler = h } diff --git a/pkg/debugapi/status.go b/pkg/debugapi/status.go index aedad8de0d3..2d3a027259f 100644 --- a/pkg/debugapi/status.go +++ b/pkg/debugapi/status.go @@ -16,7 +16,7 @@ type statusResponse struct { Version string `json:"version"` } -func (s *server) statusHandler(w http.ResponseWriter, r *http.Request) { +func statusHandler(w http.ResponseWriter, r *http.Request) { jsonhttp.OK(w, statusResponse{ Status: "ok", Version: bee.Version, diff --git a/pkg/node/node.go b/pkg/node/node.go index 7c9eb9318d5..e9ae11979de 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -128,6 +128,35 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, tracerCloser: tracerCloser, } + var debugAPIService debugapi.Service + if o.DebugAPIAddr != "" { + // set up basic debug api endpoints for debugging and /health endpoint + debugAPIService = debugapi.New(logger, tracer, o.CORSAllowedOrigins) + + debugAPIListener, err := net.Listen("tcp", o.DebugAPIAddr) + if err != nil { + return nil, fmt.Errorf("debug api listener: %w", err) + } + + debugAPIServer := &http.Server{ + IdleTimeout: 30 * time.Second, + ReadHeaderTimeout: 3 * time.Second, + Handler: debugAPIService, + ErrorLog: log.New(b.errorLogWriter, "", 0), + } + + go func() { + logger.Infof("debug api address: %s", debugAPIListener.Addr()) + + if err := debugAPIServer.Serve(debugAPIListener); err != nil && err != http.ErrServerClosed { + logger.Debugf("debug api server: %v", err) + logger.Error("unable to serve debug api") + } + }() + + b.debugAPIServer = debugAPIServer + } + stateStore, err := InitStateStore(logger, o.DataDir) if err != nil { return nil, err @@ -438,12 +467,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, b.apiCloser = apiService } - if o.DebugAPIAddr != "" { - // Debug API server - - debugAPIService := debugapi.New(swarmAddress, publicKey, pssPrivateKey.PublicKey, overlayEthAddress, p2ps, pingPong, kad, storer, logger, tracer, tagService, acc, settlement, o.SwapEnable, swapService, chequebookService, debugapi.Options{ - CORSAllowedOrigins: o.CORSAllowedOrigins, - }) + if debugAPIService != nil { // register metrics from components debugAPIService.MustRegisterMetrics(p2ps.Metrics()...) debugAPIService.MustRegisterMetrics(pingPong.Metrics()...) @@ -470,28 +494,8 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, debugAPIService.MustRegisterMetrics(l.Metrics()...) } - debugAPIListener, err := net.Listen("tcp", o.DebugAPIAddr) - if err != nil { - return nil, fmt.Errorf("debug api listener: %w", err) - } - - debugAPIServer := &http.Server{ - IdleTimeout: 30 * time.Second, - ReadHeaderTimeout: 3 * time.Second, - Handler: debugAPIService, - ErrorLog: log.New(b.errorLogWriter, "", 0), - } - - go func() { - logger.Infof("debug api address: %s", debugAPIListener.Addr()) - - if err := debugAPIServer.Serve(debugAPIListener); err != nil && err != http.ErrServerClosed { - logger.Debugf("debug api server: %v", err) - logger.Error("unable to serve debug api") - } - }() - - b.debugAPIServer = debugAPIServer + // inject dependencies and configure full debug api http path routes + debugAPIService.Configure(swarmAddress, publicKey, pssPrivateKey.PublicKey, overlayEthAddress, p2ps, pingPong, kad, storer, tagService, acc, settlement, o.SwapEnable, swapService, chequebookService) } if err := kad.Start(p2pCtx); err != nil { From 70f37db113229d5ad7810d336e3b2825cb9d595d Mon Sep 17 00:00:00 2001 From: Janos Guljas Date: Tue, 2 Mar 2021 12:19:27 +0100 Subject: [PATCH 2/3] expose /addresses debug api endpoint asap --- pkg/debugapi/debugapi.go | 16 +++++----- pkg/debugapi/debugapi_test.go | 55 +++++++++++++++++++++++++++++++---- pkg/debugapi/p2p.go | 17 +++++++---- pkg/debugapi/router.go | 8 +++-- pkg/node/node.go | 8 +++-- 5 files changed, 81 insertions(+), 23 deletions(-) diff --git a/pkg/debugapi/debugapi.go b/pkg/debugapi/debugapi.go index 239ebd5d5c5..20bd4309447 100644 --- a/pkg/debugapi/debugapi.go +++ b/pkg/debugapi/debugapi.go @@ -31,7 +31,7 @@ import ( type Service interface { http.Handler - Configure(overlay swarm.Address, publicKey, pssPublicKey ecdsa.PublicKey, ethereumAddress common.Address, p2p p2p.DebugService, pingpong pingpong.Interface, topologyDriver topology.Driver, storer storage.Storer, tags *tags.Tags, accounting accounting.Interface, settlement settlement.Interface, chequebookEnabled bool, swap swap.ApiInterface, chequebook chequebook.Service) + Configure(p2p p2p.DebugService, pingpong pingpong.Interface, topologyDriver topology.Driver, storer storage.Storer, tags *tags.Tags, accounting accounting.Interface, settlement settlement.Interface, chequebookEnabled bool, swap swap.ApiInterface, chequebook chequebook.Service) MustRegisterMetrics(cs ...prometheus.Collector) } @@ -104,11 +104,15 @@ func equalASCIIFold(s, t string) bool { } // New creates a new Debug API Service with only basic routers enabled in order -// to expose /health endpoint, Go metrics and pprof. It is useful to expose +// to expose /addresses, /health endpoints, Go metrics and pprof. It is useful to expose // these endpoints before all dependencies are configured and injected to have // access to basic debugging tools and /health endpoint. -func New(logger logging.Logger, tracer *tracing.Tracer, corsAllowedOrigins []string) Service { +func New(overlay swarm.Address, publicKey, pssPublicKey ecdsa.PublicKey, ethereumAddress common.Address, logger logging.Logger, tracer *tracing.Tracer, corsAllowedOrigins []string) Service { s := new(server) + s.Overlay = overlay + s.PublicKey = publicKey + s.PSSPublicKey = pssPublicKey + s.EthereumAddress = ethereumAddress s.Logger = logger s.Tracer = tracer s.CORSAllowedOrigins = corsAllowedOrigins @@ -122,11 +126,7 @@ func New(logger logging.Logger, tracer *tracing.Tracer, corsAllowedOrigins []str // Configure injects required dependencies and configuration parameters and // constructs HTTP routes that depend on them. It is intended and safe to call // this method only once. -func (s *server) Configure(overlay swarm.Address, publicKey, pssPublicKey ecdsa.PublicKey, ethereumAddress common.Address, p2p p2p.DebugService, pingpong pingpong.Interface, topologyDriver topology.Driver, storer storage.Storer, tags *tags.Tags, accounting accounting.Interface, settlement settlement.Interface, chequebookEnabled bool, swap swap.ApiInterface, chequebook chequebook.Service) { - s.Overlay = overlay - s.PublicKey = publicKey - s.PSSPublicKey = pssPublicKey - s.EthereumAddress = ethereumAddress +func (s *server) Configure(p2p p2p.DebugService, pingpong pingpong.Interface, topologyDriver topology.Driver, storer storage.Storer, tags *tags.Tags, accounting accounting.Interface, settlement settlement.Interface, chequebookEnabled bool, swap swap.ApiInterface, chequebook chequebook.Service) { s.P2P = p2p s.Pingpong = pingpong s.TopologyDriver = topologyDriver diff --git a/pkg/debugapi/debugapi_test.go b/pkg/debugapi/debugapi_test.go index 81f25072dd5..567d49c17d8 100644 --- a/pkg/debugapi/debugapi_test.go +++ b/pkg/debugapi/debugapi_test.go @@ -6,6 +6,7 @@ package debugapi_test import ( "crypto/ecdsa" + "encoding/hex" "io/ioutil" "net/http" "net/http/httptest" @@ -15,10 +16,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethersphere/bee" accountingmock "github.com/ethersphere/bee/pkg/accounting/mock" + "github.com/ethersphere/bee/pkg/crypto" "github.com/ethersphere/bee/pkg/debugapi" "github.com/ethersphere/bee/pkg/jsonhttp" "github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest" "github.com/ethersphere/bee/pkg/logging" + "github.com/ethersphere/bee/pkg/p2p/mock" p2pmock "github.com/ethersphere/bee/pkg/p2p/mock" "github.com/ethersphere/bee/pkg/pingpong" "github.com/ethersphere/bee/pkg/resolver" @@ -60,8 +63,8 @@ func newTestServer(t *testing.T, o testServerOptions) *testServer { settlement := swapmock.New(o.SettlementOpts...) chequebook := chequebookmock.NewChequebook(o.ChequebookOpts...) swapserv := swapmock.NewApiInterface(o.SwapOpts...) - s := debugapi.New(logging.New(ioutil.Discard, 0), nil, nil) - s.Configure(o.Overlay, o.PublicKey, o.PSSPublicKey, o.EthereumAddress, o.P2P, o.Pingpong, topologyDriver, o.Storer, o.Tags, acc, settlement, true, swapserv, chequebook) + s := debugapi.New(o.Overlay, o.PublicKey, o.PSSPublicKey, o.EthereumAddress, logging.New(ioutil.Discard, 0), nil, nil) + s.Configure(o.P2P, o.Pingpong, topologyDriver, o.Storer, o.Tags, acc, settlement, true, swapserv, chequebook) ts := httptest.NewServer(s) t.Cleanup(ts.Close) @@ -95,13 +98,38 @@ func mustMultiaddr(t *testing.T, s string) multiaddr.Multiaddr { // constructed with only basic routes and after it is configured with // dependencies. func TestServer_Configure(t *testing.T) { - var o testServerOptions + privateKey, err := crypto.GenerateSecp256k1Key() + if err != nil { + t.Fatal(err) + } + pssPrivateKey, err := crypto.GenerateSecp256k1Key() + if err != nil { + t.Fatal(err) + } + overlay := swarm.MustParseHexAddress("ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59c") + addresses := []multiaddr.Multiaddr{ + mustMultiaddr(t, "/ip4/127.0.0.1/tcp/7071/p2p/16Uiu2HAmTBuJT9LvNmBiQiNoTsxE5mtNy6YG3paw79m94CRa9sRb"), + mustMultiaddr(t, "/ip4/192.168.0.101/tcp/7071/p2p/16Uiu2HAmTBuJT9LvNmBiQiNoTsxE5mtNy6YG3paw79m94CRa9sRb"), + mustMultiaddr(t, "/ip4/127.0.0.1/udp/7071/quic/p2p/16Uiu2HAmTBuJT9LvNmBiQiNoTsxE5mtNy6YG3paw79m94CRa9sRb"), + } + + ethereumAddress := common.HexToAddress("abcd") + + o := testServerOptions{ + PublicKey: privateKey.PublicKey, + PSSPublicKey: pssPrivateKey.PublicKey, + Overlay: overlay, + EthereumAddress: ethereumAddress, + P2P: mock.New(mock.WithAddressesFunc(func() ([]multiaddr.Multiaddr, error) { + return addresses, nil + })), + } topologyDriver := topologymock.NewTopologyDriver(o.TopologyOpts...) acc := accountingmock.NewAccounting(o.AccountingOpts...) settlement := swapmock.New(o.SettlementOpts...) chequebook := chequebookmock.NewChequebook(o.ChequebookOpts...) swapserv := swapmock.NewApiInterface(o.SwapOpts...) - s := debugapi.New(logging.New(ioutil.Discard, 0), nil, nil) + s := debugapi.New(o.Overlay, o.PublicKey, o.PSSPublicKey, o.EthereumAddress, logging.New(ioutil.Discard, 0), nil, nil) ts := httptest.NewServer(s) t.Cleanup(ts.Close) @@ -123,8 +151,16 @@ func TestServer_Configure(t *testing.T) { Code: http.StatusNotFound, }), ) + jsonhttptest.Request(t, client, http.MethodGet, "/addresses", http.StatusOK, + jsonhttptest.WithExpectedJSONResponse(debugapi.AddressesResponse{ + Overlay: o.Overlay, + Ethereum: o.EthereumAddress, + PublicKey: hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(&o.PublicKey)), + PSSPublicKey: hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(&o.PSSPublicKey)), + }), + ) - s.Configure(o.Overlay, o.PublicKey, o.PSSPublicKey, o.EthereumAddress, o.P2P, o.Pingpong, topologyDriver, o.Storer, o.Tags, acc, settlement, true, swapserv, chequebook) + s.Configure(o.P2P, o.Pingpong, topologyDriver, o.Storer, o.Tags, acc, settlement, true, swapserv, chequebook) testBasicRouter(t, client) jsonhttptest.Request(t, client, http.MethodGet, "/readiness", http.StatusOK, @@ -133,6 +169,15 @@ func TestServer_Configure(t *testing.T) { Version: bee.Version, }), ) + jsonhttptest.Request(t, client, http.MethodGet, "/addresses", http.StatusOK, + jsonhttptest.WithExpectedJSONResponse(debugapi.AddressesResponse{ + Overlay: o.Overlay, + Underlay: addresses, + Ethereum: o.EthereumAddress, + PublicKey: hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(&o.PublicKey)), + PSSPublicKey: hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(&o.PSSPublicKey)), + }), + ) } func testBasicRouter(t *testing.T, client *http.Client) { diff --git a/pkg/debugapi/p2p.go b/pkg/debugapi/p2p.go index f19aa4eeaa7..be206e7fb9a 100644 --- a/pkg/debugapi/p2p.go +++ b/pkg/debugapi/p2p.go @@ -24,11 +24,18 @@ type addressesResponse struct { } func (s *server) addressesHandler(w http.ResponseWriter, r *http.Request) { - underlay, err := s.P2P.Addresses() - if err != nil { - s.Logger.Debugf("debug api: p2p addresses: %v", err) - jsonhttp.InternalServerError(w, err) - return + // initialize variable to json encode as [] instead null if p2p is nil + underlay := make([]multiaddr.Multiaddr, 0) + // addresses endpoint is exposed before p2p service is configured + // to provide information about other addresses. + if s.P2P != nil { + u, err := s.P2P.Addresses() + if err != nil { + s.Logger.Debugf("debug api: p2p addresses: %v", err) + jsonhttp.InternalServerError(w, err) + return + } + underlay = u } jsonhttp.OK(w, addressesResponse{ Overlay: s.Overlay, diff --git a/pkg/debugapi/router.go b/pkg/debugapi/router.go index a656855debd..96fcef9adf8 100644 --- a/pkg/debugapi/router.go +++ b/pkg/debugapi/router.go @@ -24,6 +24,7 @@ import ( // - pprof // - vars // - metrics +// - /addresses func (s *server) newBasicRouter() *mux.Router { router := mux.NewRouter() router.NotFoundHandler = http.HandlerFunc(jsonhttp.NotFoundHandler) @@ -54,6 +55,10 @@ func (s *server) newBasicRouter() *mux.Router { web.FinalHandlerFunc(statusHandler), )) + router.Handle("/addresses", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.addressesHandler), + }) + return router } @@ -72,9 +77,6 @@ func (s *server) newRouter() *mux.Router { "POST": http.HandlerFunc(s.pingpongHandler), }) - router.Handle("/addresses", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.addressesHandler), - }) router.Handle("/connect/{multi-address:.+}", jsonhttp.MethodHandler{ "POST": http.HandlerFunc(s.peerConnectHandler), }) diff --git a/pkg/node/node.go b/pkg/node/node.go index e9ae11979de..435203c8ef8 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -130,8 +130,12 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, var debugAPIService debugapi.Service if o.DebugAPIAddr != "" { + overlayEthAddress, err := signer.EthereumAddress() + if err != nil { + return nil, fmt.Errorf("eth address: %w", err) + } // set up basic debug api endpoints for debugging and /health endpoint - debugAPIService = debugapi.New(logger, tracer, o.CORSAllowedOrigins) + debugAPIService = debugapi.New(swarmAddress, publicKey, pssPrivateKey.PublicKey, overlayEthAddress, logger, tracer, o.CORSAllowedOrigins) debugAPIListener, err := net.Listen("tcp", o.DebugAPIAddr) if err != nil { @@ -495,7 +499,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, } // inject dependencies and configure full debug api http path routes - debugAPIService.Configure(swarmAddress, publicKey, pssPrivateKey.PublicKey, overlayEthAddress, p2ps, pingPong, kad, storer, tagService, acc, settlement, o.SwapEnable, swapService, chequebookService) + debugAPIService.Configure(p2ps, pingPong, kad, storer, tagService, acc, settlement, o.SwapEnable, swapService, chequebookService) } if err := kad.Start(p2pCtx); err != nil { From a2c48945782dced139e3b0677a7f09be8d839537 Mon Sep 17 00:00:00 2001 From: Janos Guljas Date: Tue, 2 Mar 2021 12:35:15 +0100 Subject: [PATCH 3/3] debug api test TestServer_Configure with [] underlay addresses --- pkg/debugapi/debugapi_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/debugapi/debugapi_test.go b/pkg/debugapi/debugapi_test.go index 567d49c17d8..f06eab0aafc 100644 --- a/pkg/debugapi/debugapi_test.go +++ b/pkg/debugapi/debugapi_test.go @@ -154,6 +154,7 @@ func TestServer_Configure(t *testing.T) { jsonhttptest.Request(t, client, http.MethodGet, "/addresses", http.StatusOK, jsonhttptest.WithExpectedJSONResponse(debugapi.AddressesResponse{ Overlay: o.Overlay, + Underlay: make([]multiaddr.Multiaddr, 0), Ethereum: o.EthereumAddress, PublicKey: hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(&o.PublicKey)), PSSPublicKey: hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(&o.PSSPublicKey)),