* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*
* 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.
*/
package exec
import (
"errors"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"golang.org/x/term"
"cli/constant"
"cli/pkg/clusterctrl"
"cli/pkg/cmdio"
"cli/utils"
"cli/utils/colorprint"
)
const (
numExecPositionalArgs = 2
defaultTerminalSize = 80
)
type options struct {
cmdIO *cmdio.CmdIO
clusterConfig *utils.ClusterConfig
AlsoStart bool
AlsoStop bool
Env []string
Cmd string
}
var opts options
var yrExecCmd = &cobra.Command{
Use: "exec [flags] <cluster_config.yaml> -- <command>",
Short: fmt.Sprintf("exec %s an command on the master node, and start/stop the yuanrong cluster (if need)",
constant.PlatformName),
Long: fmt.Sprintf(`exec %s an command on the master node, and start/stop the yuanrong cluster (if need)`,
constant.PlatformName),
Example: utils.RawFormat(fmt.Sprintf(`
$ %s exec cluster_config.yaml -- /path/to/program
`, constant.CliName)),
Args: cobra.MinimumNArgs(numExecPositionalArgs),
RunE: runExec,
}
func InitCMD(cio *cmdio.CmdIO) *cobra.Command {
opts.cmdIO = cio
yrExecCmd.Flags().BoolVar(&opts.AlsoStart, "start", false, "")
yrExecCmd.Flags().BoolVar(&opts.AlsoStop, "stop", false, "")
yrExecCmd.Flags().StringSliceVarP(&opts.Env, "env", "e", []string{}, "specify environment")
return yrExecCmd
}
func findOsArgsCommandAfterDoubleSlash() (string, error) {
var cmd string
for i, arg := range os.Args {
if arg == "--" {
cmd = strings.Join(os.Args[i+1:], " ")
break
}
}
if cmd == "" {
return "", errors.New("failed to get command after `--`")
}
return cmd, nil
}
func getTerminalWidthSplitter(char string) string {
width, _, err := term.GetSize(int(os.Stdout.Fd()))
if err != nil {
return strings.Repeat(char, defaultTerminalSize)
}
return strings.Repeat(char, width)
}
func runExec(_ *cobra.Command, args []string) error {
clusterConfigFilePath := args[0]
clusterConfigCmd, err := findOsArgsCommandAfterDoubleSlash()
if err != nil {
return err
}
clusterConfig, err := utils.LoadClusterConfig(clusterConfigFilePath)
if err != nil {
return err
}
clusterConfig.Env = append(clusterConfig.Env, opts.Env...)
executor := clusterctrl.NewSSHRemoteExecutor(clusterConfig.SSHCmd)
defer func() {
if opts.AlsoStop {
if err := clusterctrl.Down(clusterConfig, executor); err != nil {
colorprint.PrintInteractive(os.Stdout, fmt.Sprintf("failed to stop, err: %s", err.Error()))
}
}
}()
if opts.AlsoStart {
if err := clusterctrl.Up(clusterConfig, executor); err != nil {
return err
}
}
if wd, err := os.Getwd(); err == nil {
clusterConfigCmd = fmt.Sprintf("cd %s ;", wd) + clusterConfigCmd
}
colorprint.PrintSuccess(os.Stdout, getTerminalWidthSplitter("=")+"\n", "")
execErr := clusterctrl.Exec(clusterConfig, executor, clusterConfigCmd)
colorprint.PrintSuccess(os.Stdout, getTerminalWidthSplitter("=")+"\n", "")
return execErr
}