You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`pickTemplateEntry` (in `show.go`) is shared by show, delete, and edit:
102
+
- Calls `ListAll(baseDir)` to get templates across all resource types
103
+
- Shows select prompt with `resource/name` + description
104
+
- Returns `nil` on user cancel (Ctrl+C)
105
+
- Returns error if no templates found
106
+
64
107
## Gotchas & Edge Cases
65
108
66
109
-**Import cycle**: `cmd/template/` cannot import `cmd/vm/` for the Template type (circular dependency). Shared types live in `internal/verda-cli/template/`, re-exported by `cmd/template/types.go` via type aliases and `var` bindings.
67
110
-**`billingTypeSet` / `locationSet` flags**: Needed because `IsSet` in the wizard can't distinguish `"on-demand"` (falsy `IsSpot=false`) from "unset". When a template sets billing type or location, these booleans are set to `true` so the wizard skips those steps.
68
111
-**`NoOptDefVal` on `--from` flag**: Set to `" "` (space) so `--from` without a value is recognized as "flag changed but empty". When the user writes `verda vm create --from gpu-training`, cobra parses `gpu-training` as a positional arg; `RunE` recombines it into `opts.From`.
69
112
-**Startup script "None (skip)" label**: The wizard presents "None (skip)" as a selectable option. Previously, this label text was captured as the script name. Fixed by checking `Value != ""` before storing the name.
70
-
-**`ensurePricingCache`**: The confirm-deploy step calls this to fetch instance type and volume type pricing when the cache is empty. This happens when a template pre-filled earlier steps (skipping the steps that normally populate the cache).
113
+
-**`ensurePricingCache`**: The confirm-deploy step calls this (with parent context, not `context.Background()`) to fetch pricing when the cache is empty from template pre-fill.
71
114
-**Only first storage entry applied**: `applyTemplate()` only reads `tmpl.Storage[0]` because the wizard's convenience fields (`StorageSize`/`StorageType`) support a single additional volume.
72
115
-**AutoDescription**: `Template.AutoDescription()` joins non-empty `InstanceType`, `Image`, and `Location` with `", "` for the list view.
73
116
-**Directory permissions**: Template directories created with `0700`, files with `0644`.
74
117
-**Non-existent directory**: `List()` and `ListAll()` return `nil, nil` (not an error) when the templates directory doesn't exist yet.
118
+
-**Atomic file writes**: `Save` writes to `.tmp` file then renames to prevent corruption on crash.
119
+
-**Entry JSON tags**: `Entry` struct has `json`/`yaml` tags for consistent lowercase keys in `-o json` output.
120
+
-**Edit matchKind**: `edit.go` has its own `matchKind` function (separate from `wizard_cache.go`'s `matchesKind`) for filtering instance types by kind in the edit field editor.
75
121
76
122
## Relationships
77
123
78
124
-**`internal/verda-cli/template/`** -- Shared types (`Template`, `StorageSpec`, `Entry`) and I/O functions (`Save`, `Load`, `LoadFromPath`, `Resolve`, `List`, `ListAll`, `Delete`, `ValidateName`, `ExpandHostnamePattern`). Breaks the import cycle between `cmd/template/` and `cmd/vm/`.
79
-
-**`cmd/vm/wizard.go`** -- `WizardMode` (Deploy vs Template), `RunTemplateWizard()` (runs wizard without hostname/description/confirm steps), `TemplateResult` struct, `ensurePricingCache()`
Save, list, show, and delete reusable resource configuration templates. Templates pre-fill the `vm create` wizard so you don't repeat the same settings.
3
+
Save, list, show, edit, and delete reusable resource configuration templates. Templates pre-fill the `vm create` wizard so you don't repeat the same settings.
4
4
5
5
## Commands
6
6
7
7
| Command | Description | Key Flags |
8
8
|---------|-------------|-----------|
9
9
|`verda template create [name]`| Interactive wizard to save a template |_(none)_|
10
+
|`verda template edit [resource/name]`| Edit specific fields of a template |_(none)_|
10
11
|`verda template list`| List all saved templates |`--type`|
11
-
|`verda template show <resource/name>`| Display template details |`-o json`|
12
-
|`verda template delete <resource/name>`| Delete a template (with confirmation) |_(none)_|
12
+
|`verda template show [resource/name]`| Display template details |`-o json`|
13
+
|`verda template delete [resource/name]`| Delete a template (with confirmation) |_(none)_|
All commands with `[resource/name]` show an interactive picker when the argument is omitted.
18
+
16
19
## Usage Examples
17
20
18
21
### Create
@@ -23,13 +26,22 @@ verda template create
23
26
24
27
# Create a template with a specific name
25
28
verda template create gpu-training
26
-
27
-
# Short alias
28
-
verda tmpl create my-template
29
29
```
30
30
31
31
The create command runs the VM wizard in **template mode** -- the same 10 configuration steps (billing type through startup script) but without hostname, description, or confirm-deploy. The resulting settings are saved to disk.
32
32
33
+
### Edit
34
+
35
+
```bash
36
+
# Interactive picker, then field menu
37
+
verda template edit
38
+
39
+
# Edit a specific template
40
+
verda template edit vm/gpu-training
41
+
```
42
+
43
+
Shows a menu of all template fields with their current values. Pick a field to change, edit it with the appropriate prompt (static choices for simple fields, API-backed selection for instance type/location/image/SSH keys/startup script). Repeat until "Save & exit".
44
+
33
45
### List
34
46
35
47
```bash
@@ -39,38 +51,45 @@ verda template list
39
51
# List only VM templates
40
52
verda template list --type vm
41
53
42
-
#Short alias
43
-
verda tmpl ls
54
+
#JSON output
55
+
verda template list -o json
44
56
```
45
57
46
58
Output shows `NAME` (as `resource/name`) and an auto-generated `DESCRIPTION` built from instance type, image, and location.
47
59
48
60
### Show
49
61
50
62
```bash
63
+
# Interactive picker
64
+
verda template show
65
+
51
66
# Show a VM template
52
67
verda template show vm/gpu-training
53
68
54
69
# Output as JSON
55
70
verda template show vm/gpu-training -o json
56
71
```
57
72
73
+
Displays all template fields including hostname pattern, storage skip, and startup script skip status. Unset fields show `-`, explicitly skipped fields show `None (skipped)`.
74
+
58
75
### Delete
59
76
60
77
```bash
61
-
#Delete a VM template (prompts for confirmation)
62
-
verda template delete vm/gpu-training
78
+
#Interactive picker with confirmation
79
+
verda template delete
63
80
64
-
#Short alias
65
-
verda tmpl rm vm/gpu-training
81
+
#Delete a specific template
82
+
verda template delete vm/gpu-training
66
83
```
67
84
68
85
## Template Storage
69
86
70
87
- Files stored at `~/.verda/templates/<resource>/<name>.yaml`
88
+
- Base directory resolved via `cmdutil.TemplatesBaseDir()`
71
89
- Organized by resource type subdirectory (currently only `vm/`)
72
-
- Names must be lowercase alphanumeric with hyphens(regex: `^[a-z0-9][a-z0-9-]*$`)
90
+
- Names must be lowercase alphanumeric with hyphens, no trailing hyphens (regex: `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`)
73
91
- Auto-reformats invalid names: spaces and underscores become hyphens, uppercase becomes lowercase, other invalid characters are stripped, consecutive hyphens are collapsed
92
+
- Atomic writes: saves to `.tmp` file then renames to prevent corruption
1. Template values pre-fill the wizard's `createOptions`
113
132
2. A summary of template values is printed to stderr
114
-
3. SSH keys and startup scripts are resolved by name to ID via the API; unresolved names produce warnings
133
+
3. SSH keys and startup scripts are resolved by name to ID via the API; unresolved names produce warnings (no longer silently swallowed)
115
134
4. Only unfilled steps are prompted (hostname, description, confirm-deploy are always prompted; other steps only if the template didn't fill them)
116
135
5. The confirm-deploy step fetches pricing for the deployment summary (via `ensurePricingCache` if earlier pricing steps were skipped)
117
136
@@ -123,13 +142,11 @@ The `--from` flag uses `NoOptDefVal` so it can be used in three ways:
123
142
- `--from ./path/to/template.yaml`-- treated as a file path (contains `/` or ends with `.yaml`)
124
143
- `--from`(no value) -- shows an interactive picker of saved VM templates
125
144
126
-
When `--from` consumes no value, the template name may appear as a positional arg (e.g., `verda vm create --from gpu-training`). The `RunE` handler recombines it.
127
-
128
145
## Hostname Pattern
129
146
130
147
The `hostname_pattern` field supports two placeholders:
131
148
132
-
- `{random}`-- replaced with 3 random petname words joined by hyphens (e.g., `cold-cable-smiles`)
149
+
- `{random}`-- replaced with 3 random petname words joined by hyphens (e.g., `cold-cable-smiles`). Each `{random}` occurrence generates different words.
133
150
- `{location}`-- replaced with the lowercased location code (e.g., `fin-01`)
134
151
135
152
Example: `"gpu-{random}-{location}"`expands to something like `"gpu-cold-cable-smiles-fin-01"`.
@@ -150,18 +167,26 @@ These are captured when the user selects "None (skip)" during template creation
- **create.go** -- `template create` command; prompts for resource type and name, runs VM wizard in template mode, saves result
170
+
- **create.go** -- `template create` command; prompts for resource type and name, runs VM wizard in template mode, saves result. Contains `normalizeName`, `vmResultToTemplate`
171
+
- **edit.go** -- `template edit` command; field menu loop with API-backed editors for instance type, location, image, SSH keys, startup script. Contains `matchKind` for filtering
154
172
- **list.go** -- `template list` command; lists entries with auto-description, supports `--type` filter and structured output
0 commit comments