Skip to content

Commit a883a9a

Browse files
committed
Add manager command
1 parent 1f031cb commit a883a9a

File tree

9 files changed

+386
-20
lines changed

9 files changed

+386
-20
lines changed

cmd/manager.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package cmd
6+
7+
import (
8+
"fmt"
9+
"net/http"
10+
"os"
11+
"time"
12+
13+
"code.gitea.io/gitea/modules/private"
14+
15+
"github.com/urfave/cli"
16+
)
17+
18+
var (
19+
// CmdManager represents the manager command
20+
CmdManager = cli.Command{
21+
Name: "manager",
22+
Usage: "Manage the running gitea process",
23+
Description: "This is a command for managing the running gitea process",
24+
Subcommands: []cli.Command{
25+
subcmdShutdown,
26+
subcmdRestart,
27+
subcmdFlushQueues,
28+
},
29+
}
30+
subcmdShutdown = cli.Command{
31+
Name: "shutdown",
32+
Usage: "Gracefully shutdown the running process",
33+
Action: runShutdown,
34+
}
35+
subcmdRestart = cli.Command{
36+
Name: "restart",
37+
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
38+
Action: runRestart,
39+
}
40+
subcmdFlushQueues = cli.Command{
41+
Name: "flush-queues",
42+
Usage: "Flush queues in the running process",
43+
Action: runFlushQueues,
44+
Flags: []cli.Flag{
45+
cli.DurationFlag{
46+
Name: "timeout",
47+
Value: 60 * time.Second,
48+
Usage: "Timeout for the flushing process",
49+
},
50+
cli.BoolFlag{
51+
Name: "non-blocking",
52+
Usage: "Set to true to not wait for flush to complete before returning",
53+
},
54+
},
55+
}
56+
)
57+
58+
func runShutdown(c *cli.Context) error {
59+
setup("manager", false)
60+
statusCode, msg := private.Shutdown()
61+
switch statusCode {
62+
case http.StatusInternalServerError:
63+
fail("InternalServerError", msg)
64+
}
65+
66+
fmt.Fprintln(os.Stdout, msg)
67+
return nil
68+
}
69+
70+
func runRestart(c *cli.Context) error {
71+
setup("manager", false)
72+
statusCode, msg := private.Restart()
73+
switch statusCode {
74+
case http.StatusInternalServerError:
75+
fail("InternalServerError", msg)
76+
}
77+
78+
fmt.Fprintln(os.Stdout, msg)
79+
return nil
80+
}
81+
82+
func runFlushQueues(c *cli.Context) error {
83+
setup("manager", false)
84+
statusCode, msg := private.FlushQueues(c.Duration("timeout"), c.Bool("non-blocking"))
85+
switch statusCode {
86+
case http.StatusInternalServerError:
87+
fail("InternalServerError", msg)
88+
}
89+
90+
fmt.Fprintln(os.Stdout, msg)
91+
return nil
92+
}

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ arguments - which can alternatively be run by running the subcommand web.`
6969
cmd.CmdKeys,
7070
cmd.CmdConvert,
7171
cmd.CmdDoctor,
72+
cmd.CmdManager,
7273
}
7374
// Now adjust these commands to add our global configuration options
7475

modules/graceful/manager_unix.go

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -110,36 +110,27 @@ func (g *Manager) handleSignals(ctx context.Context) {
110110
case sig := <-signalChannel:
111111
switch sig {
112112
case syscall.SIGHUP:
113-
if setting.GracefulRestartable {
114-
log.Info("PID: %d. Received SIGHUP. Forking...", pid)
115-
err := g.doFork()
116-
if err != nil && err.Error() != "another process already forked. Ignoring this one" {
117-
log.Error("Error whilst forking from PID: %d : %v", pid, err)
118-
}
119-
} else {
120-
log.Info("PID: %d. Received SIGHUP. Not set restartable. Shutting down...", pid)
121-
122-
g.doShutdown()
123-
}
113+
log.Info("PID: %d. Received SIGHUP. Attempting GracefulShutdown...", pid)
114+
g.DoGracefulShutdown()
124115
case syscall.SIGUSR1:
125116
log.Info("PID %d. Received SIGUSR1.", pid)
126117
case syscall.SIGUSR2:
127118
log.Warn("PID %d. Received SIGUSR2. Hammering...", pid)
128-
g.doHammerTime(0 * time.Second)
119+
g.DoImmediateHammer()
129120
case syscall.SIGINT:
130121
log.Warn("PID %d. Received SIGINT. Shutting down...", pid)
131-
g.doShutdown()
122+
g.DoGracefulShutdown()
132123
case syscall.SIGTERM:
133124
log.Warn("PID %d. Received SIGTERM. Shutting down...", pid)
134-
g.doShutdown()
125+
g.DoGracefulShutdown()
135126
case syscall.SIGTSTP:
136127
log.Info("PID %d. Received SIGTSTP.", pid)
137128
default:
138129
log.Info("PID %d. Received %v.", pid, sig)
139130
}
140131
case <-ctx.Done():
141132
log.Warn("PID: %d. Background context for manager closed - %v - Shutting down...", pid, ctx.Err())
142-
g.doShutdown()
133+
g.DoGracefulShutdown()
143134
}
144135
}
145136
}
@@ -160,6 +151,31 @@ func (g *Manager) doFork() error {
160151
return err
161152
}
162153

154+
// DoGracefulRestart causes a graceful shutdown
155+
func (g *Manager) DoGracefulRestart() {
156+
if setting.GracefulRestartable {
157+
log.Info("PID: %d. Forking...", os.Getpid())
158+
err := g.doFork()
159+
if err != nil && err.Error() != "another process already forked. Ignoring this one" {
160+
log.Error("Error whilst forking from PID: %d : %v", os.Getpid(), err)
161+
}
162+
} else {
163+
log.Info("PID: %d. Not set restartable. Shutting down...", os.Getpid())
164+
165+
g.doShutdown()
166+
}
167+
}
168+
169+
// DoImmediateHammer causes an immediate hammer
170+
func (g *Manager) DoImmediateHammer() {
171+
g.doHammerTime(0 * time.Second)
172+
}
173+
174+
// DoGracefulShutdown causes a graceful shutdown
175+
func (g *Manager) DoGracefulShutdown() {
176+
g.doShutdown()
177+
}
178+
163179
// RegisterServer registers the running of a listening server, in the case of unix this means that the parent process can now die.
164180
// Any call to RegisterServer must be matched by a call to ServerDone
165181
func (g *Manager) RegisterServer() {

modules/graceful/manager_windows.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,20 @@ loop:
107107
for {
108108
select {
109109
case <-g.ctx.Done():
110-
g.doShutdown()
110+
g.DoGracefulShutdown()
111111
waitTime += setting.GracefulHammerTime
112112
break loop
113113
case change := <-changes:
114114
switch change.Cmd {
115115
case svc.Interrogate:
116116
status <- change.CurrentStatus
117117
case svc.Stop, svc.Shutdown:
118-
g.doShutdown()
118+
g.DoGracefulShutdown()
119119
waitTime += setting.GracefulHammerTime
120120
break loop
121121
case hammerCode:
122-
g.doShutdown()
123-
g.doHammerTime(0 * time.Second)
122+
g.DoGracefulShutdown()
123+
g.DoImmediateHammer()
124124
break loop
125125
default:
126126
log.Debug("Unexpected control request: %v", change.Cmd)
@@ -140,7 +140,7 @@ hammerLoop:
140140
case svc.Interrogate:
141141
status <- change.CurrentStatus
142142
case svc.Stop, svc.Shutdown, hammerCmd:
143-
g.doHammerTime(0 * time.Second)
143+
g.DoImmediateHammer()
144144
break hammerLoop
145145
default:
146146
log.Debug("Unexpected control request: %v", change.Cmd)
@@ -152,6 +152,16 @@ hammerLoop:
152152
return false, 0
153153
}
154154

155+
// DoImmediateHammer causes an immediate hammer
156+
func (g *Manager) DoImmediateHammer() {
157+
g.doHammerTime(0 * time.Second)
158+
}
159+
160+
// DoGracefulShutdown causes a graceful shutdown
161+
func (g *Manager) DoGracefulShutdown() {
162+
g.doShutdown()
163+
}
164+
155165
// RegisterServer registers the running of a listening server.
156166
// Any call to RegisterServer must be matched by a call to ServerDone
157167
func (g *Manager) RegisterServer() {

modules/private/manager.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package private
6+
7+
import (
8+
"encoding/json"
9+
"fmt"
10+
"net/http"
11+
"time"
12+
13+
"code.gitea.io/gitea/modules/setting"
14+
)
15+
16+
// Shutdown calls the internal shutdown function
17+
func Shutdown() (int, string) {
18+
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/manager/shutdown")
19+
20+
req := newInternalRequest(reqURL, "POST")
21+
resp, err := req.Response()
22+
if err != nil {
23+
return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
24+
}
25+
defer resp.Body.Close()
26+
27+
if resp.StatusCode != http.StatusOK {
28+
return resp.StatusCode, decodeJSONError(resp).Err
29+
}
30+
31+
return http.StatusOK, "Shutting down"
32+
}
33+
34+
// Restart calls the internal restart function
35+
func Restart() (int, string) {
36+
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/manager/restart")
37+
38+
req := newInternalRequest(reqURL, "POST")
39+
resp, err := req.Response()
40+
if err != nil {
41+
return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
42+
}
43+
defer resp.Body.Close()
44+
45+
if resp.StatusCode != http.StatusOK {
46+
return resp.StatusCode, decodeJSONError(resp).Err
47+
}
48+
49+
return http.StatusOK, "Restarting"
50+
}
51+
52+
// FlushOptions represents the options for the flush call
53+
type FlushOptions struct {
54+
Timeout time.Duration
55+
NonBlocking bool
56+
}
57+
58+
// FlushQueues calls the internal flush-queues function
59+
func FlushQueues(timeout time.Duration, nonBlocking bool) (int, string) {
60+
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/manager/flush-queues")
61+
62+
req := newInternalRequest(reqURL, "POST")
63+
if timeout > 0 {
64+
req.SetTimeout(timeout+10*time.Second, timeout+10*time.Second)
65+
}
66+
req = req.Header("Content-Type", "application/json")
67+
jsonBytes, _ := json.Marshal(FlushOptions{
68+
Timeout: timeout,
69+
NonBlocking: nonBlocking,
70+
})
71+
req.Body(jsonBytes)
72+
resp, err := req.Response()
73+
if err != nil {
74+
return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v", err.Error())
75+
}
76+
defer resp.Body.Close()
77+
78+
if resp.StatusCode != http.StatusOK {
79+
return resp.StatusCode, decodeJSONError(resp).Err
80+
}
81+
82+
return http.StatusOK, "Flushed"
83+
}

routers/private/internal.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,9 @@ func RegisterRoutes(m *macaron.Macaron) {
8989
m.Post("/hook/set-default-branch/:owner/:repo/:branch", SetDefaultBranch)
9090
m.Get("/serv/none/:keyid", ServNoCommand)
9191
m.Get("/serv/command/:keyid/:owner/:repo", ServCommand)
92+
m.Post("/manager/shutdown", Shutdown)
93+
m.Post("/manager/restart", Restart)
94+
m.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues)
95+
9296
}, CheckInternalToken)
9397
}

0 commit comments

Comments
 (0)