Skip to content

Move install pages out of main macaron routes #13195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 19, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 89 additions & 46 deletions cmd/web.go
Original file line number Diff line number Diff line change
@@ -19,6 +19,8 @@ import (
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/routes"

"gitea.com/macaron/macaron"

context2 "github.com/gorilla/context"
"github.com/unknwon/com"
"github.com/urfave/cli"
@@ -114,48 +116,89 @@ func runWeb(ctx *cli.Context) error {
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
routers.GlobalInit(graceful.GetManager().HammerContext())

// Set up Macaron
m := routes.NewMacaron()
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
if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
@@ -166,37 +209,40 @@ func runWeb(ctx *cli.Context) error {
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
switch setting.Protocol {
case setting.HTTP:
NoHTTPRedirector()
if handleRedirector {
NoHTTPRedirector()
}
err = runHTTP("tcp", listenAddr, context2.ClearHandler(m))
case setting.HTTPS:
if setting.EnableLetsEncrypt {
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
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))
case setting.FCGI:
NoHTTPRedirector()
if handleRedirector {
NoHTTPRedirector()
}
err = runFCGI("tcp", listenAddr, context2.ClearHandler(m))
case setting.UnixSocket:
NoHTTPRedirector()
if handleRedirector {
NoHTTPRedirector()
}
err = runHTTP("unix", listenAddr, context2.ClearHandler(m))
case setting.FCGIUnix:
NoHTTPRedirector()
if handleRedirector {
NoHTTPRedirector()
}
err = runFCGI("unix", listenAddr, context2.ClearHandler(m))
default:
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.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 changes: 6 additions & 0 deletions cmd/web_graceful.go
Original file line number Diff line number Diff line change
@@ -37,6 +37,12 @@ func NoMainListener() {
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 {
// This needs to handle stdin as fcgi point
fcgiServer := graceful.NewServer(network, listenAddr)
6 changes: 0 additions & 6 deletions modules/context/auth.go
Original file line number Diff line number Diff line change
@@ -26,12 +26,6 @@ type ToggleOptions struct {
// Toggle returns toggle options as middleware
func Toggle(options *ToggleOptions) macaron.Handler {
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)

// Check prohibit login users.
2 changes: 1 addition & 1 deletion modules/graceful/manager.go
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ const (
//
// 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
const numberOfServersToCreate = 3
const numberOfServersToCreate = 4

// Manager represents the graceful server manager interface
var manager *Manager
2 changes: 1 addition & 1 deletion modules/graceful/server.go
Original file line number Diff line number Diff line change
@@ -162,7 +162,7 @@ func (srv *Server) Serve(serve ServeFunction) error {
srv.setState(stateTerminate)
GetManager().ServerDone()
// 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 err
114 changes: 71 additions & 43 deletions routers/init.go
Original file line number Diff line number Diff line change
@@ -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.
func GlobalInit(ctx context.Context) {
setting.NewContext()
if !setting.InstallLock {
log.Fatal("Gitea is not installed")
}
if err := git.Init(ctx); err != nil {
log.Fatal("Git module init failed: %v", err)
}
@@ -134,59 +171,50 @@ func GlobalInit(ctx context.Context) {

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 {
log.Info("SQLite3 Supported")
}
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()
}
36 changes: 25 additions & 11 deletions routers/install.go
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
package routers

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

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

// InstallInit prepare for rendering installation page
func InstallInit(ctx *context.Context) {
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
}

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

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

// Create admin account
if len(form.AdminName) > 0 {
@@ -380,6 +383,11 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
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
if err = ctx.Session.Set("uid", u.ID); err != nil {
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!")
// 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.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 changes: 9 additions & 0 deletions routers/routes/routes.go
Original file line number Diff line number Diff line change
@@ -301,6 +301,15 @@ func NewMacaron() *macaron.Macaron {
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
func RegisterRoutes(m *macaron.Macaron) {
reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true})
3 changes: 2 additions & 1 deletion templates/install.tmpl
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@

<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 -->
<h4 class="ui dividing header">{{.i18n.Tr "install.db_title"}}</h4>
<p>{{.i18n.Tr "install.requite_db_desc"}}</p>
@@ -307,4 +307,5 @@
</div>
</div>
</div>
<img style="display: none" src="{{StaticUrlPrefix}}/img/loading.png"/>
{{template "base/footer" .}}
24 changes: 24 additions & 0 deletions templates/post-install.tmpl
Original file line number Diff line number Diff line change
@@ -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" .}}