From 1c5876323bd4f61ff179344a662be5e71931a3bc Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Thu, 3 Sep 2020 04:11:24 +0100 Subject: [PATCH 1/3] Restart after install page Signed-off-by: Andrew Thornton --- cmd/web.go | 1 + modules/graceful/manager.go | 17 +++++++++++++++++ modules/graceful/manager_unix.go | 18 ++++++++++++++++++ modules/graceful/manager_windows.go | 9 +++++++++ modules/graceful/restart.go | 29 +++++++++++++++++++++++++++++ options/locale/locale_en-US.ini | 1 + routers/install.go | 14 +++++--------- routers/private/manager_unix.go | 4 ++-- routers/private/manager_windows.go | 5 ++--- templates/install_success.tmpl | 19 +++++++++++++++++++ 10 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 modules/graceful/restart.go create mode 100644 templates/install_success.tmpl diff --git a/cmd/web.go b/cmd/web.go index f0e1b16e7fe12..6039eeefb5eb3 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -209,5 +209,6 @@ func runWeb(ctx *cli.Context) error { <-graceful.GetManager().Done() log.Info("PID: %d Gitea Web Finished", os.Getpid()) log.Close() + graceful.GetManager().DoFinalRestartIfNeeded() return nil } diff --git a/modules/graceful/manager.go b/modules/graceful/manager.go index 6b134e7d0cceb..a5c399f86c52c 100644 --- a/modules/graceful/manager.go +++ b/modules/graceful/manager.go @@ -6,6 +6,7 @@ package graceful import ( "context" + "fmt" "sync" "time" @@ -312,6 +313,22 @@ func (g *Manager) InformCleanup() { g.createServerWaitGroup.Done() } +// DoFinalRestartIfNeeded will restart the process if needed +func (g *Manager) DoFinalRestartIfNeeded() error { + select { + case <-g.done: + default: + return fmt.Errorf("This should only be called once the manager is done") + } + g.lock.Lock() + defer g.lock.Unlock() + if g.needsRestart { + _, err := RestartProcessNoListeners() + return err + } + return nil +} + // Done allows the manager to be viewed as a context.Context, it returns a channel that is closed when the server is finished terminating func (g *Manager) Done() <-chan struct{} { return g.done diff --git a/modules/graceful/manager_unix.go b/modules/graceful/manager_unix.go index 540974454c34c..99220b2418a27 100644 --- a/modules/graceful/manager_unix.go +++ b/modules/graceful/manager_unix.go @@ -23,6 +23,7 @@ import ( type Manager struct { isChild bool forked bool + needsRestart bool lock *sync.RWMutex state state shutdown chan struct{} @@ -169,6 +170,23 @@ func (g *Manager) DoGracefulRestart() { } } +// DoForcedRestart causes a graceful restart if can otherwise graceful shutdown and restart at end of web.go +func (g *Manager) DoForcedRestart() { + if setting.GracefulRestartable { + log.Info("PID: %d. Forking...", os.Getpid()) + err := g.doFork() + if err != nil && err.Error() != "another process already forked. Ignoring this one" { + log.Error("Error whilst forking from PID: %d : %v", os.Getpid(), err) + } + } else { + log.Info("PID: %d. Not set graceful restartable. Shutting down and will attempt restart at the end of web.go ...", os.Getpid()) + g.lock.Lock() + g.needsRestart = true + g.lock.Unlock() + g.doShutdown() + } +} + // DoImmediateHammer causes an immediate hammer func (g *Manager) DoImmediateHammer() { g.doHammerTime(0 * time.Second) diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go index d412e94f9ac54..eff195030ce02 100644 --- a/modules/graceful/manager_windows.go +++ b/modules/graceful/manager_windows.go @@ -34,6 +34,7 @@ const ( type Manager struct { ctx context.Context isChild bool + needsRestart bool lock *sync.RWMutex state state shutdown chan struct{} @@ -175,6 +176,14 @@ func (g *Manager) DoGracefulShutdown() { } } +// DoForcedRestart causes a graceful shutdown and restart during Terminate phase +func (g *Manager) DoForcedRestart() { + g.lock.Lock() + g.needsRestart = true + g.lock.Unlock() + g.DoGracefulShutdown() +} + // RegisterServer registers the running of a listening server. // Any call to RegisterServer must be matched by a call to ServerDone func (g *Manager) RegisterServer() { diff --git a/modules/graceful/restart.go b/modules/graceful/restart.go new file mode 100644 index 0000000000000..e0f9381cbe55e --- /dev/null +++ b/modules/graceful/restart.go @@ -0,0 +1,29 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package graceful + +import ( + "os" + "os/exec" +) + +// RestartProcessNoListeners starts a new process without passing it the active listeners. +func RestartProcessNoListeners() (int, error) { + // Use the original binary location. This works with symlinks such that if + // the file it points to has been changed we will use the updated symlink. + argv0, err := exec.LookPath(os.Args[0]) + if err != nil { + return 0, err + } + process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{ + Dir: originalWD, + Env: os.Environ(), + Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, + }) + if err != nil { + return 0, err + } + return process.Pid, nil +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 94d7ab27fb1b5..fb7d7e666c8e6 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -191,6 +191,7 @@ run_user_not_match = The 'run as' username is not the current username: %s -> %s save_config_failed = Failed to save configuration: %v invalid_admin_setting = Administrator account setting is invalid: %v install_success = Welcome! Thank you for choosing Gitea. Have fun and take care! +install_restart = Gitea will now attempt to restart. You will be redirected to User Login in 5 seconds. invalid_log_root_path = The log path is invalid: %v default_keep_email_private = Hide Email Addresses by Default default_keep_email_private_popup = Hide email addresses of new user accounts by default. diff --git a/routers/install.go b/routers/install.go index 9eda18f941ba6..aa41a63275d72 100644 --- a/routers/install.go +++ b/routers/install.go @@ -27,7 +27,8 @@ import ( const ( // tplInstall template for installation page - tplInstall base.TplName = "install" + tplInstall base.TplName = "install" + tplInstallSuccess base.TplName = "install_success" ) // InstallInit prepare for rendering installation page @@ -393,12 +394,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { } log.Info("First-time run install finished!") - // FIXME: This isn't really enough to completely take account of new configuration - // We should really be restarting: - // - On windows this is probably just a simple restart - // - On linux we can't just use graceful.RestartProcess() everything that was passed in on LISTEN_FDS - // (active or not) needs to be passed out and everything new passed out too. - // This means we need to prevent the cleanup goroutine from running prior to the second GlobalInit - ctx.Flash.Success(ctx.Tr("install.install_success")) - ctx.Redirect(form.AppURL + "user/login") + ctx.Data["RedirectURL"] = form.AppURL + "user/login" + ctx.HTML(200, tplInstallSuccess) + graceful.GetManager().DoForcedRestart() } diff --git a/routers/private/manager_unix.go b/routers/private/manager_unix.go index ec5e97605981d..70b35fceb6965 100644 --- a/routers/private/manager_unix.go +++ b/routers/private/manager_unix.go @@ -14,9 +14,9 @@ import ( "gitea.com/macaron/macaron" ) -// Restart causes the server to perform a graceful restart +// Restart causes the server to perform a graceful restart if possible but otherwise a graceful shutdown and restart func Restart(ctx *macaron.Context) { - graceful.GetManager().DoGracefulRestart() + graceful.GetManager().DoForcedRestart() ctx.PlainText(http.StatusOK, []byte("success")) } diff --git a/routers/private/manager_windows.go b/routers/private/manager_windows.go index ac840a9d81ce8..1ae1437120042 100644 --- a/routers/private/manager_windows.go +++ b/routers/private/manager_windows.go @@ -16,9 +16,8 @@ import ( // Restart is not implemented for Windows based servers as they can't fork func Restart(ctx *macaron.Context) { - ctx.JSON(http.StatusNotImplemented, map[string]interface{}{ - "err": "windows servers cannot be gracefully restarted - shutdown and restart manually", - }) + graceful.GetManager().DoForcedRestart() + ctx.PlainText(http.StatusOK, []byte("success")) } // Shutdown causes the server to perform a graceful shutdown diff --git a/templates/install_success.tmpl b/templates/install_success.tmpl new file mode 100644 index 0000000000000..7476a9fa45a4c --- /dev/null +++ b/templates/install_success.tmpl @@ -0,0 +1,19 @@ +{{template "base/head" .}} +
+
+
+

+ {{.i18n.Tr "install.title"}} +

+

{{.i18n.Tr "install_success"}}

+

{{.i18n.Tr "install_restart" (.RedirectURL|Escape)}}

+ +
+
+
+{{template "base/footer" .}} From f502aa10188925e32a378da4f9f6dcd98dc79dde Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Thu, 3 Sep 2020 04:25:46 +0100 Subject: [PATCH 2/3] nits Signed-off-by: Andrew Thornton --- cmd/web.go | 3 +-- templates/install_success.tmpl | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/web.go b/cmd/web.go index 6039eeefb5eb3..86718fd6efd4c 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -209,6 +209,5 @@ func runWeb(ctx *cli.Context) error { <-graceful.GetManager().Done() log.Info("PID: %d Gitea Web Finished", os.Getpid()) log.Close() - graceful.GetManager().DoFinalRestartIfNeeded() - return nil + return graceful.GetManager().DoFinalRestartIfNeeded() } diff --git a/templates/install_success.tmpl b/templates/install_success.tmpl index 7476a9fa45a4c..912b7875fdc8b 100644 --- a/templates/install_success.tmpl +++ b/templates/install_success.tmpl @@ -5,8 +5,8 @@

{{.i18n.Tr "install.title"}}

-

{{.i18n.Tr "install_success"}}

-

{{.i18n.Tr "install_restart" (.RedirectURL|Escape)}}

+

{{.i18n.Tr "install.install_success"}}

+

{{.i18n.Tr "install.install_restart" (.RedirectURL|Escape)}}