Abort on critical error during server startup (#8204)
Only a handful of critical errors are present in the codebase. They all occur during server startup (in `app.StartServer()`). Currently, when one of these critical error occurs, it is simpled mentionned in the logs – then the error is discarded, and the app attempts to continue the execution (and probably fails pretty quickly in a weird way). Rather than continuing operations in an unknow state, these errors should trigger a clean exit. This commit rewrites critical startup errors to be correctly propagated, logged, and then terminate the command execution. Additionnaly, it makes the server return a proper error code to the shell.
Этот коммит содержится в:
коммит произвёл
Chris
родитель
9a73f99885
Коммит
809a16458f
@@ -105,7 +105,11 @@ func setupTestHelper(enterprise bool) *TestHelper {
|
|||||||
if testStore != nil {
|
if testStore != nil {
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
||||||
}
|
}
|
||||||
th.App.StartServer()
|
serverErr := th.App.StartServer()
|
||||||
|
if serverErr != nil {
|
||||||
|
panic(serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
||||||
api4.Init(th.App, th.App.Srv.Router, false)
|
api4.Init(th.App, th.App.Srv.Router, false)
|
||||||
Init(th.App, th.App.Srv.Router)
|
Init(th.App, th.App.Srv.Router)
|
||||||
|
|||||||
@@ -113,7 +113,11 @@ func setupTestHelper(enterprise bool) *TestHelper {
|
|||||||
if testStore != nil {
|
if testStore != nil {
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
||||||
}
|
}
|
||||||
th.App.StartServer()
|
serverErr := th.App.StartServer()
|
||||||
|
if serverErr != nil {
|
||||||
|
panic(serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
||||||
Init(th.App, th.App.Srv.Router, true)
|
Init(th.App, th.App.Srv.Router, true)
|
||||||
wsapi.Init(th.App, th.App.Srv.WebSocketRouter)
|
wsapi.Init(th.App, th.App.Srv.WebSocketRouter)
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ func TestAppRace(t *testing.T) {
|
|||||||
a, err := New()
|
a, err := New()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
||||||
a.StartServer()
|
serverErr := a.StartServer()
|
||||||
|
require.NoError(t, serverErr)
|
||||||
a.Shutdown()
|
a.Shutdown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,11 @@ func setupTestHelper(enterprise bool) *TestHelper {
|
|||||||
if testStore != nil {
|
if testStore != nil {
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
||||||
}
|
}
|
||||||
th.App.StartServer()
|
serverErr := th.App.StartServer()
|
||||||
|
if serverErr != nil {
|
||||||
|
panic(serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
||||||
th.App.Srv.Store.MarkSystemRanUnitTests()
|
th.App.Srv.Store.MarkSystemRanUnitTests()
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
l4g "github.com/alecthomas/log4go"
|
l4g "github.com/alecthomas/log4go"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
@@ -116,7 +117,7 @@ func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Redirect(w, r, url.String(), http.StatusFound)
|
http.Redirect(w, r, url.String(), http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) StartServer() {
|
func (a *App) StartServer() error {
|
||||||
l4g.Info(utils.T("api.server.start_server.starting.info"))
|
l4g.Info(utils.T("api.server.start_server.starting.info"))
|
||||||
|
|
||||||
var handler http.Handler = &CorsWrapper{a.Config, a.Srv.Router}
|
var handler http.Handler = &CorsWrapper{a.Config, a.Srv.Router}
|
||||||
@@ -126,8 +127,7 @@ func (a *App) StartServer() {
|
|||||||
|
|
||||||
rateLimiter, err := NewRateLimiter(&a.Config().RateLimitSettings)
|
rateLimiter, err := NewRateLimiter(&a.Config().RateLimitSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l4g.Critical(err.Error())
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Srv.RateLimiter = rateLimiter
|
a.Srv.RateLimiter = rateLimiter
|
||||||
@@ -151,8 +151,8 @@ func (a *App) StartServer() {
|
|||||||
|
|
||||||
listener, err := net.Listen("tcp", addr)
|
listener, err := net.Listen("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l4g.Critical(utils.T("api.server.start_server.starting.critical"), err)
|
errors.Wrapf(err, utils.T("api.server.start_server.starting.critical"), err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
a.Srv.ListenAddr = listener.Addr().(*net.TCPAddr)
|
a.Srv.ListenAddr = listener.Addr().(*net.TCPAddr)
|
||||||
|
|
||||||
@@ -214,6 +214,8 @@ func (a *App) StartServer() {
|
|||||||
}
|
}
|
||||||
close(a.Srv.didFinishListen)
|
close(a.Srv.didFinishListen)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcpKeepAliveListener struct {
|
type tcpKeepAliveListener struct {
|
||||||
|
|||||||
50
app/server_test.go
Обычный файл
50
app/server_test.go
Обычный файл
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStartServerSuccess(t *testing.T) {
|
||||||
|
a, err := New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
||||||
|
serverErr := a.StartServer()
|
||||||
|
a.Shutdown()
|
||||||
|
require.NoError(t, serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStartServerRateLimiterCriticalError(t *testing.T) {
|
||||||
|
a, err := New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Attempt to use Rate Limiter with an invalid config
|
||||||
|
a.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.RateLimitSettings.Enable = true
|
||||||
|
*cfg.RateLimitSettings.MaxBurst = -100
|
||||||
|
})
|
||||||
|
|
||||||
|
serverErr := a.StartServer()
|
||||||
|
a.Shutdown()
|
||||||
|
require.Error(t, serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStartServerPortUnavailable(t *testing.T) {
|
||||||
|
a, err := New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Attempt to listen on a system-reserved port
|
||||||
|
a.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.ServiceSettings.ListenAddress = ":21"
|
||||||
|
})
|
||||||
|
|
||||||
|
serverErr := a.StartServer()
|
||||||
|
a.Shutdown()
|
||||||
|
require.Error(t, serverErr)
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@ func runServer(configFileLocation string, disableConfigWatch bool) error {
|
|||||||
|
|
||||||
a, err := app.New(options...)
|
a, err := app.New(options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l4g.Error(err.Error())
|
l4g.Critical(err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer a.Shutdown()
|
defer a.Shutdown()
|
||||||
@@ -87,7 +87,12 @@ func runServer(configFileLocation string, disableConfigWatch bool) error {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
a.StartServer()
|
serverErr := a.StartServer()
|
||||||
|
if serverErr != nil {
|
||||||
|
l4g.Critical(serverErr.Error())
|
||||||
|
return serverErr
|
||||||
|
}
|
||||||
|
|
||||||
api4.Init(a, a.Srv.Router, false)
|
api4.Init(a, a.Srv.Router, false)
|
||||||
api3 := api.Init(a, a.Srv.Router)
|
api3 := api.Init(a, a.Srv.Router)
|
||||||
wsapi.Init(a, a.Srv.WebSocketRouter)
|
wsapi.Init(a, a.Srv.WebSocketRouter)
|
||||||
|
|||||||
@@ -53,7 +53,11 @@ func webClientTestsCmdF(cmd *cobra.Command, args []string) error {
|
|||||||
defer a.Shutdown()
|
defer a.Shutdown()
|
||||||
|
|
||||||
utils.InitTranslations(a.Config().LocalizationSettings)
|
utils.InitTranslations(a.Config().LocalizationSettings)
|
||||||
a.StartServer()
|
serverErr := a.StartServer()
|
||||||
|
if serverErr != nil {
|
||||||
|
return serverErr
|
||||||
|
}
|
||||||
|
|
||||||
api4.Init(a, a.Srv.Router, false)
|
api4.Init(a, a.Srv.Router, false)
|
||||||
api.Init(a, a.Srv.Router)
|
api.Init(a, a.Srv.Router)
|
||||||
wsapi.Init(a, a.Srv.WebSocketRouter)
|
wsapi.Init(a, a.Srv.WebSocketRouter)
|
||||||
@@ -71,7 +75,11 @@ func serverForWebClientTestsCmdF(cmd *cobra.Command, args []string) error {
|
|||||||
defer a.Shutdown()
|
defer a.Shutdown()
|
||||||
|
|
||||||
utils.InitTranslations(a.Config().LocalizationSettings)
|
utils.InitTranslations(a.Config().LocalizationSettings)
|
||||||
a.StartServer()
|
serverErr := a.StartServer()
|
||||||
|
if serverErr != nil {
|
||||||
|
return serverErr
|
||||||
|
}
|
||||||
|
|
||||||
api4.Init(a, a.Srv.Router, false)
|
api4.Init(a, a.Srv.Router, false)
|
||||||
api.Init(a, a.Srv.Router)
|
api.Init(a, a.Srv.Router)
|
||||||
wsapi.Init(a, a.Srv.WebSocketRouter)
|
wsapi.Init(a, a.Srv.WebSocketRouter)
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ func Setup() *app.App {
|
|||||||
}
|
}
|
||||||
prevListenAddress := *a.Config().ServiceSettings.ListenAddress
|
prevListenAddress := *a.Config().ServiceSettings.ListenAddress
|
||||||
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
||||||
a.StartServer()
|
serverErr := a.StartServer()
|
||||||
|
if serverErr != nil {
|
||||||
|
panic(serverErr)
|
||||||
|
}
|
||||||
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
||||||
api4.Init(a, a.Srv.Router, false)
|
api4.Init(a, a.Srv.Router, false)
|
||||||
api3 := api.Init(a, a.Srv.Router)
|
api3 := api.Init(a, a.Srv.Router)
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user