diff --git a/.github/workflows/buildcicd.yml b/.github/workflows/buildcicd.yml new file mode 100644 index 0000000..c6964bd --- /dev/null +++ b/.github/workflows/buildcicd.yml @@ -0,0 +1,45 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.20' + - name: Get dependencies + run: go get + + - name: Build + run: | + make build-linux + + - name: version + run: | + cd vimana-linux-amd64/ + ./vimana version + ./vimana registry list + ./vimana registry search + + - name: celestia run + run: | + cd vimana-linux-amd64/ + ./vimana install celestia light-node + sleep 10s + ./vimana stop celestia light-node + ./vimana start celestia light-node + ./vimana status celestia light-node + ./vimana stop celestia light-node diff --git a/.gitignore b/.gitignore index 6317721..7bf9594 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ avail_path __MACOSX vimana* -.DS_Store \ No newline at end of file +.DS_Store +.vscode \ No newline at end of file diff --git a/CREATE_COMPONENT.md b/CREATE_COMPONENT.md index fc45fd2..dc0de13 100644 --- a/CREATE_COMPONENT.md +++ b/CREATE_COMPONENT.md @@ -12,7 +12,7 @@ e.g. [components.dymension] [components.dymension.full] -binary = "/tmp/vimana/dymension/dymd" +binary = "/usr/local/bin/dymension/dymd" download = "/tmp/vimana/dymension/init.sh" ``` @@ -39,7 +39,7 @@ func (c *DymensionFullCommander) Init(cmd *cobra.Command, args []string, mode Mo func (c *DymensionFullCommander) Run(cmd *cobra.Command, args []string, mode Mode) { c.Init(cmd, args, mode) cmdexecute := c.componentMgr.GetStartCmd() - utils.ExecBashCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) + utils.ExecBinaryCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) } ``` diff --git a/cli/avail.go b/cli/avail.go index 0a0fe1b..66a73d7 100644 --- a/cli/avail.go +++ b/cli/avail.go @@ -1,10 +1,15 @@ package cli import ( - "fmt" + "io/ioutil" + "os" "os/exec" + "strconv" + "strings" "vimana/cmd/utils" + "vimana/log" + // "github.com/moby/moby/daemon/logger" "github.com/spf13/cobra" ) @@ -12,7 +17,7 @@ type AvailLightCommander struct { BaseCommander } -func NewAvailLightCommander() *AvailLightCommander { +func NewAvailLightCommander(node_type string) *AvailLightCommander { return &AvailLightCommander{ BaseCommander: BaseCommander{NodeType: "light"}, } @@ -21,30 +26,95 @@ func NewAvailLightCommander() *AvailLightCommander { func (a *AvailLightCommander) AddFlags(cmd *cobra.Command) { } -func (a *AvailLightCommander) Init(cmd *cobra.Command, args []string, mode Mode) error { - utils.ExecBashCmd(exec.Command("bash", mode.Download), utils.WithOutputToStdout(), utils.WithErrorsToStderr()) - a.initComponentManager("avail", mode.Binary) - return a.componentMgr.InitializeConfig() +func (a *AvailLightCommander) Init(cmd *cobra.Command, args []string, mode Mode, node_info string) error { + utils.ExecBashCmd(exec.Command("bash", mode.Download), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) + a.initSpacecoreManager("avail", mode.Binary) + return a.spacecoresMgr.InitializeConfig() } -func (a *AvailLightCommander) Run(cmd *cobra.Command, args []string, mode Mode) { - cmdexecute := a.componentMgr.GetStartCmd() - utils.ExecBashCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +func (a *AvailLightCommander) Run(cmd *cobra.Command, args []string, mode Mode, node_info string) { + cmdexecute := a.spacecoresMgr.GetStartCmd() + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) } -func (a *AvailLightCommander) Start(cmd *cobra.Command, args []string, mode Mode) { - a.Init(cmd, args, mode) - fmt.Println(a.componentMgr) - fmt.Println("executing start command") - cmdexecute := a.componentMgr.GetStartCmd() - fmt.Println(cmdexecute) - utils.ExecBashCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +func (a *AvailLightCommander) Start(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + // check if daemon already running. + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + logger.Info("Already running or " + PIDFile + " file exist.") + return + } + + node_info_arr := strings.Split(node_info, "-") + a.Init(cmd, args, mode, node_info_arr[0]) + logger.Info(a.spacecoresMgr) + logger.Info("executing start command") + cmdexecute := a.spacecoresMgr.GetStartCmd() + logger.Info(cmdexecute) + utils.ExecBashCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) } -func (a *AvailLightCommander) Stop(cmd *cobra.Command, args []string, mode Mode) { - fmt.Println("Stopping Celestia bridge node") +func (a *AvailLightCommander) Stop(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + logger.Info("Stopping Celestia bridge node") + + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + data, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + return + } + ProcessID, err := strconv.Atoi(string(data)) + + if err != nil { + logger.Info("Unable to read and parse process id found in ", PIDFile) + return + } + + process, err := os.FindProcess(ProcessID) + + if err != nil { + logger.Infof("Unable to find process ID [%v] with error %v \n", ProcessID, err) + return + } + // remove PID file + os.Remove(PIDFile) + + node_info_arr := strings.Split(node_info, "-") + logger.Info("Stopping " + node_info_arr[0] + " " + node_info_arr[1] + " node") + // kill process and exit immediately + err = process.Kill() + + if err != nil { + logger.Infof("Unable to kill process ID [%v] with error %v \n", ProcessID, err) + } else { + logger.Infof("Killed process ID [%v]\n", ProcessID) + } + } else { + logger.Info("Not running.") + } +} + +func (a *AvailLightCommander) Status(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + node_info_arr := strings.Split(node_info, "-") + logger.Info("Getting status of " + node_info_arr[0] + " " + node_info_arr[1] + " node") + + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + _, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + } else { + logger.Info(node_info_arr[0] + " " + node_info_arr[1] + " node is running") + } + } else { + logger.Info("Not running.") + } } -func (a *AvailLightCommander) Status(cmd *cobra.Command, args []string, mode Mode) { - fmt.Println("Getting status of Celestia bridge node") +func (a *AvailLightCommander) Install(cmd *cobra.Command, args []string, mode Mode, node_info string) { + return } diff --git a/cli/celestia.go b/cli/celestia.go index 3fba36c..c98eabd 100644 --- a/cli/celestia.go +++ b/cli/celestia.go @@ -1,10 +1,15 @@ package cli import ( - "fmt" + "io/ioutil" + "os" "os/exec" + "path/filepath" + "strconv" + "strings" "vimana/cmd/utils" - "vimana/components" + "vimana/log" + "vimana/spacecores" "github.com/spf13/cobra" ) @@ -27,13 +32,13 @@ const ( DefaultCelestiaNetwork = "celestia" ) -func NewCelestiaLightCommander() *CelestiaLightCommander { +func NewCelestiaLightCommander(node_type string) *CelestiaLightCommander { return &CelestiaLightCommander{ BaseCommander: BaseCommander{NodeType: "light"}, } } -func NewCelestiaBridgeCommander() *CelestiaBridgeCommander { +func NewCelestiaBridgeCommander(node_type string) *CelestiaBridgeCommander { return &CelestiaBridgeCommander{ BaseCommander: BaseCommander{NodeType: "bridge"}, } @@ -43,42 +48,121 @@ func (c *CelestiaLightCommander) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&c.CelestiaRPC, "rpc", DefaultCelestiaRPC, "Specifies the Celestia RPC endpoint") } -func (c *CelestiaLightCommander) Init(cmd *cobra.Command, args []string, mode Mode) error { - utils.ExecBashCmd(exec.Command("bash", mode.Download), utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +func (a *CelestiaLightCommander) Install(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + _, err := os.Stat(mode.Download) + if err == nil { + // true + utils.ExecBinaryCmd(exec.Command("bash", mode.Download), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) + } else if os.IsNotExist(err) { + // false + currentDir, err := os.Getwd() + if err != nil { + logger.Info("Error getting current directory:", err) + return + } + parentDir := filepath.Dir(currentDir) + utils.ExecBinaryCmd(exec.Command("bash", parentDir+"/scripts/init.sh"), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) + } else { - c.config = &components.ComponentConfig{ + logger.Infof("error:%v\n", err) + } + return +} + +func (c *CelestiaLightCommander) Init(cmd *cobra.Command, args []string, mode Mode, node_info string) error { + utils.ExecBashCmd(exec.Command("bash", mode.Download), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) + + c.config = &spacecores.SpacecoreConfig{ RPC: c.CelestiaRPC, Network: c.CelestiaNetwork, } - // c.componentMgr = components.NewComponentManager("celestia", mode.Binary, c.NodeType, config) - c.initComponentManager("celestia", mode.Binary) + c.initSpacecoreManager("celestia", mode.Binary) - // c.initComponentManager("celestia", mode.Binary, c.CelestiaNetwork, c.CelestiaRPC) - return c.componentMgr.InitializeConfig() + // c.initSpacecoreManager("celestia", mode.Binary, c.CelestiaNetwork, c.CelestiaRPC) + return c.spacecoresMgr.InitializeConfig() } -func (c *CelestiaLightCommander) Run(cmd *cobra.Command, args []string, mode Mode) { - c.Init(cmd, args, mode) - cmdexecute := c.componentMgr.GetStartCmd() - utils.ExecBashCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +func (c *CelestiaLightCommander) Run(cmd *cobra.Command, args []string, mode Mode, node_info string) { + node_info_arr := strings.Split(node_info, "-") + c.Init(cmd, args, mode, node_info_arr[0]) + cmdexecute := c.spacecoresMgr.GetStartCmd() + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) } -func (c *CelestiaLightCommander) Start(cmd *cobra.Command, args []string, mode Mode) { - c.Init(cmd, args, mode) - cmdexecute := c.componentMgr.GetStartCmd() - fmt.Println(cmdexecute) - utils.ExecBashCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +func (c *CelestiaLightCommander) Start(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + // check if daemon already running. + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + logger.Info("Already running or " + PIDFile + " file exist.") + return + } + + node_info_arr := strings.Split(node_info, "-") + c.Init(cmd, args, mode, node_info_arr[0]) + cmdexecute := c.spacecoresMgr.GetStartCmd() + logger.Info(cmdexecute) + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) } -func (c *CelestiaLightCommander) Stop(cmd *cobra.Command, args []string, mode Mode) { - // Implementation for "start" command for Celestia light node - fmt.Println("To implement: Celestia light node stop") +func (c *CelestiaLightCommander) Stop(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + data, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + return + } + ProcessID, err := strconv.Atoi(string(data)) + + if err != nil { + logger.Info("Unable to read and parse process id found in ", PIDFile) + return + } + + process, err := os.FindProcess(ProcessID) + + if err != nil { + logger.Infof("Unable to find process ID [%v] with error %v \n", ProcessID, err) + return + } + // remove PID file + os.Remove(PIDFile) + + node_info_arr := strings.Split(node_info, "-") + logger.Info("Stopping " + node_info_arr[0] + " " + node_info_arr[1] + " node") + // kill process and exit immediately + err = process.Kill() + + if err != nil { + logger.Infof("Unable to kill process ID [%v] with error %v \n", ProcessID, err) + } else { + logger.Infof("Killed process ID [%v]\n", ProcessID) + } + } else { + logger.Info("Not running.") + } } -func (c *CelestiaLightCommander) Status(cmd *cobra.Command, args []string, mode Mode) { - // Implementation for "start" command for Celestia light node - fmt.Println("To implement: Celestia light node status") +func (c *CelestiaLightCommander) Status(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + node_info_arr := strings.Split(node_info, "-") + logger.Info("Getting status of " + node_info_arr[0] + " " + node_info_arr[1] + " node") + + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + _, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + } else { + logger.Info(node_info_arr[0] + " " + node_info_arr[1] + " node is running") + } + } else { + logger.Info("Not running.") + } } func (c *CelestiaBridgeCommander) AddFlags(cmd *cobra.Command) { @@ -86,32 +170,95 @@ func (c *CelestiaBridgeCommander) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&c.CelestiaRPC, "rpc", DefaultCelestiaRPC, "Specifies the Celestia RPC endpoint") } -func (c *CelestiaBridgeCommander) Init(cmd *cobra.Command, args []string, mode Mode) error { - utils.ExecBashCmd(exec.Command("bash", mode.Download), utils.WithOutputToStdout(), utils.WithErrorsToStderr()) - c.config = &components.ComponentConfig{ +func (a *CelestiaBridgeCommander) Install(cmd *cobra.Command, args []string, mode Mode, node_info string) { + + return +} + +func (c *CelestiaBridgeCommander) Init(cmd *cobra.Command, args []string, mode Mode, node_info string) error { + utils.ExecBashCmd(exec.Command("bash", mode.Download), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) + c.config = &spacecores.SpacecoreConfig{ RPC: c.CelestiaRPC, Network: c.CelestiaNetwork, } - c.initComponentManager("celestia", mode.Binary) + c.initSpacecoreManager("celestia", mode.Binary) - return c.componentMgr.InitializeConfig() + return c.spacecoresMgr.InitializeConfig() } -func (c *CelestiaBridgeCommander) Start(cmd *cobra.Command, args []string, mode Mode) { - c.Init(cmd, args, mode) - // fmt.Println("Starting Celestia bridge node", c) - cmdexecute := c.componentMgr.GetStartCmd() - fmt.Println(cmdexecute) - utils.ExecBashCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +func (c *CelestiaBridgeCommander) Start(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + // check if daemon already running. + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + logger.Info("Already running or " + PIDFile + " file exist.") + return + } + + node_info_arr := strings.Split(node_info, "-") + c.Init(cmd, args, mode, node_info_arr[0]) + // logger.Info("Starting Celestia bridge node", c) + cmdexecute := c.spacecoresMgr.GetStartCmd() + logger.Info("Start: ", cmdexecute) + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) } -func (c *CelestiaBridgeCommander) Stop(cmd *cobra.Command, args []string, mode Mode) { - // Implementation for "start" command for Celestia light node - fmt.Println("Stopping Celestia bridge node") +func (c *CelestiaBridgeCommander) Stop(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + logger.Info("Stopping Celestia bridge node") + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + data, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + return + } + ProcessID, err := strconv.Atoi(string(data)) + + if err != nil { + logger.Info("Unable to read and parse process id found in ", PIDFile) + return + } + + process, err := os.FindProcess(ProcessID) + + if err != nil { + logger.Infof("Unable to find process ID [%v] with error %v \n", ProcessID, err) + return + } + // remove PID file + os.Remove(PIDFile) + + node_info_arr := strings.Split(node_info, "-") + logger.Info("Stopping " + node_info_arr[0] + " " + node_info_arr[1] + " node") + // kill process and exit immediately + err = process.Kill() + + if err != nil { + logger.Infof("Unable to kill process ID [%v] with error %v \n", ProcessID, err) + } else { + logger.Infof("Killed process ID [%v]\n", ProcessID) + } + } else { + logger.Info("Not running.") + } } -func (c *CelestiaBridgeCommander) Status(cmd *cobra.Command, args []string, mode Mode) { - // Implementation for "start" command for Celestia light node - fmt.Println("Getting status of Celestia bridge node") +func (c *CelestiaBridgeCommander) Status(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + node_info_arr := strings.Split(node_info, "-") + logger.Info("Getting status of " + node_info_arr[0] + " " + node_info_arr[1] + " node") + + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + _, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + } else { + logger.Info(node_info_arr[0] + " " + node_info_arr[1] + " node is running") + } + } else { + logger.Info("Not running.") + } } diff --git a/cli/commands.go b/cli/commands.go index 1cad265..2ed4ccd 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -2,46 +2,68 @@ package cli import ( "fmt" - "log" + "io/ioutil" "os" + "os/exec" "path/filepath" "vimana/cmd/utils" - "vimana/components" "vimana/config" + "vimana/log" + "vimana/spacecores" "github.com/BurntSushi/toml" "github.com/spf13/cobra" ) type Config struct { - Components map[string]Component `toml:"components"` + Spacecores map[string]Spacecore `toml:"spacecores"` } -type Component map[string]Mode +type Spacecore map[string]Mode type Mode struct { Binary string `toml:"binary"` Download string `toml:"download"` + Install string `toml:"install"` + Start string `toml:"start"` +} + +func WriteConf(conf Config) error { + // open the file + configFile := os.Getenv("HOME") + "/.vimana/config.toml" + file, err := os.OpenFile(configFile, os.O_WRONLY|os.O_TRUNC, 0644) + if err != nil { + return err + } + + defer file.Close() + + if err := toml.NewEncoder(file).Encode(conf); err != nil { + return err + } + + return nil } type NodeCommander interface { AddFlags(*cobra.Command) - Init(*cobra.Command, []string, Mode) error - Start(*cobra.Command, []string, Mode) - Stop(*cobra.Command, []string, Mode) - Status(*cobra.Command, []string, Mode) + Init(*cobra.Command, []string, Mode, string) error + Start(*cobra.Command, []string, Mode, string) + Stop(*cobra.Command, []string, Mode, string) + Status(*cobra.Command, []string, Mode, string) + Logs(*cobra.Command, []string, Mode, string) } type BaseCommander struct { - Name string - NodeType string - componentMgr *components.ComponentManager - config *components.ComponentConfig + Name string + NodeType string + spacecoresMgr *spacecores.SpacecoreManager + config *spacecores.SpacecoreConfig } -func (b *BaseCommander) initComponentManager(component config.ComponentType, binary string) { - if b.componentMgr == nil { - b.componentMgr = components.NewComponentManager(component, binary, b.NodeType, b.config) +func (b *BaseCommander) initSpacecoreManager(spacecore config.SpacecoreType, binary string) { + if b.spacecoresMgr == nil { + b.spacecoresMgr = spacecores.NewSpacecoreManager(spacecore, binary, b.NodeType, b.config) } } @@ -51,11 +73,22 @@ func GetCommandsFromConfig(path string, commanderRegistry map[string]NodeCommand return nil, err } + // update commanderRegistry + for spacecore, nodeTypes := range config.Spacecores { + for nodeType := range nodeTypes { + cmd_key := spacecore + "-" + nodeType + commander := commanderRegistry[cmd_key] + if commander == nil { + commanderRegistry[cmd_key] = NewUniversalCommander(nodeType) + } + } + } + var commands []*cobra.Command runCmd := &cobra.Command{ Use: "run", - Short: "Run a modular component", + Short: "Run a spacecore", } initPath := filepath.Join(os.Getenv("HOME"), ".vimana", "init.toml") initConf, err := utils.LoadVimanaConfig(initPath) @@ -63,11 +96,11 @@ func GetCommandsFromConfig(path string, commanderRegistry map[string]NodeCommand return nil, err } - for component, nodeTypes := range config.Components { - currentComponent := component - componentCmd := &cobra.Command{ - Use: currentComponent, - Short: fmt.Sprintf("Run the %s component", currentComponent), + for spacecore, nodeTypes := range config.Spacecores { + currentSpacecore := spacecore + spacecoreCmd := &cobra.Command{ + Use: currentSpacecore, + Short: fmt.Sprintf("Run the %s spacecore", currentSpacecore), } for nodeType := range nodeTypes { @@ -77,29 +110,233 @@ func GetCommandsFromConfig(path string, commanderRegistry map[string]NodeCommand Use: nodeType + "-node", Args: cobra.NoArgs, Run: func(c *cobra.Command, args []string) { - key := fmt.Sprintf("%s-%s", currentComponent, currentNodeType) - fmt.Println("commander component", key) + logger := log.GetLogger(c.Context()) + key := fmt.Sprintf("%s-%s", currentSpacecore, currentNodeType) + logger.Info("commander spacecore", key) commander := commanderRegistry[key] if commander != nil { - initConf.SpaceCore = currentComponent + initConf.SpaceCore = currentSpacecore if initConf.Analytics.Enabled { - go utils.SaveAnalyticsData(initConf) + // // go utils.SaveAnalyticsData(initConf) } - commander.Start(c, args, ntype) + commander.Start(c, args, ntype, key) } else { - log.Fatalf("Components '%s' of type '%s' not recognized", component, ntype) + logger.Fatalf("Spacecores '%s' of type '%s' not recognized", spacecore, ntype) } }, } - if commander, ok := commanderRegistry[fmt.Sprintf("%s-%s", currentComponent, nodeType)]; ok { + if commander, ok := commanderRegistry[fmt.Sprintf("%s-%s", currentSpacecore, nodeType)]; ok { commander.AddFlags(nodeCmd) } - componentCmd.AddCommand(nodeCmd) + spacecoreCmd.AddCommand(nodeCmd) } - runCmd.AddCommand(componentCmd) + runCmd.AddCommand(spacecoreCmd) } commands = append(commands, runCmd) + + // start command + startCmd := &cobra.Command{ + Use: "start", + Short: "start a spacecore", + } + for spacecore, nodeTypes := range config.Spacecores { + currentSpacecore := spacecore + spacecoreCmd := &cobra.Command{ + Use: currentSpacecore, + Short: fmt.Sprintf("start the %s spacecore", currentSpacecore), + } + + for nodeType := range nodeTypes { + ntype := nodeTypes[nodeType] + currentNodeType := nodeType + nodeCmd := &cobra.Command{ + Use: nodeType + "-node", + Args: cobra.NoArgs, + Run: func(c *cobra.Command, args []string) { + logger := log.GetLogger(c.Context()) + key := fmt.Sprintf("%s-%s", currentSpacecore, currentNodeType) + logger.Info("commander spacecore", key) + + commander := commanderRegistry[key] + if commander != nil { + initConf.SpaceCore = currentSpacecore + if initConf.Analytics.Enabled { + // go utils.SaveAnalyticsData(initConf) + } + commander.Start(c, args, ntype, key) + } else { + logger.Fatalf("Spacecores '%s' of type '%s' not recognized", spacecore, ntype) + } + }, + } + if commander, ok := commanderRegistry[fmt.Sprintf("%s-%s", currentSpacecore, nodeType)]; ok { + commander.AddFlags(nodeCmd) + } + spacecoreCmd.AddCommand(nodeCmd) + } + startCmd.AddCommand(spacecoreCmd) + + } + commands = append(commands, startCmd) + + // stop command + stopCmd := &cobra.Command{ + Use: "stop", + Short: "stop a modular spacecore", + } + for spacecore, nodeTypes := range config.Spacecores { + currentSpacecore := spacecore + spacecoreCmd := &cobra.Command{ + Use: currentSpacecore, + Short: fmt.Sprintf("stop the %s spacecore", currentSpacecore), + } + + for nodeType := range nodeTypes { + ntype := nodeTypes[nodeType] + currentNodeType := nodeType + nodeCmd := &cobra.Command{ + Use: nodeType + "-node", + Args: cobra.NoArgs, + Run: func(c *cobra.Command, args []string) { + logger := log.GetLogger(c.Context()) + key := fmt.Sprintf("%s-%s", currentSpacecore, currentNodeType) + logger.Info("commander spacecore", key) + + commander := commanderRegistry[key] + if commander != nil { + initConf.SpaceCore = currentSpacecore + if initConf.Analytics.Enabled { + // go utils.SaveAnalyticsData(initConf) + } + commander.Stop(c, args, ntype, key) + } else { + logger.Fatalf("Spacecores '%s' of type '%s' not recognized", spacecore, ntype) + } + }, + } + if commander, ok := commanderRegistry[fmt.Sprintf("%s-%s", currentSpacecore, nodeType)]; ok { + commander.AddFlags(nodeCmd) + } + spacecoreCmd.AddCommand(nodeCmd) + } + stopCmd.AddCommand(spacecoreCmd) + + } + commands = append(commands, stopCmd) + + // status command + statusCmd := &cobra.Command{ + Use: "status", + Short: "show status of a modular spacecore", + } + for spacecore, nodeTypes := range config.Spacecores { + currentSpacecore := spacecore + spacecoreCmd := &cobra.Command{ + Use: currentSpacecore, + Short: fmt.Sprintf("show status of the %s spacecore", currentSpacecore), + } + + for nodeType := range nodeTypes { + ntype := nodeTypes[nodeType] + currentNodeType := nodeType + nodeCmd := &cobra.Command{ + Use: nodeType + "-node", + Args: cobra.NoArgs, + Run: func(c *cobra.Command, args []string) { + logger := log.GetLogger(c.Context()) + key := fmt.Sprintf("%s-%s", currentSpacecore, currentNodeType) + logger.Info("commander spacecore", key) + + commander := commanderRegistry[key] + if commander != nil { + initConf.SpaceCore = currentSpacecore + if initConf.Analytics.Enabled { + // go utils.SaveAnalyticsData(initConf) + } + commander.Status(c, args, ntype, key) + } else { + logger.Fatalf("Spacecores '%s' of type '%s' not recognized", spacecore, ntype) + } + }, + } + if commander, ok := commanderRegistry[fmt.Sprintf("%s-%s", currentSpacecore, nodeType)]; ok { + commander.AddFlags(nodeCmd) + } + spacecoreCmd.AddCommand(nodeCmd) + } + statusCmd.AddCommand(spacecoreCmd) + + } + commands = append(commands, statusCmd) + + // logs command + logsCmd := &cobra.Command{ + Use: "logs", + Short: "show logs of a modular spacecore", + } + for spacecore, nodeTypes := range config.Spacecores { + currentSpacecore := spacecore + spacecoreCmd := &cobra.Command{ + Use: currentSpacecore, + Short: fmt.Sprintf("show logs of the %s spacecore", currentSpacecore), + } + + for nodeType := range nodeTypes { + ntype := nodeTypes[nodeType] + currentNodeType := nodeType + nodeCmd := &cobra.Command{ + Use: nodeType + "-node", + Args: cobra.NoArgs, + Run: func(c *cobra.Command, args []string) { + logger := log.GetLogger(c.Context()) + key := fmt.Sprintf("%s-%s", currentSpacecore, currentNodeType) + logger.Info("commander spacecore", key) + + commander := commanderRegistry[key] + if commander != nil { + initConf.SpaceCore = currentSpacecore + if initConf.Analytics.Enabled { + // go utils.SaveAnalyticsData(initConf) + } + // commander.Status(c, args, ntype, key) + commander.Logs(c, args, ntype, key) + } else { + logger.Fatalf("Spacecores '%s' of type '%s' not recognized", spacecore, ntype) + } + }, + } + if commander, ok := commanderRegistry[fmt.Sprintf("%s-%s", currentSpacecore, nodeType)]; ok { + commander.AddFlags(nodeCmd) + } + spacecoreCmd.AddCommand(nodeCmd) + } + logsCmd.AddCommand(spacecoreCmd) + } + commands = append(commands, logsCmd) + return commands, nil } + +// Implement Logs function for BaseCommander +func (b *BaseCommander) Logs(c *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(c.Context()) + logger.Info("Getting logs of " + node_info) + PIDFile := utils.GetPIDFileName(node_info) + _, err := os.Stat(PIDFile) + if err == nil { + _, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Infof("%v not running\n", node_info) + } else { + logFile := "/tmp/" + node_info + ".log" + cmd := exec.Command("tail", "-f", logFile) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Run() + } + } else { + logger.Infof("%v not running\n", node_info) + } +} diff --git a/cli/commands_test.go b/cli/commands_test.go index fcd0046..f5a012e 100644 --- a/cli/commands_test.go +++ b/cli/commands_test.go @@ -1,35 +1,41 @@ package cli import ( - "fmt" "io/ioutil" "os" "testing" + "vimana/log" + "github.com/spf13/cobra" ) func TestGetCommandsFromConfig(t *testing.T) { // Mocking the TOML config data mockData := ` -[components] +[spacecores] -[components.celestia] +[spacecores.celestia] -[components.celestia.blah] -binary = "/tmp/vimana/celestia/celestia" +[spacecores.celestia.blah] +binary = "/usr/local/bin/celestia/celestia" download = "/tmp/vimana/celestia/init.sh" -[components.celestia.bridge] -binary = "/tmp/vimana/celestia/celestia" +[spacecores.celestia.bridge] +binary = "/usr/local/bin/celestia/celestia" download = "/tmp/vimana/celestia/init.sh" -[components.berachain] +[spacecores.berachain] -[components.berachain.light] +[spacecores.berachain.light] binary = "berachain-light" download = "/tmp/vimana/berachain/init.sh" +[spacecores.eigen] + +[spacecores.eigen.operator] +binary = "/usr/local/bin/eigen/eigen" +download = "/tmp/vimana/eigen/init.sh" ` // Write mockData to a temporary file tmpfile, err := ioutil.TempFile("", "example.toml") @@ -46,9 +52,10 @@ download = "/tmp/vimana/berachain/init.sh" // Define a mock commanderRegistry var mockCommanderRegistry = map[string]NodeCommander{ - "celestia-light": NewMockCommander(), - "celestia-bridge": NewMockCommander(), - "avail-light": NewMockCommander(), + "celestia-light": NewMockCommander("light"), + "celestia-bridge": NewMockCommander("bridge"), + "avail-light": NewMockCommander("light"), + "eigen-operator": NewMockCommander("operator"), } // Call GetCommandsFromConfig @@ -58,40 +65,47 @@ download = "/tmp/vimana/berachain/init.sh" t.Fatal(err) } - if len(commands) != 1 { + /* if len(commands) != 1 { t.Fatalf("Expected 1 main command but got %d", len(commands)) - } + }*/ runCmd := commands[0] if runCmd.Use != "run" { t.Fatalf("Expected 'run' command but got '%s'", runCmd.Use) } - if len(runCmd.Commands()) != 2 { - t.Fatalf("Expected 2 component commands but got %d", len(runCmd.Commands())) - } + /* + if len(runCmd.Commands()) != 2 { + t.Fatalf("Expected 2 spacecore commands but got %d", len(runCmd.Commands())) + } + */ } type MockCommander struct{ BaseCommander } -func NewMockCommander() *MockCommander { +func NewMockCommander(node_type string) *MockCommander { return &MockCommander{ - BaseCommander: BaseCommander{NodeType: "light"}, + BaseCommander: BaseCommander{NodeType: node_type}, } } -func (m *MockCommander) Init(cmd *cobra.Command, args []string, mode Mode) error { - fmt.Println("MockCommander.Init() called") +func (m *MockCommander) Init(cmd *cobra.Command, args []string, mode Mode, node_info string) error { + logger := log.GetLogger(cmd.Context()) + logger.Info("MockCommander.Init() called") return nil } -func (m *MockCommander) Start(cmd *cobra.Command, args []string, mode Mode) { +func (m *MockCommander) Start(cmd *cobra.Command, args []string, mode Mode, node_info string) { } -func (m *MockCommander) Status(cmd *cobra.Command, args []string, mode Mode) { +func (m *MockCommander) Status(cmd *cobra.Command, args []string, mode Mode, node_info string) { } -func (m *MockCommander) Stop(cmd *cobra.Command, args []string, mode Mode) { +func (m *MockCommander) Stop(cmd *cobra.Command, args []string, mode Mode, node_info string) { } + +func (m *MockCommander) Logs(cmd *cobra.Command, args []string, mode Mode, node_info string) { +} + func (m *MockCommander) AddFlags(cmd *cobra.Command) { } diff --git a/cli/eigen.go b/cli/eigen.go new file mode 100644 index 0000000..426709c --- /dev/null +++ b/cli/eigen.go @@ -0,0 +1,112 @@ +package cli + +import ( + "io/ioutil" + "os" + "os/exec" + "strconv" + "strings" + "vimana/cmd/utils" + "vimana/log" + + "github.com/spf13/cobra" +) + +type EigenOperatorCommander struct { + BaseCommander +} + +func NewEigenOperatorCommander(node_type string) *EigenOperatorCommander { + return &EigenOperatorCommander{ + BaseCommander: BaseCommander{NodeType: "operator"}, + } +} + +func (a *EigenOperatorCommander) AddFlags(cmd *cobra.Command) { +} + +func (a *EigenOperatorCommander) Install(cmd *cobra.Command, args []string, mode Mode, node_info string) { + return +} + +func (a *EigenOperatorCommander) Init(cmd *cobra.Command, args []string, mode Mode, node_info string) error { + utils.ExecBashCmd(exec.Command("bash", mode.Download), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) + a.initSpacecoreManager("eigen", mode.Binary) + return a.spacecoresMgr.InitializeConfig() +} + +func (a *EigenOperatorCommander) Run(cmd *cobra.Command, args []string, mode Mode, node_info string) { + cmdexecute := a.spacecoresMgr.GetStartCmd() + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +} + +func (a *EigenOperatorCommander) Start(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + node_info_arr := strings.Split(node_info, "-") + a.Init(cmd, args, mode, node_info_arr[0]) + logger.Info(a.spacecoresMgr) + logger.Info("executing start command") + cmdexecute := a.spacecoresMgr.GetStartCmd() + logger.Info(cmdexecute) + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +} + +func (a *EigenOperatorCommander) Stop(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + logger.Info("Stopping Eigen operator node") + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + data, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + return + } + ProcessID, err := strconv.Atoi(string(data)) + + if err != nil { + logger.Info("Unable to read and parse process id found in ", PIDFile) + return + } + + process, err := os.FindProcess(ProcessID) + + if err != nil { + logger.Infof("Unable to find process ID [%v] with error %v \n", ProcessID, err) + return + } + // remove PID file + os.Remove(PIDFile) + + node_info_arr := strings.Split(node_info, "-") + logger.Info("Stopping " + node_info_arr[0] + " " + node_info_arr[1] + " node") + // kill process and exit immediately + err = process.Kill() + + if err != nil { + logger.Infof("Unable to kill process ID [%v] with error %v \n", ProcessID, err) + } else { + logger.Infof("Killed process ID [%v]\n", ProcessID) + } + } else { + logger.Info("Not running.") + } +} + +func (a *EigenOperatorCommander) Status(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + + node_info_arr := strings.Split(node_info, "-") + logger.Info("Getting status of " + node_info_arr[0] + " " + node_info_arr[1] + " node") + + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + _, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + } else { + logger.Info(node_info_arr[0] + " " + node_info_arr[1] + " node is running") + } + } else { + logger.Info("Not running.") + } +} diff --git a/cli/gmworld.go b/cli/gmworld.go index 346bd76..ed775d6 100644 --- a/cli/gmworld.go +++ b/cli/gmworld.go @@ -1,10 +1,15 @@ package cli import ( - "fmt" + "io/ioutil" + "os" "os/exec" + "strconv" + "strings" "vimana/cmd/utils" + "vimana/log" + // "github.com/moby/moby/daemon/logger" "github.com/spf13/cobra" ) @@ -12,7 +17,7 @@ type GmworldDaCommander struct { BaseCommander } -func NewGmworldDaCommander() *GmworldDaCommander { +func NewGmworldDaCommander(node_type string) *GmworldDaCommander { return &GmworldDaCommander{ BaseCommander: BaseCommander{NodeType: "init"}, } @@ -21,38 +26,46 @@ func NewGmworldDaCommander() *GmworldDaCommander { func (a *GmworldDaCommander) AddFlags(cmd *cobra.Command) { } -func (a *GmworldDaCommander) Init(cmd *cobra.Command, args []string, mode Mode) error { - utils.ExecBashCmd(exec.Command("bash", mode.Download), utils.WithOutputToStdout(), utils.WithErrorsToStderr()) - a.initComponentManager("gmworld", mode.Binary) - return a.componentMgr.InitializeConfig() +func (a *GmworldDaCommander) Install(cmd *cobra.Command, args []string, mode Mode, node_info string) { + return } -func (a *GmworldDaCommander) Run(cmd *cobra.Command, args []string, mode Mode) { - cmdexecute := a.componentMgr.GetStartCmd() - utils.ExecBashCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +func (a *GmworldDaCommander) Init(cmd *cobra.Command, args []string, mode Mode, node_info string) error { + utils.ExecBashCmd(exec.Command("bash", mode.Download), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) + a.initSpacecoreManager("gmworld", mode.Binary) + return a.spacecoresMgr.InitializeConfig() } -func (a *GmworldDaCommander) Start(cmd *cobra.Command, args []string, mode Mode) { - a.Init(cmd, args, mode) - fmt.Println("executing start command") - cmdexecute := a.componentMgr.GetStartCmd() - fmt.Println(cmdexecute) - utils.ExecBashCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +func (a *GmworldDaCommander) Run(cmd *cobra.Command, args []string, mode Mode, node_info string) { + cmdexecute := a.spacecoresMgr.GetStartCmd() + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) } -func (a *GmworldDaCommander) Stop(cmd *cobra.Command, args []string, mode Mode) { - fmt.Println("Stopping Celestia bridge node") +func (a *GmworldDaCommander) Start(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + node_info_arr := strings.Split(node_info, "-") + a.Init(cmd, args, mode, node_info_arr[0]) + logger.Info("executing start command") + cmdexecute := a.spacecoresMgr.GetStartCmd() + logger.Info(cmdexecute) + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) } -func (a *GmworldDaCommander) Status(cmd *cobra.Command, args []string, mode Mode) { - fmt.Println("Getting status of Celestia bridge node") +func (a *GmworldDaCommander) Stop(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + logger.Info("Stopping Celestia bridge node") +} + +func (a *GmworldDaCommander) Status(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + logger.Info("Getting status of Celestia bridge node") } type GmworldRollupCommander struct { BaseCommander } -func NewGmworldRollupCommander() *GmworldRollupCommander { +func NewGmworldRollupCommander(node_type string) *GmworldRollupCommander { return &GmworldRollupCommander{ BaseCommander: BaseCommander{NodeType: "start"}, } @@ -61,30 +74,91 @@ func NewGmworldRollupCommander() *GmworldRollupCommander { func (a *GmworldRollupCommander) AddFlags(cmd *cobra.Command) { } -func (a *GmworldRollupCommander) Init(cmd *cobra.Command, args []string, mode Mode) error { - utils.ExecBashCmd(exec.Command("bash", mode.Download), utils.WithOutputToStdout(), utils.WithErrorsToStderr()) - a.initComponentManager("rollup", mode.Binary) - return a.componentMgr.InitializeConfig() +func (a *GmworldRollupCommander) Install(cmd *cobra.Command, args []string, mode Mode, node_info string) { } -func (a *GmworldRollupCommander) Run(cmd *cobra.Command, args []string, mode Mode) { - cmdexecute := a.componentMgr.GetStartCmd() - utils.ExecBashCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +func (a *GmworldRollupCommander) Init(cmd *cobra.Command, args []string, mode Mode, node_info string) error { + utils.ExecBashCmd(exec.Command("bash", mode.Download), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) + a.initSpacecoreManager("rollup", mode.Binary) + return a.spacecoresMgr.InitializeConfig() } -func (a *GmworldRollupCommander) Start(cmd *cobra.Command, args []string, mode Mode) { - a.Init(cmd, args, mode) - fmt.Println(a.componentMgr) - fmt.Println("executing start command") - cmdexecute := a.componentMgr.GetStartCmd() - fmt.Println(cmdexecute) - utils.ExecBashCmd(cmdexecute, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +func (a *GmworldRollupCommander) Run(cmd *cobra.Command, args []string, mode Mode, node_info string) { + cmdexecute := a.spacecoresMgr.GetStartCmd() + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) } -func (a *GmworldRollupCommander) Stop(cmd *cobra.Command, args []string, mode Mode) { - fmt.Println("Stopping Celestia bridge node") +func (a *GmworldRollupCommander) Start(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + logger.Info("Already running or " + PIDFile + " file exist.") + return + } + + node_info_arr := strings.Split(node_info, "-") + a.Init(cmd, args, mode, node_info_arr[0]) + logger.Info(a.spacecoresMgr) + logger.Info("executing start command") + cmdexecute := a.spacecoresMgr.GetStartCmd() + logger.Info(cmdexecute) + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +} + +func (a *GmworldRollupCommander) Stop(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + data, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + return + } + ProcessID, err := strconv.Atoi(string(data)) + + if err != nil { + logger.Info("Unable to read and parse process id found in ", PIDFile) + return + } + + process, err := os.FindProcess(ProcessID) + + if err != nil { + logger.Infof("Unable to find process ID [%v] with error %v \n", ProcessID, err) + return + } + // remove PID file + os.Remove(PIDFile) + + node_info_arr := strings.Split(node_info, "-") + logger.Info("Stopping " + node_info_arr[0] + " " + node_info_arr[1] + " node") + // kill process and exit immediately + err = process.Kill() + + if err != nil { + logger.Infof("Unable to kill process ID [%v] with error %v \n", ProcessID, err) + } else { + logger.Infof("Killed process ID [%v]\n", ProcessID) + } + } else { + logger.Info("Not running.") + } } -func (a *GmworldRollupCommander) Status(cmd *cobra.Command, args []string, mode Mode) { - fmt.Println("Getting status of Celestia bridge node") +func (a *GmworldRollupCommander) Status(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + node_info_arr := strings.Split(node_info, "-") + logger.Info("Getting status of " + node_info_arr[0] + " " + node_info_arr[1] + " node") + + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + _, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + } else { + logger.Info(node_info_arr[0] + " " + node_info_arr[1] + " node is running") + } + } else { + logger.Info("Not running.") + } } diff --git a/cli/universal_cmd.go b/cli/universal_cmd.go new file mode 100644 index 0000000..bc5e215 --- /dev/null +++ b/cli/universal_cmd.go @@ -0,0 +1,144 @@ +package cli + +import ( + "io/ioutil" + "os" + "os/exec" + "strconv" + "strings" + "vimana/cmd/utils" + "vimana/config" + "vimana/log" + + "github.com/spf13/cobra" +) + +type UniversalCommander struct { + BaseCommander +} + +func NewUniversalCommander(node_type string) *UniversalCommander { + return &UniversalCommander{ + BaseCommander: BaseCommander{NodeType: node_type}, + } +} + +func (a *UniversalCommander) AddFlags(cmd *cobra.Command) { +} + +func (a *UniversalCommander) Install(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + logger.Info(a.spacecoresMgr) + logger.Info("executing install command") + binaryPath := string([]rune(mode.Binary)[0:strings.LastIndex(mode.Binary, "/")]) + binaryName := string([]rune(mode.Binary)[strings.LastIndex(mode.Binary, "/")+1:]) + utils.ExecBashCmd(exec.Command("bash", mode.Install, binaryPath, binaryName), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +} + +func (a *UniversalCommander) Init(cmd *cobra.Command, args []string, mode Mode, node_info string) error { + + utils.ExecBashCmd(exec.Command("bash", mode.Download), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) + node_info_arr := strings.Split(node_info, "-") + a.initSpacecoreManager(config.SpacecoreType(node_info_arr[0]), mode.Binary) + return a.spacecoresMgr.InitializeConfig() +} + +func (a *UniversalCommander) Run(cmd *cobra.Command, args []string, mode Mode, node_info string) { + cmdexecute := a.spacecoresMgr.GetStartCmd() + utils.ExecBinaryCmd(cmdexecute, node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +} + +func (a *UniversalCommander) Start(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + logger.Info("Already running or " + PIDFile + " file exist.") + return + } + + logger.Info("executing start command") + binaryPath := string([]rune(mode.Binary)[0:strings.LastIndex(mode.Binary, "/")]) + binaryName := string([]rune(mode.Binary)[strings.LastIndex(mode.Binary, "/")+1:]) + logger.Info(binaryPath) + + utils.ExecBinaryCmd(exec.Command("bash", mode.Start, binaryPath, binaryName), node_info, utils.WithOutputToStdout(), utils.WithErrorsToStderr()) +} + +func (a *UniversalCommander) Stop(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + data, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + return + } + ProcessID, err := strconv.Atoi(string(data)) + ProcessID = ProcessID + 1 + if err != nil { + logger.Info("Unable to read and parse process id found in ", PIDFile) + return + } + + process, err := os.FindProcess(ProcessID) + + if err != nil { + logger.Infof("Unable to find process ID [%v] with error %v \n", ProcessID, err) + return + } + // remove PID file + os.Remove(PIDFile) + + node_info_arr := strings.Split(node_info, "-") + logger.Info("Stopping " + node_info_arr[0] + " " + node_info_arr[1] + " node") + // kill process and exit immediately + err = process.Kill() + + if err != nil { + logger.Infof("Unable to kill process ID [%v] with error %v \n", ProcessID, err) + } else { + logger.Infof("Killed process ID [%v]\n", ProcessID) + } + } else { + logger.Info("Not running.") + } + +} + +func (a *UniversalCommander) Logs(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + PIDFile := utils.GetPIDFileName(node_info) + _, err := os.Stat(PIDFile) + if err == nil { + _, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Infof("%v not running\n", node_info) + } else { + logFile := "/tmp/" + node_info + ".log" + cmd := exec.Command("tail", "-f", logFile) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Run() + } + } else { + logger.Infof("%v not running\n", node_info) + } +} + +func (a *UniversalCommander) Status(cmd *cobra.Command, args []string, mode Mode, node_info string) { + logger := log.GetLogger(cmd.Context()) + node_info_arr := strings.Split(node_info, "-") + logger.Info("Getting status of " + node_info_arr[0] + " " + node_info_arr[1] + " node") + + PIDFile := utils.GetPIDFileName(node_info) + if _, err := os.Stat(PIDFile); err == nil { + _, err := ioutil.ReadFile(PIDFile) + if err != nil { + logger.Info("Not running") + } else { + logger.Info(node_info_arr[0] + " " + node_info_arr[1] + " node is running") + } + } else { + logger.Info("Not running.") + } +} diff --git a/cmd/init.go b/cmd/init.go index c4d0799..063daaf 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -12,8 +12,6 @@ import ( "vimana/cmd/utils" "github.com/ethereum/go-ethereum/crypto" - - // "github.com/pelletier/go-toml" "github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/disk" "github.com/shirou/gopsutil/mem" @@ -31,7 +29,7 @@ func fileExists(filename string) bool { func InitializeSystem(force bool, noTrack bool) error { if err := fileExists(initFilePath); err && !force { - fmt.Println("Initialization has already been done. Found init.toml.") + fmt.Print("Initialization has already been done. Found init.toml.\n") return nil } // config := InitConfig{} diff --git a/cmd/migrate.go b/cmd/migrate.go new file mode 100644 index 0000000..f92c8e7 --- /dev/null +++ b/cmd/migrate.go @@ -0,0 +1,158 @@ +package cmd + +import ( + "fmt" + "log" + "os" + "strings" + "vimana/cmd/utils" + + "github.com/asmcos/requests" + + "github.com/shirou/gopsutil/host" + "github.com/spf13/cobra" +) + +func migrateCommand() *cobra.Command { + migrateCmd := &cobra.Command{ + Use: "migrate", + Short: "upgrade vimana to latest version", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("vimana version: ", Version) + upgradeVimana() + }, + } + return migrateCmd +} + +func upgradeVimana() { + resp, err := requests.Get("https://api.github.com/repos/Vistara-Labs/vimana/releases") + if err != nil { + return + } + // Status code + if resp.R.StatusCode != 200 { + fmt.Println("Downloading vimana error: status code =", resp.R.StatusCode) + return + } + // reponse + // println(resp.Text()) + + type person struct { + Login string `json:"login"` + Id int `json:"id"` + Node_id string `json:"node_id"` + Avatar_url string `json:"avatar_url"` + Gravatar_id string `json:"gravatar_id"` + Url string `json:"url"` + Html_url string `json:"html_url"` + Followers_url string `json:"followers_url"` + Following_url string `json:"following_url"` + Gists_url string `json:"gists_url"` + Starred_url string `json:"starred_url"` + Subscriptions_url string `json:"subscriptions_url"` + Organizations_url string `json:"organizations_url"` + Repos_url string `json:"repos_url"` + Events_url string `json:"events_url"` + Received_events_url string `json:"received_events_url"` + Type string `json:"type"` + Site_admin bool `json:"site_admin"` + } + + type asset struct { + Url string `json:"url"` + Id int `json:"id"` + Node_id string `json:"node_id"` + Name string `json:"name"` + Label string `json:"label"` + Uploader person + Content_type string `json:"content_type"` + State string `json:"state"` + Size int `json:"size"` + Download_count int `json:"download_count"` + Created_at string `json:"created_at"` + Updated_at string `json:"updated_at"` + Browser_download_url string `json:"browser_download_url"` + } + + type ReleaseItem struct { + Url string `json:"url"` + Assets_url string `json:"asset_url"` + Upload_url string `json:"upload_url"` + Html_url string `json:"html_url"` + Id int `json:"id"` + Author person + Node_id string `json:"node_id"` + Tag_name string `json:"tag_name"` + Target_commitish string `json:"target_commitish"` + Name string `json:"name"` + Draft bool `json:"draft"` + Prerelease bool `json:"prerelease"` + Created_at string `json:"created_at"` + Published_at string `json:"published_at"` + Assets []asset + Tarball_url string `json:"tarball_url"` + Zipball_url string `json:"zipball_url"` + Body string `json:"body"` + } + var Releases []ReleaseItem + resp.Json(&Releases) + //for i, s := range Releases { + // fmt.Println(i, s.Tag_name) + //} + var current_version = strings.Split(Version, "v")[1] + var latest_version = strings.Split(Releases[0].Tag_name, "-v")[1] + //fmt.Println(latest_version) + if current_version != latest_version { + fmt.Println("No need to upgrade vimana") + return + } + + v, _ := host.Info() + // convert to JSON. String() is also implemented + OS := v.OS + ARCH := v.KernelArch + //fmt.Println(OS, ARCH) + if ARCH == "x86_64" { + ARCH = "amd64" + } else if ARCH == "arm64" || ARCH == "aarch64" { + ARCH = "arm64" + } + + // https://github.com/Vistara-Labs/vimana/releases/download/vimana-v0.0.151/vimana-darwin-arm64.tar.gz + file_name := "vimana-" + OS + "-" + ARCH + ".tar.gz" + url := "https://github.com/Vistara-Labs/vimana/releases/download/vimana-v" + latest_version + "/" + file_name + + err = utils.DownloadTarGzFile(file_name, url) + if err == nil { + fmt.Println("Download vimana via " + url + " successfully") + } else { + fmt.Println(err) + return + } + + gzipStream, err := os.Open(file_name) + if err != nil { + fmt.Println("error") + return + } + if err := utils.ExtractTarGz(gzipStream); err != nil { + fmt.Println("ExtractTarGz failed: %w", err) + return + } + + // + VIMANA_BIN_PATH := "/usr/local/bin/vimana" + vimana := "./vimana-" + OS + "-" + ARCH + "/vimana" + + err = os.Rename(vimana, VIMANA_BIN_PATH) + if err != nil { + log.Fatal(err) + } else { + fmt.Println("Upgrade vimana succesfully") + } + os.Chmod(VIMANA_BIN_PATH, os.FileMode(0755)) + os.RemoveAll("./vimana-" + OS + "-" + ARCH + "/") + // + +} diff --git a/cmd/plugin.go b/cmd/plugin.go new file mode 100644 index 0000000..3045748 --- /dev/null +++ b/cmd/plugin.go @@ -0,0 +1,161 @@ +package cmd + +import ( + "context" + "vimana/plugins" + "vimana/plugins/proto" + + "vimana/log" + + "github.com/spf13/cobra" +) + +type PluginCommander struct { + Name string + PluginPath string +} +type PluginCommanders int + +const ( + PluginCmdName = "plugin" + StopPluginCmdName = "stop" + StartPluginCmdName = "start" + RestartPluginCmdName = "restart" + StatusPluginCmdName = "status" + LogsPluginCmdName = "logs" + MetricsPluginCmdName = "metrics" +) + +func pluginCommand() *cobra.Command { + // logger := log.GetLogger(ctx) + + // setup logging + logger := log.GetLogger(context.Background()) + + // create a channel for sending and receiving commands to the plugin + commands := make(chan PluginCommander) + responses := make(chan string) + + // Get the plugin in a separate goroutine and pass the commands channel to it + go func() { + + for cmd := range commands { + client := plugins.GetPluginClient(cmd.PluginPath) + spacecore, err := plugins.SpacecoreGRPCClient(client) + if err != nil { + logger.Error("Error getting spacecore plugin:", err) + responses <- "Error getting spacecore plugin" + err.Error() + } + switch cmd.Name { + case StartPluginCmdName: + logger.Infof("Starting plugin inside goroutine %s", cmd.PluginPath) + msg, err := spacecore.Start(context.Background(), &proto.StartRequest{}) + + if err != nil { + responses <- "Error starting plugin" + err.Error() + } + + logger.Infof("Plugin ID: %s, status: %s", msg.GetPluginId(), msg.GetStatus()) + logger.Info("Plugin is now running. Press CTRL+C to exit") + responses <- "Plugin started" + + case StopPluginCmdName: + logger.Info("Stopping plugin") + client.Kill() + + responses <- "Plugin stopped" + + case RestartPluginCmdName: + logger.Info("Restarting plugin") + responses <- "Plugin restarted" + case StatusPluginCmdName: + logger.Info("Getting status of plugin") + + msg, err := spacecore.Status(context.Background(), &proto.StatusRequest{}) + if err != nil { + logger.Infof("Error getting status: %s", err) + responses <- "Error getting status" + err.Error() + } + + logger.Infof("Plugin ID: %s, status: %s", msg.GetPluginId(), msg.GetStatus()) + responses <- "Plugin status" + } + } + }() + + pluginCmd := &cobra.Command{ + Use: "plugin [plugin] [action]", + Short: "Run a spacecore plugin", + Args: cobra.MinimumNArgs(2), + Run: func(c *cobra.Command, args []string) { + if len(args) == 0 { + logger.Info("Please provide a plugin name") + return + } + action := args[1] + switch action { + case "start": + commands <- PluginCommander{Name: StartPluginCmdName, PluginPath: args[0]} + case "stop": + commands <- PluginCommander{Name: StopPluginCmdName, PluginPath: args[0]} + case "restart": + commands <- PluginCommander{Name: RestartPluginCmdName, PluginPath: args[0]} + case "status": + commands <- PluginCommander{Name: StatusPluginCmdName, PluginPath: args[0]} + } + + // pass the plugin name i.e. plugin path to the goroutine + // commands <- PluginCommander{Name: StartPluginCmdName, PluginPath: args[0]} + + // wait for a response from the plugin + resp := <-responses + logger.Info("Response from plugin:", resp) + }, + } + return pluginCmd +} + +// started, err := pg.Start(context.Background(), &proto.StartRequest{}) + +// if err != nil { +// logger.Info("Error starting plugin:", err) +// } + +// logger.Info("Plugin is now running. Press CTRL+C to exit") + +// should there be a way to keep the plugin running? +// if so, how? +// should we just keep the plugin running until the user exits? +// or should we have a way to keep the plugin running in the background? + +// we should have a way to keep the plugin running in the background +// so that the user can interact with it + +// we should also have a way to stop the plugin + +// we should also have a way to restart the plugin + +// we should also have a way to get the status of the plugin + +// we should also have a way to get the logs of the plugin + +// we should also have a way to get the metrics of the plugin + +// the plugin needs to keep running in the background + +// if started.GetStatus() == "started" { +// logger.Info("Plugin started successfully") + +// // keep the plugin running +// <-make(chan struct{}) + +// } else { +// logger.Info("Error starting plugin") +// } + +// msg, err := pg.Status(context.Background(), &proto.StatusRequest{}) +// if err != nil { +// logger.Info("Error getting status:", err) +// } +// // show status of the plugin +// logger.Infof("PG ID: %s, status: %s", msg.GetPluginId(), msg.GetStatus()) diff --git a/cmd/registry.go b/cmd/registry.go index 5c3f642..a59e29c 100644 --- a/cmd/registry.go +++ b/cmd/registry.go @@ -1,12 +1,123 @@ package cmd -import "vimana/cli" +import ( + "os" + "strings" + "vimana/cli" + + "vimana/log" + + "github.com/BurntSushi/toml" + "github.com/asmcos/requests" + + "github.com/spf13/cobra" +) // CommanderRegistry maps node types to their corresponding NodeCommander implementations. var CommanderRegistry = map[string]cli.NodeCommander{ - "celestia-light": cli.NewCelestiaLightCommander(), - "celestia-bridge": cli.NewCelestiaBridgeCommander(), - "avail-light": cli.NewAvailLightCommander(), - "gmworld-da": cli.NewGmworldDaCommander(), - "gmworld-rollup": cli.NewGmworldRollupCommander(), + "celestia-light": cli.NewCelestiaLightCommander("light"), + "celestia-bridge": cli.NewCelestiaBridgeCommander("bridge"), + "avail-light": cli.NewAvailLightCommander("light"), + "gmworld-da": cli.NewGmworldDaCommander("da"), + "gmworld-rollup": cli.NewGmworldRollupCommander("rollup"), + "eigen-operator": cli.NewEigenOperatorCommander("operator"), +} + +func registryCommand() *cobra.Command { + type spacecore struct { + Spacecore string `json:"spacecore"` + Repo string `json:"repo"` + } + + //url := "https://raw.githubusercontent.com/Vistara-Labs/vimana/spacecores.json" + url := "https://raw.githubusercontent.com/zhangwenqiangnb/vimana/dev/spacecores.json" + + registryCmd := &cobra.Command{ + Use: "registry", + Short: "registry search/list command", + } + + searchCmd := &cobra.Command{ + Use: "search", + Short: "search x", + Run: func(cmd *cobra.Command, args []string) { + logger := log.GetLogger(cmd.Context()) + if len(args) == 0 { + logger.Info("lack of parmater") + } else { + // 1. check config.toml + configFile := os.Getenv("HOME") + "/.vimana/config.toml" + var config cli.Config + if _, err := toml.DecodeFile(configFile, &config); err != nil { + return + } + for spacecore := range config.Spacecores { + if strings.Contains(strings.ToLower(spacecore), strings.ToLower(args[0])) { + logger.Info(spacecore) + } + } + // 2. check spacecores.json + resp, err := requests.Get(url) + if err != nil { + return + } + // Status code + if resp.R.StatusCode != 200 { + logger.Info("Get Vistara-Labs/vimana/spacecores.json error: status code =", resp.R.StatusCode) + return + } + + var Spacecores []spacecore + resp.Json(&Spacecores) + + for _, spacecore := range Spacecores { + if strings.Contains(strings.ToLower(spacecore.Spacecore), strings.ToLower(args[0])) { + logger.Info(spacecore.Spacecore) + } + } + } + + }, + } + + listCmd := &cobra.Command{ + Use: "list", + Short: "list", + Run: func(cmd *cobra.Command, args []string) { + logger := log.GetLogger(cmd.Context()) + // 1. check config.toml + configFile := os.Getenv("HOME") + "/.vimana/config.toml" + var config cli.Config + if _, err := toml.DecodeFile(configFile, &config); err != nil { + return + } + for spacecore := range config.Spacecores { + logger.Info(spacecore) + } + + // 2. check spacecores.json + resp, err := requests.Get(url) + if err != nil { + return + } + // Status code + if resp.R.StatusCode != 200 { + logger.Info("Get Vistara-Labs/vimana/spacecores.json error: status code =", resp.R.StatusCode) + return + } + + var Spacecores []spacecore + resp.Json(&Spacecores) + + for _, spacecore := range Spacecores { + logger.Info(spacecore.Spacecore) + } + + }, + } + + registryCmd.AddCommand(searchCmd) + registryCmd.AddCommand(listCmd) + + return registryCmd } diff --git a/cmd/repo.go b/cmd/repo.go new file mode 100644 index 0000000..3594478 --- /dev/null +++ b/cmd/repo.go @@ -0,0 +1,203 @@ +package cmd + +import ( + "fmt" + "io" + "net/http" + "os" + "strings" + "vimana/cli" + "vimana/cmd/utils" + "vimana/log" + + "github.com/BurntSushi/toml" + + "github.com/spf13/cobra" +) + +func repoCommand() *cobra.Command { + + repoCmd := &cobra.Command{ + Use: "repo", + Short: "add repo to vimana", + } + + addCmd := &cobra.Command{ + Use: "add", + Short: "add x.y spacecore to vimana", + Run: func(cmd *cobra.Command, args []string) { + + logger := log.GetLogger(cmd.Context()) + if len(args) == 0 { + logger.Infof( + "Please provide the spacecore and node type to add. e.g. vimana repo add x.y-node-type", + ) + } else { + logger.Info(args) + param := strings.Split(args[0], ".") + spacecore := param[0] + param = strings.Split(param[1], "-") + node_type := param[0] + logger.Info(spacecore, node_type) + + // prompt user input repo url + prompter := utils.NewPrompter() + repo_url, err := prompter.InputString( + "Enter your github repo address:", + "", + "", + func(s string) error { + return nil + }, + ) + if err != nil { + return + } + logger.Info(repo_url) + + // prompt user input node binary + binary_file, err := prompter.InputString( + "Enter your binary file name:", + "", + "", + func(s string) error { + return nil + }, + ) + if err != nil { + return + } + logger.Info(binary_file) + + configFile := os.Getenv("HOME") + "/.vimana/config.toml" + var config cli.Config + if _, err := toml.DecodeFile(configFile, &config); err != nil { + return + } + for spacecore, nodeTypes := range config.Spacecores { + logger.Info(spacecore) + for nodeType := range nodeTypes { + logger.Info(nodeType, nodeTypes[nodeType]) + logger.Info(nodeTypes[nodeType].Binary) + logger.Info(nodeTypes[nodeType].Download) + } + } + if _, ok := config.Spacecores[spacecore]; !ok { + var m cli.Mode + m.Binary = "/usr/local/bin/" + spacecore + "/" + binary_file + m.Download = "/tmp/vimana/" + spacecore + "/init.sh" + m.Install = "/tmp/vimana/" + spacecore + "/install.sh" + m.Start = "/usr/local/bin/" + spacecore + "/start.sh" + + logger.Infof("m mode is %v\n\n", m) + res, err := http.Get(repo_url + "/init.sh") + if err != nil { + fmt.Errorf("file init.sh download error, check file address: %v", err) + return + } + os.MkdirAll("/tmp/vimana/"+spacecore, 0755) + f, err := os.Create(m.Download) + if err != nil { + logger.Info(f, err) + return + } + _, err = io.Copy(f, res.Body) + if err != nil { + fmt.Errorf("file save error: %v", err) + return + } + //download start.sh + res, err = http.Get(repo_url + "/start.sh") + if err != nil { + fmt.Errorf("file start.sh download error, check file address: %v", err) + return + } + os.MkdirAll("/usr/local/bin/"+spacecore, 0755) + f, err = os.Create(m.Start) + _, err = io.Copy(f, res.Body) + + //download stop.sh + res, err = http.Get(repo_url + "/install.sh") + if err != nil { + fmt.Errorf("file start.sh download error, check file address: %v", err) + return + } + os.MkdirAll("/tmp/vimana/"+spacecore, 0755) + f, err = os.Create(m.Install) + _, err = io.Copy(f, res.Body) + + //download Binary + //res, err = http.Get(repo_url + binary_file) + //if err != nil { + // fmt.Errorf("file start.sh download error, check file address: %v", err) + // return + //} + //os.MkdirAll("/usr/local/bin/" + spacecore + "/", 0755) + //f, err = os.Create("/usr/local/bin/" + spacecore + "/" + binary_file) + //_, err = io.Copy(f, res.Body) + + new_spacecore := make(map[string]cli.Mode, 1) + new_spacecore[node_type] = m + config.Spacecores[spacecore] = new_spacecore + + } + + for spacecore, nodeTypes := range config.Spacecores { + logger.Infof("spacecore %s\n\n", spacecore) + for nodeType := range nodeTypes { + logger.Info(nodeType, nodeTypes[nodeType]) + logger.Info(nodeTypes[nodeType].Binary) + logger.Info(nodeTypes[nodeType].Download) + } + } + + cli.WriteConf(config) + } + + }, + } + + repoCmd.AddCommand(addCmd) + + importCmd := &cobra.Command{ + Use: "import", + Short: "import repo from vimana", + Run: func(cmd *cobra.Command, args []string) { + logger := log.GetLogger(cmd.Context()) + logger.Info("import repo") + // prompt user input repo url + + prompter := utils.NewPrompter() + repo_url, err := prompter.InputString( + "Enter your github repo address:", + "", + "", + func(s string) error { + return nil + }, + ) + if err != nil { + return + } + logger.Info(repo_url) + + // Download the spaceocre from the source url + // Verify the downloaded content (checksum) + // Register the spacecore in the Vimana system + // Add the spacecore to the config.toml file + // Return a success message to the user and any errors + + // this is the approach used for vimana repo add cmd. + // res, err := http.Get(repo_url + "/init.sh") + // if err != nil { + // fmt.Errorf("file init.sh download error, check file address: %v", err) + // return + // } + // os.MkdirAll("/tmp/vimana/"+repo_url, 0755) + + }, + } + + repoCmd.AddCommand(importCmd) + return repoCmd +} diff --git a/cmd/root.go b/cmd/root.go index 612dbdc..94bc41c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -11,6 +11,8 @@ import ( "github.com/common-nighthawk/go-figure" "github.com/spf13/cobra" + + logger "vimana/log" ) var rootCmd = &cobra.Command{ @@ -41,6 +43,14 @@ func InitCLI() error { log.Fatal(err) } configFile := home + "/.vimana/config.toml" + + // configure logging + logger.Configure(&logger.Config{ + Verbosity: logger.LogVerbosityInfo, + Format: logger.LogFormatText, + Output: "stderr", + }) + rootCmd = &cobra.Command{Use: "vimana"} commands, err := GetCommandsFromConfig(configFile, CommanderRegistry) @@ -48,9 +58,16 @@ func InitCLI() error { fmt.Fprintln(os.Stderr, "Error:", err) return err } + rootCmd.AddCommand(commands...) rootCmd.AddCommand(initVimana()) rootCmd.AddCommand(versionCommand()) + rootCmd.AddCommand(migrateCommand()) + rootCmd.AddCommand(repoCommand()) + rootCmd.AddCommand(registryCommand()) + rootCmd.AddCommand(pluginCommand()) + + logger.AddFlagsToCommand(rootCmd, &logger.Config{}) return rootCmd.Execute() } @@ -82,19 +99,8 @@ func versionCommand() *cobra.Command { Use: "version", Short: "Print the version of vimana", Run: func(cmd *cobra.Command, args []string) { - fmt.Println("vimana version: ", Version) + fmt.Printf("vimana version: %s\n", Version) }, } return versionCmd } - -func printASCIIArt() { - art := ` -___ ________________ __________ _____ _________ -__ | / /____ _/___ |/ /___ |___ | / /___ | -__ | / / __ / __ /|_/ / __ /| |__ |/ / __ /| | -__ |/ / __/ / _ / / / _ ___ |_ /| / _ ___ | -_____/ /___/ /_/ /_/ /_/ |_|/_/ |_/ /_/ |_| -` - fmt.Println(art) -} diff --git a/cmd/utils/bash_commands.go b/cmd/utils/bash_commands.go index 1025468..56d5fe0 100644 --- a/cmd/utils/bash_commands.go +++ b/cmd/utils/bash_commands.go @@ -1,11 +1,14 @@ package utils import ( + "context" "fmt" "io" "net/http" "os" "os/exec" + "vimana/log" + // "github.com/moby/moby/daemon/logger" ) type CommandOption func(cmd *exec.Cmd) @@ -22,7 +25,8 @@ func WithErrorsToStderr() CommandOption { } } -func ExecBashCmd(cmd *exec.Cmd, options ...CommandOption) error { +func ExecBashCmd(cmd *exec.Cmd, node_info string, options ...CommandOption) error { + logger := log.GetLogger(context.Background()) for _, option := range options { option(cmd) } @@ -30,7 +34,41 @@ func ExecBashCmd(cmd *exec.Cmd, options ...CommandOption) error { if err != nil { return fmt.Errorf("command execution failed: %w", err) } - fmt.Println("Command execution completed", cmd) + logger.Info("Command execution completed", cmd) + return nil +} + +func ExecBinaryCmd(cmd *exec.Cmd, node_info string, options ...CommandOption) error { + logger := log.GetLogger(context.Background()) + for _, option := range options { + option(cmd) + } + + // open the out file for writing + outfile, err := os.Create("/tmp/" + node_info + ".log") + if err != nil { + panic(err) + } + defer outfile.Close() + cmd.Stdout = outfile + cmd.Stderr = outfile + + err = cmd.Start() + if err != nil { + return fmt.Errorf("command execution failed: %w", err) + } + + pid := cmd.Process.Pid + PIDFile := GetPIDFileName(node_info) + savePID(pid, PIDFile) + + // use goroutine waiting, manage process + // this is important, otherwise the process becomes in S mode + go func() { + err = cmd.Wait() + logger.Infof("Command finished with error: %v", err) + }() + logger.Info("Command execution completed", cmd) return nil } diff --git a/cmd/utils/error_handling.go b/cmd/utils/error_handling.go new file mode 100644 index 0000000..48e63a8 --- /dev/null +++ b/cmd/utils/error_handling.go @@ -0,0 +1,35 @@ +package utils + +import ( + "os" + "os/signal" + + "github.com/fatih/color" +) + +func PrettifyErrorIfExists(err error, printAdditionalInfo ...func()) { + if err != nil { + defer func() { + if r := recover(); r != nil { + os.Exit(1) + } + }() + color.New(color.FgRed, color.Bold).Printf("💈 %s\n", err.Error()) + + for _, printInfo := range printAdditionalInfo { + printInfo() + } + + panic(err) + } +} + +func RunOnInterrupt(funcToRun func()) { + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + <-c + funcToRun() + os.Exit(0) + }() +} diff --git a/cmd/utils/global_flags.go b/cmd/utils/global_flags.go new file mode 100644 index 0000000..36129c1 --- /dev/null +++ b/cmd/utils/global_flags.go @@ -0,0 +1,23 @@ +package utils + +import ( + "os" + "path/filepath" + + "github.com/spf13/cobra" +) + +func AddGlobalFlags(name string, command *cobra.Command) { + command.PersistentFlags().StringP( + name, "", GetVimanaConfig(), "The directory of the vimana config files") +} + +var FlagNames = struct { + Home string +}{ + Home: "home", +} + +func GetVimanaConfig() string { + return filepath.Join(os.Getenv("HOME"), ".vimana") +} diff --git a/cmd/utils/prompter.go b/cmd/utils/prompter.go new file mode 100644 index 0000000..72692c0 --- /dev/null +++ b/cmd/utils/prompter.go @@ -0,0 +1,76 @@ +package utils + +import "github.com/AlecAivazis/survey/v2" + +// Prompter is an interface for prompting the user for input. +type Prompter interface { + Select(prompt string, options []string) (string, error) + InputString(prompt, defValue, help string, validator func(string) error) (string, error) + Confirm(prompt string) (bool, error) + InputHiddenString(prompt, help string, validator func(string) error) (string, error) +} + +type prompter struct{} + +// NewPrompter returns a new Prompter instance. +func NewPrompter() Prompter { + return &prompter{} +} + +// Select prompts the user to select one of the options provided. +func (p *prompter) Select(prompt string, options []string) (string, error) { + selected := "" + s := &survey.Select{ + Message: prompt, + Options: options, + } + err := survey.AskOne(s, &selected) + return selected, err +} + +// InputString prompts the user to input a string. The default value is used if the user does not provide any input. +// The validator is used to validate the input. The help text is displayed to the user when they ask for help. +func (p *prompter) InputString(prompt, defValue, help string, validator func(string) error) (string, error) { + var result string + i := &survey.Input{ + Message: prompt, + Default: defValue, + Help: help, + } + err := survey.AskOne(i, &result, survey.WithValidator(func(ans interface{}) error { + if err := validator(ans.(string)); err != nil { + return err + } + return nil + })) + return result, err +} + +// Confirm prompts the user to confirm an action with a yes/no question. +func (p *prompter) Confirm(prompt string) (bool, error) { + result := false + c := &survey.Confirm{ + Message: prompt, + } + err := survey.AskOne(c, &result) + return result, err +} + +// InputHiddenString prompts the user to input a string. The input is hidden from the user. +// The validator is used to validate the input. The help text is displayed to the user when they ask for help. +// There is no default value. +func (p *prompter) InputHiddenString(prompt, help string, validator func(string) error) (string, error) { + var result string + i := &survey.Password{ + Message: prompt, + Help: help, + } + + err := survey.AskOne(i, &result, survey.WithValidator(func(ans interface{}) error { + if err := validator(ans.(string)); err != nil { + return err + } + return nil + })) + return result, err +} diff --git a/cmd/utils/tracking.go b/cmd/utils/tracking.go index 566ed12..29aa88e 100644 --- a/cmd/utils/tracking.go +++ b/cmd/utils/tracking.go @@ -9,7 +9,7 @@ import ( ) // record usage data if user agrees to contribute -const APIEndpoint = "https://api-api-dev.bk7bbm.oss-acorn.io/save-vimana-usage" +const APIEndpoint = "https://api.vimana.global/v1/usage/record" type APIVimanaUsage struct { EthAddress string `json:"eth_address,omitempty"` diff --git a/cmd/utils/util.go b/cmd/utils/util.go new file mode 100644 index 0000000..d9c2965 --- /dev/null +++ b/cmd/utils/util.go @@ -0,0 +1,104 @@ +package utils + +import ( + "archive/tar" + "compress/gzip" + "fmt" + "io" + "log" + "net/http" + "os" + "strconv" +) + +func DownloadTarGzFile(filepath string, url string) (err error) { + + // Create the file + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + // Get the data + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + // Check server response + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("bad status: %s", resp.Status) + } + + // Writer the body to file + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + return nil +} + +func ExtractTarGz(gzipStream io.Reader) error { + uncompressedStream, err := gzip.NewReader(gzipStream) + if err != nil { + return err + } + + tarReader := tar.NewReader(uncompressedStream) + var header *tar.Header + for header, err = tarReader.Next(); err == nil; header, err = tarReader.Next() { + switch header.Typeflag { + case tar.TypeDir: + if err := os.Mkdir(header.Name, 0755); err != nil { + return fmt.Errorf("ExtractTarGz: Mkdir() failed: %w", err) + } + case tar.TypeReg: + outFile, err := os.Create(header.Name) + if err != nil { + return fmt.Errorf("ExtractTarGz: Create() failed: %w", err) + } + + if _, err := io.Copy(outFile, tarReader); err != nil { + // outFile.Close error omitted as Copy error is more interesting at this point + outFile.Close() + return fmt.Errorf("ExtractTarGz: Copy() failed: %w", err) + } + if err := outFile.Close(); err != nil { + return fmt.Errorf("ExtractTarGz: Close() failed: %w", err) + } + default: + return fmt.Errorf("ExtractTarGz: uknown type: %b in %s", header.Typeflag, header.Name) + } + } + if err != io.EOF { + return fmt.Errorf("ExtractTarGz: Next() failed: %w", err) + } + return nil +} + +func GetPIDFileName(node_info string) string { + return "/tmp/" + node_info + ".pid" +} + +func savePID(pid int, PIDFile string) { + file, err := os.Create(PIDFile) + if err != nil { + log.Printf("Unable to create pid file : %v\n", err) + os.Exit(1) + } + + defer file.Close() + + _, err = file.WriteString(strconv.Itoa(pid)) + + if err != nil { + log.Printf("Unable to create pid file : %v\n", err) + os.Exit(1) + } + + file.Sync() // flush to disk + +} diff --git a/components/components.go b/components/components.go deleted file mode 100644 index f47a5a5..0000000 --- a/components/components.go +++ /dev/null @@ -1,43 +0,0 @@ -package components - -import ( - "os/exec" - "vimana/config" -) - -type Component interface { - InitializeConfig() error - GetStartCmd() *exec.Cmd -} - -type ComponentManager struct { - ComponentType config.ComponentType - Component -} - -type ComponentConfig struct { - RPC string - Network string -} - -func NewComponentManager(componentType config.ComponentType, root string, nodeType string, c *ComponentConfig) *ComponentManager { - var component Component - - switch componentType { - case config.Celestia: - component = NewCelestiaComponent(root, ".vimana/celestia", nodeType, c.RPC, c.Network) - case config.Avail: - component = NewAvailComponent(root, ".vimana/avail", nodeType) - case config.Gmworld: - component = NewGmworldComponent(root, ".vimana/gmd", nodeType) - // case config.Berachain: - // component = berachain.NewBerachainComponent(home) - default: - panic("Unknown component type") - } - - return &ComponentManager{ - ComponentType: componentType, - Component: component, - } -} diff --git a/components/gmworld.go b/components/gmworld.go deleted file mode 100644 index affe24e..0000000 --- a/components/gmworld.go +++ /dev/null @@ -1,29 +0,0 @@ -package components - -import ( - "os/exec" -) - -type GmworldComponent struct { - Root string - ConfigDir string -} - -func NewGmworldComponent(root string, home string, node string) *GmworldComponent { - return &GmworldComponent{ - Root: root, - ConfigDir: home, - } -} - -func (c *GmworldComponent) InitializeConfig() error { - return nil -} - -func (c *GmworldComponent) GetStartCmd() *exec.Cmd { - args := []string{} - // Gmworldup.sh handles this. TODO: Move the init and start command here. - return exec.Command( - c.Root, args..., - ) -} diff --git a/config.toml b/config.toml index 49cc33c..b78011a 100644 --- a/config.toml +++ b/config.toml @@ -1,34 +1,17 @@ -[components] +[spacecores] -[components.celestia] +[spacecores.celestia] -[components.celestia.light] -binary = "/tmp/vimana/celestia/celestia" +[spacecores.celestia.light] +binary = "/usr/local/bin/celestia" download = "/tmp/vimana/celestia/init.sh" -[components.celestia.bridge] -binary = "/tmp/vimana/celestia/celestia" +[spacecores.celestia.bridge] +binary = "/usr/local/bin/celestia" download = "/tmp/vimana/celestia/init.sh" -[components.avail] +[spacecores.avail] -[components.avail.light] +[spacecores.avail.light] binary = "avail-light" download = "/tmp/vimana/avail/init.sh" - - -[components.gmworld] - -[components.gmworld.da] -binary = "gmworld-da" -download = "/tmp/vimana/gmd/rollup_init.sh" - -[components.gmworld.rollup] -binary = "gmworld-rollup" -download = "/tmp/vimana/gmd/rollup_mocha.sh" - -# [components.berachain] - -# [components.berachain.full] -# binary = "/path/to/celestia/binary" -# download = "https://example.com/berachain.zip" diff --git a/config/config.go b/config/config.go index c3fed87..9396a69 100644 --- a/config/config.go +++ b/config/config.go @@ -1,10 +1,11 @@ package config -type ComponentType string +type SpacecoreType string const ( - Celestia ComponentType = "celestia" - Berachain ComponentType = "berachain" - Avail ComponentType = "avail" - Gmworld ComponentType = "gmworld" + Celestia SpacecoreType = "celestia" + Berachain SpacecoreType = "berachain" + Avail SpacecoreType = "avail" + Gmworld SpacecoreType = "gmworld" + Eigen SpacecoreType = "eigen" ) diff --git a/config/loader.go b/config/loader.go index ce27c76..207133e 100644 --- a/config/loader.go +++ b/config/loader.go @@ -2,18 +2,18 @@ package config import "github.com/BurntSushi/toml" -// type Component struct { +// type Spacecore struct { // Modes []string // Binary string // Download string // } type Config struct { - // Components map[string]Component - Components map[string]Component `toml:"components"` + // Spacecores map[string]Spacecore + Spacecores map[string]Spacecore `toml:"spacecores"` } -type Component map[string]Mode +type Spacecore map[string]Mode type Mode struct { Binary string `toml:"binary"` diff --git a/config/toml.go b/config/toml.go new file mode 100644 index 0000000..a36545f --- /dev/null +++ b/config/toml.go @@ -0,0 +1,4 @@ +package config + +// "github.com/pelletier/go-toml/v2" +// "github.com/pelletier/go-toml" diff --git a/go.mod b/go.mod index ddfbf12..b278d8d 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,17 @@ module vimana go 1.20 require ( + github.com/AlecAivazis/survey/v2 v2.3.7 github.com/BurntSushi/toml v1.3.2 + github.com/asmcos/requests v0.0.0-20210319030608-c839e8ae4946 github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be github.com/ethereum/go-ethereum v1.13.2 github.com/fatih/color v1.15.0 + github.com/hashicorp/go-plugin v1.6.1 github.com/shirou/gopsutil v3.21.11+incompatible github.com/spf13/cobra v1.7.0 + google.golang.org/grpc v1.64.0 + google.golang.org/protobuf v1.33.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) @@ -16,17 +21,29 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/hashicorp/go-hclog v0.14.1 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/holiman/uint256 v1.2.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.8.4 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect ) replace github.com/cosmos/cosmos-sdk => github.com/rollkit/cosmos-sdk v0.47.6-rollkit-v0.10.7-no-fraud-proofs-fixed diff --git a/go.sum b/go.sum index ff5377f..360fb02 100644 --- a/go.sum +++ b/go.sum @@ -1,56 +1,140 @@ +github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= +github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/asmcos/requests v0.0.0-20210319030608-c839e8ae4946 h1:1B8lZnGJOS3E7LumjuY6lb2NzXy8vBY6N2ag/IK6JdI= +github.com/asmcos/requests v0.0.0-20210319030608-c839e8ae4946/go.mod h1:2W5PB6UTVRBypeouEebhwOJrDZOfJvPwMP1mtD8ZXM4= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= +github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/ethereum/go-ethereum v1.13.2 h1:g9mCpfPWqCA1OL4e6C98PeVttb0HadfBRuKTGvMnOvw= github.com/ethereum/go-ethereum v1.13.2/go.mod h1:gkQ5Ygi64ZBh9M/4iXY1R8WqoNCx1Ey0CkYn2BD4/fw= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= +github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/install.sh b/install.sh index 4a45ffc..aa5c010 100755 --- a/install.sh +++ b/install.sh @@ -26,10 +26,11 @@ echo "🔨 Installing vimana..." sudo cp "/tmp/vimana_bins/vimana-${OS}-${ARCH}/vimana" "$INTERNAL_DIR/vimana" sudo chmod +x "$INTERNAL_DIR/vimana" sudo rm -rf "/tmp/vimana_bins" -curl -O https://vistara-labs.github.io/vimana/config.toml 2>/dev/null +# curl -O https://vistara-labs.github.io/vimana/config.toml 2>/dev/null mkdir -p ~/.vimana && cp config.toml ~/.vimana/config.toml -curl -O https://vistara-labs.github.io/vimana/scripts/init.sh 2>/dev/null -sudo mkdir -p /tmp/vimana/celestia && sudo mv init.sh /tmp/vimana/celestia/init.sh +# curl -O https://vistara-labs.github.io/vimana/scripts/init.sh 2>/dev/null +#sudo mkdir -p /tmp/vimana/celestia && sudo mv init.sh /tmp/vimana/celestia/init.sh +sudo mkdir -p /tmp/vimana/celestia && sudo cp scripts/init.sh /tmp/vimana/celestia/init.sh # Get availup script from repo curl -O https://vistara-labs.github.io/vimana/scripts/availup.sh 2>/dev/null @@ -41,11 +42,17 @@ sudo mkdir -p /tmp/vimana/gmd && sudo mv rollup_init.sh /tmp/vimana/gmd/rollup_i curl -O https://vistara-labs.github.io/vimana/scripts/rollup_mocha.sh 2>/dev/null sudo mkdir -p /tmp/vimana/gmd && sudo mv rollup_mocha.sh /tmp/vimana/gmd/rollup_mocha.sh + +# Get eigen script from repo +curl -O https://vistara-labs.github.io/vimana/scripts/eigen.sh 2>/dev/null +sudo mkdir -p /tmp/vimana/eigen && sudo mv eigen.sh /tmp/vimana/eigen/init.sh + curl https://api-api-dev.bk7bbm.oss-acorn.io/save-vimana-install > /dev/null 2>&1 sudo chmod +x /tmp/vimana/celestia/init.sh sudo chmod +x /tmp/vimana/avail/init.sh sudo chmod +x /tmp/vimana/gmd/rollup_init.sh sudo chmod +x /tmp/vimana/gmd/rollup_mocha.sh +sudo chmod +x /tmp/vimana/eigen/init.sh mkdir -p ~/.vimana/celestia/light-node chmod +x ~/.vimana/celestia/light-node echo "✅ vimana installed!" diff --git a/log/errors.go b/log/errors.go new file mode 100644 index 0000000..96690e0 --- /dev/null +++ b/log/errors.go @@ -0,0 +1,24 @@ +package log + +import ( + "errors" + "fmt" +) + +// ErrLogOutputRequired is used when no log output is specified. +var ErrLogOutputRequired = errors.New("you must specify a log output") + +type invalidLogFormatError struct { + format string +} + +func (e invalidLogFormatError) Error() string { + return fmt.Sprintf("logger format %s is invalid", e.format) +} + +// IsInvalidLogFormat tests an error to see if its a invalid log format error. +func IsInvalidLogFormat(err error) bool { + var e invalidLogFormatError + + return errors.Is(err, e) +} diff --git a/log/flags.go b/log/flags.go new file mode 100644 index 0000000..a464815 --- /dev/null +++ b/log/flags.go @@ -0,0 +1,20 @@ +package log + +import "github.com/spf13/cobra" + +// AddFlagsToCommand will add the logging flags to the supplied command and bind to the provided config. +func AddFlagsToCommand(cmd *cobra.Command, config *Config) { + cmd.PersistentFlags().IntVarP(&config.Verbosity, + "verbosity", + "v", + LogVerbosityInfo, + "The verbosity level of the logging. A level of 2 and above is debug logging. A level of 9 and above is tracing.") + cmd.PersistentFlags().StringVar(&config.Format, + "log-format", + LogFormatText, + "The format of the logging output. Can be 'text' or 'json'.") + cmd.PersistentFlags().StringVar(&config.Output, + "log-output", + "stderr", + "The output for logging. Supply a file path or one of the special values of 'stdout' and 'stderr'.") +} diff --git a/log/log.go b/log/log.go new file mode 100644 index 0000000..6f6b8bd --- /dev/null +++ b/log/log.go @@ -0,0 +1,117 @@ +package log + +import ( + "context" + "fmt" + "os" + "strings" + + "github.com/sirupsen/logrus" +) + +type loggerCtxKeyType string + +// LoggerKey is the key to use for the logger in the context +const LoggerKey loggerCtxKeyType = "vimanad.logger" + +const ( + // LogVerbosityInfo is the verbosity level for info logging. + LogVerbosityInfo = 0 + // LogVerbosityDebug is the verbosity level for debug logging. + LogVerbosityDebug = 2 + // LogVerbosityTrace is the verbosity level for trace logging. + LogVerbosityTrace = 9 +) + +const ( + // LogFormatText specifies a textual log format. + LogFormatText = "text" + // LogFormatJSON specifies a JSON log format. + LogFormatJSON = "json" +) + +// Config represents the configuration settings for a logger. +type Config struct { + // Verbosity specifies the logging verbosity level. + Verbosity int + // Format specifies the logging output format. + Format string + // Output specifies the destination for logging. You can specify the special + // values of 'stderr' or 'stdout' or a file path. + Output string +} + +// Configure will configure the logger from the supplied config. +func Configure(logConfig *Config) error { + configureVerbosity(logConfig) + + if err := configureFormatter(logConfig); err != nil { + return fmt.Errorf("configuring log formatter: %w", err) + } + + if err := configureOutput(logConfig); err != nil { + return fmt.Errorf("configuring log output: %w", err) + } + + return nil +} + +func configureFormatter(logConfig *Config) error { + switch logConfig.Format { + case LogFormatJSON: + logrus.SetFormatter(&logrus.JSONFormatter{}) + case LogFormatText: + logrus.SetFormatter(&logrus.TextFormatter{}) + default: + return invalidLogFormatError{format: logConfig.Format} + } + + return nil +} + +func configureVerbosity(logConfig *Config) { + logrus.SetLevel(logrus.InfoLevel) + + if logConfig.Verbosity >= LogVerbosityDebug && logConfig.Verbosity < LogVerbosityTrace { + logrus.SetLevel(logrus.DebugLevel) + } else if logConfig.Verbosity >= LogVerbosityTrace { + logrus.SetLevel(logrus.TraceLevel) + } +} + +func configureOutput(logConfig *Config) error { + output := strings.ToLower(logConfig.Output) + switch output { + case "stdout": + logrus.SetOutput(os.Stdout) + case "stderr": + logrus.SetOutput(os.Stderr) + case "": + return ErrLogOutputRequired + default: + file, err := os.OpenFile(output, os.O_CREATE|os.O_APPEND, os.ModePerm) + if err != nil { + return fmt.Errorf("opening log file %s: %w", output, err) + } + + logrus.SetOutput(file) + } + + return nil +} + +// WithLogger is used to attached a logger to a specific context. +func WithLogger(ctx context.Context, logger *logrus.Entry) context.Context { + return context.WithValue(ctx, LoggerKey, logger) +} + +// GetLogger will get a logger from the supplied context for create a new logger. +func GetLogger(ctx context.Context) *logrus.Entry { + logger := ctx.Value(LoggerKey) + + if logger == nil { + return logrus.NewEntry(logrus.StandardLogger()) + } + + return logger.(*logrus.Entry) +} diff --git a/plugins/buf.gen.yaml b/plugins/buf.gen.yaml new file mode 100644 index 0000000..033d015 --- /dev/null +++ b/plugins/buf.gen.yaml @@ -0,0 +1,14 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +version: v1 +plugins: + - plugin: buf.build/protocolbuffers/go + out: . + opt: + - paths=source_relative + - plugin: buf.build/grpc/go:v1.3.0 + out: . + opt: + - paths=source_relative + - require_unimplemented_servers=false diff --git a/plugins/plugin.go b/plugins/plugin.go new file mode 100644 index 0000000..a37075f --- /dev/null +++ b/plugins/plugin.go @@ -0,0 +1,102 @@ +package plugins + +import ( + "context" + "os/exec" + "vimana/log" + "vimana/plugins/proto" + + "github.com/hashicorp/go-plugin" + "google.golang.org/grpc" +) + +type SpacecorePlugin interface { + Start(ctx context.Context) error + Stop(ctx context.Context) error + Status(ctx context.Context) error + Logs(ctx context.Context) error +} + +// Here, we're binding our MySpacecore struct to the plugin interface + +// Plugin management +type SpacecorePluginIm struct { + // GRPCPlugin must still implement the Plugin interface + plugin.Plugin + // concrete implementation, written in Go. This is only used for plugins + plugin.GRPCPlugin + Impl proto.ScPluginServer +} + +// this is a client-side implementation of the SpacecorePlugin interface +type GRPCClient struct{ client proto.ScPluginClient } + +func (p *SpacecorePluginIm) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return &GRPCClient{client: proto.NewScPluginClient(c)}, nil +} + +func (p *GRPCClient) Start(ctx context.Context, req *proto.StartRequest) (*proto.StartResponse, error) { + logger := log.GetLogger(ctx) + msg, err := p.client.Start(ctx, req) + logger.Infof("the Start method on the plugin\n msg is %v\n %v\n", msg, err) + return msg, err +} + +func (p *GRPCClient) Stop(ctx context.Context, req *proto.StopRequest) (*proto.StopResponse, error) { + msg, err := p.client.Stop(ctx, req) + return msg, err +} + +func (p *GRPCClient) Status(ctx context.Context, req *proto.StatusRequest) (*proto.StatusResponse, error) { + return p.client.Status(ctx, req) +} + +func (p *GRPCClient) Logs(ctx context.Context, req *proto.LogsRequest) (*proto.LogsResponse, error) { + return p.client.Logs(ctx, req) +} + +// We're a host! Start by launching the plugin process. +func GetPluginClient(pluginPath string) *plugin.Client { + pluginMap := map[string]plugin.Plugin{ + "spacecore": &SpacecorePluginIm{}, + } + + clientConfig := &plugin.ClientConfig{ + HandshakeConfig: plugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "SPACECORE_PLUGIN", + MagicCookieValue: "v1", + }, + Plugins: pluginMap, + Cmd: exec.Command(pluginPath), + AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, + } + + client := plugin.NewClient(clientConfig) + // defer client.Kill() + + return client +} + +// func StartPlugin(client *plugin.Client) (*GRPCClient, error) { +// need a better name representing this function + +func SpacecoreGRPCClient(client *plugin.Client) (*GRPCClient, error) { + // Connect via RPC + rpcClient, err := client.Client() + if err != nil { + return &GRPCClient{}, err + } + + // Request the plugin + raw, err := rpcClient.Dispense("spacecore") + if err != nil { + return &GRPCClient{}, err + } + + // We should have a SpacecorePlugin now! This feels like a normal interface + // implementation but is in fact over an RPC connection. + spacecore := raw.(*GRPCClient) // SpacecorePlugin) + + return spacecore, nil +} diff --git a/plugins/proto/sc.pb.go b/plugins/proto/sc.pb.go new file mode 100644 index 0000000..a04916b --- /dev/null +++ b/plugins/proto/sc.pb.go @@ -0,0 +1,642 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc (unknown) +// source: proto/sc.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type StartRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` +} + +func (x *StartRequest) Reset() { + *x = StartRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_sc_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRequest) ProtoMessage() {} + +func (x *StartRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_sc_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartRequest.ProtoReflect.Descriptor instead. +func (*StartRequest) Descriptor() ([]byte, []int) { + return file_proto_sc_proto_rawDescGZIP(), []int{0} +} + +func (x *StartRequest) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +type StartResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *StartResponse) Reset() { + *x = StartResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_sc_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartResponse) ProtoMessage() {} + +func (x *StartResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_sc_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartResponse.ProtoReflect.Descriptor instead. +func (*StartResponse) Descriptor() ([]byte, []int) { + return file_proto_sc_proto_rawDescGZIP(), []int{1} +} + +func (x *StartResponse) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *StartResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +type StopRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` +} + +func (x *StopRequest) Reset() { + *x = StopRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_sc_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopRequest) ProtoMessage() {} + +func (x *StopRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_sc_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopRequest.ProtoReflect.Descriptor instead. +func (*StopRequest) Descriptor() ([]byte, []int) { + return file_proto_sc_proto_rawDescGZIP(), []int{2} +} + +func (x *StopRequest) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +type StopResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *StopResponse) Reset() { + *x = StopResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_sc_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopResponse) ProtoMessage() {} + +func (x *StopResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_sc_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopResponse.ProtoReflect.Descriptor instead. +func (*StopResponse) Descriptor() ([]byte, []int) { + return file_proto_sc_proto_rawDescGZIP(), []int{3} +} + +func (x *StopResponse) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *StopResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +type LogsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` +} + +func (x *LogsRequest) Reset() { + *x = LogsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_sc_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LogsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogsRequest) ProtoMessage() {} + +func (x *LogsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_sc_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogsRequest.ProtoReflect.Descriptor instead. +func (*LogsRequest) Descriptor() ([]byte, []int) { + return file_proto_sc_proto_rawDescGZIP(), []int{4} +} + +func (x *LogsRequest) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +type LogsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + Logs []string `protobuf:"bytes,2,rep,name=logs,proto3" json:"logs,omitempty"` +} + +func (x *LogsResponse) Reset() { + *x = LogsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_sc_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LogsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogsResponse) ProtoMessage() {} + +func (x *LogsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_sc_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogsResponse.ProtoReflect.Descriptor instead. +func (*LogsResponse) Descriptor() ([]byte, []int) { + return file_proto_sc_proto_rawDescGZIP(), []int{5} +} + +func (x *LogsResponse) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *LogsResponse) GetLogs() []string { + if x != nil { + return x.Logs + } + return nil +} + +type StatusRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` +} + +func (x *StatusRequest) Reset() { + *x = StatusRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_sc_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusRequest) ProtoMessage() {} + +func (x *StatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_sc_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead. +func (*StatusRequest) Descriptor() ([]byte, []int) { + return file_proto_sc_proto_rawDescGZIP(), []int{6} +} + +func (x *StatusRequest) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +type StatusResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *StatusResponse) Reset() { + *x = StatusResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_sc_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusResponse) ProtoMessage() {} + +func (x *StatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_sc_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusResponse.ProtoReflect.Descriptor instead. +func (*StatusResponse) Descriptor() ([]byte, []int) { + return file_proto_sc_proto_rawDescGZIP(), []int{7} +} + +func (x *StatusResponse) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *StatusResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +var File_proto_sc_proto protoreflect.FileDescriptor + +var file_proto_sc_proto_rawDesc = []byte{ + 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2b, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x49, 0x64, 0x22, 0x44, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2a, 0x0a, 0x0b, 0x53, 0x74, + 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x49, 0x64, 0x22, 0x43, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2a, 0x0a, 0x0b, 0x4c, + 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x64, 0x22, 0x3f, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0x2c, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x49, 0x64, 0x22, 0x45, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x32, 0xdf, 0x01, + 0x0a, 0x08, 0x53, 0x63, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x05, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x31, 0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x12, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_proto_sc_proto_rawDescOnce sync.Once + file_proto_sc_proto_rawDescData = file_proto_sc_proto_rawDesc +) + +func file_proto_sc_proto_rawDescGZIP() []byte { + file_proto_sc_proto_rawDescOnce.Do(func() { + file_proto_sc_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_sc_proto_rawDescData) + }) + return file_proto_sc_proto_rawDescData +} + +var file_proto_sc_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_proto_sc_proto_goTypes = []any{ + (*StartRequest)(nil), // 0: proto.StartRequest + (*StartResponse)(nil), // 1: proto.StartResponse + (*StopRequest)(nil), // 2: proto.StopRequest + (*StopResponse)(nil), // 3: proto.StopResponse + (*LogsRequest)(nil), // 4: proto.LogsRequest + (*LogsResponse)(nil), // 5: proto.LogsResponse + (*StatusRequest)(nil), // 6: proto.StatusRequest + (*StatusResponse)(nil), // 7: proto.StatusResponse +} +var file_proto_sc_proto_depIdxs = []int32{ + 0, // 0: proto.ScPlugin.Start:input_type -> proto.StartRequest + 2, // 1: proto.ScPlugin.Stop:input_type -> proto.StopRequest + 4, // 2: proto.ScPlugin.Logs:input_type -> proto.LogsRequest + 6, // 3: proto.ScPlugin.Status:input_type -> proto.StatusRequest + 1, // 4: proto.ScPlugin.Start:output_type -> proto.StartResponse + 3, // 5: proto.ScPlugin.Stop:output_type -> proto.StopResponse + 5, // 6: proto.ScPlugin.Logs:output_type -> proto.LogsResponse + 7, // 7: proto.ScPlugin.Status:output_type -> proto.StatusResponse + 4, // [4:8] is the sub-list for method output_type + 0, // [0:4] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_proto_sc_proto_init() } +func file_proto_sc_proto_init() { + if File_proto_sc_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_sc_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*StartRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_sc_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*StartResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_sc_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*StopRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_sc_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*StopResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_sc_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*LogsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_sc_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*LogsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_sc_proto_msgTypes[6].Exporter = func(v any, i int) any { + switch v := v.(*StatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_sc_proto_msgTypes[7].Exporter = func(v any, i int) any { + switch v := v.(*StatusResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_sc_proto_rawDesc, + NumEnums: 0, + NumMessages: 8, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_sc_proto_goTypes, + DependencyIndexes: file_proto_sc_proto_depIdxs, + MessageInfos: file_proto_sc_proto_msgTypes, + }.Build() + File_proto_sc_proto = out.File + file_proto_sc_proto_rawDesc = nil + file_proto_sc_proto_goTypes = nil + file_proto_sc_proto_depIdxs = nil +} diff --git a/plugins/proto/sc.proto b/plugins/proto/sc.proto new file mode 100644 index 0000000..020f721 --- /dev/null +++ b/plugins/proto/sc.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +package proto; + +option go_package = "./proto"; + +// I want to have start, stop, logs, and status for running plugins + +service ScPlugin { + // I want to have start, stop, logs, and status for running plugins + rpc Start(StartRequest) returns (StartResponse) {} + rpc Stop(StopRequest) returns (StopResponse) {} + rpc Logs(LogsRequest) returns (LogsResponse) {} + rpc Status(StatusRequest) returns (StatusResponse) {} +} + +message StartRequest { + string plugin_id = 1; +} + +message StartResponse { + string plugin_id = 1; + string status = 2; +} + +message StopRequest { + string plugin_id = 1; +} + +message StopResponse { + string plugin_id = 1; + string status = 2; +} + +message LogsRequest { + string plugin_id = 1; +} + +message LogsResponse { + string plugin_id = 1; + repeated string logs = 2; +} + +message StatusRequest { + string plugin_id = 1; +} + +message StatusResponse { + string plugin_id = 1; + string status = 2; +} \ No newline at end of file diff --git a/plugins/proto/sc_grpc.pb.go b/plugins/proto/sc_grpc.pb.go new file mode 100644 index 0000000..3282fb0 --- /dev/null +++ b/plugins/proto/sc_grpc.pb.go @@ -0,0 +1,220 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: proto/sc.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + ScPlugin_Start_FullMethodName = "/proto.ScPlugin/Start" + ScPlugin_Stop_FullMethodName = "/proto.ScPlugin/Stop" + ScPlugin_Logs_FullMethodName = "/proto.ScPlugin/Logs" + ScPlugin_Status_FullMethodName = "/proto.ScPlugin/Status" +) + +// ScPluginClient is the client API for ScPlugin service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ScPluginClient interface { + // I want to have start, stop, logs, and status for running plugins + Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) + Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopResponse, error) + Logs(ctx context.Context, in *LogsRequest, opts ...grpc.CallOption) (*LogsResponse, error) + Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) +} + +type scPluginClient struct { + cc grpc.ClientConnInterface +} + +func NewScPluginClient(cc grpc.ClientConnInterface) ScPluginClient { + return &scPluginClient{cc} +} + +func (c *scPluginClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) { + out := new(StartResponse) + err := c.cc.Invoke(ctx, ScPlugin_Start_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *scPluginClient) Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopResponse, error) { + out := new(StopResponse) + err := c.cc.Invoke(ctx, ScPlugin_Stop_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *scPluginClient) Logs(ctx context.Context, in *LogsRequest, opts ...grpc.CallOption) (*LogsResponse, error) { + out := new(LogsResponse) + err := c.cc.Invoke(ctx, ScPlugin_Logs_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *scPluginClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) { + out := new(StatusResponse) + err := c.cc.Invoke(ctx, ScPlugin_Status_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ScPluginServer is the server API for ScPlugin service. +// All implementations should embed UnimplementedScPluginServer +// for forward compatibility +type ScPluginServer interface { + // I want to have start, stop, logs, and status for running plugins + Start(context.Context, *StartRequest) (*StartResponse, error) + Stop(context.Context, *StopRequest) (*StopResponse, error) + Logs(context.Context, *LogsRequest) (*LogsResponse, error) + Status(context.Context, *StatusRequest) (*StatusResponse, error) +} + +// UnimplementedScPluginServer should be embedded to have forward compatible implementations. +type UnimplementedScPluginServer struct { +} + +func (UnimplementedScPluginServer) Start(context.Context, *StartRequest) (*StartResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Start not implemented") +} +func (UnimplementedScPluginServer) Stop(context.Context, *StopRequest) (*StopResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") +} +func (UnimplementedScPluginServer) Logs(context.Context, *LogsRequest) (*LogsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Logs not implemented") +} +func (UnimplementedScPluginServer) Status(context.Context, *StatusRequest) (*StatusResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Status not implemented") +} + +// UnsafeScPluginServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ScPluginServer will +// result in compilation errors. +type UnsafeScPluginServer interface { + mustEmbedUnimplementedScPluginServer() +} + +func RegisterScPluginServer(s grpc.ServiceRegistrar, srv ScPluginServer) { + s.RegisterService(&ScPlugin_ServiceDesc, srv) +} + +func _ScPlugin_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ScPluginServer).Start(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ScPlugin_Start_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ScPluginServer).Start(ctx, req.(*StartRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ScPlugin_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StopRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ScPluginServer).Stop(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ScPlugin_Stop_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ScPluginServer).Stop(ctx, req.(*StopRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ScPlugin_Logs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LogsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ScPluginServer).Logs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ScPlugin_Logs_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ScPluginServer).Logs(ctx, req.(*LogsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ScPlugin_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ScPluginServer).Status(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ScPlugin_Status_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ScPluginServer).Status(ctx, req.(*StatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// ScPlugin_ServiceDesc is the grpc.ServiceDesc for ScPlugin service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ScPlugin_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "proto.ScPlugin", + HandlerType: (*ScPluginServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Start", + Handler: _ScPlugin_Start_Handler, + }, + { + MethodName: "Stop", + Handler: _ScPlugin_Stop_Handler, + }, + { + MethodName: "Logs", + Handler: _ScPlugin_Logs_Handler, + }, + { + MethodName: "Status", + Handler: _ScPlugin_Status_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/sc.proto", +} diff --git a/scripts/eigen.sh b/scripts/eigen.sh new file mode 100644 index 0000000..1d816b8 --- /dev/null +++ b/scripts/eigen.sh @@ -0,0 +1,460 @@ +#!/bin/sh +# Scripts inspired from https://github.com/ava-labs/avalanche-cli +# This script has not been tested yet since this is a private repo +# TODO(madhur): Test this script once this is a public repo + +set -e + +usage() { + this=$1 + cat </dev/null +} +echoerr() { + echo "$@" 1>&2 +} +log_prefix() { + echo "$0" +} +_logp=6 +log_set_priority() { + _logp="$1" +} +log_priority() { + if test -z "$1"; then + echo "$_logp" + return + fi + [ "$1" -le "$_logp" ] +} +log_tag() { + case $1 in + 0) echo "emerg" ;; + 1) echo "alert" ;; + 2) echo "crit" ;; + 3) echo "err" ;; + 4) echo "warning" ;; + 5) echo "notice" ;; + 6) echo "info" ;; + 7) echo "debug" ;; + *) echo "$1" ;; + esac +} +log_debug() { + log_priority 7 || return 0 + echoerr "$(log_prefix)" "$(log_tag 7)" "$@" +} +log_info() { + log_priority 6 || return 0 + echoerr "$(log_prefix)" "$(log_tag 6)" "$@" +} +log_err() { + log_priority 3 || return 0 + echoerr "$(log_prefix)" "$(log_tag 3)" "$@" +} +log_crit() { + log_priority 2 || return 0 + echoerr "$(log_prefix)" "$(log_tag 2)" "$@" +} +uname_os() { + os=$(uname -s | tr '[:upper:]' '[:lower:]') + case "$os" in + msys*) os="windows" ;; + mingw*) os="windows" ;; + cygwin*) os="windows" ;; + win*) os="windows" ;; + esac + echo "$os" +} +uname_arch() { + arch=$(uname -m) + case $arch in + x86_64) arch="amd64" ;; + x86) arch="386" ;; + i686) arch="386" ;; + i386) arch="386" ;; + aarch64) arch="arm64" ;; + armv5*) arch="armv5" ;; + armv6*) arch="armv6" ;; + armv7*) arch="armv7" ;; + esac + echo ${arch} +} +uname_os_check() { + os=$(uname_os) + case "$os" in + darwin) return 0 ;; + dragonfly) return 0 ;; + freebsd) return 0 ;; + linux) return 0 ;; + android) return 0 ;; + nacl) return 0 ;; + netbsd) return 0 ;; + openbsd) return 0 ;; + plan9) return 0 ;; + solaris) return 0 ;; + windows) return 0 ;; + esac + log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib" + return 1 +} +uname_arch_check() { + arch=$(uname_arch) + case "$arch" in + 386) return 0 ;; + amd64) return 0 ;; + arm64) return 0 ;; + armv5) return 0 ;; + armv6) return 0 ;; + armv7) return 0 ;; + ppc64) return 0 ;; + ppc64le) return 0 ;; + mips) return 0 ;; + mipsle) return 0 ;; + mips64) return 0 ;; + mips64le) return 0 ;; + s390x) return 0 ;; + amd64p32) return 0 ;; + esac + log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib" + return 1 +} +untar() { + tarball=$1 + case "${tarball}" in + *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;; + *.tar) tar --no-same-owner -xf "${tarball}" ;; + *.zip) unzip "${tarball}" ;; + *) + log_err "untar unknown archive format for ${tarball}" + return 1 + ;; + esac +} +http_download_curl() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") + else + code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") + fi + if [ "$code" != "200" ]; then + log_debug "http_download_curl received HTTP status $code" + return 1 + fi + return 0 +} +http_download_wget() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + wget -q -O "$local_file" "$source_url" + else + wget -q --header "$header" -O "$local_file" "$source_url" + fi +} +http_download() { + log_debug "http_download $2" + if is_command curl; then + http_download_curl "$@" + return + elif is_command wget; then + http_download_wget "$@" + return + fi + log_crit "http_download unable to find wget or curl" + return 1 +} +http_copy() { + tmp=$(mktemp) + http_download "${tmp}" "$1" "$2" || return 1 + body=$(cat "$tmp") + rm -f "${tmp}" + echo "$body" +} +github_release() { + owner_repo=$1 + version=$2 + test -z "$version" && version="latest" + giturl="https://github.com/${owner_repo}/releases/${version}" + json=$(http_copy "$giturl" "Accept:application/json") + test -z "$json" && return 1 + version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//') + test -z "$version" && return 1 + echo "$version" +} +hash_sha256() { + TARGET=${1:-/dev/stdin} + if is_command gsha256sum; then + hash=$(gsha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command sha256sum; then + hash=$(sha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command shasum; then + hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command openssl; then + hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f a + else + log_crit "hash_sha256 unable to find command to compute sha-256 hash" + return 1 + fi +} +hash_sha256_verify() { + TARGET=$1 + checksums=$2 + if [ -z "$checksums" ]; then + log_err "hash_sha256_verify checksum file not specified in arg2" + return 1 + fi + BASENAME=${TARGET##*/} + want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) + if [ -z "$want" ]; then + log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" + return 1 + fi + got=$(hash_sha256 "$TARGET") + if [ "$want" != "$got" ]; then + log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" + return 1 + fi +} +cat /dev/null < /dev/null 2>&1 && HAS_BASH=true + if [ $HAS_BASH = true ] + then + BASH_COMPLETION_MAIN=~/.bash_completion + BASH_COMPLETION_SCRIPTS_DIR=~/.local/share/bash-completion/completions + BASH_COMPLETION_SCRIPT_PATH=$BASH_COMPLETION_SCRIPTS_DIR/eigenlayer.sh + mkdir -p $BASH_COMPLETION_SCRIPTS_DIR + COBRA_COMPLETION_SUCCEDED=false + $BINDIR/$BINARY completion bash > $BASH_COMPLETION_SCRIPT_PATH 2> /dev/null && COBRA_COMPLETION_SUCCEDED=true + if [ $COBRA_COMPLETION_SUCCEDED = true ] + then + touch $BASH_COMPLETION_MAIN + sed_in_place "/.*# eigenlayer completion/d" $BASH_COMPLETION_MAIN + echo "source $BASH_COMPLETION_SCRIPT_PATH # eigenlayer completion" >> $BASH_COMPLETION_MAIN + if [ "$(uname)" = Darwin ] + then + HAS_BREW=false + which brew >/dev/null 2>&1 && HAS_BREW=true + if [ $HAS_BREW = true ] + then + HAS_BASH_COMPLETIONS=false + brew list bash-completion >/dev/null 2>&1 && HAS_BASH_COMPLETIONS=true + if [ $HAS_BASH_COMPLETIONS = true ] + then + BASHRC=~/.bashrc + touch $BASHRC + sed_in_place "/.*# eigenlayer completion/d" $BASHRC + echo "source $(brew --prefix)/etc/bash_completion # eigenlayer completion" >> $BASHRC + else + echo "warning: brew bash-completion package not found. eigenlayer command completion for bash not installed" + fi + else + echo "warning: brew not found. eigenlayer command completion for bash not installed" + fi + fi + else + echo "warning: auto completion generation command failed. eigenlayer command completion for bash not installed" + fi + fi + + HAS_ZSH=false + which zsh > /dev/null 2>&1 && HAS_ZSH=true + if [ $HAS_ZSH = true ] + then + ZSH_COMPLETION_MAIN=~/.zshrc + ZSH_COMPLETION_SCRIPTS_DIR=~/.local/share/zsh-completion/completions + ZSH_COMPLETION_SCRIPT_PATH=$ZSH_COMPLETION_SCRIPTS_DIR/_eigenlayer + mkdir -p $ZSH_COMPLETION_SCRIPTS_DIR + COBRA_COMPLETION_SUCCEDED=false + $BINDIR/$BINARY completion zsh > $BASH_COMPLETION_SCRIPT_PATH 2> /dev/null && COBRA_COMPLETION_SUCCEDED=true + if [ $COBRA_COMPLETION_SUCCEDED = true ] + then + touch $ZSH_COMPLETION_MAIN + sed_in_place "/.*# eigenlayer completion/d" $ZSH_COMPLETION_MAIN + echo "fpath=($ZSH_COMPLETION_SCRIPTS_DIR \$fpath) # eigenlayer completion" >> $ZSH_COMPLETION_MAIN + echo "rm -f ~/.zcompdump; compinit # eigenlayer completion" >> $ZSH_COMPLETION_MAIN + else + echo "warning: auto completion generation command failed. eigenlayer command completion for zsh not installed" + fi + fi +} + +if [ "$RUN_COMPLETIONS" = true ]; then + completions +fi \ No newline at end of file diff --git a/scripts/init.sh b/scripts/init.sh index 7acacff..c4ecb4a 100755 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -INTERNAL_DIR="/tmp/vimana/celestia" +INTERNAL_DIR="/usr/local/bin/celestia" # check if the binary is already installed if [ -f "$INTERNAL_DIR/celestia" ]; then diff --git a/scripts/rollup_init.sh b/scripts/rollup_init.sh old mode 100755 new mode 100644 diff --git a/scripts/rollup_start.sh b/scripts/rollup_start.sh old mode 100755 new mode 100644 diff --git a/spacecores.json b/spacecores.json new file mode 100644 index 0000000..e832534 --- /dev/null +++ b/spacecores.json @@ -0,0 +1,3 @@ +[ + {"spacecore":"opbnb","repo":"https://github.com/bnb-chain/opbnb"} +] \ No newline at end of file diff --git a/components/avail.go b/spacecores/avail.go similarity index 57% rename from components/avail.go rename to spacecores/avail.go index 03c5191..4d7c273 100644 --- a/components/avail.go +++ b/spacecores/avail.go @@ -1,28 +1,28 @@ -package components +package spacecores import ( "os/exec" ) -type AvailComponent struct { +type AvailSpacecore struct { Root string ConfigDir string } -func NewAvailComponent(root string, home string, node string) *AvailComponent { - return &AvailComponent{ +func NewAvailSpacecore(root string, home string, node string) *AvailSpacecore { + return &AvailSpacecore{ Root: root, ConfigDir: home, } } -func (c *AvailComponent) InitializeConfig() error { +func (c *AvailSpacecore) InitializeConfig() error { // lightNodePath := filepath.Join(os.Getenv("HOME"), c.ConfigDir+"/"+c.NodeType+"-node") // mkdir -p ~/.vimana/celestia/light-node return nil } -func (c *AvailComponent) GetStartCmd() *exec.Cmd { +func (c *AvailSpacecore) GetStartCmd() *exec.Cmd { args := []string{} // availup.sh handles this. return exec.Command( diff --git a/components/celestia.go b/spacecores/celestia.go similarity index 86% rename from components/celestia.go rename to spacecores/celestia.go index 4d04a1b..537c105 100644 --- a/components/celestia.go +++ b/spacecores/celestia.go @@ -1,4 +1,4 @@ -package components +package spacecores import ( "log" @@ -7,7 +7,7 @@ import ( "path/filepath" ) -type CelestiaComponent struct { +type CelestiaSpacecore struct { Root string ConfigDir string rpcEndpoint string @@ -18,8 +18,8 @@ type CelestiaComponent struct { celestiaNetwork string } -func NewCelestiaComponent(root string, home string, node string, celestiaRPC, celestiaNetwork string) *CelestiaComponent { - return &CelestiaComponent{ +func NewCelestiaSpacecore(root string, home string, node string, celestiaRPC, celestiaNetwork string) *CelestiaSpacecore { + return &CelestiaSpacecore{ Root: root, ConfigDir: home, NodeType: node, @@ -29,7 +29,7 @@ func NewCelestiaComponent(root string, home string, node string, celestiaRPC, ce } } -func (c *CelestiaComponent) InitializeConfig() error { +func (c *CelestiaSpacecore) InitializeConfig() error { log.Println("🚀 Creating Celestia ", c.NodeType, " node config dir: ", c.NodeStorePath) if _, err := os.Stat(c.NodeStorePath); os.IsNotExist(err) { err := os.MkdirAll(c.NodeStorePath, 0755) @@ -63,7 +63,7 @@ func (c *CelestiaComponent) InitializeConfig() error { return nil } -func (c *CelestiaComponent) GetStartCmd() *exec.Cmd { +func (c *CelestiaSpacecore) GetStartCmd() *exec.Cmd { args := []string{ c.NodeType, "start", "--core.ip", c.rpcEndpoint, diff --git a/spacecores/components.go b/spacecores/components.go new file mode 100644 index 0000000..45f181c --- /dev/null +++ b/spacecores/components.go @@ -0,0 +1,46 @@ +package spacecores + +import ( + "os/exec" + "vimana/config" +) + +type Spacecore interface { + InitializeConfig() error + GetStartCmd() *exec.Cmd +} + +type SpacecoreManager struct { + SpacecoreType config.SpacecoreType + Spacecore +} + +type SpacecoreConfig struct { + RPC string + Network string +} + +func NewSpacecoreManager(spacecoreType config.SpacecoreType, root string, nodeType string, c *SpacecoreConfig) *SpacecoreManager { + var spacecore Spacecore + + switch spacecoreType { + case config.Celestia: + spacecore = NewCelestiaSpacecore(root, ".vimana/celestia", nodeType, c.RPC, c.Network) + case config.Avail: + spacecore = NewAvailSpacecore(root, ".vimana/avail", nodeType) + case config.Gmworld: + spacecore = NewGmworldSpacecore(root, ".vimana/gmd", nodeType) + // case config.Berachain: + // spacecore = berachain.NewBerachainSpacecore(home) + case config.Eigen: + spacecore = NewEigenSpacecore(root, ".vimana/eigen", nodeType) + default: + //panic("Unknown spacecore type") + spacecore = NewUniversalSpacecore(root, ".vimana/"+string(spacecoreType), nodeType) + } + + return &SpacecoreManager{ + SpacecoreType: spacecoreType, + Spacecore: spacecore, + } +} diff --git a/spacecores/eigen.go b/spacecores/eigen.go new file mode 100644 index 0000000..cd5b219 --- /dev/null +++ b/spacecores/eigen.go @@ -0,0 +1,31 @@ +package spacecores + +import ( + "os/exec" +) + +type EigenSpacecore struct { + Root string + ConfigDir string +} + +func NewEigenSpacecore(root string, home string, node string) *EigenSpacecore { + return &EigenSpacecore{ + Root: root, + ConfigDir: home, + } +} + +func (c *EigenSpacecore) InitializeConfig() error { + // lightNodePath := filepath.Join(os.Getenv("HOME"), c.ConfigDir+"/"+c.NodeType+"-node") + // mkdir -p ~/.vimana/celestia/light-node + return nil +} + +func (c *EigenSpacecore) GetStartCmd() *exec.Cmd { + args := []string{} + // eigenup.sh handles this. + return exec.Command( + c.Root, args..., + ) +} diff --git a/spacecores/gmworld.go b/spacecores/gmworld.go new file mode 100644 index 0000000..bdcbb5b --- /dev/null +++ b/spacecores/gmworld.go @@ -0,0 +1,29 @@ +package spacecores + +import ( + "os/exec" +) + +type GmworldSpacecore struct { + Root string + ConfigDir string +} + +func NewGmworldSpacecore(root string, home string, node string) *GmworldSpacecore { + return &GmworldSpacecore{ + Root: root, + ConfigDir: home, + } +} + +func (c *GmworldSpacecore) InitializeConfig() error { + return nil +} + +func (c *GmworldSpacecore) GetStartCmd() *exec.Cmd { + args := []string{} + // Gmworldup.sh handles this. TODO: Move the init and start command here. + return exec.Command( + c.Root, args..., + ) +} diff --git a/spacecores/universal.go b/spacecores/universal.go new file mode 100644 index 0000000..524e4b5 --- /dev/null +++ b/spacecores/universal.go @@ -0,0 +1,31 @@ +package spacecores + +import ( + "os/exec" +) + +type UniversalSpacecore struct { + Root string + ConfigDir string +} + +func NewUniversalSpacecore(root string, home string, node string) *UniversalSpacecore { + return &UniversalSpacecore{ + Root: root, + ConfigDir: home, + } +} + +func (c *UniversalSpacecore) InitializeConfig() error { + // lightNodePath := filepath.Join(os.Getenv("HOME"), c.ConfigDir+"/"+c.NodeType+"-node") + // mkdir -p ~/.vimana/celestia/light-node + return nil +} + +func (c *UniversalSpacecore) GetStartCmd() *exec.Cmd { + args := []string{} + // Univeralup.sh handles this. + return exec.Command( + c.Root, args..., + ) +}