- exec/rm/rmi/stop/restart 支持名称、ID 或唯一 ID 前缀,目标不存在或 前缀有歧义时在调用 docker 前报错;exec 默认 -it 并以 -- 分隔命令 - logs 新增 --since/--grep/--save;df 改为按占用排序的可读排行; stats 默认单次快照,无参数时统计全部运行中容器 - 破坏性确认:Command.ConfirmBuiltin 提供将删除对象的清单, rm/rmi 无 --yes 时列清单并退出码 2,拒绝 -f,运行中容器提示先 stop - 抽出 executor/dockertargets.go 统一镜像/容器解析与 docker 调用
107 lines
3.5 KiB
Go
107 lines
3.5 KiB
Go
package executor
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// execValueOption lists docker exec options that take a separate value, so the
|
|
// value is not mistaken for the container name or the command.
|
|
var execValueOption = map[string]bool{
|
|
"u": true,
|
|
"user": true,
|
|
"w": true,
|
|
"workdir": true,
|
|
"e": true,
|
|
"env": true,
|
|
"env-file": true,
|
|
"detach-keys": true,
|
|
}
|
|
|
|
// runDockerExec implements `kopsc docker exec <容器> <命令...>`.
|
|
//
|
|
// Interactive use is the default, so `-it` is no longer typed: kopsc always
|
|
// runs docker exec with -it and separates the command with `--`, which keeps
|
|
// the command's own flags from being read as docker options. Passing -i/-t/-it
|
|
// explicitly is an error, because the flag is now implied.
|
|
//
|
|
// Arguments before the container name are docker exec options and are
|
|
// forwarded; everything after the container name is the command, verbatim.
|
|
func runDockerExec(ctx context.Context, req BuiltinRequest) error {
|
|
args := req.Args
|
|
if err := rejectInteractiveFlags(args); err != nil {
|
|
return err
|
|
}
|
|
if len(args) == 0 {
|
|
return errors.New("缺少容器;用法: kopsc docker exec <容器> <命令...>")
|
|
}
|
|
|
|
var opts []string
|
|
target := ""
|
|
for i := 0; i < len(args); i++ {
|
|
arg := args[i]
|
|
switch {
|
|
case arg == "--":
|
|
// An explicit separator before the container name: what follows is
|
|
// still the container, then the command.
|
|
if i+1 < len(args) {
|
|
i++
|
|
target = args[i]
|
|
return dockerRun(ctx, execArgv(opts, target, args[i+1:]...), req)
|
|
}
|
|
return errors.New("`--` 之后缺少容器;用法: kopsc docker exec <容器> <命令...>")
|
|
case !strings.HasPrefix(arg, "-") || arg == "-":
|
|
// The first positional argument is the container; the rest is the
|
|
// command, which may itself contain flags.
|
|
target = arg
|
|
return dockerRun(ctx, execArgv(opts, target, args[i+1:]...), req)
|
|
default:
|
|
// A docker exec option; keep its value attached to it.
|
|
opts = append(opts, arg)
|
|
if !strings.Contains(arg, "=") && execValueOption[strings.TrimLeft(arg, "-")] {
|
|
if i+1 >= len(args) {
|
|
return fmt.Errorf("选项 %s 缺少参数", arg)
|
|
}
|
|
i++
|
|
opts = append(opts, args[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
return errors.New("缺少容器;用法: kopsc docker exec <容器> <命令...>")
|
|
}
|
|
|
|
// execArgv builds `exec -it <已有的 docker 选项> <容器> -- <命令...>`.
|
|
func execArgv(opts []string, target string, command ...string) []string {
|
|
argv := append([]string{"exec", "-it"}, opts...)
|
|
argv = append(argv, target, "--")
|
|
return append(argv, command...)
|
|
}
|
|
|
|
// rejectInteractiveFlags refuses -i/-t/-it/--interactive/--tty: kopsc always
|
|
// provides them.
|
|
func rejectInteractiveFlags(args []string) error {
|
|
for _, arg := range args {
|
|
if arg == "--" || !strings.HasPrefix(arg, "-") || arg == "-" {
|
|
return nil
|
|
}
|
|
switch {
|
|
case arg == "--interactive" || arg == "--tty" || arg == "-i" || arg == "-t" || arg == "-it" || arg == "-ti":
|
|
return errors.New("kopsc docker exec 已默认以交互方式(-it)执行,无需再传 " + arg +
|
|
";用法: kopsc docker exec <容器> <命令...>")
|
|
}
|
|
if strings.HasPrefix(arg, "--") {
|
|
continue
|
|
}
|
|
// Bundled short flags such as -di still carry -i or -t.
|
|
short := strings.TrimPrefix(arg, "-")
|
|
if strings.ContainsRune(short, 'i') || strings.ContainsRune(short, 't') {
|
|
return errors.New("kopsc docker exec 已默认以交互方式(-it)执行,无需再传 " + arg +
|
|
";用法: kopsc docker exec <容器> <命令...>")
|
|
}
|
|
}
|
|
return nil
|
|
}
|