Skip to content

cubemaster(cli): support positional template-id in tpl info/delete/redo#891

Merged
fslongjin merged 3 commits into
TencentCloud:masterfrom
sparkzky:fix/template-cli-positional-args
Jul 14, 2026
Merged

cubemaster(cli): support positional template-id in tpl info/delete/redo#891
fslongjin merged 3 commits into
TencentCloud:masterfrom
sparkzky:fix/template-cli-positional-args

Conversation

@sparkzky

Copy link
Copy Markdown
Contributor

Motivation

The cubemastercli tpl subcommands required --template-id <id> as a flag for every invocation. This contradicts the docker/kubectl convention where resource identifiers are positional arguments:

# What users instinctively type (docker/kubectl style):
cubemastercli tpl info tpl-abc123        # ← was rejected with "template-id is required"
cubemastercli tpl delete tpl-abc123
cubemastercli tpl redo tpl-abc123

# What they had to type instead:
cubemastercli tpl info --template-id tpl-abc123

This friction is a usability papercut — the codebase already had the correct pattern in destroy (positional <sandbox-id>) and create/commit (flag-fallback-to-positional), but the template commands did not follow it.

Changes

Code (cubemaster:)

  • New resolveTemplateID(c) helper — checks --template-id flag first, falls back to the first positional arg (c.Args().First()), returns empty if neither is present.
  • tpl info — accepts positional <template-id>, added ArgsUsage.
  • tpl delete — accepts positional <template-id>, added ArgsUsage.
  • tpl redo — accepts positional <template-id>, added ArgsUsage.
  • tpl render — intentionally untouched. Its positional slot is already the file path, so --template-id is the correct interface there.

Backward compatibility: --template-id flag still works exactly as before and takes priority over the positional arg.

Docs (docs:)

Updated all tpl info / tpl delete examples in EN + ZH to positional syntax, with backward-compat notes. tpl render examples left unchanged.

Tests

4 unit tests for resolveTemplateID: flag-only, positional-only, flag-overrides-positional, empty.

Verification

Unit tests: go test -run TestResolveTemplateID ./cmd/cubemastercli/commands/cubebox/ → 4/4 PASS. Full package tests pass with no regressions.

E2E on live PVM cluster (cubemaster on 0.0.0.0:8089, real templates):

Test Result
tpl info tpl-b394e8e5... (positional) ✅ Full metadata + replica table
tpl info --template-id tpl-b394e8e5... (flag) ✅ Byte-identical to positional (except ephemeral requestID)
tpl info tpl-b394e8e5... --json --include-request ✅ Includes create_request
tpl info (no args) template-id is required
tpl info tpl-doesnotexist template not found
tpl redo tpl-b394e8e5... --detach (positional) ✅ Server accepted redo job, returned job_id
tpl delete / tpl redo (no args) template-id is required
Help output for all three ✅ Shows <template-id> in USAGE

Assisted-by: Oh My Pi:zai/glm-5.2

sparkzky added 2 commits July 10, 2026 17:55
The template subcommands (info, delete, redo) required --template-id as a
flag, which contradicts the docker/kubectl convention where resource
identifiers are positional arguments (e.g. 'docker rm <id>', 'kubectl
delete pod <name>').

Extract a resolveTemplateID helper that checks --template-id first, then
falls back to the first positional argument. All three commands now
accept:

  cubemastercli tpl info <template-id>
  cubemastercli tpl delete <template-id>
  cubemastercli tpl redo <template-id>

The --template-id flag still works unchanged (backward compatible, flag
takes priority). The render command is intentionally untouched because
its positional slot is already used for the file path argument.

Assisted-by: Oh My Pi:zai/glm-5.2
Signed-off-by: sparkzky <sparkhhhhhhhhhh@outlook.com>
Update all 'tpl info' and 'tpl delete' examples to use the positional
form (cubemastercli tpl info <template-id>) matching docker/kubectl
conventions. 'tpl render' examples are left unchanged since render uses
the positional slot for the file path argument.

Updated in both EN and ZH docs:
- guide/template-inspection-and-preview.md
- guide/tutorials/template-from-image.md
- zh/guide/tutorials/template-build-practice.md (quick reference)

Added backward-compat notes mentioning --template-id equivalence.

Assisted-by: Oh My Pi:zai/glm-5.2
Signed-off-by: sparkzky <sparkhhhhhhhhhh@outlook.com>
@fslongjin fslongjin self-assigned this Jul 12, 2026
@ls-ggg

ls-ggg commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

LGTM @fslongjin cc

@ls-ggg

ls-ggg commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Please squash the commits according to the guidelines.

@fslongjin fslongjin moved this from Todo to In progress in CubeSandbox Jul 14, 2026
Add TestResolveTemplateIDFromAllTemplateCommands to verify that
resolveTemplateID works correctly when called with contexts built
from each of the three template commands (info, delete, redo).

This guards against accidentally reverting one of the Actions back
to c.String("template-id"), which would silently drop positional
arg support since the Go compiler cannot catch the difference.

6 sub-tests cover: positional-only and flag-overrides-positional
scenarios for TemplateInfoCommand, TemplateDeleteCommand, and
TemplateRedoCommand.

Signed-off-by: jinlong <jinlong@tencent.com>
@fslongjin

Copy link
Copy Markdown
Member

Thanks for your contribution~

@fslongjin fslongjin merged commit 05cb041 into TencentCloud:master Jul 14, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in CubeSandbox Jul 14, 2026
@cubesandboxbot

Copy link
Copy Markdown

Review: #891 — cubemaster(cli): support positional template-id in tpl info/delete/redo

Overall

A clean, well-scoped change that brings the tpl subcommands in line with docker/kubectl conventions. The resolveTemplateID helper is well-named, backward compatibility is preserved, and the table-driven tests are thorough. A few items worth addressing:


Finding 1: Extra positional arguments silently ignored (moderate)

resolveTemplateID uses c.Args().First(), which silently discards any additional positional arguments. If a user types:

cubemastercli tpl info tpl-abc extra-arg

The second argument is swallowed without error. This can mask typos or confusion about the API.

The current return type (string) has no channel to signal this. Consider either:

Option A — Return (string, error) and validate count:

func resolveTemplateID(c *cli.Context) (string, error) {
    if id := c.String("template-id"); id != "" {
        return id, nil
    }
    switch n := c.NArg(); {
    case n == 0:
        return "", errors.New("template-id is required")
    case n == 1:
        return c.Args().First(), nil
    default:
        return "", fmt.Errorf("expected at most 1 positional argument, got %d", n)
    }
}

This also moves the "template-id is required" error to a single location (currently duplicated in every consumer).

Option B (minimal) — At minimum, log a warning when c.NArg() > 1 to surface the silent discard.


Finding 2: Pre-existing — template ID not URL-encoded in query string

The template ID is interpolated directly into the URL via fmt.Sprintf on line 497:

url := fmt.Sprintf("http://%s/cube/template?template_id=%s", net.JoinHostPort(host, port), templateID)

A template ID containing &, =, or # would inject additional query parameters. This is a pre-existing pattern (not introduced by this PR, and consistent across similar code for jobID, buildID, and sandboxID). Worth filing a separate tech-debt ticket to move URL construction to net/url.Values.


Minor: Documentation note format inconsistency

In docs/zh/guide/tutorials/template-build-practice.md, the backward-compat note is a bash code comment rather than a standalone paragraph (as used in the other 4 docs). Understandable given this is a Quick Reference section — style is yours to decide, just flagging the inconsistency.


Assessment

The PR is correct, well-tested, and improves the CLI UX meaningfully. The findings above are minor — none block merging.

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.

},
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants