feat: 扩充 Docker 命令并新增 diagnose 状态诊断
- Docker:新增只读、生命周期、清理、Compose 共 32 条命令,Linux 自动适配 podman - 危险命令 clearall/prunevolumes 未带 --yes 时仅提示并以退出码 2 结束 - 需要容器/镜像名的命令做必填参数校验,-- 之后参数计入位置参数 - diagnose:自动识别 Docker/Podman 容器或 K8s Pod,输出状态、根因、依据、建议并附日志与事件 - diagnose 中 -n 表示 namespace,其余命令 -n 仍为 --dry-run - 新增内置动作桥接与平台变体 helper,补充单元/集成/CLI 测试与 README
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
package diagnose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Options controls what a diagnosis collects.
|
||||
type Options struct {
|
||||
Kind string // "", "docker" or "k8s"
|
||||
Namespace string
|
||||
Context string
|
||||
Lines int
|
||||
NoLogs bool
|
||||
NoEvents bool
|
||||
Container string
|
||||
}
|
||||
|
||||
// DefaultLines is the number of log lines collected by default.
|
||||
const DefaultLines = 50
|
||||
|
||||
// ParseArgs parses the arguments forwarded by the CLI. It returns the target
|
||||
// (container or Pod name) and the collection options.
|
||||
func ParseArgs(args []string) (string, Options, error) {
|
||||
opts := Options{Lines: DefaultLines}
|
||||
var targets []string
|
||||
positionalOnly := false
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
arg := args[i]
|
||||
switch {
|
||||
case positionalOnly:
|
||||
targets = append(targets, arg)
|
||||
|
||||
case arg == "--":
|
||||
positionalOnly = true
|
||||
|
||||
case arg == "--kind" || arg == "--kind=":
|
||||
value, err := valueFor(arg, args, &i, "--kind")
|
||||
if err != nil {
|
||||
return "", opts, err
|
||||
}
|
||||
opts.Kind = strings.ToLower(value)
|
||||
if opts.Kind != "docker" && opts.Kind != "k8s" {
|
||||
return "", opts, fmt.Errorf("--kind 只支持 docker 或 k8s,收到 %q", value)
|
||||
}
|
||||
|
||||
case strings.HasPrefix(arg, "--kind="):
|
||||
opts.Kind = strings.ToLower(strings.TrimPrefix(arg, "--kind="))
|
||||
if opts.Kind != "docker" && opts.Kind != "k8s" {
|
||||
return "", opts, fmt.Errorf("--kind 只支持 docker 或 k8s,收到 %q", opts.Kind)
|
||||
}
|
||||
|
||||
case arg == "--namespace" || arg == "-N":
|
||||
value, err := valueFor(arg, args, &i, arg)
|
||||
if err != nil {
|
||||
return "", opts, err
|
||||
}
|
||||
opts.Namespace = value
|
||||
|
||||
case strings.HasPrefix(arg, "--namespace="):
|
||||
opts.Namespace = strings.TrimPrefix(arg, "--namespace=")
|
||||
|
||||
case arg == "--context":
|
||||
value, err := valueFor(arg, args, &i, arg)
|
||||
if err != nil {
|
||||
return "", opts, err
|
||||
}
|
||||
opts.Context = value
|
||||
|
||||
case strings.HasPrefix(arg, "--context="):
|
||||
opts.Context = strings.TrimPrefix(arg, "--context=")
|
||||
|
||||
case arg == "--lines":
|
||||
value, err := valueFor(arg, args, &i, arg)
|
||||
if err != nil {
|
||||
return "", opts, err
|
||||
}
|
||||
n, err := strconv.Atoi(value)
|
||||
if err != nil || n <= 0 {
|
||||
return "", opts, fmt.Errorf("--lines 需要正整数,收到 %q", value)
|
||||
}
|
||||
opts.Lines = n
|
||||
|
||||
case strings.HasPrefix(arg, "--lines="):
|
||||
n, err := strconv.Atoi(strings.TrimPrefix(arg, "--lines="))
|
||||
if err != nil || n <= 0 {
|
||||
return "", opts, fmt.Errorf("--lines 需要正整数,收到 %q", strings.TrimPrefix(arg, "--lines="))
|
||||
}
|
||||
opts.Lines = n
|
||||
|
||||
case arg == "--no-logs":
|
||||
opts.NoLogs = true
|
||||
|
||||
case arg == "--no-events":
|
||||
opts.NoEvents = true
|
||||
|
||||
case arg == "--container" || arg == "-c":
|
||||
value, err := valueFor(arg, args, &i, arg)
|
||||
if err != nil {
|
||||
return "", opts, err
|
||||
}
|
||||
opts.Container = value
|
||||
|
||||
case strings.HasPrefix(arg, "--container="):
|
||||
opts.Container = strings.TrimPrefix(arg, "--container=")
|
||||
|
||||
case strings.HasPrefix(arg, "-") && arg != "-":
|
||||
return "", opts, fmt.Errorf("未知选项 %q", arg)
|
||||
|
||||
default:
|
||||
targets = append(targets, arg)
|
||||
}
|
||||
}
|
||||
|
||||
if len(targets) == 0 {
|
||||
return "", opts, fmt.Errorf("必须提供容器名或 Pod 名,例如 kopsc diagnose web")
|
||||
}
|
||||
if len(targets) > 1 {
|
||||
return "", opts, fmt.Errorf("一次只能诊断一个目标,收到 %d 个:%s", len(targets), strings.Join(targets, ", "))
|
||||
}
|
||||
|
||||
if opts.Kind == "docker" && (opts.Namespace != "" || opts.Context != "") {
|
||||
return "", opts, fmt.Errorf("Docker 诊断不支持 --namespace/--context;请改用 --kind k8s")
|
||||
}
|
||||
|
||||
return targets[0], opts, nil
|
||||
}
|
||||
|
||||
// valueFor reads the value of a flag written as "--flag value" or "--flag=".
|
||||
func valueFor(flag string, args []string, index *int, name string) (string, error) {
|
||||
if strings.HasSuffix(flag, "=") {
|
||||
return "", fmt.Errorf("%s 缺少参数", name)
|
||||
}
|
||||
if *index+1 >= len(args) {
|
||||
return "", fmt.Errorf("%s 缺少参数,例如 %s value", name, name)
|
||||
}
|
||||
*index = *index + 1
|
||||
return args[*index], nil
|
||||
}
|
||||
Reference in New Issue
Block a user