summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorValery Piashchynski <[email protected]>2022-05-31 16:01:30 +0200
committerGitHub <[email protected]>2022-05-31 16:01:30 +0200
commit854c6b9a6d22c99984a8064e64e444c32209c9bd (patch)
tree4901ed62317c13d451b62c2f9d32f33854d03c91
parentf0179128570e6ed005086d9c9dbcd11964955c24 (diff)
parent2aba7854e5b916a9a72b3c87c1eba0d3071006d7 (diff)
[#1163]: feat(CLI): `rr stop` command
-rw-r--r--.pid1
-rw-r--r--internal/cli/root.go67
-rw-r--r--internal/cli/serve/command.go6
-rw-r--r--internal/cli/stop/command.go63
-rw-r--r--internal/cli/stop/command_test.go27
5 files changed, 142 insertions, 22 deletions
diff --git a/.pid b/.pid
new file mode 100644
index 00000000..9af205b0
--- /dev/null
+++ b/.pid
@@ -0,0 +1 @@
+181292 \ No newline at end of file
diff --git a/internal/cli/root.go b/internal/cli/root.go
index 50f93795..1af7ef41 100644
--- a/internal/cli/root.go
+++ b/internal/cli/root.go
@@ -5,10 +5,12 @@ import (
"os"
"path/filepath"
"runtime"
+ "strconv"
"github.com/roadrunner-server/errors"
"github.com/roadrunner-server/roadrunner/v2/internal/cli/reset"
"github.com/roadrunner-server/roadrunner/v2/internal/cli/serve"
+ "github.com/roadrunner-server/roadrunner/v2/internal/cli/stop"
"github.com/roadrunner-server/roadrunner/v2/internal/cli/workers"
dbg "github.com/roadrunner-server/roadrunner/v2/internal/debug"
"github.com/roadrunner-server/roadrunner/v2/internal/meta"
@@ -17,21 +19,31 @@ import (
"github.com/spf13/cobra"
)
-// NewCommand creates root command.
-func NewCommand(cmdName string) *cobra.Command { //nolint:funlen
- const (
- envDotenv string = "DOTENV_PATH" // env var name: path to the .env file
- )
+const (
+ // env var name: path to the .env file
+ envDotenv string = "DOTENV_PATH"
+ pidFileName string = ".pid"
+)
- var ( // flag values
- cfgFile = strPtr("") // path to the .rr.yaml
- workDir string // working directory
- dotenv string // path to the .env file
- debug bool // debug mode
- override = &[]string{} // override config values
- // do not print startup message
- silent = boolPtr(false)
- )
+// NewCommand creates root command.
+func NewCommand(cmdName string) *cobra.Command { //nolint:funlen,gocognit
+ // path to the .rr.yaml
+ cfgFile := toPtr("")
+ // pidfile path
+ pidFile := toPtr(false)
+ // force stop RR
+ forceStop := toPtr(false)
+ // override config values
+ override := &[]string{}
+ // do not print startup message
+ silent := toPtr(false)
+
+ // working directory
+ var workDir string
+ // path to the .env file
+ var dotenv string
+ // debug mode
+ var debug bool
cmd := &cobra.Command{
Use: cmdName,
@@ -81,12 +93,30 @@ func NewCommand(cmdName string) *cobra.Command { //nolint:funlen
go func() { _ = srv.Start(":6061") }() // TODO implement graceful server stopping
}
+ // user wanted to write a .pid file
+ if *pidFile {
+ f, err := os.Create(pidFileName)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ _ = f.Close()
+ }()
+
+ _, err = f.WriteString(strconv.Itoa(os.Getpid()))
+ if err != nil {
+ return err
+ }
+ }
+
return nil
},
}
f := cmd.PersistentFlags()
+ f.BoolVarP(forceStop, "force", "f", false, "force stop")
+ f.BoolVarP(pidFile, "pid", "p", false, "create a .pid file")
f.StringVarP(cfgFile, "config", "c", ".rr.yaml", "config file")
f.StringVarP(&workDir, "WorkDir", "w", "", "working directory")
f.StringVarP(&dotenv, "dotenv", "", "", fmt.Sprintf("dotenv file [$%s]", envDotenv))
@@ -98,15 +128,12 @@ func NewCommand(cmdName string) *cobra.Command { //nolint:funlen
workers.NewCommand(cfgFile, override),
reset.NewCommand(cfgFile, override, silent),
serve.NewCommand(override, cfgFile, silent),
+ stop.NewCommand(silent, forceStop),
)
return cmd
}
-func strPtr(s string) *string {
- return &s
-}
-
-func boolPtr(b bool) *bool {
- return &b
+func toPtr[T any](val T) *T {
+ return &val
}
diff --git a/internal/cli/serve/command.go b/internal/cli/serve/command.go
index 3a1e945c..a85beb89 100644
--- a/internal/cli/serve/command.go
+++ b/internal/cli/serve/command.go
@@ -5,6 +5,7 @@ import (
"os"
"os/signal"
"syscall"
+ "time"
"github.com/roadrunner-server/roadrunner/v2/internal/container"
"github.com/roadrunner-server/roadrunner/v2/internal/meta"
@@ -73,7 +74,7 @@ func NewCommand(override *[]string, cfgFile *string, silent *bool) *cobra.Comman
return errors.E(op, err)
}
- oss, stop := make(chan os.Signal, 2), make(chan struct{}, 1) //nolint:gomnd
+ oss, stop := make(chan os.Signal, 5), make(chan struct{}, 1) //nolint:gomnd
signal.Notify(oss, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
go func() {
@@ -98,8 +99,9 @@ func NewCommand(override *[]string, cfgFile *string, silent *bool) *cobra.Comman
case e := <-errCh:
return fmt.Errorf("error: %w\nplugin: %s", e.Error, e.VertexID)
case <-stop: // stop the container after first signal
- fmt.Printf("stop signal received, grace timeout is: %f seconds\n", containerCfg.GracePeriod.Seconds())
+ fmt.Printf("stop signal received, grace timeout is: %0.f seconds\n", containerCfg.GracePeriod.Seconds())
+ time.Sleep(time.Second * 100)
if err = endureContainer.Stop(); err != nil {
return fmt.Errorf("error: %w", err)
}
diff --git a/internal/cli/stop/command.go b/internal/cli/stop/command.go
new file mode 100644
index 00000000..533f11d6
--- /dev/null
+++ b/internal/cli/stop/command.go
@@ -0,0 +1,63 @@
+package stop
+
+import (
+ "log"
+ "os"
+ "strconv"
+ "syscall"
+ "time"
+
+ "github.com/roadrunner-server/errors"
+ "github.com/spf13/cobra"
+)
+
+const (
+ // sync with root.go
+ pidFileName string = ".pid"
+)
+
+// NewCommand creates `serve` command.
+func NewCommand(silent *bool, force *bool) *cobra.Command {
+ return &cobra.Command{
+ Use: "stop",
+ Short: "Stop RoadRunner server",
+ RunE: func(*cobra.Command, []string) error {
+ const op = errors.Op("rr_stop")
+
+ data, err := os.ReadFile(pidFileName)
+ if err != nil {
+ return errors.Errorf("%v, to create a .pid file, you must run RR with the following options: './rr serve -p'", err)
+ }
+
+ pid, err := strconv.Atoi(string(data))
+ if err != nil {
+ return errors.E(op, err)
+ }
+
+ process, err := os.FindProcess(pid)
+ if err != nil {
+ return errors.E(op, err)
+ }
+
+ if !*silent {
+ log.Printf("stopping process with PID: %d", pid)
+ }
+
+ err = process.Signal(syscall.SIGTERM)
+ if err != nil {
+ return errors.E(op, err)
+ }
+
+ if *force {
+ // RR may lost the signal if we immediately send it
+ time.Sleep(time.Second)
+ err = process.Signal(syscall.SIGTERM)
+ if err != nil {
+ return errors.E(op, err)
+ }
+ }
+
+ return nil
+ },
+ }
+}
diff --git a/internal/cli/stop/command_test.go b/internal/cli/stop/command_test.go
new file mode 100644
index 00000000..13f01bed
--- /dev/null
+++ b/internal/cli/stop/command_test.go
@@ -0,0 +1,27 @@
+package stop_test
+
+import (
+ "testing"
+
+ "github.com/roadrunner-server/roadrunner/v2/internal/cli/stop"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCommandProperties(t *testing.T) {
+ cmd := stop.NewCommand(toPtr(false), toPtr(false))
+
+ assert.Equal(t, "stop", cmd.Use)
+ assert.NotNil(t, cmd.RunE)
+}
+
+func TestCommandTrue(t *testing.T) {
+ cmd := stop.NewCommand(toPtr(true), toPtr(true))
+
+ assert.Equal(t, "stop", cmd.Use)
+ assert.NotNil(t, cmd.RunE)
+}
+
+func toPtr[T any](val T) *T {
+ return &val
+}