diff --git a/main/main.go b/main/main.go index b91d47b16075..878d5587c9b9 100644 --- a/main/main.go +++ b/main/main.go @@ -8,12 +8,15 @@ import ( "errors" "fmt" "os" + "os/signal" + "syscall" "github.com/spf13/pflag" "golang.org/x/term" - "github.com/ava-labs/avalanchego/app" "github.com/ava-labs/avalanchego/config" + "github.com/ava-labs/avalanchego/node" + "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/version" ) @@ -24,7 +27,6 @@ func main() { if errors.Is(err, pflag.ErrHelp) { os.Exit(0) } - if err != nil { fmt.Printf("couldn't configure flags: %s\n", err) os.Exit(1) @@ -58,15 +60,63 @@ func main() { } if term.IsTerminal(int(os.Stdout.Fd())) { - fmt.Println(app.Header) + fmt.Println(Header) } - nodeApp, err := app.New(nodeConfig) + runner, err := node.NewRunner(nodeConfig) if err != nil { fmt.Printf("couldn't start node: %s\n", err) os.Exit(1) } - exitCode := app.Run(nodeApp) - os.Exit(exitCode) + os.Exit(Run(runner)) +} + +const Header = ` _____ .__ .__ + / _ \___ _______ | | _____ ____ ____ | |__ ____ ,_ o + / /_\ \ \/ /\__ \ | | \__ \ / \_/ ___\| | \_/ __ \ / //\, + / | \ / / __ \| |__/ __ \| | \ \___| Y \ ___/ \>> | + \____|__ /\_/ (____ /____(____ /___| /\___ >___| /\___ > \\ + \/ \/ \/ \/ \/ \/ \/` + +func Run(app *node.Runner) int { + // start running the application + app.Start() + + // register terminationSignals to kill the application + terminationSignals := make(chan os.Signal, 1) + signal.Notify(terminationSignals, syscall.SIGINT, syscall.SIGTERM) + + stackTraceSignal := make(chan os.Signal, 1) + signal.Notify(stackTraceSignal, syscall.SIGABRT) + + // start up a new go routine to handle attempts to kill the application + go func() { + for range terminationSignals { + app.Stop() + return + } + }() + + // start a goroutine to listen on SIGABRT signals, + // to print the stack trace to standard error. + go func() { + for range stackTraceSignal { + fmt.Fprint(os.Stderr, utils.GetStacktrace(true)) + } + }() + + // wait for the app to exit and get the exit code response + exitCode := app.ExitCode() + + // shut down the termination signal go routine + signal.Stop(terminationSignals) + close(terminationSignals) + + // shut down the stack trace go routine + signal.Stop(stackTraceSignal) + close(stackTraceSignal) + + // return the exit code that the application reported + return exitCode } diff --git a/app/app.go b/node/runner.go similarity index 51% rename from app/app.go rename to node/runner.go index 09369bdec2eb..708d068ef193 100644 --- a/app/app.go +++ b/node/runner.go @@ -1,19 +1,14 @@ // Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package app +package node import ( "fmt" - "os" - "os/signal" "sync" - "syscall" "go.uber.org/zap" - "github.com/ava-labs/avalanchego/node" - "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/perms" "github.com/ava-labs/avalanchego/utils/ulimit" @@ -21,31 +16,15 @@ import ( nodeconfig "github.com/ava-labs/avalanchego/config/node" ) -const Header = ` _____ .__ .__ - / _ \___ _______ | | _____ ____ ____ | |__ ____ ,_ o - / /_\ \ \/ /\__ \ | | \__ \ / \_/ ___\| | \_/ __ \ / //\, - / | \ / / __ \| |__/ __ \| | \ \___| Y \ ___/ \>> | - \____|__ /\_/ (____ /____(____ /___| /\___ >___| /\___ > \\ - \/ \/ \/ \/ \/ \/ \/` - -var _ App = (*app)(nil) - -type App interface { - // Start kicks off the application and returns immediately. - // Start should only be called once. - Start() - - // Stop notifies the application to exit and returns immediately. - // Stop should only be called after [Start]. - // It is safe to call Stop multiple times. - Stop() - - // ExitCode should only be called after [Start] returns. It - // should block until the application finishes - ExitCode() int +// Runner is a wrapper around a *Node that runs in this process +type Runner struct { + node *Node + log logging.Logger + logFactory logging.Factory + exitWG sync.WaitGroup } -func New(config nodeconfig.Config) (App, error) { +func NewRunner(config nodeconfig.Config) (*Runner, error) { // Set the data directory permissions to be read write. if err := perms.ChmodR(config.DatabaseConfig.Path, true, perms.ReadWriteExecute); err != nil { return nil, fmt.Errorf("failed to restrict the permissions of the database directory with: %w", err) @@ -71,7 +50,7 @@ func New(config nodeconfig.Config) (App, error) { return nil, err } - n, err := node.New(&config, logFactory, log) + n, err := New(&config, logFactory, log) if err != nil { log.Fatal("failed to initialize node", zap.Error(err)) log.Stop() @@ -79,66 +58,16 @@ func New(config nodeconfig.Config) (App, error) { return nil, fmt.Errorf("failed to initialize node: %w", err) } - return &app{ + return &Runner{ node: n, log: log, logFactory: logFactory, }, nil } -func Run(app App) int { - // start running the application - app.Start() - - // register terminationSignals to kill the application - terminationSignals := make(chan os.Signal, 1) - signal.Notify(terminationSignals, syscall.SIGINT, syscall.SIGTERM) - - stackTraceSignal := make(chan os.Signal, 1) - signal.Notify(stackTraceSignal, syscall.SIGABRT) - - // start up a new go routine to handle attempts to kill the application - go func() { - for range terminationSignals { - app.Stop() - return - } - }() - - // start a goroutine to listen on SIGABRT signals, - // to print the stack trace to standard error. - go func() { - for range stackTraceSignal { - fmt.Fprint(os.Stderr, utils.GetStacktrace(true)) - } - }() - - // wait for the app to exit and get the exit code response - exitCode := app.ExitCode() - - // shut down the termination signal go routine - signal.Stop(terminationSignals) - close(terminationSignals) - - // shut down the stack trace go routine - signal.Stop(stackTraceSignal) - close(stackTraceSignal) - - // return the exit code that the application reported - return exitCode -} - -// app is a wrapper around a node that runs in this process -type app struct { - node *node.Node - log logging.Logger - logFactory logging.Factory - exitWG sync.WaitGroup -} - // Start the business logic of the node (as opposed to config reading, etc). // Does not block until the node is done. -func (a *app) Start() { +func (a *Runner) Start() { // [p.ExitCode] will block until [p.exitWG.Done] is called a.exitWG.Add(1) go func() { @@ -166,13 +95,13 @@ func (a *app) Start() { // Stop attempts to shutdown the currently running node. This function will // block until Shutdown returns. -func (a *app) Stop() { +func (a *Runner) Stop() { a.node.Shutdown(0) } // ExitCode returns the exit code that the node is reporting. This function // blocks until the node has been shut down. -func (a *app) ExitCode() int { +func (a *Runner) ExitCode() int { a.exitWG.Wait() return a.node.ExitCode() }