Browse Source

Move install pages out of main macaron routes (#13195)

* Move install pages out of main macaron loop

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Update templates/post-install.tmpl

Co-authored-by: Lauris BH <lauris@nix.lv>

* remove prefetch

Signed-off-by: Andrew Thornton <art27@cantab.net>
tags/v1.15.0-dev
zeripath GitHub 4 years ago
parent
commit
c5fc110a3b
10 changed files with 228 additions and 109 deletions
  1. +89
    -46
      cmd/web.go
  2. +6
    -0
      cmd/web_graceful.go
  3. +0
    -6
      modules/context/auth.go
  4. +1
    -1
      modules/graceful/manager.go
  5. +1
    -1
      modules/graceful/server.go
  6. +71
    -43
      routers/init.go
  7. +25
    -11
      routers/install.go
  8. +9
    -0
      routers/routes/routes.go
  9. +2
    -1
      templates/install.tmpl
  10. +24
    -0
      templates/post-install.tmpl

+ 89
- 46
cmd/web.go View File

@@ -19,6 +19,8 @@ import (
"code.gitea.io/gitea/routers" "code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/routes" "code.gitea.io/gitea/routers/routes"


"gitea.com/macaron/macaron"

context2 "github.com/gorilla/context" context2 "github.com/gorilla/context"
"github.com/unknwon/com" "github.com/unknwon/com"
"github.com/urfave/cli" "github.com/urfave/cli"
@@ -114,6 +116,39 @@ func runWeb(ctx *cli.Context) error {
setting.WritePIDFile = true setting.WritePIDFile = true
} }


// Flag for port number in case first time run conflict.
if ctx.IsSet("port") {
if err := setPort(ctx.String("port")); err != nil {
return err
}
}

// Perform pre-initialization
needsInstall := routers.PreInstallInit(graceful.GetManager().HammerContext())
if needsInstall {
m := routes.NewMacaron()
routes.RegisterInstallRoute(m)
err := listen(m, false)
select {
case <-graceful.GetManager().IsShutdown():
<-graceful.GetManager().Done()
log.Info("PID: %d Gitea Web Finished", os.Getpid())
log.Close()
return err
default:
}
} else {
NoInstallListener()
}

if setting.EnablePprof {
go func() {
log.Info("Starting pprof server on localhost:6060")
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
}()
}

log.Info("Global init")
// Perform global initialization // Perform global initialization
routers.GlobalInit(graceful.GetManager().HammerContext()) routers.GlobalInit(graceful.GetManager().HammerContext())


@@ -121,41 +156,49 @@ func runWeb(ctx *cli.Context) error {
m := routes.NewMacaron() m := routes.NewMacaron()
routes.RegisterRoutes(m) routes.RegisterRoutes(m)


// Flag for port number in case first time run conflict.
if ctx.IsSet("port") {
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, ctx.String("port"), 1)
setting.HTTPPort = ctx.String("port")
err := listen(m, true)
<-graceful.GetManager().Done()
log.Info("PID: %d Gitea Web Finished", os.Getpid())
log.Close()
return err
}


switch setting.Protocol {
case setting.UnixSocket:
case setting.FCGI:
case setting.FCGIUnix:
default:
// Save LOCAL_ROOT_URL if port changed
cfg := ini.Empty()
if com.IsFile(setting.CustomConf) {
// Keeps custom settings if there is already something.
if err := cfg.Append(setting.CustomConf); err != nil {
return fmt.Errorf("Failed to load custom conf '%s': %v", setting.CustomConf, err)
}
}
func setPort(port string) error {
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1)
setting.HTTPPort = port


defaultLocalURL := string(setting.Protocol) + "://"
if setting.HTTPAddr == "0.0.0.0" {
defaultLocalURL += "localhost"
} else {
defaultLocalURL += setting.HTTPAddr
switch setting.Protocol {
case setting.UnixSocket:
case setting.FCGI:
case setting.FCGIUnix:
default:
// Save LOCAL_ROOT_URL if port changed
cfg := ini.Empty()
if com.IsFile(setting.CustomConf) {
// Keeps custom settings if there is already something.
if err := cfg.Append(setting.CustomConf); err != nil {
return fmt.Errorf("Failed to load custom conf '%s': %v", setting.CustomConf, err)
} }
defaultLocalURL += ":" + setting.HTTPPort + "/"
}

defaultLocalURL := string(setting.Protocol) + "://"
if setting.HTTPAddr == "0.0.0.0" {
defaultLocalURL += "localhost"
} else {
defaultLocalURL += setting.HTTPAddr
}
defaultLocalURL += ":" + setting.HTTPPort + "/"


cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)


if err := cfg.SaveTo(setting.CustomConf); err != nil {
return fmt.Errorf("Error saving generated JWT Secret to custom config: %v", err)
}
if err := cfg.SaveTo(setting.CustomConf); err != nil {
return fmt.Errorf("Error saving generated JWT Secret to custom config: %v", err)
} }
} }
return nil
}


