Files
kenaito-kopsc/internal/executor/dockerstats.go
T
odboy f5ae80c1a8 feat: 将更多 Docker 命令改为内置实现,统一按名称/ID 解析目标
- 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 调用
2026-11-01 08:01:12 +08:00

100 lines
2.9 KiB
Go

package executor
import (
"context"
"strings"
)
// runDockerStats implements `kopsc docker stats [容器...]`.
//
// Plain `docker stats` prints an endless live table, which is rarely what
// anyone wants from a one-shot helper, so kopsc always asks for a single
// snapshot (`--no-stream`). With no container named, every running container
// is reported; with names, those are resolved to the running containers and
// only they are shown. Options such as --format are forwarded.
func runDockerStats(ctx context.Context, req BuiltinRequest) error {
opts, targets, passthrough, err := parseDockerStats(req.Args)
if err != nil {
return err
}
if len(targets) > 0 {
refs, err := listContainers(ctx)
if err != nil {
return err
}
resolved := make([]string, 0, len(targets))
for _, target := range targets {
ref, err := matchContainerRef(refs, target)
if err != nil {
return err
}
if !ref.Running() {
return &NotRunningError{Name: ref.Names}
}
resolved = append(resolved, ref.Names)
}
targets = resolved
}
// docker stats takes the containers after its options; keeping the
// forwarded options last matches how a caller would type them.
args := append([]string{"stats"}, opts...)
args = append(args, targets...)
args = append(args, passthrough...)
return dockerRun(ctx, args, req)
}
// NotRunningError reports a container docker stats cannot sample.
type NotRunningError struct {
Name string
}
func (e *NotRunningError) Error() string {
return "容器 " + e.Name + " 未在运行,docker stats 只能统计运行中的容器;先启动请用 kopsc docker start " + e.Name
}
// parseDockerStats separates options from container names. --no-stream is
// added by kopsc, so it is kept as an option and never duplicated.
func parseDockerStats(args []string) (opts, targets, passthrough []string, err error) {
opts = []string{"--no-stream"}
afterDashDash := false
for i := 0; i < len(args); i++ {
arg := args[i]
switch {
case afterDashDash:
targets = append(targets, arg)
case arg == "--":
afterDashDash = true
case !strings.HasPrefix(arg, "-") || arg == "-":
targets = append(targets, arg)
case arg == "--no-stream":
// Already requested by kopsc.
default:
passthrough = append(passthrough, arg)
if !strings.Contains(arg, "=") && statsValueOption[strings.TrimLeft(arg, "-")] {
if i+1 >= len(args) {
return nil, nil, nil, &MissingOptionValueError{Option: arg}
}
i++
passthrough = append(passthrough, args[i])
}
}
}
return opts, targets, passthrough, nil
}
// statsValueOption lists docker stats options that take a separate value.
var statsValueOption = map[string]bool{
"format": true,
}
// MissingOptionValueError reports an option given without its value.
type MissingOptionValueError struct {
Option string
}
func (e *MissingOptionValueError) Error() string {
return e.Option + " 缺少参数"
}