Skip to content

Commit

Permalink
Do not crash when application start with bad configuration (#466)
Browse files Browse the repository at this point in the history
Close #422 Close #465
  • Loading branch information
notbot00 authored Jan 29, 2023
2 parents 95488fd + b2daca6 commit 382f5e0
Show file tree
Hide file tree
Showing 18 changed files with 164 additions and 85 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
- [xxxx](https://github.com/vegaprotocol/vega/issues/xxxx) -

### 🛠 Improvements
- [xxxx](https://github.com/vegaprotocol/vega/issues/xxxx) -
- [422](https://github.com/vegaprotocol/vega/issues/422) - Do not crash when application start with bad configuration

### 🐛 Fixes
- [xxxx](https://github.com/vegaprotocol/vega/issues/xxxx) -
- [465](https://github.com/vegaprotocol/vega/issues/465) - Fall back to standard output logger if the startup logger can't be built

## 0.8.2

Expand Down
4 changes: 2 additions & 2 deletions app/config_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ func (l *ConfigLoader) GetConfig() (Config, error) {
cfg := DefaultConfig()

if err := paths.ReadStructuredFile(l.configFilePath, &cfg); err != nil {
return Config{}, fmt.Errorf("could not read configuration file: %w", err)
return Config{}, fmt.Errorf("could not read configuration file at %q: %w", l.configFilePath, err)
}

if err := cfg.EnsureIsValid(); err != nil {
return Config{}, err
return Config{}, fmt.Errorf("the configuration at %q is invalid: %w", l.configFilePath, err)
}

return cfg, nil
Expand Down
3 changes: 1 addition & 2 deletions backend/api_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import (

var ErrAPIv1Unsupported = errors.New("sending transactions through the API v1 is no longer supported")

type unsupportedV1APIPolicy struct {
}
type unsupportedV1APIPolicy struct{}

func (u *unsupportedV1APIPolicy) Ask(_ *v1.SubmitTransactionRequest, _ string, _ time.Time) (bool, error) {
return false, ErrAPIv1Unsupported
Expand Down
12 changes: 12 additions & 0 deletions backend/api_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func (h *Handler) APIV2DescribeAPIToken(token string) (connections.TokenDescript
}

func (h *Handler) SubmitWalletAPIRequest(request jsonrpc.Request) (*jsonrpc.Response, error) {
if err := h.ensureBackendStarted(); err != nil {
return nil, err
}

h.log.Debug("Entering SubmitWalletAPIRequest", zap.String("method", request.Method))
defer h.log.Debug("Leaving SubmitWalletAPIRequest", zap.String("method", request.Method))

Expand All @@ -56,9 +60,17 @@ func (h *Handler) SubmitWalletAPIRequest(request jsonrpc.Request) (*jsonrpc.Resp
}

func (h *Handler) RespondToInteraction(interaction interactor.Interaction) error {
if err := h.ensureBackendStarted(); err != nil {
return err
}

h.log.Debug("Entering RespondToInteraction")
defer h.log.Debug("Leaving RespondToInteraction")

if err := h.ensureAppIsInitialised(); err != nil {
return err
}

if interaction.TraceID == "" {
return ErrTraceIDIsRequired
}
Expand Down
18 changes: 15 additions & 3 deletions backend/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (
"go.uber.org/zap"
)

var ErrAppIsNotInitialised = errors.New("the application has not been initialised")
var (
ErrBackendNotStarted = errors.New("the application backend is not started")
ErrAppIsNotInitialised = errors.New("the application has not been initialised")
)

func (h *Handler) IsAppInitialised() (bool, error) {
isConfigInit, err := h.isAppInitialised()
Expand Down Expand Up @@ -42,7 +45,7 @@ func (h *Handler) InitialiseApp(req *InitialiseAppRequest) error {
return fmt.Errorf("could not save the application configuration: %w", err)
}

h.appInitialised = true
h.appInitialised.Store(true)

if err := h.reloadBackendComponentsFromConfig(); err != nil {
h.log.Error("Could not reload the backend components during the application initialisation", zap.Error(err))
Expand All @@ -52,8 +55,17 @@ func (h *Handler) InitialiseApp(req *InitialiseAppRequest) error {
return nil
}

func (h *Handler) ensureBackendStarted() error {
if !h.backendStarted.Load() {
h.log.Error("The application backend is not started")
return ErrBackendNotStarted
}

return nil
}

func (h *Handler) ensureAppIsInitialised() error {
if !h.appInitialised {
if !h.appInitialised.Load() {
h.log.Error("The application is not initialised")
return ErrAppIsNotInitialised
}
Expand Down
8 changes: 8 additions & 0 deletions backend/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ func (h *Handler) SearchForExistingConfiguration() (*SearchForExistingConfigurat

// GetAppConfig return the application configuration.
func (h *Handler) GetAppConfig() (app.Config, error) {
if err := h.ensureBackendStarted(); err != nil {
return app.Config{}, err
}

h.log.Debug("Entering GetAppConfig")
defer h.log.Debug("Leaving GetAppConfig")

Expand All @@ -70,6 +74,10 @@ func (h *Handler) GetAppConfig() (app.Config, error) {
// UpdateAppConfig update the application configuration. This requires a restart
// to take effect.
func (h *Handler) UpdateAppConfig(updatedConfig app.Config) error {
if err := h.ensureBackendStarted(); err != nil {
return err
}

h.log.Debug("Entering UpdateAppConfig")
defer h.log.Debug("Leaving UpdateAppConfig")

Expand Down
83 changes: 50 additions & 33 deletions backend/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package backend
import (
"context"
"fmt"
"sync"
"sync/atomic"

vgclose "code.vegaprotocol.io/vega/libs/close"
"code.vegaprotocol.io/vega/libs/jsonrpc"
Expand Down Expand Up @@ -30,10 +32,14 @@ type Handler struct {
// the one to inject on runtime methods like menu, event, dialogs, etc.
ctx context.Context

// To prevent multiple startup of the back and thus resource leaks.
backendStarted atomic.Bool
startupMu sync.Mutex

// appInitialised represents the initialization state of the application
// and is used to prevent calls to the API when the application is not
// initialised.
appInitialised bool
// initialized.
appInitialised atomic.Bool

log *zap.Logger
// logLevel is a reference to the logger's log level to dynamically change
Expand Down Expand Up @@ -67,60 +73,71 @@ type Handler struct {
tokenStore *tokenStoreV1.EmptyStore
}

func NewHandler() (*Handler, error) {
h := &Handler{}
func NewHandler() *Handler {
return &Handler{}
}

// Startup is called during application startup
func (h *Handler) Startup(ctx context.Context) {
h.ctx = ctx
}

// DOMReady is called after the front-end dom has been loaded
func (h *Handler) DOMReady(_ context.Context) {
// Add your action here
}

// Shutdown is called during application termination
func (h *Handler) Shutdown(_ context.Context) {
h.closeAllResources()
h.backendStarted.Store(false)
}

var err error
func (h *Handler) StartupBackend() (err error) {
h.startupMu.Lock()
defer h.startupMu.Unlock()

if h.backendStarted.Load() {
return nil
}

defer func() {
if err == nil {
// Only set the backend as started on success.
h.backendStarted.Store(true)
}
}()

if err := h.initializeAppLogger(); err != nil {
return nil, err
return err
}

h.configLoader, err = app.NewConfigLoader()
if err != nil {
return nil, fmt.Errorf("could not create the configuration loader: %w", err)
return fmt.Errorf("could not create the configuration loader: %w", err)
}

h.runningServiceManager = newServiceManager()

h.appInitialised, err = h.isAppInitialised()
appInitialised, err := h.isAppInitialised()
if err != nil {
return nil, fmt.Errorf("could not verify wheter the application is initialized or not: %w", err)
return fmt.Errorf("could not verify wheter the application is initialized or not: %w", err)
}

h.appInitialised.Store(appInitialised)

// If the application is not initialized, it means it's the first time the
// user is running the application. As a result, we can't load the backend
// components that require an existing configuration. The user will have
// to go through the application initialization process, that is part of
// the "on-boarding" workflow on the front-end.
if h.appInitialised {
if h.appInitialised.Load() {
if err := h.reloadBackendComponentsFromConfig(); err != nil {
return nil, fmt.Errorf("could not load the backend components during the application start up: %w", err)
return fmt.Errorf("could not load the backend components during the application start up: %w", err)
}
}

return h, nil
}

// Startup is called during application startup
func (h *Handler) Startup(ctx context.Context) {
h.ctx = ctx

h.log.Debug("Entering Startup")
defer h.log.Debug("Leaving Startup")
}

// DOMReady is called after the front-end dom has been loaded
func (h *Handler) DOMReady(_ context.Context) {
// Add your action here
}

// Shutdown is called during application termination
func (h *Handler) Shutdown(_ context.Context) {
h.log.Debug("Entering Shutdown")
defer h.log.Debug("Leaving Shutdown")

h.closeAllResources()
return nil
}

func (h *Handler) initializeAppLogger() error {
Expand Down
27 changes: 16 additions & 11 deletions backend/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ func (r StartServiceRequest) Check() error {
}

func (h *Handler) StartService(req *StartServiceRequest) (err error) {
if err := h.ensureBackendStarted(); err != nil {
return err
}

h.log.Debug("Entering StartService")
defer h.log.Debug("Leaving StartService")

Expand Down Expand Up @@ -153,6 +157,10 @@ type GetCurrentServiceInfo struct {
}

func (h *Handler) GetCurrentServiceInfo() (GetCurrentServiceInfo, error) {
if err := h.ensureBackendStarted(); err != nil {
return GetCurrentServiceInfo{}, err
}

h.log.Debug("Entering GetCurrentServiceInfo")
defer h.log.Debug("Leaving GetCurrentServiceInfo")

Expand All @@ -174,15 +182,6 @@ func (h *Handler) GetCurrentServiceInfo() (GetCurrentServiceInfo, error) {
}, nil
}

func (h *Handler) ensurePortCanBeBound(svcURL string) error {
if _, err := http.Get("http://" + svcURL); err == nil {
// If there is no error, it means the server managed to establish a
// connection of some kind. It's not good for us.
return fmt.Errorf("could not start the service as an application is already served on %q", svcURL)
}
return nil
}

func (h *Handler) listenToIncomingInteractions(ctx context.Context) {
log := h.log.Named("service-interactions-listener")
log.Info("Listening to incoming interactions")
Expand All @@ -191,7 +190,10 @@ func (h *Handler) listenToIncomingInteractions(ctx context.Context) {
case <-ctx.Done():
log.Info("Stopping the listening to incoming interactions")
return
case interaction := <-h.runningServiceManager.receptionChan:
case interaction, ok := <-h.runningServiceManager.receptionChan:
if !ok {
return
}
h.emitReceivedInteraction(log, interaction)
}
}
Expand All @@ -206,7 +208,10 @@ func (h *Handler) listenToServiceRuntimeError(jobCtx context.Context, errChan <-
case <-jobCtx.Done():
log.Info("Stopping the listening to the service runtime error")
return
case err := <-errChan:
case err, ok := <-errChan:
if !ok {
return
}
h.log.Error("An error occurred while running the service", zap.Error(err))
h.runningServiceManager.ShutdownService()
runtime.EventsEmit(h.ctx, ServiceStoppedWithError, struct {
Expand Down
12 changes: 10 additions & 2 deletions backend/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,24 @@ func (h *Handler) GetLatestRelease() (LatestRelease, error) {
return latestRelease, nil
}

func (h *Handler) GetVersion() *GetVersionResponse {
func (h *Handler) GetVersion() (*GetVersionResponse, error) {
if err := h.ensureBackendStarted(); err != nil {
return nil, err
}

h.log.Debug("Entering GetVersion")
defer h.log.Debug("Leaving GetVersion")

if err := h.ensureAppIsInitialised(); err != nil {
return nil, err
}

compatibility, _ := wversion.CheckSoftwareCompatibility(h.networkStore, wversion.GetNetworkVersionThroughGRPC)

return &GetVersionResponse{
Version: app.Version,
GitHash: app.VersionHash,
Backend: wversion.GetSoftwareVersionInfo(),
Compatibility: compatibility,
}
}, nil
}
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"@sentry/react": "7.19.0",
"@sentry/tracing": "7.19.0",
"@vegaprotocol/wallet-admin": "0.1.2",
"@vegaprotocol/wallet-ui": "0.1.31",
"@vegaprotocol/wallet-ui": "0.1.32",
"core-js": "3.26.1",
"jest-environment-jsdom": "29.3.1",
"loglevel": "1.8.1",
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json.md5
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3dd6d2d4a7b698e1a3c5db54a4f020ac
685406a25b4dfc732f5abd5f3f2f1101
Loading

0 comments on commit 382f5e0

Please sign in to comment.