diff --git a/cli/command/cmd.go b/cli/command/cmd.go index 91faca5bd..57363a6a5 100644 --- a/cli/command/cmd.go +++ b/cli/command/cmd.go @@ -32,6 +32,7 @@ import ( "github.com/opencurve/curveadm/cli/command/cluster" "github.com/opencurve/curveadm/cli/command/config" "github.com/opencurve/curveadm/cli/command/hosts" + "github.com/opencurve/curveadm/cli/command/install" "github.com/opencurve/curveadm/cli/command/monitor" "github.com/opencurve/curveadm/cli/command/pfs" "github.com/opencurve/curveadm/cli/command/playground" @@ -66,6 +67,7 @@ func addSubCommands(cmd *cobra.Command, curveadm *cli.CurveAdm) { target.NewTargetCommand(curveadm), // curveadm target ... pfs.NewPFSCommand(curveadm), // curveadm pfs ... monitor.NewMonitorCommand(curveadm), // curveadm monitor ... + install.NewInstallCommand(curveadm), // curveadm install ... NewAuditCommand(curveadm), // curveadm audit NewCleanCommand(curveadm), // curveadm clean diff --git a/cli/command/install/cmd.go b/cli/command/install/cmd.go new file mode 100644 index 000000000..342e4f16b --- /dev/null +++ b/cli/command/install/cmd.go @@ -0,0 +1,21 @@ +package install + +import ( + "github.com/opencurve/curveadm/cli/cli" + cliutil "github.com/opencurve/curveadm/internal/utils" + "github.com/spf13/cobra" +) + +func NewInstallCommand(curveadm *cli.CurveAdm) *cobra.Command { + cmd := &cobra.Command{ + Use: "install", + Short: "Manage install", + Args: cliutil.NoArgs, + RunE: cliutil.ShowHelp(curveadm.Err()), + } + + cmd.AddCommand( + NewInstallToolCommand(curveadm), + ) + return cmd +} diff --git a/cli/command/install/tool.go b/cli/command/install/tool.go new file mode 100644 index 000000000..07b77d5d7 --- /dev/null +++ b/cli/command/install/tool.go @@ -0,0 +1,89 @@ +package install + +import ( + "github.com/fatih/color" + "github.com/opencurve/curveadm/cli/cli" + comm "github.com/opencurve/curveadm/internal/common" + "github.com/opencurve/curveadm/internal/configure/topology" + "github.com/opencurve/curveadm/internal/errno" + "github.com/opencurve/curveadm/internal/playbook" + cliutil "github.com/opencurve/curveadm/internal/utils" + "github.com/spf13/cobra" +) + +var ( + INSTALL_TOOL_PLAYBOOK_STEPS = []int{ + playbook.INSTALL_TOOL, + } +) + +type installOptions struct { + host string + path string + confPath string +} + +func NewInstallToolCommand(curveadm *cli.CurveAdm) *cobra.Command { + var options installOptions + + cmd := &cobra.Command{ + Use: "tool [OPTIONS]", + Short: "Install tool v2 on the specified host", + Args: cliutil.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + return runInstallTool(curveadm, options) + }, + DisableFlagsInUseLine: true, + } + + flags := cmd.Flags() + flags.StringVar(&options.host, "host", "localhost", "Specify target host") + flags.StringVar(&options.path, "path", "/usr/local/bin/curve", "Specify target install path of tool v2") + flags.StringVar(&options.confPath, "confPath", "/etc/curve/curve.yaml", "Specify target config path of tool v2") + + return cmd +} + +func genInstallToolPlaybook(curveadm *cli.CurveAdm, + dcs []*topology.DeployConfig, + options installOptions, +) (*playbook.Playbook, error) { + configs := curveadm.FilterDeployConfig(dcs, topology.FilterOption{Id: "*", Role: "*", Host: options.host})[:1] + if len(configs) == 0 { + return nil, errno.ERR_NO_SERVICES_MATCHED + } + steps := INSTALL_TOOL_PLAYBOOK_STEPS + pb := playbook.NewPlaybook(curveadm) + for _, step := range steps { + pb.AddStep(&playbook.PlaybookStep{ + Type: step, + Configs: configs, + Options: map[string]interface{}{ + comm.KEY_INSTALL_PATH: options.path, + comm.KEY_INSTALL_CONF_PATH: options.confPath, + }, + }) + } + return pb, nil +} + +func runInstallTool(curveadm *cli.CurveAdm, options installOptions) error { + dcs, err := curveadm.ParseTopology() + if err != nil { + return err + } + + pb, err := genInstallToolPlaybook(curveadm, dcs, options) + if err != nil { + return err + } + + err = pb.Run() + if err != nil { + return err + } + + curveadm.WriteOutln(color.GreenString("Install %s to %s success."), + "curve tool v2", options.host) + return nil +} diff --git a/internal/common/common.go b/internal/common/common.go index ff726c663..c28696ea1 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -117,6 +117,10 @@ const ( KEY_SERVICE_HOSTS = "SERVICE_HOSTS" KEY_MONITOR_STATUS = "MONITOR_STATUS" CLEANED_MONITOR_CONF = "-" + + // install + KEY_INSTALL_PATH = "INSTALL_PATH" + KEY_INSTALL_CONF_PATH = "INSTALL_CONF_PATH" ) // others diff --git a/internal/playbook/factory.go b/internal/playbook/factory.go index b4a52e4f5..52d786a75 100644 --- a/internal/playbook/factory.go +++ b/internal/playbook/factory.go @@ -31,6 +31,7 @@ import ( "github.com/opencurve/curveadm/internal/task/task/checker" comm "github.com/opencurve/curveadm/internal/task/task/common" "github.com/opencurve/curveadm/internal/task/task/fs" + "github.com/opencurve/curveadm/internal/task/task/install" "github.com/opencurve/curveadm/internal/task/task/monitor" pg "github.com/opencurve/curveadm/internal/task/task/playground" "github.com/opencurve/curveadm/internal/tasks" @@ -84,6 +85,7 @@ const ( INSTALL_CLIENT UNINSTALL_CLIENT ATTACH_LEADER_OR_RANDOM_CONTAINER + INSTALL_TOOL // bs FORMAT_CHUNKFILE_POOL @@ -250,6 +252,8 @@ func (p *Playbook) createTasks(step *PlaybookStep) (*tasks.Tasks, error) { t, err = comm.NewInstallClientTask(curveadm, config.GetCC(i)) case UNINSTALL_CLIENT: t, err = comm.NewUninstallClientTask(curveadm, nil) + case INSTALL_TOOL: + t, err = install.NewInstallToolTask(curveadm, config.GetDC(i)) // bs case FORMAT_CHUNKFILE_POOL: t, err = bs.NewFormatChunkfilePoolTask(curveadm, config.GetFC(i)) diff --git a/internal/task/task/install/install_tool.go b/internal/task/task/install/install_tool.go new file mode 100644 index 000000000..6352c1786 --- /dev/null +++ b/internal/task/task/install/install_tool.go @@ -0,0 +1,79 @@ +package install + +import ( + "fmt" + "github.com/opencurve/curveadm/cli/cli" + comm "github.com/opencurve/curveadm/internal/common" + "github.com/opencurve/curveadm/internal/configure/topology" + "github.com/opencurve/curveadm/internal/errno" + "github.com/opencurve/curveadm/internal/task/step" + "github.com/opencurve/curveadm/internal/task/task" + tui "github.com/opencurve/curveadm/internal/tui/common" + "github.com/opencurve/curveadm/pkg/module" + "path/filepath" +) + +func checkPathExist(path string, sshConfig *module.SSHConfig, curveadm *cli.CurveAdm) error { + sshClient, err := module.NewSSHClient(*sshConfig) + if err != nil { + return errno.ERR_SSH_CONNECT_FAILED.E(err) + } + + module := module.NewModule(sshClient) + cmd := module.Shell().Stat(path) + if _, err := cmd.Execute(curveadm.ExecOptions()); err == nil { + if pass := tui.ConfirmYes(tui.PromptPathExist(path)); !pass { + return errno.ERR_CANCEL_OPERATION + } + } + return nil +} + +func NewInstallToolTask(curveadm *cli.CurveAdm, dc *topology.DeployConfig) (*task.Task, error) { + layout := dc.GetProjectLayout() + path := curveadm.MemStorage().Get(comm.KEY_INSTALL_PATH).(string) + confPath := curveadm.MemStorage().Get(comm.KEY_INSTALL_CONF_PATH).(string) + hc, err := curveadm.GetHost(dc.GetHost()) + if err != nil { + return nil, err + } + + serviceId := curveadm.GetServiceId(dc.GetId()) + containerId, err := curveadm.GetContainerId(serviceId) + if err != nil { + return nil, err + } + + if err = checkPathExist(path, hc.GetSSHConfig(), curveadm); err != nil { + return nil, err + } + if err = checkPathExist(confPath, hc.GetSSHConfig(), curveadm); err != nil { + return nil, err + } + + subname := fmt.Sprintf("host=%s", dc.GetHost()) + t := task.NewTask("Install tool v2", subname, hc.GetSSHConfig()) + + t.AddStep(&step.CreateDirectory{ + Paths: []string{filepath.Dir(path)}, + ExecOptions: curveadm.ExecOptions(), + }) + t.AddStep(&step.CopyFromContainer{ + ContainerSrcPath: layout.ToolsV2BinaryPath, + ContainerId: containerId, + HostDestPath: path, + ExecOptions: curveadm.ExecOptions(), + }) + t.AddStep(&step.CreateDirectory{ + Paths: []string{filepath.Dir(confPath)}, + ExecOptions: curveadm.ExecOptions(), + }) + t.AddStep(&step.CopyFromContainer{ + ContainerSrcPath: layout.ToolsV2ConfSystemPath, + ContainerId: containerId, + HostDestPath: confPath, + ExecOptions: curveadm.ExecOptions(), + }) + + return t, nil +} diff --git a/internal/tui/common/prompt.go b/internal/tui/common/prompt.go index 5479b7dc1..cc6e4906c 100644 --- a/internal/tui/common/prompt.go +++ b/internal/tui/common/prompt.go @@ -71,6 +71,9 @@ to watch the formatting progress. ` PROMPT_CANCEL_OPERATION = `[x] {{.operation}} canceled` + PROMPT_PATH_EXIST = `{{.path}} already exists. +` + DEFAULT_CONFIRM_PROMPT = "Do you want to continue?" ) @@ -210,7 +213,7 @@ func PromptErrorCode(code int, description, clue, logpath string) string { if len(clue) > 0 { prompt.data["clue"] = prettyClue(clue) } - prompt.data["website"] = fmt.Sprintf("https://github.com/opencurve/curveadm/wiki/errno%d#%06d", code / 100000, code) + prompt.data["website"] = fmt.Sprintf("https://github.com/opencurve/curveadm/wiki/errno%d#%06d", code/100000, code) if len(logpath) > 0 { prompt.data["logpath"] = logpath } @@ -230,3 +233,9 @@ func PromptAutoUpgrade(version string) string { prompt.data["version"] = version return prompt.Build() } + +func PromptPathExist(path string) string { + prompt := NewPrompt(color.YellowString(PROMPT_PATH_EXIST) + DEFAULT_CONFIRM_PROMPT) + prompt.data["path"] = path + return prompt.Build() +}