mirror of
https://github.com/tendermint/tendermint.git
synced 2026-02-11 22:31:09 +00:00
refactor debug code into separate package and rename to inspect
This commit is contained in:
@@ -1,88 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
rpccore "github.com/tendermint/tendermint/rpc/core"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/indexer"
|
||||
"github.com/tendermint/tendermint/store"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// Debug manages an RPC service that exports methods to debug a failed node.
|
||||
// After a node shuts down due to a consensus failure,, it will no longer start
|
||||
// up and cannot easily be inspected. A Debug value provides a similar interface
|
||||
// to the node, using the underlying Tendermint data stores, without bringing up
|
||||
// any other components. A caller can query the Debug service to inspect the
|
||||
// persisted state and debug the failure.
|
||||
type Debug struct {
|
||||
service.BaseService
|
||||
|
||||
routes rpccore.RoutesMap
|
||||
|
||||
rpcConfig *cfg.RPCConfig
|
||||
listeners []net.Listener
|
||||
}
|
||||
|
||||
func NewDebugFromConfig(config *cfg.Config) (*Debug, error) {
|
||||
blockStoreDB, err := cfg.DefaultDBProvider(&cfg.DBContext{ID: _blockStoreID, Config: config})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockStore := store.NewBlockStore(blockStoreDB)
|
||||
stateDB, err := cfg.DefaultDBProvider(&cfg.DBContext{ID: _stateStoreID, Config: config})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
genDocFunc := defaultGenesisDocProviderFunc(config)
|
||||
genDoc, err := genDocFunc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sinks, err := indexerSinksFromConfig(config, cfg.DefaultDBProvider, genDoc.ChainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stateStore := sm.NewStore(stateDB)
|
||||
return NewDebug(config.RPC, blockStore, stateStore, sinks), nil
|
||||
}
|
||||
|
||||
func NewDebug(rpcConfig *cfg.RPCConfig, blockStore sm.BlockStore, stateStore sm.Store, eventSinks []indexer.EventSink) *Debug {
|
||||
routes := rpccore.DebugRoutes(stateStore, blockStore, eventSinks)
|
||||
return &Debug{
|
||||
routes: routes,
|
||||
rpcConfig: rpcConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDefaultDebug() (*Debug, error) {
|
||||
config := cfg.Config{
|
||||
BaseConfig: cfg.DefaultBaseConfig(),
|
||||
RPC: cfg.DefaultRPCConfig(),
|
||||
TxIndex: cfg.DefaultTxIndexConfig(),
|
||||
}
|
||||
return NewDebugFromConfig(&config)
|
||||
}
|
||||
|
||||
func (debug *Debug) OnStart() error {
|
||||
l := log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo, false)
|
||||
listeners, err := startRPCServers(debug.rpcConfig, l, debug.routes, &types.NopEventBus{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
debug.listeners = listeners
|
||||
return nil
|
||||
}
|
||||
|
||||
func (debug *Debug) OnStop() {
|
||||
for i := len(debug.listeners) - 1; i >= 0; i-- {
|
||||
err := debug.listeners[i].Close()
|
||||
if err != nil {
|
||||
debug.Logger.Error("Error stopping debug rpc listener", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package node_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/fortytw2/leaktest"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci_types "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/config"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
"github.com/tendermint/tendermint/node"
|
||||
http_client "github.com/tendermint/tendermint/rpc/client/http"
|
||||
"github.com/tendermint/tendermint/state/indexer"
|
||||
indexer_mocks "github.com/tendermint/tendermint/state/indexer/mocks"
|
||||
state_mocks "github.com/tendermint/tendermint/state/mocks"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func TestDebugConstructor(t *testing.T) {
|
||||
config := cfg.ResetTestRoot("test")
|
||||
t.Cleanup(leaktest.Check(t))
|
||||
defer func() { _ = os.RemoveAll(config.RootDir) }()
|
||||
t.Run("from config", func(t *testing.T) {
|
||||
d, err := node.NewDebugFromConfig(config)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, d)
|
||||
|
||||
d.OnStop()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestDebugRun(t *testing.T) {
|
||||
config := cfg.ResetTestRoot("test")
|
||||
t.Cleanup(leaktest.Check(t))
|
||||
defer func() { _ = os.RemoveAll(config.RootDir) }()
|
||||
t.Run("from config", func(t *testing.T) {
|
||||
d, err := node.NewDebugFromConfig(config)
|
||||
require.NoError(t, err)
|
||||
err = d.OnStart()
|
||||
require.NoError(t, err)
|
||||
d.OnStop()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestDebugServeInfoRPC(t *testing.T) {
|
||||
testHeight := int64(1)
|
||||
testBlock := new(types.Block)
|
||||
testBlock.Header.Height = testHeight
|
||||
testBlock.Header.LastCommitHash = []byte("test hash")
|
||||
stateStoreMock := &state_mocks.Store{}
|
||||
|
||||
blockStoreMock := &state_mocks.BlockStore{}
|
||||
blockStoreMock.On("Height").Return(testHeight)
|
||||
blockStoreMock.On("Base").Return(int64(0))
|
||||
blockStoreMock.On("LoadBlockMeta", testHeight).Return(&types.BlockMeta{})
|
||||
blockStoreMock.On("LoadBlock", testHeight).Return(testBlock)
|
||||
eventSinkMock := &indexer_mocks.EventSink{}
|
||||
|
||||
rpcConfig := config.TestRPCConfig()
|
||||
d := node.NewDebug(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock})
|
||||
require.NoError(t, d.OnStart())
|
||||
cli, err := http_client.New(rpcConfig.ListenAddress)
|
||||
require.NoError(t, err)
|
||||
resultBlock, err := cli.Block(context.Background(), &testHeight)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, testBlock.Height, resultBlock.Block.Height)
|
||||
require.Equal(t, testBlock.LastCommitHash, resultBlock.Block.LastCommitHash)
|
||||
|
||||
d.OnStop()
|
||||
|
||||
blockStoreMock.AssertExpectations(t)
|
||||
stateStoreMock.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestDebugTxSearch(t *testing.T) {
|
||||
testHash := []byte("test")
|
||||
testTx := []byte("tx")
|
||||
testQuery := fmt.Sprintf("tx.hash='%s'", string(testHash))
|
||||
testTxResult := &abci_types.TxResult{
|
||||
Height: 1,
|
||||
Index: 100,
|
||||
Tx: testTx,
|
||||
}
|
||||
|
||||
stateStoreMock := &state_mocks.Store{}
|
||||
blockStoreMock := &state_mocks.BlockStore{}
|
||||
eventSinkMock := &indexer_mocks.EventSink{}
|
||||
eventSinkMock.On("Type").Return(indexer.KV)
|
||||
eventSinkMock.On("SearchTxEvents", mock.Anything, mock.MatchedBy(func(q *query.Query) bool { return testQuery == q.String() })).
|
||||
Return([]*abci_types.TxResult{testTxResult}, nil)
|
||||
|
||||
rpcConfig := config.TestRPCConfig()
|
||||
d := node.NewDebug(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock})
|
||||
require.NoError(t, d.OnStart())
|
||||
cli, err := http_client.New(rpcConfig.ListenAddress)
|
||||
require.NoError(t, err)
|
||||
|
||||
var page int = 1
|
||||
resultTxSearch, err := cli.TxSearch(context.Background(), testQuery, false, &page, &page, "")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resultTxSearch.Txs, 1)
|
||||
require.Equal(t, types.Tx(testTx), resultTxSearch.Txs[0].Tx)
|
||||
|
||||
d.OnStop()
|
||||
|
||||
eventSinkMock.AssertExpectations(t)
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"math"
|
||||
"net"
|
||||
_ "net/http/pprof" // nolint: gosec // securely exposed on separate, optional port
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
@@ -33,9 +32,7 @@ import (
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/indexer"
|
||||
kv "github.com/tendermint/tendermint/state/indexer/sink/kv"
|
||||
null "github.com/tendermint/tendermint/state/indexer/sink/null"
|
||||
psql "github.com/tendermint/tendermint/state/indexer/sink/psql"
|
||||
"github.com/tendermint/tendermint/state/indexer/sink"
|
||||
"github.com/tendermint/tendermint/store"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
@@ -83,21 +80,7 @@ func createAndStartIndexerService(
|
||||
logger log.Logger,
|
||||
chainID string,
|
||||
) (*indexer.Service, []indexer.EventSink, error) {
|
||||
|
||||
eventSinks := []indexer.EventSink{}
|
||||
|
||||
// check for duplicated sinks
|
||||
sinks := map[string]bool{}
|
||||
for _, s := range config.TxIndex.Indexer {
|
||||
sl := strings.ToLower(s)
|
||||
if sinks[sl] {
|
||||
return nil, nil, errors.New("found duplicated sinks, please check the tx-index section in the config.toml")
|
||||
}
|
||||
|
||||
sinks[sl] = true
|
||||
}
|
||||
|
||||
eventSinks, err := indexerSinksFromConfig(config, dbProvider, chainID)
|
||||
eventSinks, err := sink.EventSinksFromConfig(config, dbProvider, chainID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -111,57 +94,6 @@ func createAndStartIndexerService(
|
||||
return indexerService, eventSinks, nil
|
||||
}
|
||||
|
||||
func indexerSinksFromConfig(config *cfg.Config, dbProvider cfg.DBProvider, chainID string) ([]indexer.EventSink, error) {
|
||||
if len(config.TxIndex.Indexer) == 0 {
|
||||
return []indexer.EventSink{null.NewEventSink()}, nil
|
||||
}
|
||||
|
||||
// check for duplicated sinks
|
||||
sinks := map[string]struct{}{}
|
||||
for _, s := range config.TxIndex.Indexer {
|
||||
sl := strings.ToLower(s)
|
||||
if _, ok := sinks[sl]; ok {
|
||||
return nil, errors.New("found duplicated sinks, please check the tx-index section in the config.toml")
|
||||
}
|
||||
sinks[sl] = struct{}{}
|
||||
}
|
||||
|
||||
eventSinks := []indexer.EventSink{}
|
||||
for k := range sinks {
|
||||
switch k {
|
||||
case string(indexer.NULL):
|
||||
// When we see null in the config, the eventsinks will be reset with the
|
||||
// nullEventSink.
|
||||
return []indexer.EventSink{null.NewEventSink()}, nil
|
||||
|
||||
case string(indexer.KV):
|
||||
store, err := dbProvider(&cfg.DBContext{ID: "tx_index", Config: config})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eventSinks = append(eventSinks, kv.NewEventSink(store))
|
||||
|
||||
case string(indexer.PSQL):
|
||||
conn := config.TxIndex.PsqlConn
|
||||
if conn == "" {
|
||||
return nil, errors.New("the psql connection settings cannot be empty")
|
||||
}
|
||||
|
||||
es, _, err := psql.NewEventSink(conn, chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eventSinks = append(eventSinks, es)
|
||||
|
||||
default:
|
||||
return nil, errors.New("unsupported event sink type")
|
||||
}
|
||||
}
|
||||
return eventSinks, nil
|
||||
}
|
||||
|
||||
func doHandshake(
|
||||
stateStore sm.Store,
|
||||
state sm.State,
|
||||
|
||||
Reference in New Issue
Block a user