Skip to content

Commit

Permalink
go/control/status: Add fields for quick overview of node status
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrus committed Apr 20, 2022
1 parent 95df7f0 commit 94ee61c
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 0 deletions.
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
13 changes: 13 additions & 0 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 Down Expand Up @@ -77,6 +78,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 comute 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 +200,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

0 comments on commit 94ee61c

Please sign in to comment.