diff --git a/cli/command/cmd.go b/cli/command/cmd.go index d9d66cd90..0c19d9548 100644 --- a/cli/command/cmd.go +++ b/cli/command/cmd.go @@ -70,6 +70,7 @@ func addSubCommands(cmd *cobra.Command, curveadm *cli.CurveAdm) { NewCompletionCommand(curveadm), // curveadm completion NewDeployCommand(curveadm), // curveadm deploy NewEnterCommand(curveadm), // curveadm enter + NewExecCommand(curveadm), // curveadm exec NewFormatCommand(curveadm), // curveadm format NewMigrateCommand(curveadm), // curveadm migrate NewPrecheckCommand(curveadm), // curveadm precheck diff --git a/cli/command/exec.go b/cli/command/exec.go new file mode 100644 index 000000000..334ffb10b --- /dev/null +++ b/cli/command/exec.go @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 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: 2021-10-15 + * Author: Jingli Chen (Wine93) + */ + +// __SIGN_BY_WINE93__ + +package command + +import ( + "github.com/opencurve/curveadm/cli/cli" + "github.com/opencurve/curveadm/internal/configure/topology" + "github.com/opencurve/curveadm/internal/errno" + "github.com/opencurve/curveadm/internal/tools" + "github.com/spf13/cobra" +) + +type execOptions struct { + id string + cmd string +} + +func NewExecCommand(curveadm *cli.CurveAdm) *cobra.Command { + var options execOptions + + cmd := &cobra.Command{ + Use: "exec ID [OPTIONS]", + Short: "Exec a cmd in service container", + PreRunE: func(cmd *cobra.Command, args []string) error { + options.id = args[0] + return curveadm.CheckId(options.id) + }, + RunE: func(cmd *cobra.Command, args []string) error { + return runExec(curveadm, options) + }, + DisableFlagsInUseLine: true, + } + + flags := cmd.Flags() + flags.StringVar(&options.cmd, "cmd", "*", "Specify command to exec") + + return cmd +} + +func runExec(curveadm *cli.CurveAdm, options execOptions) error { + // 1) parse cluster topology + dcs, err := curveadm.ParseTopology() + if err != nil { + return err + } + + // 2) filter service + dcs = curveadm.FilterDeployConfig(dcs, topology.FilterOption{ + Id: options.id, + Role: "*", + Host: "*", + }) + if len(dcs) == 0 { + return errno.ERR_NO_SERVICES_MATCHED + } + + // 3) get container id + dc := dcs[0] + serviceId := curveadm.GetServiceId(dc.GetId()) + containerId, err := curveadm.GetContainerId(serviceId) + if err != nil { + return err + } + + // 4) exec cmd in remote container + return tools.ExecCmdInRemoteContainer(curveadm, dc.GetHost(), containerId, options.cmd) +} diff --git a/cli/command/playground/enter.go b/cli/command/playground/enter.go index 5d82c89d7..a264af4de 100644 --- a/cli/command/playground/enter.go +++ b/cli/command/playground/enter.go @@ -62,6 +62,6 @@ func runEnter(curveadm *cli.CurveAdm, options enterOptions) error { F("id=%s", id) } - // 2) attch remote container + // 2) attch local container return tools.AttachLocalContainer(curveadm, playgrounds[0].Name) } diff --git a/internal/tools/ssh.go b/internal/tools/ssh.go index e8a1f70c1..25f1a19b7 100644 --- a/internal/tools/ssh.go +++ b/internal/tools/ssh.go @@ -37,11 +37,12 @@ import ( ) const ( - TEMPLATE_SCP = `scp -P {{.port}} {{or .options ""}} {{.source}} {{.user}}@{{.host}}:{{.target}}` - TEMPLATE_SSH_COMMAND = `ssh {{.user}}@{{.host}} -p {{.port}} {{or .options ""}} {{or .become ""}} {{.command}}` - TEMPLATE_SSH_ATTACH = `ssh -tt {{.user}}@{{.host}} -p {{.port}} {{or .options ""}} {{or .become ""}} {{.command}}` - TEMPLATE_COMMAND_EXEC_CONTAINER = `{{.sudo}} docker exec -it {{.container_id}} /bin/bash -c "cd {{.home_dir}}; /bin/bash"` - TEMPLATE_LOCAL_EXEC_CONTAINER = `docker exec -it {{.container_id}} /bin/bash` // FIXME: merge it + TEMPLATE_SCP = `scp -P {{.port}} {{or .options ""}} {{.source}} {{.user}}@{{.host}}:{{.target}}` + TEMPLATE_SSH_COMMAND = `ssh {{.user}}@{{.host}} -p {{.port}} {{or .options ""}} {{or .become ""}} {{.command}}` + TEMPLATE_SSH_ATTACH = `ssh -tt {{.user}}@{{.host}} -p {{.port}} {{or .options ""}} {{or .become ""}} {{.command}}` + TEMPLATE_COMMAND_EXEC_CONTAINER = `{{.sudo}} docker exec -it {{.container_id}} /bin/bash -c "cd {{.home_dir}}; /bin/bash"` + TEMPLATE_LOCAL_EXEC_CONTAINER = `docker exec -it {{.container_id}} /bin/bash` // FIXME: merge it + TEMPLATE_COMMAND_EXEC_CONTAINER_NOATTACH = `{{.sudo}} docker exec -t {{.container_id}} /bin/bash -c "{{.command}}"` ) func prepareOptions(curveadm *cli.CurveAdm, host string, become bool, extra map[string]interface{}) (map[string]interface{}, error) { @@ -168,6 +169,27 @@ func AttachLocalContainer(curveadm *cli.CurveAdm, containerId string) error { return runCommand(curveadm, command, map[string]interface{}{}) } +func ExecCmdInRemoteContainer(curveadm *cli.CurveAdm, host, containerId, cmd string) error { + data := map[string]interface{}{ + "sudo": curveadm.Config().GetSudoAlias(), + "container_id": containerId, + "command": cmd, + } + tmpl := template.Must(template.New("command").Parse(TEMPLATE_COMMAND_EXEC_CONTAINER_NOATTACH)) + buffer := bytes.NewBufferString("") + if err := tmpl.Execute(buffer, data); err != nil { + return errno.ERR_BUILD_TEMPLATE_FAILED.E(err) + } + command := buffer.String() + + options, err := prepareOptions(curveadm, host, true, + map[string]interface{}{"command": command}) + if err != nil { + return err + } + return ssh(curveadm, options) +} + func Scp(curveadm *cli.CurveAdm, host, source, target string) error { options, err := prepareOptions(curveadm, host, false, map[string]interface{}{