Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions CubeMaster/cmd/cubemastercli/commands/cubebox/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,23 @@ var TemplateCreateCommand = cli.Command{
},
}

// resolveTemplateID returns the template ID from the --template-id flag,
// falling back to the first positional argument to match docker/kubectl
// conventions (e.g. `cubemastercli tpl info <template-id>`).
func resolveTemplateID(c *cli.Context) string {
if id := c.String("template-id"); id != "" {
return id
}
if c.NArg() > 0 {
return c.Args().First()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c.Args().First() silently discards any additional positional arguments. A user typing cubemastercli tpl info tpl-abc extra-arg gets no feedback about the extra arg. Consider either:

Option A — Change signature to (string, error) and add an NArg() > 1 check.
Option B (minimal) — At minimum log a warning when extra args are detected.

}
return ""
}

var TemplateInfoCommand = cli.Command{
Name: "info",
Usage: "show template metadata and node replicas",
Name: "info",
Usage: "show template metadata and node replicas",
ArgsUsage: "<template-id>",
Flags: []cli.Flag{
cli.StringFlag{
Name: "template-id",
Expand All @@ -467,7 +481,7 @@ var TemplateInfoCommand = cli.Command{
},
},
Action: func(c *cli.Context) error {
templateID := c.String("template-id")
templateID := resolveTemplateID(c)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-existing note (not blocking): templateID is interpolated directly via fmt.Sprintf("...?template_id=%s", ...) without URL encoding. A template ID containing &, =, or # could inject query parameters. Using net/url.Values would be more robust — the same pattern exists for jobID and buildID elsewhere in this file and would benefit from a separate cleanup pass.

if templateID == "" {
return errors.New("template-id is required")
}
Expand Down Expand Up @@ -590,16 +604,17 @@ var TemplateRenderCommand = cli.Command{
}

var TemplateDeleteCommand = cli.Command{
Name: "delete",
Usage: "delete template metadata and node replicas",
Name: "delete",
Usage: "delete template metadata and node replicas",
ArgsUsage: "<template-id>",
Flags: []cli.Flag{
cli.StringFlag{
Name: "template-id",
Usage: "template id to delete",
},
},
Action: func(c *cli.Context) error {
templateID := c.String("template-id")
templateID := resolveTemplateID(c)
if templateID == "" {
return errors.New("template-id is required")
}
Expand Down Expand Up @@ -849,8 +864,9 @@ var TemplateCreateFromImageCommand = cli.Command{
}

var TemplateRedoCommand = cli.Command{
Name: "redo",
Usage: "redo a template on all, specific, or failed nodes",
Name: "redo",
Usage: "redo a template on all, specific, or failed nodes",
ArgsUsage: "<template-id>",
Flags: []cli.Flag{
cli.StringFlag{Name: "template-id", Usage: "template id to redo"},
cli.StringSliceFlag{Name: "node", Usage: "redo only the specified node id or host ip; repeat to specify multiple nodes"},
Expand All @@ -861,7 +877,7 @@ var TemplateRedoCommand = cli.Command{
cli.BoolFlag{Name: "json", Usage: "print raw json response"},
},
Action: func(c *cli.Context) error {
templateID := c.String("template-id")
templateID := resolveTemplateID(c)
if templateID == "" {
return errors.New("template-id is required")
}
Expand Down
99 changes: 99 additions & 0 deletions CubeMaster/cmd/cubemastercli/commands/cubebox/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,3 +432,102 @@ func TestPrintTemplateSummaryIncludesOptionalMetadata(t *testing.T) {
t.Fatalf("stdout=%q, missing replica table header", stdout)
}
}

func TestResolveTemplateIDFromFlag(t *testing.T) {
ctx := newRedoContext(t, []string{"--template-id", "tpl-1"})
if got := resolveTemplateID(ctx); got != "tpl-1" {
t.Fatalf("got %q, want tpl-1", got)
}
}

func TestResolveTemplateIDFromPositional(t *testing.T) {
ctx := newRedoContext(t, []string{"tpl-1"})
if got := resolveTemplateID(ctx); got != "tpl-1" {
t.Fatalf("got %q, want tpl-1", got)
}
}

func TestResolveTemplateIDFlagOverridesPositional(t *testing.T) {
ctx := newRedoContext(t, []string{"--template-id", "flag-id", "positional-id"})
if got := resolveTemplateID(ctx); got != "flag-id" {
t.Fatalf("got %q, want flag-id", got)
}
}

func TestResolveTemplateIDEmpty(t *testing.T) {
ctx := newRedoContext(t, nil)
if got := resolveTemplateID(ctx); got != "" {
t.Fatalf("got %q, want empty", got)
}
}

// newCmdContext builds a *cli.Context from a command definition and args,
// used to verify resolveTemplateID works correctly with each command's
// specific flag set (info, delete, redo).
func newCmdContext(t *testing.T, cmd cli.Command, args []string) *cli.Context {
t.Helper()
set := flag.NewFlagSet(cmd.Name, flag.ContinueOnError)
for _, cliFlag := range cmd.Flags {
cliFlag.Apply(set)
}
if err := set.Parse(args); err != nil {
t.Fatalf("parse args %v: %v", args, err)
}
ctx := cli.NewContext(nil, set, nil)
ctx.Command = cmd
return ctx
}

func TestResolveTemplateIDFromAllTemplateCommands(t *testing.T) {
tests := []struct {
name string
cmd cli.Command
args []string
want string
}{
{
name: "info via positional arg",
cmd: TemplateInfoCommand,
args: []string{"tpl-info-1"},
want: "tpl-info-1",
},
{
name: "delete via positional arg",
cmd: TemplateDeleteCommand,
args: []string{"tpl-delete-1"},
want: "tpl-delete-1",
},
{
name: "redo via positional arg",
cmd: TemplateRedoCommand,
args: []string{"tpl-redo-1"},
want: "tpl-redo-1",
},
{
name: "info flag overrides positional",
cmd: TemplateInfoCommand,
args: []string{"--template-id", "flag-id", "positional-id"},
want: "flag-id",
},
{
name: "delete flag overrides positional",
cmd: TemplateDeleteCommand,
args: []string{"--template-id", "flag-id", "positional-id"},
want: "flag-id",
},
{
name: "redo flag overrides positional",
cmd: TemplateRedoCommand,
args: []string{"--template-id", "flag-id", "positional-id"},
want: "flag-id",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := newCmdContext(t, tt.cmd, tt.args)
if got := resolveTemplateID(ctx); got != tt.want {
t.Fatalf("got %q, want %q", got, tt.want)
}
})
}
}
12 changes: 7 additions & 5 deletions docs/guide/template-inspection-and-preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,21 @@ In practice:
Basic metadata:

```bash
cubemastercli tpl info --template-id <template-id>
cubemastercli tpl info <template-id>
```

The template ID can be passed as a positional argument (docker/kubectl style) or with `--template-id`; both forms are equivalent.

Raw JSON output:

```bash
cubemastercli tpl info --template-id <template-id> --json
cubemastercli tpl info <template-id> --json
```

Include the stored request body:

```bash
cubemastercli tpl info --template-id <template-id> --json --include-request
cubemastercli tpl info <template-id> --json --include-request
```

Typical response shape:
Expand Down Expand Up @@ -142,13 +144,13 @@ When you only have a `template_id` and want to understand what sandbox creation
1. Check template status and replicas.

```bash
cubemastercli tpl info --template-id <template-id>
cubemastercli tpl info <template-id>
```

2. If you need to know what the template itself stores, inspect `create_request`.

```bash
cubemastercli tpl info --template-id <template-id> --json --include-request
cubemastercli tpl info <template-id> --json --include-request
```

3. Preview the effective request that would be used right now.
Expand Down
10 changes: 6 additions & 4 deletions docs/guide/tutorials/template-from-image.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,19 +193,21 @@ cubemastercli tpl list --json | jq '.data[].template_id'
### Inspect a single template

```bash
cubemastercli tpl info --template-id tpl-748094d2f2374b0a8a37e6ec
cubemastercli tpl info tpl-748094d2f2374b0a8a37e6ec
```

The template ID can be passed as a positional argument (docker/kubectl style) or with `--template-id`; both forms are equivalent.

Add `--json` for machine-readable output:

```bash
cubemastercli tpl info --template-id tpl-748094d2f2374b0a8a37e6ec --json
cubemastercli tpl info tpl-748094d2f2374b0a8a37e6ec --json
```

Add `--include-request` when you want to inspect the stored template request body:

```bash
cubemastercli tpl info --template-id tpl-748094d2f2374b0a8a37e6ec --json --include-request
cubemastercli tpl info tpl-748094d2f2374b0a8a37e6ec --json --include-request
```

If you want to preview the effective sandbox payload after template resolution, use:
Expand All @@ -221,7 +223,7 @@ For a user-oriented walkthrough of what each output means and how to preview the
## Deleting a Template

```bash
cubemastercli tpl delete --template-id tpl-748094d2f2374b0a8a37e6ec
cubemastercli tpl delete tpl-748094d2f2374b0a8a37e6ec
```

On success:
Expand Down
12 changes: 7 additions & 5 deletions docs/zh/guide/template-inspection-and-preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,21 @@
查看基础信息:

```bash
cubemastercli tpl info --template-id <template-id>
cubemastercli tpl info <template-id>
```

模板 ID 既可以用位置参数传入(与 docker/kubectl 风格一致),也可以继续使用 `--template-id`,两种写法等价。

查看原始 JSON:

```bash
cubemastercli tpl info --template-id <template-id> --json
cubemastercli tpl info <template-id> --json
```

把模板里保存的请求体一并带出来:

```bash
cubemastercli tpl info --template-id <template-id> --json --include-request
cubemastercli tpl info <template-id> --json --include-request
```

典型返回结构如下:
Expand Down Expand Up @@ -142,13 +144,13 @@ cubemastercli tpl render -f req.json --template-id <template-id> --json
1. 先看模板状态和副本。

```bash
cubemastercli tpl info --template-id <template-id>
cubemastercli tpl info <template-id>
```

2. 如果你想确认模板本身存了什么,再看 `create_request`。

```bash
cubemastercli tpl info --template-id <template-id> --json --include-request
cubemastercli tpl info <template-id> --json --include-request
```

3. 再看当前真正会生效的请求预览。
Expand Down
6 changes: 3 additions & 3 deletions docs/zh/guide/tutorials/template-build-practice.md
Original file line number Diff line number Diff line change
Expand Up @@ -608,11 +608,11 @@ print(os.environ['RUNTIME_VAR'])
cubemastercli tpl list
cubemastercli tpl list -o wide

# 查看模板详情
cubemastercli tpl info --template-id <template_id>
# 查看模板详情(模板 ID 用位置参数传入,等价于 --template-id)
cubemastercli tpl info <template_id>

# 删除模板
cubemastercli tpl delete --template-id <template_id>
cubemastercli tpl delete <template_id>

# 必要环境变量(使用 SDK 时)
export CUBE_TEMPLATE_ID=<template_id>
Expand Down
10 changes: 6 additions & 4 deletions docs/zh/guide/tutorials/template-from-image.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,19 +175,21 @@ cubemastercli tpl list --json | jq '.data[].template_id'
### 查看单个模板详情

```bash
cubemastercli tpl info --template-id tpl-748094d2f2374b0a8a37e6ec
cubemastercli tpl info tpl-748094d2f2374b0a8a37e6ec
```

模板 ID 既可以用位置参数传入(与 docker/kubectl 风格一致),也可以继续使用 `--template-id`,两种写法等价。

需要机器可读输出时,加上 `--json`:

```bash
cubemastercli tpl info --template-id tpl-748094d2f2374b0a8a37e6ec --json
cubemastercli tpl info tpl-748094d2f2374b0a8a37e6ec --json
```

如果想查看模板里保存的创建请求体,可再加 `--include-request`:

```bash
cubemastercli tpl info --template-id tpl-748094d2f2374b0a8a37e6ec --json --include-request
cubemastercli tpl info tpl-748094d2f2374b0a8a37e6ec --json --include-request
```

如果想预览创建沙箱时最终生效的请求,可使用:
Expand All @@ -203,7 +205,7 @@ cubemastercli tpl render --template-id tpl-748094d2f2374b0a8a37e6ec --json
## 删除模板

```bash
cubemastercli tpl delete --template-id tpl-748094d2f2374b0a8a37e6ec
cubemastercli tpl delete tpl-748094d2f2374b0a8a37e6ec
```

成功后输出:
Expand Down
Loading