github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/internal/cli/server/command.go (about)

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"os/signal"
     8  	"strings"
     9  	"syscall"
    10  
    11  	"github.com/maticnetwork/heimdall/cmd/heimdalld/service"
    12  	"github.com/mitchellh/cli"
    13  	"github.com/pelletier/go-toml"
    14  
    15  	"github.com/ethereum/go-ethereum/log"
    16  )
    17  
    18  // Command is the command to start the sever
    19  type Command struct {
    20  	UI cli.Ui
    21  
    22  	// cli configuration
    23  	cliConfig *Config
    24  
    25  	// final configuration
    26  	config *Config
    27  
    28  	configFile string
    29  
    30  	srv *Server
    31  }
    32  
    33  // MarkDown implements cli.MarkDown interface
    34  func (c *Command) MarkDown() string {
    35  	items := []string{
    36  		"# Server",
    37  		"The ```bor server``` command runs the Bor client.",
    38  		c.Flags().MarkDown(),
    39  	}
    40  
    41  	return strings.Join(items, "\n\n")
    42  }
    43  
    44  // Help implements the cli.Command interface
    45  func (c *Command) Help() string {
    46  	return `Usage: bor [options]
    47  
    48  	Run the Bor server.
    49    ` + c.Flags().Help()
    50  }
    51  
    52  // Synopsis implements the cli.Command interface
    53  func (c *Command) Synopsis() string {
    54  	return "Run the Bor server"
    55  }
    56  
    57  func (c *Command) extractFlags(args []string) error {
    58  	config := *DefaultConfig()
    59  
    60  	flags := c.Flags()
    61  	if err := flags.Parse(args); err != nil {
    62  		c.UI.Error(err.Error())
    63  		c.config = &config
    64  
    65  		return err
    66  	}
    67  
    68  	// TODO: Check if this can be removed or not
    69  	// read cli flags
    70  	if err := config.Merge(c.cliConfig); err != nil {
    71  		c.UI.Error(err.Error())
    72  		c.config = &config
    73  
    74  		return err
    75  	}
    76  	// read if config file is provided, this will overwrite the cli flags, if provided
    77  	if c.configFile != "" {
    78  		log.Warn("Config File provided, this will overwrite the cli flags", "path", c.configFile)
    79  		cfg, err := readConfigFile(c.configFile)
    80  		if err != nil {
    81  			c.UI.Error(err.Error())
    82  			c.config = &config
    83  
    84  			return err
    85  		}
    86  		if err := config.Merge(cfg); err != nil {
    87  			c.UI.Error(err.Error())
    88  			c.config = &config
    89  
    90  			return err
    91  		}
    92  	}
    93  
    94  	// nolint: nestif
    95  	// check for log-level and verbosity here
    96  	if c.configFile != "" {
    97  		data, _ := toml.LoadFile(c.configFile)
    98  		if data.Has("verbosity") && data.Has("log-level") {
    99  			log.Warn("Config contains both, verbosity and log-level, log-level will be deprecated soon. Use verbosity only.", "using", data.Get("verbosity"))
   100  		} else if !data.Has("verbosity") && data.Has("log-level") {
   101  			log.Warn("Config contains log-level only, note that log-level will be deprecated soon. Use verbosity instead.", "using", data.Get("log-level"))
   102  			config.Verbosity = VerbosityStringToInt(strings.ToLower(data.Get("log-level").(string)))
   103  		}
   104  	} else {
   105  		tempFlag := 0
   106  		for _, val := range args {
   107  			if (strings.HasPrefix(val, "-verbosity") || strings.HasPrefix(val, "--verbosity")) && config.LogLevel != "" {
   108  				tempFlag = 1
   109  				break
   110  			}
   111  		}
   112  		if tempFlag == 1 {
   113  			log.Warn("Both, verbosity and log-level flags are provided, log-level will be deprecated soon. Use verbosity only.", "using", config.Verbosity)
   114  		} else if tempFlag == 0 && config.LogLevel != "" {
   115  			log.Warn("Only log-level flag is provided, note that log-level will be deprecated soon. Use verbosity instead.", "using", config.LogLevel)
   116  			config.Verbosity = VerbosityStringToInt(strings.ToLower(config.LogLevel))
   117  		}
   118  	}
   119  
   120  	c.config = &config
   121  
   122  	return nil
   123  }
   124  
   125  // Run implements the cli.Command interface
   126  func (c *Command) Run(args []string) int {
   127  	err := c.extractFlags(args)
   128  	if err != nil {
   129  		c.UI.Error(err.Error())
   130  		return 1
   131  	}
   132  
   133  	if c.config.Heimdall.RunHeimdall {
   134  		shutdownCtx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
   135  		defer stop()
   136  
   137  		go func() {
   138  			service.NewHeimdallService(shutdownCtx, c.getHeimdallArgs())
   139  		}()
   140  	}
   141  
   142  	srv, err := NewServer(c.config, WithGRPCAddress())
   143  	if err != nil {
   144  		c.UI.Error(err.Error())
   145  		return 1
   146  	}
   147  	c.srv = srv
   148  
   149  	return c.handleSignals()
   150  }
   151  
   152  func (c *Command) handleSignals() int {
   153  	signalCh := make(chan os.Signal, 4)
   154  	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
   155  
   156  	sig := <-signalCh
   157  
   158  	c.UI.Output(fmt.Sprintf("Caught signal: %v", sig))
   159  	c.UI.Output("Gracefully shutting down agent...")
   160  
   161  	gracefulCh := make(chan struct{})
   162  	go func() {
   163  		c.srv.Stop()
   164  		close(gracefulCh)
   165  	}()
   166  
   167  	for i := 10; i > 0; i-- {
   168  		select {
   169  		case <-signalCh:
   170  			log.Warn("Already shutting down, interrupt more force stop.", "times", i-1)
   171  		case <-gracefulCh:
   172  			return 0
   173  		}
   174  	}
   175  	return 1
   176  }
   177  
   178  // GetConfig returns the user specified config
   179  func (c *Command) GetConfig() *Config {
   180  	return c.cliConfig
   181  }
   182  
   183  func (c *Command) getHeimdallArgs() []string {
   184  	heimdallArgs := strings.Split(c.config.Heimdall.RunHeimdallArgs, ",")
   185  	return append([]string{"start"}, heimdallArgs...)
   186  }