Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

go/control/status: Add fields for quick overview of node status #4669

Merged
merged 1 commit into from
Apr 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/4669.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go/control/status: Add fields for quick overview of node status
51 changes: 51 additions & 0 deletions go/consensus/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package api

import (
"context"
"fmt"
"strings"
"time"

Expand Down Expand Up @@ -202,8 +203,58 @@ type Vote struct {
VotingPower uint64 `json:"voting_power"`
}

// StatusState is the concise status state of the consensus backend.
type StatusState uint8

var (
// StatusStateReady is the ready status state.
StatusStateReady StatusState = 0
// StatusStateSyncing is the syncing status state.
StatusStateSyncing StatusState = 1
)

// String returns a string representation of a status state.
func (s StatusState) String() string {
switch s {
case StatusStateReady:
return "ready"
case StatusStateSyncing:
return "syncing"
default:
return "[invalid status state]"
}
}

// MarshalText encodes a StatusState into text form.
func (s StatusState) MarshalText() ([]byte, error) {
switch s {
case StatusStateReady:
return []byte(StatusStateReady.String()), nil
case StatusStateSyncing:
return []byte(StatusStateSyncing.String()), nil
default:
return nil, fmt.Errorf("invalid StatusState: %d", s)
}
}

// UnmarshalText decodes a text slice into a StatusState.
func (s *StatusState) UnmarshalText(text []byte) error {
switch string(text) {
case StatusStateReady.String():
*s = StatusStateReady
case StatusStateSyncing.String():
*s = StatusStateSyncing
default:
return fmt.Errorf("invalid StatusState: %s", string(text))
}
return nil
}

// Status is the current status overview.
type Status struct { // nolint: maligned
// Status is an concise status of the consensus backend.
Status StatusState `json:"status"`

// Version is the version of the consensus protocol that the node is using.
Version version.Version `json:"version"`
// Backend is the consensus backend identifier.
Expand Down
10 changes: 10 additions & 0 deletions go/consensus/tendermint/full/full.go
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ func (t *fullService) GetUnconfirmedTransactions(ctx context.Context) ([][]byte,

func (t *fullService) GetStatus(ctx context.Context) (*consensusAPI.Status, error) {
status := &consensusAPI.Status{
Status: consensusAPI.StatusStateSyncing,
Version: version.ConsensusProtocol,
Backend: api.BackendName,
Features: t.SupportedFeatures(),
Expand All @@ -774,6 +775,15 @@ func (t *fullService) GetStatus(ctx context.Context) (*consensusAPI.Status, erro
status.ChainContext = t.genesis.ChainContext()
status.GenesisHeight = t.genesis.Height
if t.started() {
// Check if node is synced.
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-t.Synced():
status.Status = consensusAPI.StatusStateReady
default:
}

// Only attempt to fetch blocks in case the consensus service has started as otherwise
// requests will block.
genBlk, err := t.GetBlock(ctx, t.genesis.Height)
Expand Down
1 change: 1 addition & 0 deletions go/consensus/tendermint/seed/seed.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ func (srv *seedService) SupportedFeatures() consensus.FeatureMask {
// Implements Backend.
func (srv *seedService) GetStatus(ctx context.Context) (*consensus.Status, error) {
status := &consensus.Status{
Status: consensus.StatusStateReady,
Version: version.ConsensusProtocol,
Backend: api.BackendName,
Features: srv.SupportedFeatures(),
Expand Down
6 changes: 6 additions & 0 deletions go/oasis-test-runner/scenario/e2e/consensus_state_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ func (sc *consensusStateSyncImpl) Run(childEnv *env.Env) error {
if err != nil {
return fmt.Errorf("failed to get status for validator %s: %w", v.Name, err)
}
if status.Consensus.Status != consensus.StatusStateReady {
return fmt.Errorf("validator %s not ready", v.Name)
}

if status.Registration.Descriptor == nil {
return fmt.Errorf("validator %s has not registered", v.Name)
Expand Down Expand Up @@ -160,6 +163,9 @@ func (sc *consensusStateSyncImpl) Run(childEnv *env.Env) error {
if err != nil {
return fmt.Errorf("failed to fetch validator status: %w", err)
}
if status.Consensus.Status != consensus.StatusStateReady {
return fmt.Errorf("synced validator not ready")
}

// Make sure that the last retained height has been set correctly.
if lrh := status.Consensus.LastRetainedHeight; lrh < 20 {
Expand Down
3 changes: 3 additions & 0 deletions go/oasis-test-runner/scenario/e2e/early_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ func (sc *earlyQueryImpl) Run(childEnv *env.Env) error {
if err != nil {
return fmt.Errorf("failed to get status for node: %w", err)
}
if status.Consensus.Status != consensus.StatusStateSyncing {
return fmt.Errorf("node reports as ready before chain is initialized")
}
if status.Consensus.LatestHeight != 0 {
return fmt.Errorf("node reports non-zero latest height before chain is initialized")
}
Expand Down
24 changes: 21 additions & 3 deletions go/oasis-test-runner/scenario/e2e/runtime/node_shutdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

tlsCert "github.com/oasisprotocol/oasis-core/go/common/crypto/tls"
"github.com/oasisprotocol/oasis-core/go/common/pubsub"
"github.com/oasisprotocol/oasis-core/go/worker/common/api"

"github.com/oasisprotocol/oasis-core/go/common/identity"
consensusAPI "github.com/oasisprotocol/oasis-core/go/consensus/api"
Expand All @@ -25,7 +26,7 @@ type nodeShutdownImpl struct {

func newNodeShutdownImpl() scenario.Scenario {
sc := &nodeShutdownImpl{
runtimeImpl: *newRuntimeImpl("node-shutdown", nil),
runtimeImpl: *newRuntimeImpl("node-shutdown", BasicKVTestClient),
}
return sc
}
Expand All @@ -52,11 +53,16 @@ func (sc *nodeShutdownImpl) Fixture() (*oasis.NetworkFixture, error) {
return f, nil
}

func (sc *nodeShutdownImpl) Run(childEnv *env.Env) error {
func (sc *nodeShutdownImpl) Run(childEnv *env.Env) error { //nolint: gocyclo
ctx := context.Background()
var err error

if err = sc.Net.Start(); err != nil {
if err = sc.startNetworkAndTestClient(ctx, childEnv); err != nil {
return err
}

// Wait for the client to exit.
if err = sc.waitTestClientOnly(); err != nil {
return err
}

Expand All @@ -77,6 +83,15 @@ func (sc *nodeShutdownImpl) Run(childEnv *env.Env) error {
if err != nil {
return fmt.Errorf("failed to get status for node: %w", err)
}
if status.Consensus.Status != consensusAPI.StatusStateReady {
return fmt.Errorf("node consensus status should be '%s', got: '%s'", consensusAPI.StatusStateReady, status.Consensus.Status)
}
if status.Runtimes[runtimeID].Committee == nil {
return fmt.Errorf("node committee status missing")
}
if st := status.Runtimes[runtimeID].Committee.Status; st != api.StatusStateReady {
return fmt.Errorf("node compute worker status should be '%s', got: '%s'", api.StatusStateReady, st)
}
if status.Registration.Descriptor == nil {
return fmt.Errorf("node has not registered")
}
Expand Down Expand Up @@ -190,6 +205,9 @@ func (sc *nodeShutdownImpl) Run(childEnv *env.Env) error {
if err != nil {
return err
}
if status.Consensus.Status != consensusAPI.StatusStateReady {
return fmt.Errorf("node consensus status should be '%s', got: '%s'", consensusAPI.StatusStateReady, status.Consensus.Status)
}
if status.Registration.NodeStatus != nil {
return fmt.Errorf("node should not be registered")
}
Expand Down
3 changes: 3 additions & 0 deletions go/oasis-test-runner/scenario/e2e/seed_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ func (sc *seedAPI) Run(childEnv *env.Env) error { // nolint: gocyclo
if err != nil {
return fmt.Errorf("failed to get status for node: %w", err)
}
if status.Consensus.Status != consensusAPI.StatusStateReady {
return fmt.Errorf("seed node consensus status should be '%s', got: '%s'", consensusAPI.StatusStateReady, status.Consensus.Status)
}
if status.Consensus.LatestHeight != int64(0) {
return fmt.Errorf("seed node latest height should be 0, got: %d", status.Consensus.LatestHeight)
}
Expand Down
100 changes: 100 additions & 0 deletions go/worker/common/api/api.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,112 @@
package api

import (
"fmt"

"github.com/oasisprotocol/oasis-core/go/common/version"
scheduler "github.com/oasisprotocol/oasis-core/go/scheduler/api"
)

// StatusState is the concise status state of the common runtime worker.
type StatusState uint8

const (
// StatusStateReady is the ready status state.
StatusStateReady StatusState = 0
// StatusStateWaitingConsensusSync is the waiting for consensus sync status state.
StatusStateWaitingConsensusSync StatusState = 1
// StatusStateWaitingRuntimeRegistry is the waiting for runtime registry descriptor status state.
StatusStateWaitingRuntimeRegistry StatusState = 2
// StatusStateWaitingKeymanager is the waiting for keymanager status state.
StatusStateWaitingKeymanager StatusState = 3
// StatusStateWaitingHostedRuntime is the waiting for the hosted runtime status state.
StatusStateWaitingHostedRuntime StatusState = 4
// StatusStateWaitingHistoryReindex is the waiting for runtime history reindex status state.
StatusStateWaitingHistoryReindex StatusState = 5
// StatusStateWaitingWorkersInit is the waiting for workers to initialize status state.
StatusStateWaitingWorkersInit StatusState = 6
// StatusStateRuntimeSuspended is the runtime suspended status state.
StatusStateRuntimeSuspended StatusState = 7
)

// String returns a string representation of a status state.
func (s StatusState) String() string {
switch s {
case StatusStateReady:
return "ready"
case StatusStateWaitingConsensusSync:
return "waiting for consensus sync"
case StatusStateWaitingRuntimeRegistry:
return "waiting for runtime registry descriptor"
case StatusStateWaitingKeymanager:
return "waiting for available keymanager"
case StatusStateWaitingHostedRuntime:
return "waiting for hosted runtime provision"
case StatusStateWaitingHistoryReindex:
return "waiting for history reindex"
case StatusStateWaitingWorkersInit:
return "waiting for workers to initialize"
case StatusStateRuntimeSuspended:
return "runtime suspended"
default:
return "[invalid status state]"
}
}

// MarshalText encodes a StatusState into text form.
func (s StatusState) MarshalText() ([]byte, error) {
switch s {
case StatusStateReady:
return []byte(StatusStateReady.String()), nil
case StatusStateWaitingConsensusSync:
return []byte(StatusStateWaitingConsensusSync.String()), nil
case StatusStateWaitingRuntimeRegistry:
return []byte(StatusStateWaitingRuntimeRegistry.String()), nil
case StatusStateWaitingKeymanager:
return []byte(StatusStateWaitingKeymanager.String()), nil
case StatusStateWaitingHostedRuntime:
return []byte(StatusStateWaitingHostedRuntime.String()), nil
case StatusStateWaitingHistoryReindex:
return []byte(StatusStateWaitingHistoryReindex.String()), nil
case StatusStateWaitingWorkersInit:
return []byte(StatusStateWaitingWorkersInit.String()), nil
case StatusStateRuntimeSuspended:
return []byte(StatusStateRuntimeSuspended.String()), nil
default:
return nil, fmt.Errorf("invalid StatusState: %d", s)
}
}

// UnmarshalText decodes a text slice into a StatusState.
func (s *StatusState) UnmarshalText(text []byte) error {
switch string(text) {
case StatusStateReady.String():
*s = StatusStateReady
case StatusStateWaitingConsensusSync.String():
*s = StatusStateWaitingConsensusSync
case StatusStateWaitingRuntimeRegistry.String():
*s = StatusStateWaitingRuntimeRegistry
case StatusStateWaitingKeymanager.String():
*s = StatusStateWaitingKeymanager
case StatusStateWaitingHostedRuntime.String():
*s = StatusStateWaitingHostedRuntime
case StatusStateWaitingHistoryReindex.String():
*s = StatusStateWaitingHistoryReindex
case StatusStateWaitingWorkersInit.String():
*s = StatusStateWaitingWorkersInit
case StatusStateRuntimeSuspended.String():
*s = StatusStateRuntimeSuspended
default:
return fmt.Errorf("invalid StatusState: %s", string(text))
}
return nil
}

// Status is the common runtime worker status.
type Status struct {
// Status is an concise status of the committee node.
Status StatusState `json:"status"`

// ActiveVersion is the currently active version.
ActiveVersion *version.Version `json:"active_version"`

Expand Down
Loading