diff options
author | Valery Piashchynski <[email protected]> | 2020-10-13 13:55:20 +0300 |
---|---|---|
committer | Valery Piashchynski <[email protected]> | 2020-10-13 13:55:20 +0300 |
commit | 0dc44d54cfcc9dd3fa09a41136f35a9a8d26b994 (patch) | |
tree | ffcb65010bebe9f5b5436192979e64b2402a6ec0 /cmd | |
parent | 08d6b6b7f773f83b286cd48c1a0fbec9a62fb42b (diff) |
Initial commit of RR 2.0v2.0.0-alpha1
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/rr/LICENSE | 21 | ||||
-rw-r--r-- | cmd/rr/cmd/root.go | 157 | ||||
-rw-r--r-- | cmd/rr/cmd/serve.go | 63 | ||||
-rw-r--r-- | cmd/rr/cmd/stop.go | 51 | ||||
-rw-r--r-- | cmd/rr/cmd/version.go | 9 | ||||
-rw-r--r-- | cmd/rr/http/debug.go | 138 | ||||
-rw-r--r-- | cmd/rr/http/metrics.go | 123 | ||||
-rw-r--r-- | cmd/rr/http/reset.go | 53 | ||||
-rw-r--r-- | cmd/rr/http/workers.go | 100 | ||||
-rw-r--r-- | cmd/rr/limit/debug.go | 71 | ||||
-rw-r--r-- | cmd/rr/limit/metrics.go | 63 | ||||
-rw-r--r-- | cmd/rr/main.go | 59 | ||||
-rw-r--r-- | cmd/util/config.go | 181 | ||||
-rw-r--r-- | cmd/util/cprint.go | 47 | ||||
-rw-r--r-- | cmd/util/debug.go | 61 | ||||
-rw-r--r-- | cmd/util/exit.go | 15 | ||||
-rw-r--r-- | cmd/util/rpc.go | 18 | ||||
-rw-r--r-- | cmd/util/table.go | 60 |
18 files changed, 0 insertions, 1290 deletions
diff --git a/cmd/rr/LICENSE b/cmd/rr/LICENSE deleted file mode 100644 index efb98c87..00000000 --- a/cmd/rr/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 SpiralScout - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.
\ No newline at end of file diff --git a/cmd/rr/cmd/root.go b/cmd/rr/cmd/root.go deleted file mode 100644 index 456cfc6a..00000000 --- a/cmd/rr/cmd/root.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) 2018 SpiralScout -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package cmd - -import ( - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "github.com/spiral/roadrunner/cmd/util" - "github.com/spiral/roadrunner/service" - "github.com/spiral/roadrunner/service/limit" - "log" - "net/http" - "net/http/pprof" - "os" -) - -// Services bus for all the commands. -var ( - cfgFile, workDir, logFormat string - override []string - mergeJson string - - // Verbose enables verbosity mode (container specific). - Verbose bool - - // Debug enables debug mode (service specific). - Debug bool - - // Logger - shared logger. - Logger = logrus.New() - - // Container - shared service bus. - Container = service.NewContainer(Logger) - - // CLI is application endpoint. - CLI = &cobra.Command{ - Use: "rr", - SilenceErrors: true, - SilenceUsage: true, - Short: util.Sprintf( - "<green>RoadRunner</reset>, PHP Application Server\nVersion: <yellow+hb>%s</reset>, %s", - Version, - BuildTime, - ), - } -) - -// Execute adds all child commands to the CLI command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the CLI. -func Execute() { - if err := CLI.Execute(); err != nil { - util.ExitWithError(err) - } -} - -func init() { - CLI.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output") - CLI.PersistentFlags().BoolVarP(&Debug, "debug", "d", false, "debug mode") - CLI.PersistentFlags().StringVarP(&logFormat, "logFormat", "l", "color", "select log formatter (color, json, plain)") - CLI.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is .rr.yaml)") - CLI.PersistentFlags().StringVarP(&workDir, "workDir", "w", "", "work directory") - CLI.PersistentFlags().StringVarP(&mergeJson, "jsonConfig", "j", "", "merge json configuration") - - CLI.PersistentFlags().StringArrayVarP( - &override, - "override", - "o", - nil, - "override config value (dot.notation=value)", - ) - - cobra.OnInitialize(func() { - if Verbose { - Logger.SetLevel(logrus.DebugLevel) - } - - configureLogger(logFormat) - - cfg, err := util.LoadConfig(cfgFile, []string{"."}, ".rr", override, mergeJson) - if err != nil { - Logger.Warnf("config: %s", err) - return - } - - if workDir != "" { - if err := os.Chdir(workDir); err != nil { - util.ExitWithError(err) - } - } - - if err := Container.Init(cfg); err != nil { - util.ExitWithError(err) - } - - // global watcher config - if Verbose { - wcv, _ := Container.Get(limit.ID) - if wcv, ok := wcv.(*limit.Service); ok { - wcv.AddListener(func(event int, ctx interface{}) { - util.LogEvent(Logger, event, ctx) - }) - } - } - - // if debug --> also run pprof service - if Debug { - go runDebugServer() - } - }) -} -func runDebugServer() { - mux := http.NewServeMux() - mux.HandleFunc("/debug/pprof/", pprof.Index) - mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) - mux.HandleFunc("/debug/pprof/profile", pprof.Profile) - mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) - mux.HandleFunc("/debug/pprof/trace", pprof.Trace) - srv := http.Server{ - Addr: ":6061", - Handler: mux, - } - - if err := srv.ListenAndServe(); err != nil { - log.Fatal(err) - } -} - -func configureLogger(format string) { - util.Colorize = false - switch format { - case "color", "default": - util.Colorize = true - Logger.Formatter = &logrus.TextFormatter{ForceColors: true} - case "plain": - Logger.Formatter = &logrus.TextFormatter{DisableColors: true} - case "json": - Logger.Formatter = &logrus.JSONFormatter{} - } -} diff --git a/cmd/rr/cmd/serve.go b/cmd/rr/cmd/serve.go deleted file mode 100644 index cafbdd4f..00000000 --- a/cmd/rr/cmd/serve.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2018 SpiralScout -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package cmd - -import ( - "github.com/spf13/cobra" - "os" - "os/signal" - "sync" - "syscall" -) - -func init() { - CLI.AddCommand(&cobra.Command{ - Use: "serve", - Short: "Serve RoadRunner service(s)", - RunE: serveHandler, - }) -} - -func serveHandler(cmd *cobra.Command, args []string) error { - // https://golang.org/pkg/os/signal/#Notify - // should be of buffer size at least 1 - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) - - wg := &sync.WaitGroup{} - - wg.Add(1) - go func() { - defer wg.Done() - // get the signal - <-c - Container.Stop() - }() - - // blocking operation - if err := Container.Serve(); err != nil { - return err - } - - wg.Wait() - - return nil -} diff --git a/cmd/rr/cmd/stop.go b/cmd/rr/cmd/stop.go deleted file mode 100644 index 7b4794e7..00000000 --- a/cmd/rr/cmd/stop.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2018 SpiralScout -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package cmd - -import ( - "github.com/spf13/cobra" - "github.com/spiral/roadrunner/cmd/util" -) - -func init() { - CLI.AddCommand(&cobra.Command{ - Use: "stop", - Short: "Stop RoadRunner server", - RunE: stopHandler, - }) -} - -func stopHandler(cmd *cobra.Command, args []string) error { - client, err := util.RPCClient(Container) - if err != nil { - return err - } - - util.Printf("<green>Stopping RoadRunner</reset>: ") - - var r string - if err := client.Call("system.Stop", true, &r); err != nil { - return err - } - - util.Printf("<green+hb>done</reset>\n") - return client.Close() -} diff --git a/cmd/rr/cmd/version.go b/cmd/rr/cmd/version.go deleted file mode 100644 index a550c682..00000000 --- a/cmd/rr/cmd/version.go +++ /dev/null @@ -1,9 +0,0 @@ -package cmd - -var ( - // Version - defines build version. - Version = "local" - - // BuildTime - defined build time. - BuildTime = "development" -) diff --git a/cmd/rr/http/debug.go b/cmd/rr/http/debug.go deleted file mode 100644 index ae383e8d..00000000 --- a/cmd/rr/http/debug.go +++ /dev/null @@ -1,138 +0,0 @@ -package http - -import ( - "fmt" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "github.com/spiral/roadrunner" - rr "github.com/spiral/roadrunner/cmd/rr/cmd" - "github.com/spiral/roadrunner/cmd/util" - rrhttp "github.com/spiral/roadrunner/service/http" - "net" - "net/http" - "strings" - "time" -) - -func init() { - cobra.OnInitialize(func() { - if rr.Debug { - svc, _ := rr.Container.Get(rrhttp.ID) - if svc, ok := svc.(*rrhttp.Service); ok { - svc.AddListener((&debugger{logger: rr.Logger}).listener) - } - } - }) -} - -// listener provide debug callback for system events. With colors! -type debugger struct{ logger *logrus.Logger } - -// listener listens to http events and generates nice looking output. -func (s *debugger) listener(event int, ctx interface{}) { - if util.LogEvent(s.logger, event, ctx) { - // handler by default debug package - return - } - - // http events - switch event { - case rrhttp.EventResponse: - e := ctx.(*rrhttp.ResponseEvent) - s.logger.Info(util.Sprintf( - "<cyan+h>%s</reset> %s %s <white+hb>%s</reset> %s", - e.Request.RemoteAddr, - elapsed(e.Elapsed()), - statusColor(e.Response.Status), - e.Request.Method, - e.Request.URI, - )) - - case rrhttp.EventError: - e := ctx.(*rrhttp.ErrorEvent) - - if _, ok := e.Error.(roadrunner.JobError); ok { - s.logger.Info(util.Sprintf( - "<cyan+h>%s</reset> %s %s <white+hb>%s</reset> %s", - addr(e.Request.RemoteAddr), - elapsed(e.Elapsed()), - statusColor(500), - e.Request.Method, - uri(e.Request), - )) - } else { - s.logger.Info(util.Sprintf( - "<cyan+h>%s</reset> %s %s <white+hb>%s</reset> %s <red>%s</reset>", - addr(e.Request.RemoteAddr), - elapsed(e.Elapsed()), - statusColor(500), - e.Request.Method, - uri(e.Request), - e.Error, - )) - } - } -} - -func statusColor(status int) string { - if status < 300 { - return util.Sprintf("<green>%v</reset>", status) - } - - if status < 400 { - return util.Sprintf("<cyan>%v</reset>", status) - } - - if status < 500 { - return util.Sprintf("<yellow>%v</reset>", status) - } - - return util.Sprintf("<red>%v</reset>", status) -} - -func uri(r *http.Request) string { - if r.TLS != nil { - return fmt.Sprintf("https://%s%s", r.Host, r.URL.String()) - } - - return fmt.Sprintf("http://%s%s", r.Host, r.URL.String()) -} - -// fits duration into 5 characters -func elapsed(d time.Duration) string { - var v string - switch { - case d > 100*time.Second: - v = fmt.Sprintf("%.1fs", d.Seconds()) - case d > 10*time.Second: - v = fmt.Sprintf("%.2fs", d.Seconds()) - case d > time.Second: - v = fmt.Sprintf("%.3fs", d.Seconds()) - case d > 100*time.Millisecond: - v = fmt.Sprintf("%.0fms", d.Seconds()*1000) - case d > 10*time.Millisecond: - v = fmt.Sprintf("%.1fms", d.Seconds()*1000) - default: - v = fmt.Sprintf("%.2fms", d.Seconds()*1000) - } - - if d > time.Second { - return util.Sprintf("<red>{%v}</reset>", v) - } - - if d > time.Millisecond*500 { - return util.Sprintf("<yellow>{%v}</reset>", v) - } - - return util.Sprintf("<gray+hb>{%v}</reset>", v) -} - -func addr(addr string) string { - // otherwise, return remote address as is - if !strings.ContainsRune(addr, ':') { - return addr - } - - addr, _, _ = net.SplitHostPort(addr) - return addr -} diff --git a/cmd/rr/http/metrics.go b/cmd/rr/http/metrics.go deleted file mode 100644 index 21bbbaf1..00000000 --- a/cmd/rr/http/metrics.go +++ /dev/null @@ -1,123 +0,0 @@ -package http - -import ( - "github.com/prometheus/client_golang/prometheus" - "github.com/spf13/cobra" - rr "github.com/spiral/roadrunner/cmd/rr/cmd" - rrhttp "github.com/spiral/roadrunner/service/http" - "github.com/spiral/roadrunner/service/metrics" - "github.com/spiral/roadrunner/util" - "strconv" - "time" -) - -func init() { - cobra.OnInitialize(func() { - svc, _ := rr.Container.Get(metrics.ID) - mtr, ok := svc.(*metrics.Service) - if !ok || !mtr.Enabled() { - return - } - - ht, _ := rr.Container.Get(rrhttp.ID) - if ht, ok := ht.(*rrhttp.Service); ok { - collector := newCollector() - - // register metrics - mtr.MustRegister(collector.requestCounter) - mtr.MustRegister(collector.requestDuration) - mtr.MustRegister(collector.workersMemory) - - // collect events - ht.AddListener(collector.listener) - - // update memory usage every 10 seconds - go collector.collectMemory(ht, time.Second*10) - } - }) -} - -// listener provide debug callback for system events. With colors! -type metricCollector struct { - requestCounter *prometheus.CounterVec - requestDuration *prometheus.HistogramVec - workersMemory prometheus.Gauge -} - -func newCollector() *metricCollector { - return &metricCollector{ - requestCounter: prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "rr_http_request_total", - Help: "Total number of handled http requests after server restart.", - }, - []string{"status"}, - ), - requestDuration: prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "rr_http_request_duration_seconds", - Help: "HTTP request duration.", - }, - []string{"status"}, - ), - workersMemory: prometheus.NewGauge( - prometheus.GaugeOpts{ - Name: "rr_http_workers_memory_bytes", - Help: "Memory usage by HTTP workers.", - }, - ), - } -} - -// listener listens to http events and generates nice looking output. -func (c *metricCollector) listener(event int, ctx interface{}) { - // http events - switch event { - case rrhttp.EventResponse: - e := ctx.(*rrhttp.ResponseEvent) - - c.requestCounter.With(prometheus.Labels{ - "status": strconv.Itoa(e.Response.Status), - }).Inc() - - c.requestDuration.With(prometheus.Labels{ - "status": strconv.Itoa(e.Response.Status), - }).Observe(e.Elapsed().Seconds()) - - case rrhttp.EventError: - e := ctx.(*rrhttp.ErrorEvent) - - c.requestCounter.With(prometheus.Labels{ - "status": "500", - }).Inc() - - c.requestDuration.With(prometheus.Labels{ - "status": "500", - }).Observe(e.Elapsed().Seconds()) - } -} - -// collect memory usage by server workers -func (c *metricCollector) collectMemory(service *rrhttp.Service, tick time.Duration) { - started := false - for { - server := service.Server() - if server == nil && started { - // stopped - return - } - - started = true - - if workers, err := util.ServerState(server); err == nil { - sum := 0.0 - for _, w := range workers { - sum = sum + float64(w.MemoryUsage) - } - - c.workersMemory.Set(sum) - } - - time.Sleep(tick) - } -} diff --git a/cmd/rr/http/reset.go b/cmd/rr/http/reset.go deleted file mode 100644 index 3008848a..00000000 --- a/cmd/rr/http/reset.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2018 SpiralScout -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package http - -import ( - "github.com/spf13/cobra" - rr "github.com/spiral/roadrunner/cmd/rr/cmd" - "github.com/spiral/roadrunner/cmd/util" -) - -func init() { - rr.CLI.AddCommand(&cobra.Command{ - Use: "http:reset", - Short: "Reload RoadRunner worker pool for the HTTP service", - RunE: reloadHandler, - }) -} - -func reloadHandler(cmd *cobra.Command, args []string) error { - client, err := util.RPCClient(rr.Container) - if err != nil { - return err - } - defer client.Close() - - util.Printf("<green>Restarting http worker pool</reset>: ") - - var r string - if err := client.Call("http.Reset", true, &r); err != nil { - return err - } - - util.Printf("<green+hb>done</reset>\n") - return nil -} diff --git a/cmd/rr/http/workers.go b/cmd/rr/http/workers.go deleted file mode 100644 index 4444b87f..00000000 --- a/cmd/rr/http/workers.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2018 SpiralScout -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package http - -import ( - tm "github.com/buger/goterm" - "github.com/spf13/cobra" - rr "github.com/spiral/roadrunner/cmd/rr/cmd" - "github.com/spiral/roadrunner/cmd/util" - "github.com/spiral/roadrunner/service/http" - "net/rpc" - "os" - "os/signal" - "syscall" - "time" -) - -var ( - interactive bool - stopSignal = make(chan os.Signal, 1) -) - -func init() { - workersCommand := &cobra.Command{ - Use: "http:workers", - Short: "List workers associated with RoadRunner HTTP service", - RunE: workersHandler, - } - - workersCommand.Flags().BoolVarP( - &interactive, - "interactive", - "i", - false, - "render interactive workers table", - ) - - rr.CLI.AddCommand(workersCommand) - - signal.Notify(stopSignal, syscall.SIGTERM) - signal.Notify(stopSignal, syscall.SIGINT) -} - -func workersHandler(cmd *cobra.Command, args []string) (err error) { - defer func() { - if r, ok := recover().(error); ok { - err = r - } - }() - - client, err := util.RPCClient(rr.Container) - if err != nil { - return err - } - defer client.Close() - - if !interactive { - showWorkers(client) - return nil - } - - tm.Clear() - for { - select { - case <-stopSignal: - return nil - case <-time.NewTicker(time.Millisecond * 500).C: - tm.MoveCursor(1, 1) - showWorkers(client) - tm.Flush() - } - } -} - -func showWorkers(client *rpc.Client) { - var r http.WorkerList - if err := client.Call("http.Workers", true, &r); err != nil { - panic(err) - } - - util.WorkerTable(r.Workers).Render() -} diff --git a/cmd/rr/limit/debug.go b/cmd/rr/limit/debug.go deleted file mode 100644 index b9d919dc..00000000 --- a/cmd/rr/limit/debug.go +++ /dev/null @@ -1,71 +0,0 @@ -package limit - -import ( - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "github.com/spiral/roadrunner" - rr "github.com/spiral/roadrunner/cmd/rr/cmd" - "github.com/spiral/roadrunner/cmd/util" - "github.com/spiral/roadrunner/service/limit" -) - -func init() { - cobra.OnInitialize(func() { - if rr.Debug { - svc, _ := rr.Container.Get(limit.ID) - if svc, ok := svc.(*limit.Service); ok { - svc.AddListener((&debugger{logger: rr.Logger}).listener) - } - } - }) -} - -// listener provide debug callback for system events. With colors! -type debugger struct{ logger *logrus.Logger } - -// listener listens to http events and generates nice looking output. -func (s *debugger) listener(event int, ctx interface{}) { - if util.LogEvent(s.logger, event, ctx) { - // handler by default debug package - return - } - - // watchers - switch event { - case limit.EventTTL: - w := ctx.(roadrunner.WorkerError) - s.logger.Debug(util.Sprintf( - "<white+hb>worker.%v</reset> <yellow>%s</reset>", - *w.Worker.Pid, - w.Caused, - )) - return - - case limit.EventIdleTTL: - w := ctx.(roadrunner.WorkerError) - s.logger.Debug(util.Sprintf( - "<white+hb>worker.%v</reset> <yellow>%s</reset>", - *w.Worker.Pid, - w.Caused, - )) - return - - case limit.EventMaxMemory: - w := ctx.(roadrunner.WorkerError) - s.logger.Error(util.Sprintf( - "<white+hb>worker.%v</reset> <red>%s</reset>", - *w.Worker.Pid, - w.Caused, - )) - return - - case limit.EventExecTTL: - w := ctx.(roadrunner.WorkerError) - s.logger.Error(util.Sprintf( - "<white+hb>worker.%v</reset> <red>%s</reset>", - *w.Worker.Pid, - w.Caused, - )) - return - } -} diff --git a/cmd/rr/limit/metrics.go b/cmd/rr/limit/metrics.go deleted file mode 100644 index 947f53fe..00000000 --- a/cmd/rr/limit/metrics.go +++ /dev/null @@ -1,63 +0,0 @@ -package limit - -import ( - "github.com/prometheus/client_golang/prometheus" - "github.com/spf13/cobra" - rr "github.com/spiral/roadrunner/cmd/rr/cmd" - rrlimit "github.com/spiral/roadrunner/service/limit" - "github.com/spiral/roadrunner/service/metrics" -) - -func init() { - cobra.OnInitialize(func() { - svc, _ := rr.Container.Get(metrics.ID) - mtr, ok := svc.(*metrics.Service) - if !ok || !mtr.Enabled() { - return - } - - ht, _ := rr.Container.Get(rrlimit.ID) - if ht, ok := ht.(*rrlimit.Service); ok { - collector := newCollector() - - // register metrics - mtr.MustRegister(collector.maxMemory) - - // collect events - ht.AddListener(collector.listener) - } - }) -} - -// listener provide debug callback for system events. With colors! -type metricCollector struct { - maxMemory prometheus.Counter - maxExecutionTime prometheus.Counter -} - -func newCollector() *metricCollector { - return &metricCollector{ - maxMemory: prometheus.NewCounter( - prometheus.CounterOpts{ - Name: "rr_limit_max_memory", - Help: "Total number of workers that was killed because they reached max memory limit.", - }, - ), - maxExecutionTime: prometheus.NewCounter( - prometheus.CounterOpts{ - Name: "rr_limit_max_execution_time", - Help: "Total number of workers that was killed because they reached max execution time limit.", - }, - ), - } -} - -// listener listens to http events and generates nice looking output. -func (c *metricCollector) listener(event int, ctx interface{}) { - switch event { - case rrlimit.EventMaxMemory: - c.maxMemory.Inc() - case rrlimit.EventExecTTL: - c.maxExecutionTime.Inc() - } -} diff --git a/cmd/rr/main.go b/cmd/rr/main.go deleted file mode 100644 index 54a1f060..00000000 --- a/cmd/rr/main.go +++ /dev/null @@ -1,59 +0,0 @@ -// MIT License -// -// Copyright (c) 2018 SpiralScout -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package main - -import ( - rr "github.com/spiral/roadrunner/cmd/rr/cmd" - - // services (plugins) - "github.com/spiral/roadrunner/service/env" - "github.com/spiral/roadrunner/service/gzip" - "github.com/spiral/roadrunner/service/headers" - "github.com/spiral/roadrunner/service/health" - "github.com/spiral/roadrunner/service/http" - "github.com/spiral/roadrunner/service/limit" - "github.com/spiral/roadrunner/service/metrics" - "github.com/spiral/roadrunner/service/reload" - "github.com/spiral/roadrunner/service/rpc" - "github.com/spiral/roadrunner/service/static" - - // additional commands and debug handlers - _ "github.com/spiral/roadrunner/cmd/rr/http" - _ "github.com/spiral/roadrunner/cmd/rr/limit" -) - -func main() { - rr.Container.Register(env.ID, &env.Service{}) - rr.Container.Register(rpc.ID, &rpc.Service{}) - rr.Container.Register(http.ID, &http.Service{}) - rr.Container.Register(metrics.ID, &metrics.Service{}) - rr.Container.Register(headers.ID, &headers.Service{}) - rr.Container.Register(static.ID, &static.Service{}) - rr.Container.Register(limit.ID, &limit.Service{}) - rr.Container.Register(health.ID, &health.Service{}) - rr.Container.Register(gzip.ID, &gzip.Service{}) - rr.Container.Register(reload.ID, &reload.Service{}) - - // you can register additional commands using cmd.CLI - rr.Execute() -} diff --git a/cmd/util/config.go b/cmd/util/config.go deleted file mode 100644 index 08e01a89..00000000 --- a/cmd/util/config.go +++ /dev/null @@ -1,181 +0,0 @@ -package util - -import ( - "bytes" - "fmt" - "github.com/spf13/viper" - "github.com/spiral/roadrunner/service" - "os" - "path/filepath" - "strings" -) - -// ConfigWrapper provides interface bridge between v configs and service.Config. -type ConfigWrapper struct { - v *viper.Viper -} - -// Get nested config section (sub-map), returns nil if section not found. -func (w *ConfigWrapper) Get(key string) service.Config { - sub := w.v.Sub(key) - if sub == nil { - return nil - } - - return &ConfigWrapper{sub} -} - -// Unmarshal unmarshal config data into given struct. -func (w *ConfigWrapper) Unmarshal(out interface{}) error { - return w.v.Unmarshal(out) -} - -// LoadConfig config and merge it's values with set of flags. -func LoadConfig(cfgFile string, path []string, name string, flags []string, jsonConfig string) (*ConfigWrapper, error) { - cfg := viper.New() - - if cfgFile != "" { - if absPath, err := filepath.Abs(cfgFile); err == nil { - cfgFile = absPath - - // force working absPath related to config file - if err := os.Chdir(filepath.Dir(absPath)); err != nil { - return nil, err - } - } - - // Use cfg file from the flag. - cfg.SetConfigFile(cfgFile) - - if dir, err := filepath.Abs(cfgFile); err == nil { - // force working absPath related to config file - if err := os.Chdir(filepath.Dir(dir)); err != nil { - return nil, err - } - } - } else { - // automatic location - for _, p := range path { - cfg.AddConfigPath(p) - } - - cfg.SetConfigName(name) - } - - // read in environment variables that match - cfg.AutomaticEnv() - cfg.SetEnvPrefix("rr") - cfg.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - - // If a cfg file is found, read it in. - if err := cfg.ReadInConfig(); err != nil { - if len(flags) == 0 && jsonConfig == "" { - return nil, err - } - } - - // merge included configs - if include, ok := cfg.Get("include").([]interface{}); ok { - for _, file := range include { - filename, ok := file.(string) - if !ok { - continue - } - - partial := viper.New() - partial.AutomaticEnv() - partial.SetEnvPrefix("rr") - partial.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - partial.SetConfigFile(filename) - - if err := partial.ReadInConfig(); err != nil { - return nil, err - } - - // merging - if err := cfg.MergeConfigMap(partial.AllSettings()); err != nil { - return nil, err - } - } - } - - // automatically inject ENV variables using ${ENV} pattern - for _, key := range cfg.AllKeys() { - val := cfg.Get(key) - cfg.Set(key, parseEnv(val)) - } - - // merge with console flags - if len(flags) != 0 { - for _, f := range flags { - k, v, err := parseFlag(f) - if err != nil { - return nil, err - } - - cfg.Set(k, v) - } - } - - if jsonConfig != "" { - jConfig := viper.New() - jConfig.AutomaticEnv() - jConfig.SetEnvPrefix("rr") - jConfig.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - - jConfig.SetConfigType("json") - if err := jConfig.ReadConfig(bytes.NewBufferString(jsonConfig)); err != nil { - return nil, err - } - - // merging - if err := cfg.MergeConfigMap(jConfig.AllSettings()); err != nil { - return nil, err - } - } - - merged := viper.New() - - // we have to copy all the merged values into new config in order normalize it (viper bug?) - if err := merged.MergeConfigMap(cfg.AllSettings()); err != nil { - return nil, err - } - - return &ConfigWrapper{merged}, nil -} - -func parseFlag(flag string) (string, string, error) { - if !strings.Contains(flag, "=") { - return "", "", fmt.Errorf("invalid flag `%s`", flag) - } - - parts := strings.SplitN(strings.TrimLeft(flag, " \"'`"), "=", 2) - - return strings.Trim(parts[0], " \n\t"), parseValue(strings.Trim(parts[1], " \n\t")), nil -} - -func parseValue(value string) string { - escape := []rune(value)[0] - - if escape == '"' || escape == '\'' || escape == '`' { - value = strings.Trim(value, string(escape)) - value = strings.Replace(value, fmt.Sprintf("\\%s", string(escape)), string(escape), -1) - } - - return value -} - -func parseEnv(value interface{}) interface{} { - str, ok := value.(string) - if !ok || len(str) <= 3 { - return value - } - - if str[0:2] == "${" && str[len(str)-1:] == "}" { - if v, ok := os.LookupEnv(str[2 : len(str)-1]); ok { - return v - } - } - - return str -} diff --git a/cmd/util/cprint.go b/cmd/util/cprint.go deleted file mode 100644 index 3a986fd6..00000000 --- a/cmd/util/cprint.go +++ /dev/null @@ -1,47 +0,0 @@ -package util - -import ( - "fmt" - "github.com/mgutz/ansi" - "os" - "regexp" - "strings" -) - -var ( - reg *regexp.Regexp - - // Colorize enables colors support. - Colorize = true -) - -func init() { - reg, _ = regexp.Compile(`<([^>]+)>`) -} - -// Printf works identically to fmt.Print but adds `<white+hb>color formatting support for CLI</reset>`. -func Printf(format string, args ...interface{}) { - fmt.Print(Sprintf(format, args...)) -} - -// Sprintf works identically to fmt.Sprintf but adds `<white+hb>color formatting support for CLI</reset>`. -func Sprintf(format string, args ...interface{}) string { - format = reg.ReplaceAllStringFunc(format, func(s string) string { - if !Colorize { - return "" - } - - return ansi.ColorCode(strings.Trim(s, "<>/")) - }) - - return fmt.Sprintf(format, args...) -} - -// Panicf prints `<white+hb>color formatted message to STDERR</reset>`. -func Panicf(format string, args ...interface{}) error { - _, err := fmt.Fprint(os.Stderr, Sprintf(format, args...)) - if err != nil { - return err - } - return nil -} diff --git a/cmd/util/debug.go b/cmd/util/debug.go deleted file mode 100644 index 9b94510d..00000000 --- a/cmd/util/debug.go +++ /dev/null @@ -1,61 +0,0 @@ -package util - -import ( - "github.com/sirupsen/logrus" - "github.com/spiral/roadrunner" - "strings" -) - -// LogEvent outputs rr event into given logger and return false if event was not handled. -func LogEvent(logger *logrus.Logger, event int, ctx interface{}) bool { - switch event { - case roadrunner.EventWorkerKill: - w := ctx.(*roadrunner.Worker) - logger.Warning(Sprintf( - "<white+hb>worker.%v</reset> <yellow>killed</reset>", - *w.Pid, - )) - return true - case roadrunner.EventWorkerError: - err := ctx.(roadrunner.WorkerError) - logger.Error(Sprintf( - "<white+hb>worker.%v</reset> <red>%s</reset>", - *err.Worker.Pid, - err.Caused, - )) - return true - } - - // outputs - switch event { - case roadrunner.EventStderrOutput: - for _, line := range strings.Split(string(ctx.([]byte)), "\n") { - if line == "" { - continue - } - - logger.Warning(strings.Trim(line, "\r\n")) - } - - return true - } - - // rr server events - switch event { - case roadrunner.EventServerFailure: - logger.Error(Sprintf("<red>server is dead</reset>")) - return true - } - - // pool events - switch event { - case roadrunner.EventPoolConstruct: - logger.Debug(Sprintf("<cyan>new worker pool</reset>")) - return true - case roadrunner.EventPoolError: - logger.Error(Sprintf("<red>%s</reset>", ctx)) - return true - } - - return false -} diff --git a/cmd/util/exit.go b/cmd/util/exit.go deleted file mode 100644 index 8871a483..00000000 --- a/cmd/util/exit.go +++ /dev/null @@ -1,15 +0,0 @@ -package util - -import ( - "os" -) - -// ExitWithError prints error and exits with error code`. -func ExitWithError(err error) { - errP := Panicf("<red+hb>Error:</reset> <red>%s</reset>\n", err) - if errP != nil { - // in case of error during Panicf, print this error via build-int print function - println("error occurred during fmt.Fprint: " + err.Error()) - } - os.Exit(1) -} diff --git a/cmd/util/rpc.go b/cmd/util/rpc.go deleted file mode 100644 index 8ff6720a..00000000 --- a/cmd/util/rpc.go +++ /dev/null @@ -1,18 +0,0 @@ -package util - -import ( - "errors" - "github.com/spiral/roadrunner/service" - rrpc "github.com/spiral/roadrunner/service/rpc" - "net/rpc" -) - -// RPCClient returns RPC client associated with given rr service container. -func RPCClient(container service.Container) (*rpc.Client, error) { - svc, st := container.Get(rrpc.ID) - if st < service.StatusOK { - return nil, errors.New("RPC service is not configured") - } - - return svc.(*rrpc.Service).Client() -} diff --git a/cmd/util/table.go b/cmd/util/table.go deleted file mode 100644 index c0e20837..00000000 --- a/cmd/util/table.go +++ /dev/null @@ -1,60 +0,0 @@ -package util - -import ( - "github.com/dustin/go-humanize" - "github.com/olekukonko/tablewriter" - rrutil "github.com/spiral/roadrunner/util" - "os" - "strconv" - "time" -) - -// WorkerTable renders table with information about rr server workers. -func WorkerTable(workers []*rrutil.State) *tablewriter.Table { - tw := tablewriter.NewWriter(os.Stdout) - tw.SetHeader([]string{"PID", "Status", "Execs", "Memory", "Created"}) - tw.SetColMinWidth(0, 7) - tw.SetColMinWidth(1, 9) - tw.SetColMinWidth(2, 7) - tw.SetColMinWidth(3, 7) - tw.SetColMinWidth(4, 18) - - for _, w := range workers { - tw.Append([]string{ - strconv.Itoa(w.Pid), - renderStatus(w.Status), - renderJobs(w.NumJobs), - humanize.Bytes(w.MemoryUsage), - renderAlive(time.Unix(0, w.Created)), - }) - } - - return tw -} - -func renderStatus(status string) string { - switch status { - case "inactive": - return Sprintf("<yellow>inactive</reset>") - case "ready": - return Sprintf("<cyan>ready</reset>") - case "working": - return Sprintf("<green>working</reset>") - case "invalid": - return Sprintf("<yellow>invalid</reset>") - case "stopped": - return Sprintf("<red>stopped</reset>") - case "errored": - return Sprintf("<red>errored</reset>") - } - - return status -} - -func renderJobs(number int64) string { - return humanize.Comma(int64(number)) -} - -func renderAlive(t time.Time) string { - return humanize.RelTime(t, time.Now(), "ago", "") -} |