From 9e888455c977226c736276534817a4a86ad722ec Mon Sep 17 00:00:00 2001 From: 0fatal <2816813070@qq.com> Date: Mon, 6 Nov 2023 13:47:40 +0000 Subject: [PATCH] Feature(copy tool): add copy tool2 command Signed-off-by: 0fatal <2816813070@qq.com> --- cli/command/cmd.go | 2 + cli/command/copy/cmd.go | 43 ++++++++++ cli/command/copy/tool.go | 111 +++++++++++++++++++++++++ internal/common/common.go | 4 + internal/playbook/factory.go | 3 + internal/task/task/common/copy_tool.go | 101 ++++++++++++++++++++++ internal/tui/common/prompt.go | 11 ++- 7 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 cli/command/copy/cmd.go create mode 100644 cli/command/copy/tool.go create mode 100644 internal/task/task/common/copy_tool.go diff --git a/cli/command/cmd.go b/cli/command/cmd.go index 91faca5bd..9889f2b11 100644 --- a/cli/command/cmd.go +++ b/cli/command/cmd.go @@ -31,6 +31,7 @@ import ( "github.com/opencurve/curveadm/cli/command/client" "github.com/opencurve/curveadm/cli/command/cluster" "github.com/opencurve/curveadm/cli/command/config" + copycmd "github.com/opencurve/curveadm/cli/command/copy" "github.com/opencurve/curveadm/cli/command/hosts" "github.com/opencurve/curveadm/cli/command/monitor" "github.com/opencurve/curveadm/cli/command/pfs" @@ -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 ... + copycmd.NewCopyCommand(curveadm), // curveadm copy ... NewAuditCommand(curveadm), // curveadm audit NewCleanCommand(curveadm), // curveadm clean diff --git a/cli/command/copy/cmd.go b/cli/command/copy/cmd.go new file mode 100644 index 000000000..16add53f6 --- /dev/null +++ b/cli/command/copy/cmd.go @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 NetEase Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Project: CurveAdm + * Created Date: 2023-12-25 + * Author: Xinyu Zhuo (0fatal) + */ + +package copy + +import ( + "github.com/opencurve/curveadm/cli/cli" + cliutil "github.com/opencurve/curveadm/internal/utils" + "github.com/spf13/cobra" +) + +func NewCopyCommand(curveadm *cli.CurveAdm) *cobra.Command { + cmd := &cobra.Command{ + Use: "copy", + Short: "Manage copy", + Args: cliutil.NoArgs, + RunE: cliutil.ShowHelp(curveadm.Err()), + } + + cmd.AddCommand( + NewCopyTool2Command(curveadm), + ) + return cmd +} diff --git a/cli/command/copy/tool.go b/cli/command/copy/tool.go new file mode 100644 index 000000000..a68c1a7a1 --- /dev/null +++ b/cli/command/copy/tool.go @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023 NetEase Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Project: CurveAdm + * Created Date: 2023-12-25 + * Author: Xinyu Zhuo (0fatal) + */ + +package copy + +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 ( + COPY_TOOL_PLAYBOOK_STEPS = []int{ + playbook.COPY_TOOL, + } +) + +type copyOptions struct { + host string + path string + confPath string +} + +func NewCopyTool2Command(curveadm *cli.CurveAdm) *cobra.Command { + var options copyOptions + + cmd := &cobra.Command{ + Use: "tool [OPTIONS]", + Short: "Copy tool v2 on the specified host", + Args: cliutil.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + return runCopyTool(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 copy path of tool v2") + flags.StringVar(&options.confPath, "confPath", "/etc/curve/curve.yaml", "Specify target config path of tool v2") + + return cmd +} + +func genCopyToolPlaybook(curveadm *cli.CurveAdm, + dcs []*topology.DeployConfig, + options copyOptions, +) (*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 := COPY_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_COPY_PATH: options.path, + comm.KEY_COPY_CONF_PATH: options.confPath, + }, + }) + } + return pb, nil +} + +func runCopyTool(curveadm *cli.CurveAdm, options copyOptions) error { + dcs, err := curveadm.ParseTopology() + if err != nil { + return err + } + + pb, err := genCopyToolPlaybook(curveadm, dcs, options) + if err != nil { + return err + } + + err = pb.Run() + if err != nil { + return err + } + + curveadm.WriteOutln(color.GreenString("Copy %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..b41cdd31e 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 = "-" + + // copy + KEY_COPY_PATH = "COPY_PATH" + KEY_COPY_CONF_PATH = "COPY_CONF_PATH" ) // others diff --git a/internal/playbook/factory.go b/internal/playbook/factory.go index b4a52e4f5..55dbb9a22 100644 --- a/internal/playbook/factory.go +++ b/internal/playbook/factory.go @@ -84,6 +84,7 @@ const ( INSTALL_CLIENT UNINSTALL_CLIENT ATTACH_LEADER_OR_RANDOM_CONTAINER + COPY_TOOL // bs FORMAT_CHUNKFILE_POOL @@ -250,6 +251,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 COPY_TOOL: + t, err = comm.NewCopyToolTask(curveadm, config.GetDC(i)) // bs case FORMAT_CHUNKFILE_POOL: t, err = bs.NewFormatChunkfilePoolTask(curveadm, config.GetFC(i)) diff --git a/internal/task/task/common/copy_tool.go b/internal/task/task/common/copy_tool.go new file mode 100644 index 000000000..202f16fc2 --- /dev/null +++ b/internal/task/task/common/copy_tool.go @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023 NetEase Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Project: CurveAdm + * Created Date: 2023-12-25 + * Author: Xinyu Zhuo (0fatal) + */ + +package common + +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 NewCopyToolTask(curveadm *cli.CurveAdm, dc *topology.DeployConfig) (*task.Task, error) { + layout := dc.GetProjectLayout() + path := curveadm.MemStorage().Get(comm.KEY_COPY_PATH).(string) + confPath := curveadm.MemStorage().Get(comm.KEY_COPY_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("Copy version 2 tool to host", 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() +}