From 8d7ec3ea403610c34303be462ff3f22f036ac011 Mon Sep 17 00:00:00 2001 From: Ethan Reesor Date: Mon, 18 Sep 2023 11:49:52 -0500 Subject: [PATCH] Handle sigterm [#3398] And remove the service code --- cmd/accumulated/cmd_run.go | 39 ++++------- cmd/accumulated/cmd_run_devnet.go | 2 +- cmd/accumulated/cmd_run_dual.go | 36 ++-------- cmd/accumulated/cmd_service.go | 106 ------------------------------ cmd/accumulated/main.go | 16 +---- cmd/accumulated/program.go | 44 ++++++++++--- go.mod | 1 - go.sum | 3 - internal/logging/service.go | 77 ---------------------- vdk/logger/log_writer.go | 23 ++----- 10 files changed, 59 insertions(+), 288 deletions(-) delete mode 100644 cmd/accumulated/cmd_service.go delete mode 100644 internal/logging/service.go diff --git a/cmd/accumulated/cmd_run.go b/cmd/accumulated/cmd_run.go index 17482b841..fd22399c5 100644 --- a/cmd/accumulated/cmd_run.go +++ b/cmd/accumulated/cmd_run.go @@ -21,12 +21,12 @@ import ( tmconfig "github.com/cometbft/cometbft/config" service2 "github.com/cometbft/cometbft/libs/service" "github.com/fatih/color" - "github.com/kardianos/service" "github.com/rs/zerolog" "github.com/spf13/cobra" "gitlab.com/accumulatenetwork/accumulate/internal/logging" "gitlab.com/accumulatenetwork/accumulate/pkg/database/keyvalue/badger" "gitlab.com/accumulatenetwork/accumulate/test/testing" + "golang.org/x/exp/slog" ) var cmdRun = &cobra.Command{ @@ -90,37 +90,27 @@ func initRunFlags(cmd *cobra.Command, forService bool) { func runNode(cmd *cobra.Command, _ []string) (string, error) { prog := NewProgram(cmd, singleNodeWorkDir, nil) - svc, err := service.New(prog, serviceConfig) - if err != nil { - return "", err - } - - logger, err := svc.Logger(nil) - if err != nil { - return "", err - } - if flagRun.CiStopAfter != 0 { - go watchDog(prog, svc, flagRun.CiStopAfter) + go watchDog(prog, flagRun.CiStopAfter) } color.HiGreen("------ starting a new node ------") - err = svc.Run() + err := prog.Run() if err != nil { //if it is already stopped, that is ok. if !errors.Is(err, service2.ErrAlreadyStopped) { - _ = logger.Error(err) + slog.Error("Service failed", err) return "", err } } return "shutdown complete", nil } -func watchDog(prog *Program, svc service.Service, duration time.Duration) { +func watchDog(prog *Program, duration time.Duration) { time.Sleep(duration) //this will cause tendermint to stop and exit cleanly. - _ = prog.Stop(svc) + _ = prog.Stop() //the following will stop the Run() interrupt(syscall.Getpid()) @@ -128,7 +118,7 @@ func watchDog(prog *Program, svc service.Service, duration time.Duration) { type logAnnotator func(io.Writer, string, bool) io.Writer -func newLogWriter(s service.Service) func(string, logAnnotator) (io.Writer, error) { +func newLogWriter() func(string, logAnnotator) (io.Writer, error) { // Each log file writer must be created once, otherwise different copies // will step on each other var logFile *rotateWriter @@ -156,17 +146,12 @@ func newLogWriter(s service.Service) func(string, logAnnotator) (io.Writer, erro return func(format string, annotate logAnnotator) (io.Writer, error) { var mainWriter io.Writer var err error - if !service.Interactive() && s != nil { - mainWriter, err = logging.NewServiceLogger(s, format) - check(err) - } else { - mainWriter = os.Stderr - if annotate != nil { - mainWriter = annotate(mainWriter, format, true) - } - mainWriter, err = logging.NewConsoleWriterWith(mainWriter, format) - check(err) + mainWriter = os.Stderr + if annotate != nil { + mainWriter = annotate(mainWriter, format, true) } + mainWriter, err = logging.NewConsoleWriterWith(mainWriter, format) + check(err) var writers multiWriter writers = append(writers, mainWriter) diff --git a/cmd/accumulated/cmd_run_devnet.go b/cmd/accumulated/cmd_run_devnet.go index 8469ef15f..882755522 100644 --- a/cmd/accumulated/cmd_run_devnet.go +++ b/cmd/accumulated/cmd_run_devnet.go @@ -96,7 +96,7 @@ func runDevNet(*cobra.Command, []string) { didStop := make(chan struct{}, len(vals)*2+len(bsns)) done := new(sync.WaitGroup) - logWriter := newLogWriter(nil) + logWriter := newLogWriter() var daemons []*accumulated.Daemon started := new(sync.WaitGroup) diff --git a/cmd/accumulated/cmd_run_dual.go b/cmd/accumulated/cmd_run_dual.go index 33d75800a..288923d38 100644 --- a/cmd/accumulated/cmd_run_dual.go +++ b/cmd/accumulated/cmd_run_dual.go @@ -8,15 +8,13 @@ package main import ( "net/http" - "os" - "os/signal" "time" service2 "github.com/cometbft/cometbft/libs/service" - "github.com/kardianos/service" "github.com/spf13/cobra" "gitlab.com/accumulatenetwork/accumulate/pkg/database/keyvalue/badger" "gitlab.com/accumulatenetwork/accumulate/pkg/errors" + "golang.org/x/exp/slog" ) var cmdRunDual = &cobra.Command{ @@ -67,41 +65,15 @@ func runDualNode(cmd *cobra.Command, args []string) (string, error) { flagRun.EnableTimingLogs = flagRunDual.EnableTimingLogs - // TODO: This only works on POSIX platforms. We need to seriously refactor - // how service mode works. - serviceConfig.Option = service.KeyValue{ - "RunWait": func() { - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, os.Interrupt) - defer signal.Stop(sigChan) - - select { - case <-prog.primary.Done(): - case <-prog.secondary.Done(): - case <-sigChan: - } - }, - } - - svc, err := service.New(prog, serviceConfig) - if err != nil { - return "", err - } - - logger, err := svc.Logger(nil) - if err != nil { - return "", err - } - if flagRunDual.CiStopAfter != 0 { - go watchDog(prog, svc, flagRunDual.CiStopAfter) + go watchDog(prog, flagRunDual.CiStopAfter) } - err = svc.Run() + err := prog.Run() if err != nil { //if it is already stopped, that is ok. if !errors.Is(err, service2.ErrAlreadyStopped) { - _ = logger.Error(err) + slog.Error("Service failed", err) return "", err } } diff --git a/cmd/accumulated/cmd_service.go b/cmd/accumulated/cmd_service.go deleted file mode 100644 index 4ef831810..000000000 --- a/cmd/accumulated/cmd_service.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2022 The Accumulate Authors -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - -package main - -import ( - "errors" - "fmt" - - "github.com/kardianos/service" - "github.com/spf13/cobra" -) - -const ( - ServiceInstall = "install" - ServiceReinstall = "reinstall" - ServiceUninstall = "uninstall" - ServiceStart = "start" - ServiceStop = "stop" -) - -var serviceConfig = &service.Config{ - Name: "accumulated", - DisplayName: "Accumulated", - Description: cmdMain.Short, - Arguments: []string{"run"}, -} - -var cmdService = &cobra.Command{ - Use: "service [[un|re]install|start|stop]", - ValidArgs: []string{ServiceInstall, ServiceReinstall, ServiceUninstall, ServiceStart, ServiceStop}, - Args: composeArgs(cobra.OnlyValidArgs, cobra.MaximumNArgs(1)), - Run: manageService, -} - -var flagService = struct { - UserService bool -}{} - -func init() { - cmdMain.AddCommand(cmdService) - - initRunFlags(cmdService, true) - cmdService.Flags().StringVar(&serviceConfig.UserName, "service-user", "", "Service user name") - cmdService.Flags().BoolVar(&flagService.UserService, "user-service", false, "Install as a user service instead of a system service") -} - -func manageService(cmd *cobra.Command, args []string) { - if len(args) > 1 { - printUsageAndExit1(cmd, args) - } - - if flagService.UserService { - serviceConfig.Option["UserService"] = true - } - - prog := NewProgram(cmd, singleNodeWorkDir, nil) - svc, err := service.New(prog, serviceConfig) - check(err) - - if len(args) == 0 { - st, err := svc.Status() - if errors.Is(err, service.ErrNotInstalled) { - fmt.Printf("Accumulated service is not installed\n") - return - } - check(err) - - switch st { - case service.StatusRunning: - fmt.Printf("Accumulated service is running\n") - case service.StatusStopped: - fmt.Printf("Accumulated service is stopped\n") - default: - fmt.Printf("Accumulated service status is unknown\n") - } - } - - switch args[0] { - case ServiceInstall, ServiceReinstall: - wd, err := singleNodeWorkDir(cmd) - check(err) - - // TODO Better way of doing this? - serviceConfig.Arguments = append(serviceConfig.Arguments, "--work-dir", wd) - } - - switch args[0] { - case ServiceInstall: - check(svc.Install()) - case ServiceReinstall: - check(svc.Uninstall()) - check(svc.Install()) - case ServiceUninstall: - check(svc.Uninstall()) - case ServiceStart: - check(svc.Start()) - case ServiceStop: - check(svc.Stop()) - default: - printUsageAndExit1(cmd, args) - } -} diff --git a/cmd/accumulated/main.go b/cmd/accumulated/main.go index 07399df23..cc849bd1a 100644 --- a/cmd/accumulated/main.go +++ b/cmd/accumulated/main.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Accumulate Authors +// Copyright 2023 The Accumulate Authors // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file or at @@ -83,17 +83,3 @@ func warnf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, format, args...) } } - -func composeArgs(fn cobra.PositionalArgs, fns ...cobra.PositionalArgs) cobra.PositionalArgs { - if len(fns) == 0 { - return fn - } - - rest := composeArgs(fns[0], fns[1:]...) - return func(cmd *cobra.Command, args []string) error { - if err := fn(cmd, args); err != nil { - return err - } - return rest(cmd, args) - } -} diff --git a/cmd/accumulated/program.go b/cmd/accumulated/program.go index 99d21b68c..d55a708f4 100644 --- a/cmd/accumulated/program.go +++ b/cmd/accumulated/program.go @@ -7,12 +7,14 @@ package main import ( + "context" "fmt" "io" "os" + "os/signal" "path/filepath" + "syscall" - "github.com/kardianos/service" "github.com/spf13/cobra" "gitlab.com/accumulatenetwork/accumulate/internal/node/config" accumulated "gitlab.com/accumulatenetwork/accumulate/internal/node/daemon" @@ -44,17 +46,25 @@ func singleNodeWorkDir(cmd *cobra.Command) (string, error) { return flagMain.WorkDir, nil } - if service.Interactive() { - fmt.Fprint(os.Stderr, "Error: at least one of --work-dir or --node is required\n") - printUsageAndExit1(cmd, []string{}) - return "", nil // Not reached - } else { - return "", fmt.Errorf("at least one of --work-dir or --node is required") + fmt.Fprint(os.Stderr, "Error: at least one of --work-dir or --node is required\n") + printUsageAndExit1(cmd, []string{}) + return "", nil // Not reached +} + +func (p *Program) Run() error { + ctx := contextForMainProcess(context.Background()) + + err := p.Start() + if err != nil { + return err } + + <-ctx.Done() + return p.Stop() } -func (p *Program) Start(s service.Service) (err error) { - logWriter := newLogWriter(s) +func (p *Program) Start() (err error) { + logWriter := newLogWriter() primaryDir, err := p.primaryDir(p.cmd) if err != nil { @@ -105,7 +115,7 @@ func (p *Program) Start(s service.Service) (err error) { return startDual(p.primary, p.secondary) } -func (p *Program) Stop(service.Service) error { +func (p *Program) Stop() error { if p.secondary == nil { return p.primary.Stop() } @@ -158,3 +168,17 @@ func stopDual(primary, secondary *accumulated.Daemon) error { errg.Go(secondary.Stop) return errg.Wait() } + +func contextForMainProcess(ctx context.Context) context.Context { + ctx, cancel := context.WithCancel(ctx) + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + go func() { + <-sigs + signal.Stop(sigs) + cancel() + }() + + return ctx +} diff --git a/go.mod b/go.mod index a241507fa..e83514e50 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,6 @@ require ( github.com/go-playground/validator/v10 v10.9.0 github.com/golang/mock v1.6.0 // indirect github.com/golangci/golangci-lint v1.52.2 - github.com/kardianos/service v1.2.0 github.com/pelletier/go-toml v1.9.5 github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/common v0.42.0 // indirect diff --git a/go.sum b/go.sum index 29736801f..09cd3c0e8 100644 --- a/go.sum +++ b/go.sum @@ -633,8 +633,6 @@ github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/junk1tm/musttag v0.5.0 h1:bV1DTdi38Hi4pG4OVWa7Kap0hi0o7EczuK6wQt9zPOM= github.com/junk1tm/musttag v0.5.0/go.mod h1:PcR7BA+oREQYvHwgjIDmw3exJeds5JzRcvEJTfjrA0M= -github.com/kardianos/service v1.2.0 h1:bGuZ/epo3vrt8IPC7mnKQolqFeYJb7Cs8Rk4PSOBB/g= -github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -1382,7 +1380,6 @@ golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/logging/service.go b/internal/logging/service.go deleted file mode 100644 index 282523e61..000000000 --- a/internal/logging/service.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2023 The Accumulate Authors -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - -package logging - -import ( - "bytes" - "fmt" - "strings" - "sync" - - tmconfig "github.com/cometbft/cometbft/config" - "github.com/kardianos/service" - "github.com/rs/zerolog" -) - -type ServiceLogger struct { - Service service.Logger - - fmt *zerolog.ConsoleWriter - buf *bytes.Buffer - mu *sync.Mutex -} - -var _ zerolog.LevelWriter = (*ServiceLogger)(nil) - -func NewServiceLogger(svc service.Service, format string) (*ServiceLogger, error) { - logger := new(ServiceLogger) - var err error - logger.Service, err = svc.Logger(nil) - if err != nil { - return nil, err - } - - switch strings.ToLower(format) { - case tmconfig.LogFormatPlain: - logger.buf = new(bytes.Buffer) - logger.mu = new(sync.Mutex) - logger.fmt = newConsoleWriter(logger.buf) - - case tmconfig.LogFormatJSON: - - default: - return nil, fmt.Errorf("unsupported log format: %s", format) - } - - return logger, nil -} - -func (l *ServiceLogger) Write(b []byte) (int, error) { - return l.WriteLevel(zerolog.NoLevel, b) -} - -func (l *ServiceLogger) WriteLevel(level zerolog.Level, b []byte) (int, error) { - // Use zerolog's console writer to format the log message - if l.fmt != nil { - l.mu.Lock() - l.buf.Reset() - _, _ = l.fmt.Write(b) - b = make([]byte, l.buf.Len()) - copy(b, l.buf.Bytes()) - l.mu.Unlock() - } - - switch level { - case zerolog.PanicLevel, zerolog.FatalLevel, zerolog.ErrorLevel: - _ = l.Service.Error(string(b)) - case zerolog.WarnLevel: - _ = l.Service.Warning(string(b)) - default: - _ = l.Service.Info(string(b)) - } - return len(b), nil -} diff --git a/vdk/logger/log_writer.go b/vdk/logger/log_writer.go index a6fdcd5ad..f29d691b1 100644 --- a/vdk/logger/log_writer.go +++ b/vdk/logger/log_writer.go @@ -12,14 +12,12 @@ import ( "strings" "time" - "github.com/kardianos/service" "github.com/rs/zerolog" "gitlab.com/accumulatenetwork/accumulate/internal/logging" "gitlab.com/accumulatenetwork/accumulate/vdk/utils" ) type LogWriterConfig struct { - Service service.Service LogFile string JsonLogFile string } @@ -61,20 +59,13 @@ func NewLogWriter(config LogWriterConfig) (LogWriter, error) { return func(format string, annotate LogAnnotator) (io.Writer, error) { var mainWriter io.Writer var err error - if !service.Interactive() && config.Service != nil { - mainWriter, err = logging.NewServiceLogger(config.Service, format) - if err != nil { - return nil, err - } - } else { - mainWriter = os.Stderr - if annotate != nil { - mainWriter = annotate(mainWriter, format, true) - } - mainWriter, err = logging.NewConsoleWriterWith(mainWriter, format) - if err != nil { - return nil, err - } + mainWriter = os.Stderr + if annotate != nil { + mainWriter = annotate(mainWriter, format, true) + } + mainWriter, err = logging.NewConsoleWriterWith(mainWriter, format) + if err != nil { + return nil, err } var writers multiWriter