Skip to content

Commit

Permalink
go/control: Add registration status to node status
Browse files Browse the repository at this point in the history
This updates the response returned by the `GetStatus` method exposed by the
node controller service to include a `Registration` field that contains
information about the node's current registration.
  • Loading branch information
kostko committed Jun 22, 2020
1 parent 91c4320 commit 77cbcb6
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 26 deletions.
5 changes: 5 additions & 0 deletions .changelog/3038.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
go/control: Add registration status to node status

This updates the response returned by the `GetStatus` method exposed by the
node controller service to include a `Registration` field that contains
information about the node's current registration.
21 changes: 20 additions & 1 deletion go/control/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package api

import (
"context"
"time"

"github.com/oasisprotocol/oasis-core/go/common/errors"
"github.com/oasisprotocol/oasis-core/go/common/node"
consensus "github.com/oasisprotocol/oasis-core/go/consensus/api"
epochtime "github.com/oasisprotocol/oasis-core/go/epochtime/api"
upgrade "github.com/oasisprotocol/oasis-core/go/upgrade/api"
Expand Down Expand Up @@ -49,15 +51,32 @@ type Status struct {

// Consensus is the status overview of the consensus layer.
Consensus consensus.Status `json:"consensus"`

// Registration is the node's registration status.
Registration RegistrationStatus `json:"registration"`
}

// ControlledNode is an interface the node presents for shutting itself down.
// RegistrationStatus is the node registration status.
type RegistrationStatus struct {
// LastRegistration is the time of the last successful registration with the consensus registry
// service. In case the node did not successfully register yet, it will be the zero timestamp.
LastRegistration time.Time `json:"last_registration"`

// Descriptor is the node descriptor that the node successfully registered with. In case the
// node did not successfully register yet, it will be nil.
Descriptor *node.Node `json:"descriptor,omitempty"`
}

// ControlledNode is an internal interface that the controlled oasis-node must provide.
type ControlledNode interface {
// RequestShutdown is the method called by the control server to trigger node shutdown.
RequestShutdown() (<-chan struct{}, error)

// Ready returns a channel that is closed once node is ready.
Ready() <-chan struct{}

// GetRegistrationStatus returns the node's current registration status.
GetRegistrationStatus(ctx context.Context) (*RegistrationStatus, error)
}

// DebugModuleName is the module name for the debug controller service.
Expand Down
9 changes: 8 additions & 1 deletion go/control/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package control

import (
"context"
"fmt"

"github.com/oasisprotocol/oasis-core/go/common/version"
consensus "github.com/oasisprotocol/oasis-core/go/consensus/api"
Expand Down Expand Up @@ -83,12 +84,18 @@ func (c *nodeController) CancelUpgrade(ctx context.Context) error {
func (c *nodeController) GetStatus(ctx context.Context) (*control.Status, error) {
cs, err := c.consensus.GetStatus(ctx)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get consensus status: %w", err)
}

rs, err := c.node.GetRegistrationStatus(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get registration status: %w", err)
}

return &control.Status{
SoftwareVersion: version.SoftwareVersion,
Consensus: *cs,
Registration: *rs,
}, nil
}

Expand Down
30 changes: 30 additions & 0 deletions go/oasis-node/cmd/debug/txsource/workload/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/oasisprotocol/oasis-core/go/common/node"
"github.com/oasisprotocol/oasis-core/go/common/quantity"
consensus "github.com/oasisprotocol/oasis-core/go/consensus/api"
control "github.com/oasisprotocol/oasis-core/go/control/api"
epochtime "github.com/oasisprotocol/oasis-core/go/epochtime/api"
registry "github.com/oasisprotocol/oasis-core/go/registry/api"
runtimeClient "github.com/oasisprotocol/oasis-core/go/runtime/client/api"
Expand Down Expand Up @@ -57,6 +58,7 @@ type queries struct {
stakingParams staking.ConsensusParameters
schedulerParams scheduler.ConsensusParameters

control control.NodeController
staking staking.Backend
consensus consensus.ClientBackend
registry registry.Backend
Expand Down Expand Up @@ -439,6 +441,30 @@ func (q *queries) doRuntimeQueries(ctx context.Context, rng *rand.Rand) error {
return nil
}

func (q *queries) doControlQueries(ctx context.Context, rng *rand.Rand) error {
q.logger.Debug("Doing node control queries")

isReady, err := q.control.IsReady(ctx)
if err != nil {
return fmt.Errorf("control.IsReady error: %w", err)
}

status, err := q.control.GetStatus(ctx)
if err != nil {
return fmt.Errorf("control.GetStatus error: %w", err)
}

// If the node reports itself as ready, it should also be registered and report its own
// registration descriptor.
if isReady && status.Registration.Descriptor == nil {
return fmt.Errorf("node reports as ready but does not report a registration descriptor")
}

q.logger.Debug("Done node control queries")

return nil
}

func (q *queries) doQueries(ctx context.Context, rng *rand.Rand) error {
block, err := q.consensus.GetBlock(ctx, consensus.HeightLatest)
if err != nil {
Expand Down Expand Up @@ -470,6 +496,9 @@ func (q *queries) doQueries(ctx context.Context, rng *rand.Rand) error {
"height_latest", block.Height,
)

if err := q.doControlQueries(ctx, rng); err != nil {
return fmt.Errorf("control queries error: %w", err)
}
if err := q.doConsensusQueries(ctx, rng, height); err != nil {
return fmt.Errorf("consensus queries error: %w", err)
}
Expand Down Expand Up @@ -499,6 +528,7 @@ func (q *queries) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.C

q.logger = logging.GetLogger("cmd/txsource/workload/queries")

q.control = control.NewNodeControllerClient(conn)
q.consensus = cnsc
q.registry = registry.NewRegistryClient(conn)
q.runtime = runtimeClient.NewRuntimeClient(conn)
Expand Down
31 changes: 31 additions & 0 deletions go/oasis-node/cmd/node/control.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package node

import (
"context"

control "github.com/oasisprotocol/oasis-core/go/control/api"
)

var _ control.ControlledNode = (*Node)(nil)

// Implements control.ControlledNode.
func (n *Node) RequestShutdown() (<-chan struct{}, error) {
if err := n.RegistrationWorker.RequestDeregistration(); err != nil {
return nil, err
}
// This returns only the registration worker's event channel,
// otherwise the caller (usually the control grpc server) will only
// get notified once everything is already torn down - perhaps
// including the server.
return n.RegistrationWorker.Quit(), nil
}

// Implements control.ControlledNode.
func (n *Node) Ready() <-chan struct{} {
return n.readyCh
}

// Implements control.ControlledNode.
func (n *Node) GetRegistrationStatus(ctx context.Context) (*control.RegistrationStatus, error) {
return n.RegistrationWorker.GetRegistrationStatus(ctx)
}
24 changes: 2 additions & 22 deletions go/oasis-node/cmd/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,8 @@ import (
workerStorage "github.com/oasisprotocol/oasis-core/go/worker/storage"
)

var (
_ controlAPI.ControlledNode = (*Node)(nil)

// Flags has the configuration flags.
Flags = flag.NewFlagSet("", flag.ContinueOnError)
)
// Flags has the configuration flags.
var Flags = flag.NewFlagSet("", flag.ContinueOnError)

const exportsSubDir = "exports"

Expand Down Expand Up @@ -161,22 +157,6 @@ func (n *Node) Wait() {
n.svcMgr.Wait()
}

func (n *Node) RequestShutdown() (<-chan struct{}, error) {
if err := n.RegistrationWorker.RequestDeregistration(); err != nil {
return nil, err
}
// This returns only the registration worker's event channel,
// otherwise the caller (usually the control grpc server) will only
// get notified once everything is already torn down - perhaps
// including the server.
return n.RegistrationWorker.Quit(), nil
}

// Ready returns the ready channel which gets closed once the node is ready.
func (n *Node) Ready() <-chan struct{} {
return n.readyCh
}

func (n *Node) waitReady(logger *logging.Logger) {
if n.NodeController == nil {
logger.Error("failed while waiting for node: node controller not initialized")
Expand Down
14 changes: 12 additions & 2 deletions go/oasis-test-runner/scenario/e2e/node_shutdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,28 @@ func (sc *nodeShutdownImpl) Run(childEnv *env.Env) error {
return err
}

sc.logger.Info("requesting node shutdown")
sc.logger.Info("waiting for the node to become ready")
computeWorker := sc.runtimeImpl.net.ComputeWorkers()[0]

// Wait for the node to be ready since we didn't wait for any clients.
nodeCtrl, err := oasis.NewController(computeWorker.SocketPath())
if err != nil {
return err
}
if err = nodeCtrl.WaitSync(context.Background()); err != nil {
if err = nodeCtrl.WaitReady(context.Background()); err != nil {
return err
}

// Make sure that the GetStatus endpoint returns sensible values.
status, err := nodeCtrl.GetStatus(context.Background())
if err != nil {
return fmt.Errorf("failed to get status for node: %w", err)
}
if status.Registration.Descriptor == nil {
return fmt.Errorf("node has not registered")
}

sc.logger.Info("requesting node shutdown")
args := []string{
"control", "shutdown",
"--log.level", "debug",
Expand Down
19 changes: 19 additions & 0 deletions go/worker/registration/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/oasisprotocol/oasis-core/go/common/node"
"github.com/oasisprotocol/oasis-core/go/common/persistent"
consensus "github.com/oasisprotocol/oasis-core/go/consensus/api"
control "github.com/oasisprotocol/oasis-core/go/control/api"
epochtime "github.com/oasisprotocol/oasis-core/go/epochtime/api"
"github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/flags"
registry "github.com/oasisprotocol/oasis-core/go/registry/api"
Expand Down Expand Up @@ -169,6 +170,8 @@ type Worker struct { // nolint: maligned

roleProviders []*roleProvider
registerCh chan struct{}

status control.RegistrationStatus
}

// DebugForceallowUnroutableAddresses allows unroutable addresses.
Expand Down Expand Up @@ -461,6 +464,16 @@ func (w *Worker) registrationStopped() {
}
}

// GetRegistrationStatus returns the node's current registration status.
func (w *Worker) GetRegistrationStatus(ctx context.Context) (*control.RegistrationStatus, error) {
w.RLock()
defer w.RUnlock()

status := new(control.RegistrationStatus)
*status = w.status
return status, nil
}

// InitialRegistrationCh returns the initial registration channel.
func (w *Worker) InitialRegistrationCh() chan struct{} {
return w.initialRegCh
Expand Down Expand Up @@ -704,6 +717,12 @@ func (w *Worker) registerNode(epoch epochtime.EpochTime, hook RegisterNodeHook)
return err
}

// Update the registration status on successful registration.
w.RLock()
w.status.LastRegistration = time.Now()
w.status.Descriptor = &nodeDesc
w.RUnlock()

w.logger.Info("node registered with the registry")
return nil
}
Expand Down

0 comments on commit 77cbcb6

Please sign in to comment.