func listen(m *macaron.Macaron, handleRedirector bool) error {
listenAddr := setting.HTTPAddr listenAddr := setting.HTTPAddr
if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix { if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort) listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
@@ -166,37 +209,40 @@ func runWeb(ctx *cli.Context) error {
log.Info("LFS server enabled") log.Info("LFS server enabled")
} }


if setting.EnablePprof {
go func() {
log.Info("Starting pprof server on localhost:6060")
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
}()
}

var err error var err error
switch setting.Protocol { switch setting.Protocol {
case setting.HTTP: case setting.HTTP:
NoHTTPRedirector()
if handleRedirector {
NoHTTPRedirector()
}
err = runHTTP("tcp", listenAddr, context2.ClearHandler(m)) err = runHTTP("tcp", listenAddr, context2.ClearHandler(m))
case setting.HTTPS: case setting.HTTPS:
if setting.EnableLetsEncrypt { if setting.EnableLetsEncrypt {
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m)) err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
break break
} }
if setting.RedirectOtherPort {
go runHTTPRedirector()
} else {
NoHTTPRedirector()
if handleRedirector {
if setting.RedirectOtherPort {
go runHTTPRedirector()
} else {
NoHTTPRedirector()
}
} }
err = runHTTPS("tcp", listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m)) err = runHTTPS("tcp", listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
case setting.FCGI: case setting.FCGI:
NoHTTPRedirector()
if handleRedirector {
NoHTTPRedirector()
}
err = runFCGI("tcp", listenAddr, context2.ClearHandler(m)) err = runFCGI("tcp", listenAddr, context2.ClearHandler(m))
case setting.UnixSocket: case setting.UnixSocket:
NoHTTPRedirector()
if handleRedirector {
NoHTTPRedirector()
}
err = runHTTP("unix", listenAddr, context2.ClearHandler(m)) err = runHTTP("unix", listenAddr, context2.ClearHandler(m))
case setting.FCGIUnix: case setting.FCGIUnix:
NoHTTPRedirector()
if handleRedirector {
NoHTTPRedirector()
}
err = runFCGI("unix", listenAddr, context2.ClearHandler(m)) err = runFCGI("unix", listenAddr, context2.ClearHandler(m))
default: default:
log.Fatal("Invalid protocol: %s", setting.Protocol) log.Fatal("Invalid protocol: %s", setting.Protocol)
@@ -206,8 +252,5 @@ func runWeb(ctx *cli.Context) error {
log.Critical("Failed to start server: %v", err) log.Critical("Failed to start server: %v", err)
} }
log.Info("HTTP Listener: %s Closed", listenAddr) log.Info("HTTP Listener: %s Closed", listenAddr)
<-graceful.GetManager().Done()
log.Info("PID: %d Gitea Web Finished", os.Getpid())
log.Close()
return nil
return err
} }

+ 6
- 0
cmd/web_graceful.go View File

@@ -37,6 +37,12 @@ func NoMainListener() {
graceful.GetManager().InformCleanup() graceful.GetManager().InformCleanup()
} }


// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener
// for our install HTTP/HTTPS service
func NoInstallListener() {
graceful.GetManager().InformCleanup()
}

func runFCGI(network, listenAddr string, m http.Handler) error { func runFCGI(network, listenAddr string, m http.Handler) error {
// This needs to handle stdin as fcgi point // This needs to handle stdin as fcgi point
fcgiServer := graceful.NewServer(network, listenAddr) fcgiServer := graceful.NewServer(network, listenAddr)


+ 0
- 6
modules/context/auth.go View File

@@ -26,12 +26,6 @@ type ToggleOptions struct {
// Toggle returns toggle options as middleware // Toggle returns toggle options as middleware
func Toggle(options *ToggleOptions) macaron.Handler { func Toggle(options *ToggleOptions) macaron.Handler {
return func(ctx *Context) { return func(ctx *Context) {
// Cannot view any page before installation.
if !setting.InstallLock {
ctx.Redirect(setting.AppSubURL + "/install")
return
}

isAPIPath := auth.IsAPIPath(ctx.Req.URL.Path) isAPIPath := auth.IsAPIPath(ctx.Req.URL.Path)


// Check prohibit login users. // Check prohibit login users.


+ 1
- 1
modules/graceful/manager.go View File

@@ -31,7 +31,7 @@ const (
// //
// If you add an additional place you must increment this number // If you add an additional place you must increment this number
// and add a function to call manager.InformCleanup if it's not going to be used // and add a function to call manager.InformCleanup if it's not going to be used
const numberOfServersToCreate = 3
const numberOfServersToCreate = 4


// Manager represents the graceful server manager interface // Manager represents the graceful server manager interface
var manager *Manager var manager *Manager


+ 1
- 1
modules/graceful/server.go View File

@@ -162,7 +162,7 @@ func (srv *Server) Serve(serve ServeFunction) error {
srv.setState(stateTerminate) srv.setState(stateTerminate)
GetManager().ServerDone() GetManager().ServerDone()
// use of closed means that the listeners are closed - i.e. we should be shutting down - return nil // use of closed means that the listeners are closed - i.e. we should be shutting down - return nil
if err != nil && strings.Contains(err.Error(), "use of closed") {
if err == nil || strings.Contains(err.Error(), "use of closed") || strings.Contains(err.Error(), "http: Server closed") {
return nil return nil
} }
return err return err


+ 71
- 43
routers/init.go View File

@@ -117,9 +117,46 @@ func InitLocales() {
}) })
} }


// PreInstallInit preloads the configuration to check if we need to run install
func PreInstallInit(ctx context.Context) bool {
setting.NewContext()
if !setting.InstallLock {
log.Trace("AppPath: %s", setting.AppPath)
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
log.Trace("Custom path: %s", setting.CustomPath)
log.Trace("Log path: %s", setting.LogRootPath)
log.Trace("Preparing to run install page")
InitLocales()
if setting.EnableSQLite3 {
log.Info("SQLite3 Supported")
}
setting.InitDBConfig()
svg.Init()
}

return !setting.InstallLock
}

// PostInstallInit rereads the settings and starts up the database
func PostInstallInit(ctx context.Context) {
setting.NewContext()
setting.InitDBConfig()
if setting.InstallLock {
if err := initDBEngine(ctx); err == nil {
log.Info("ORM engine initialization successful!")
} else {
log.Fatal("ORM engine initialization failed: %v", err)
}
svg.Init()
}
}

// GlobalInit is for global configuration reload-able. // GlobalInit is for global configuration reload-able.
func GlobalInit(ctx context.Context) { func GlobalInit(ctx context.Context) {
setting.NewContext() setting.NewContext()
if !setting.InstallLock {
log.Fatal("Gitea is not installed")
}
if err := git.Init(ctx); err != nil { if err := git.Init(ctx); err != nil {
log.Fatal("Git module init failed: %v", err) log.Fatal("Git module init failed: %v", err)
} }
@@ -134,59 +171,50 @@ func GlobalInit(ctx context.Context) {


NewServices() NewServices()


if setting.InstallLock {
highlight.NewContext()
external.RegisterParsers()
markup.Init()
if err := initDBEngine(ctx); err == nil {
log.Info("ORM engine initialization successful!")
} else {
log.Fatal("ORM engine initialization failed: %v", err)
}
highlight.NewContext()
external.RegisterParsers()
markup.Init()
if err := initDBEngine(ctx); err == nil {
log.Info("ORM engine initialization successful!")
} else {
log.Fatal("ORM engine initialization failed: %v", err)
}


if err := models.InitOAuth2(); err != nil {
log.Fatal("Failed to initialize OAuth2 support: %v", err)
}
if err := models.InitOAuth2(); err != nil {
log.Fatal("Failed to initialize OAuth2 support: %v", err)
}


models.NewRepoContext()
models.NewRepoContext()


// Booting long running goroutines.
cron.NewContext()
issue_indexer.InitIssueIndexer(false)
code_indexer.Init()
if err := stats_indexer.Init(); err != nil {
log.Fatal("Failed to initialize repository stats indexer queue: %v", err)
}
mirror_service.InitSyncMirrors()
webhook.InitDeliverHooks()
if err := pull_service.Init(); err != nil {
log.Fatal("Failed to initialize test pull requests queue: %v", err)
}
if err := task.Init(); err != nil {
log.Fatal("Failed to initialize task scheduler: %v", err)
}
eventsource.GetManager().Init()
// Booting long running goroutines.
cron.NewContext()
issue_indexer.InitIssueIndexer(false)
code_indexer.Init()
if err := stats_indexer.Init(); err != nil {
log.Fatal("Failed to initialize repository stats indexer queue: %v", err)
} }
mirror_service.InitSyncMirrors()
webhook.InitDeliverHooks()
if err := pull_service.Init(); err != nil {
log.Fatal("Failed to initialize test pull requests queue: %v", err)
}
if err := task.Init(); err != nil {
log.Fatal("Failed to initialize task scheduler: %v", err)
}
eventsource.GetManager().Init()

if setting.EnableSQLite3 { if setting.EnableSQLite3 {
log.Info("SQLite3 Supported") log.Info("SQLite3 Supported")
} }
checkRunMode() checkRunMode()


// Now because Install will re-run GlobalInit once it has set InstallLock
// we can't tell if the ssh port will remain unused until that's done.
// However, see FIXME comment in install.go
if setting.InstallLock {
if setting.SSH.StartBuiltinServer {
ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
log.Info("SSH server started on %s:%d. Cipher list (%v), key exchange algorithms (%v), MACs (%v)", setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
} else {
ssh.Unused()
}
}

if setting.InstallLock {
sso.Init()
if setting.SSH.StartBuiltinServer {
ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
log.Info("SSH server started on %s:%d. Cipher list (%v), key exchange algorithms (%v), MACs (%v)", setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
} else {
ssh.Unused()
} }
sso.Init()


svg.Init() svg.Init()
} }

+ 25
- 11
routers/install.go View File

@@ -5,7 +5,7 @@
package routers package routers


import ( import (
"errors"
"net/http"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@@ -27,13 +27,15 @@ import (


const ( const (
// tplInstall template for installation page // tplInstall template for installation page
tplInstall base.TplName = "install"
tplInstall base.TplName = "install"
tplPostInstall base.TplName = "post-install"
) )


// InstallInit prepare for rendering installation page // InstallInit prepare for rendering installation page
func InstallInit(ctx *context.Context) { func InstallInit(ctx *context.Context) {
if setting.InstallLock { if setting.InstallLock {
ctx.NotFound("Install", errors.New("Installation is prohibited"))
ctx.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login")
ctx.HTML(200, tplPostInstall)
return return
} }


@@ -357,7 +359,8 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
return return
} }


GlobalInit(graceful.GetManager().HammerContext())
// Re-read settings
PostInstallInit(ctx.Req.Context())


// Create admin account // Create admin account
if len(form.AdminName) > 0 { if len(form.AdminName) > 0 {
@@ -380,6 +383,11 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
u, _ = models.GetUserByName(u.Name) u, _ = models.GetUserByName(u.Name)
} }


days := 86400 * setting.LogInRememberDays
ctx.SetCookie(setting.CookieUserName, u.Name, days, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd),
setting.CookieRememberName, u.Name, days, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)

// Auto-login for admin // Auto-login for admin
if err = ctx.Session.Set("uid", u.ID); err != nil { if err = ctx.Session.Set("uid", u.ID); err != nil {
ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form)
@@ -397,12 +405,18 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
} }


log.Info("First-time run install finished!") 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.Flash.Success(ctx.Tr("install.install_success"))
ctx.Redirect(form.AppURL + "user/login")

ctx.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login")
ctx.HTML(200, tplPostInstall)

// Now get the http.Server from this request and shut it down
// NB: This is not our hammerable graceful shutdown this is http.Server.Shutdown
srv := ctx.Req.Context().Value(http.ServerContextKey).(*http.Server)
go func() {
if err := srv.Shutdown(graceful.GetManager().HammerContext()); err != nil {
log.Error("Unable to shutdown the install server! Error: %v", err)
}
}()
} }

+ 9
- 0
routers/routes/routes.go View File

@@ -301,6 +301,15 @@ func NewMacaron() *macaron.Macaron {
return m return m
} }


// RegisterInstallRoute registers the install routes
func RegisterInstallRoute(m *macaron.Macaron) {
m.Combo("/", routers.InstallInit).Get(routers.Install).
Post(binding.BindIgnErr(auth.InstallForm{}), routers.InstallPost)
m.NotFound(func(ctx *context.Context) {
ctx.Redirect(setting.AppURL, 302)
})
}

// RegisterRoutes routes routes to Macaron // RegisterRoutes routes routes to Macaron
func RegisterRoutes(m *macaron.Macaron) { func RegisterRoutes(m *macaron.Macaron) {
reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true}) reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true})


+ 2
- 1
templates/install.tmpl View File

@@ -10,7 +10,7 @@


<p>{{.i18n.Tr "install.docker_helper" "https://docs.gitea.io/en-us/install-with-docker/" | Safe}}</p> <p>{{.i18n.Tr "install.docker_helper" "https://docs.gitea.io/en-us/install-with-docker/" | Safe}}</p>


<form class="ui form" action="{{AppSubUrl}}/install" method="post">
<form class="ui form" action="{{AppSubUrl}}/" method="post">
<!-- Database Settings --> <!-- Database Settings -->
<h4 class="ui dividing header">{{.i18n.Tr "install.db_title"}}</h4> <h4 class="ui dividing header">{{.i18n.Tr "install.db_title"}}</h4>
<p>{{.i18n.Tr "install.requite_db_desc"}}</p> <p>{{.i18n.Tr "install.requite_db_desc"}}</p>
@@ -307,4 +307,5 @@
</div> </div>
</div> </div>
</div> </div>
<img style="display: none" src="{{StaticUrlPrefix}}/img/loading.png"/>
{{template "base/footer" .}} {{template "base/footer" .}}

+ 24
- 0
templates/post-install.tmpl View File

@@ -0,0 +1,24 @@
{{template "base/head" .}}
<div class="install">
<div class="ui container">
<div class="ui grid">
<div class="sixteen wide column content">
<div class="home">
<div class="ui stackable middle very relaxed page grid">
<div id="repo_migrating" class="sixteen wide center aligned centered column">
<div>
<img src="{{StaticUrlPrefix}}/img/loading.png"/>
</div>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
<div class="sixteen wide center aligned centered column">
<p><a href="{{AppUrl}}user/login">{{AppUrl}}user/login</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

Loading…
Cancel
Save